rnd-20080129-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
63 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
64
65 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
66
67 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
68
69 #if USE_DELAYED_GFX_REDRAW
70 #define TEST_DrawLevelField(x, y)                               \
71         GfxRedraw[x][y] |= GFX_REDRAW_TILE
72 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
73         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
74 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
75         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
76 #define TEST_DrawTwinkleOnField(x, y)                           \
77         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
78 #else
79 #define TEST_DrawLevelField(x, y)                               \
80              DrawLevelField(x, y)
81 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
82              DrawLevelFieldCrumbledSand(x, y)
83 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
84              DrawLevelFieldCrumbledSandNeighbours(x, y)
85 #define TEST_DrawTwinkleOnField(x, y)                           \
86              DrawTwinkleOnField(x, y)
87 #endif
88
89
90 /* for DigField() */
91 #define DF_NO_PUSH              0
92 #define DF_DIG                  1
93 #define DF_SNAP                 2
94
95 /* for MovePlayer() */
96 #define MP_NO_ACTION            0
97 #define MP_MOVING               1
98 #define MP_ACTION               2
99 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
100
101 /* for ScrollPlayer() */
102 #define SCROLL_INIT             0
103 #define SCROLL_GO_ON            1
104
105 /* for Bang()/Explode() */
106 #define EX_PHASE_START          0
107 #define EX_TYPE_NONE            0
108 #define EX_TYPE_NORMAL          (1 << 0)
109 #define EX_TYPE_CENTER          (1 << 1)
110 #define EX_TYPE_BORDER          (1 << 2)
111 #define EX_TYPE_CROSS           (1 << 3)
112 #define EX_TYPE_DYNA            (1 << 4)
113 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
114
115 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
116 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
117 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
118 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
119
120 /* special positions in the game control window (relative to control window) */
121 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
122 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
123 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
124 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
125 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
126 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
127 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
128 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
129 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
130 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
131 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
132 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
133 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
134 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
135 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
136 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
137
138 /* special positions in the game control window (relative to main window) */
139 #define DX_LEVEL1               (DX + XX_LEVEL1)
140 #define DX_LEVEL2               (DX + XX_LEVEL2)
141 #define DX_LEVEL                (DX + XX_LEVEL)
142 #define DY_LEVEL                (DY + YY_LEVEL)
143 #define DX_EMERALDS             (DX + XX_EMERALDS)
144 #define DY_EMERALDS             (DY + YY_EMERALDS)
145 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
146 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
147 #define DX_KEYS                 (DX + XX_KEYS)
148 #define DY_KEYS                 (DY + YY_KEYS)
149 #define DX_SCORE                (DX + XX_SCORE)
150 #define DY_SCORE                (DY + YY_SCORE)
151 #define DX_TIME1                (DX + XX_TIME1)
152 #define DX_TIME2                (DX + XX_TIME2)
153 #define DX_TIME                 (DX + XX_TIME)
154 #define DY_TIME                 (DY + YY_TIME)
155
156 #if 1
157 /* game panel display and control definitions */
158
159 #define GAME_PANEL_LEVEL_NUMBER                 0
160 #define GAME_PANEL_GEMS                         1
161 #define GAME_PANEL_INVENTORY_COUNT              2
162 #define GAME_PANEL_INVENTORY_FIRST_1            3
163 #define GAME_PANEL_INVENTORY_FIRST_2            4
164 #define GAME_PANEL_INVENTORY_FIRST_3            5
165 #define GAME_PANEL_INVENTORY_FIRST_4            6
166 #define GAME_PANEL_INVENTORY_FIRST_5            7
167 #define GAME_PANEL_INVENTORY_FIRST_6            8
168 #define GAME_PANEL_INVENTORY_FIRST_7            9
169 #define GAME_PANEL_INVENTORY_FIRST_8            10
170 #define GAME_PANEL_INVENTORY_LAST_1             11
171 #define GAME_PANEL_INVENTORY_LAST_2             12
172 #define GAME_PANEL_INVENTORY_LAST_3             13
173 #define GAME_PANEL_INVENTORY_LAST_4             14
174 #define GAME_PANEL_INVENTORY_LAST_5             15
175 #define GAME_PANEL_INVENTORY_LAST_6             16
176 #define GAME_PANEL_INVENTORY_LAST_7             17
177 #define GAME_PANEL_INVENTORY_LAST_8             18
178 #define GAME_PANEL_KEY_1                        19
179 #define GAME_PANEL_KEY_2                        20
180 #define GAME_PANEL_KEY_3                        21
181 #define GAME_PANEL_KEY_4                        22
182 #define GAME_PANEL_KEY_5                        23
183 #define GAME_PANEL_KEY_6                        24
184 #define GAME_PANEL_KEY_7                        25
185 #define GAME_PANEL_KEY_8                        26
186 #define GAME_PANEL_KEY_WHITE                    27
187 #define GAME_PANEL_KEY_WHITE_COUNT              28
188 #define GAME_PANEL_SCORE                        29
189 #define GAME_PANEL_HIGHSCORE                    30
190 #define GAME_PANEL_TIME                         31
191 #define GAME_PANEL_TIME_HH                      32
192 #define GAME_PANEL_TIME_MM                      33
193 #define GAME_PANEL_TIME_SS                      34
194 #define GAME_PANEL_SHIELD_NORMAL                35
195 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
196 #define GAME_PANEL_SHIELD_DEADLY                37
197 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
198 #define GAME_PANEL_EXIT                         39
199 #define GAME_PANEL_EMC_MAGIC_BALL               40
200 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
201 #define GAME_PANEL_LIGHT_SWITCH                 42
202 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
203 #define GAME_PANEL_TIMEGATE_SWITCH              44
204 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
205 #define GAME_PANEL_SWITCHGATE_SWITCH            46
206 #define GAME_PANEL_EMC_LENSES                   47
207 #define GAME_PANEL_EMC_LENSES_TIME              48
208 #define GAME_PANEL_EMC_MAGNIFIER                49
209 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
210 #define GAME_PANEL_BALLOON_SWITCH               51
211 #define GAME_PANEL_DYNABOMB_NUMBER              52
212 #define GAME_PANEL_DYNABOMB_SIZE                53
213 #define GAME_PANEL_DYNABOMB_POWER               54
214 #define GAME_PANEL_PENGUINS                     55
215 #define GAME_PANEL_SOKOBAN_OBJECTS              56
216 #define GAME_PANEL_SOKOBAN_FIELDS               57
217 #define GAME_PANEL_ROBOT_WHEEL                  58
218 #define GAME_PANEL_CONVEYOR_BELT_1              59
219 #define GAME_PANEL_CONVEYOR_BELT_2              60
220 #define GAME_PANEL_CONVEYOR_BELT_3              61
221 #define GAME_PANEL_CONVEYOR_BELT_4              62
222 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
223 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
224 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
225 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
226 #define GAME_PANEL_MAGIC_WALL                   67
227 #define GAME_PANEL_MAGIC_WALL_TIME              68
228 #define GAME_PANEL_GRAVITY_STATE                69
229 #define GAME_PANEL_GRAPHIC_1                    70
230 #define GAME_PANEL_GRAPHIC_2                    71
231 #define GAME_PANEL_GRAPHIC_3                    72
232 #define GAME_PANEL_GRAPHIC_4                    73
233 #define GAME_PANEL_GRAPHIC_5                    74
234 #define GAME_PANEL_GRAPHIC_6                    75
235 #define GAME_PANEL_GRAPHIC_7                    76
236 #define GAME_PANEL_GRAPHIC_8                    77
237 #define GAME_PANEL_ELEMENT_1                    78
238 #define GAME_PANEL_ELEMENT_2                    79
239 #define GAME_PANEL_ELEMENT_3                    80
240 #define GAME_PANEL_ELEMENT_4                    81
241 #define GAME_PANEL_ELEMENT_5                    82
242 #define GAME_PANEL_ELEMENT_6                    83
243 #define GAME_PANEL_ELEMENT_7                    84
244 #define GAME_PANEL_ELEMENT_8                    85
245 #define GAME_PANEL_ELEMENT_COUNT_1              86
246 #define GAME_PANEL_ELEMENT_COUNT_2              87
247 #define GAME_PANEL_ELEMENT_COUNT_3              88
248 #define GAME_PANEL_ELEMENT_COUNT_4              89
249 #define GAME_PANEL_ELEMENT_COUNT_5              90
250 #define GAME_PANEL_ELEMENT_COUNT_6              91
251 #define GAME_PANEL_ELEMENT_COUNT_7              92
252 #define GAME_PANEL_ELEMENT_COUNT_8              93
253 #define GAME_PANEL_CE_SCORE_1                   94
254 #define GAME_PANEL_CE_SCORE_2                   95
255 #define GAME_PANEL_CE_SCORE_3                   96
256 #define GAME_PANEL_CE_SCORE_4                   97
257 #define GAME_PANEL_CE_SCORE_5                   98
258 #define GAME_PANEL_CE_SCORE_6                   99
259 #define GAME_PANEL_CE_SCORE_7                   100
260 #define GAME_PANEL_CE_SCORE_8                   101
261 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
262 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
263 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
264 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
265 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
266 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
267 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
268 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
269 #define GAME_PANEL_PLAYER_NAME                  110
270 #define GAME_PANEL_LEVEL_NAME                   111
271 #define GAME_PANEL_LEVEL_AUTHOR                 112
272
273 #define NUM_GAME_PANEL_CONTROLS                 113
274
275 struct GamePanelOrderInfo
276 {
277   int nr;
278   int sort_priority;
279 };
280
281 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
282
283 struct GamePanelControlInfo
284 {
285   int nr;
286
287   struct TextPosInfo *pos;
288   int type;
289
290   int value, last_value;
291   int frame, last_frame;
292   int gfx_frame;
293   int gfx_random;
294 };
295
296 static struct GamePanelControlInfo game_panel_controls[] =
297 {
298   {
299     GAME_PANEL_LEVEL_NUMBER,
300     &game.panel.level_number,
301     TYPE_INTEGER,
302   },
303   {
304     GAME_PANEL_GEMS,
305     &game.panel.gems,
306     TYPE_INTEGER,
307   },
308   {
309     GAME_PANEL_INVENTORY_COUNT,
310     &game.panel.inventory_count,
311     TYPE_INTEGER,
312   },
313   {
314     GAME_PANEL_INVENTORY_FIRST_1,
315     &game.panel.inventory_first[0],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_FIRST_2,
320     &game.panel.inventory_first[1],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_INVENTORY_FIRST_3,
325     &game.panel.inventory_first[2],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_INVENTORY_FIRST_4,
330     &game.panel.inventory_first[3],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_INVENTORY_FIRST_5,
335     &game.panel.inventory_first[4],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_INVENTORY_FIRST_6,
340     &game.panel.inventory_first[5],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_INVENTORY_FIRST_7,
345     &game.panel.inventory_first[6],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_INVENTORY_FIRST_8,
350     &game.panel.inventory_first[7],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_INVENTORY_LAST_1,
355     &game.panel.inventory_last[0],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_INVENTORY_LAST_2,
360     &game.panel.inventory_last[1],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_INVENTORY_LAST_3,
365     &game.panel.inventory_last[2],
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_INVENTORY_LAST_4,
370     &game.panel.inventory_last[3],
371     TYPE_ELEMENT,
372   },
373   {
374     GAME_PANEL_INVENTORY_LAST_5,
375     &game.panel.inventory_last[4],
376     TYPE_ELEMENT,
377   },
378   {
379     GAME_PANEL_INVENTORY_LAST_6,
380     &game.panel.inventory_last[5],
381     TYPE_ELEMENT,
382   },
383   {
384     GAME_PANEL_INVENTORY_LAST_7,
385     &game.panel.inventory_last[6],
386     TYPE_ELEMENT,
387   },
388   {
389     GAME_PANEL_INVENTORY_LAST_8,
390     &game.panel.inventory_last[7],
391     TYPE_ELEMENT,
392   },
393   {
394     GAME_PANEL_KEY_1,
395     &game.panel.key[0],
396     TYPE_ELEMENT,
397   },
398   {
399     GAME_PANEL_KEY_2,
400     &game.panel.key[1],
401     TYPE_ELEMENT,
402   },
403   {
404     GAME_PANEL_KEY_3,
405     &game.panel.key[2],
406     TYPE_ELEMENT,
407   },
408   {
409     GAME_PANEL_KEY_4,
410     &game.panel.key[3],
411     TYPE_ELEMENT,
412   },
413   {
414     GAME_PANEL_KEY_5,
415     &game.panel.key[4],
416     TYPE_ELEMENT,
417   },
418   {
419     GAME_PANEL_KEY_6,
420     &game.panel.key[5],
421     TYPE_ELEMENT,
422   },
423   {
424     GAME_PANEL_KEY_7,
425     &game.panel.key[6],
426     TYPE_ELEMENT,
427   },
428   {
429     GAME_PANEL_KEY_8,
430     &game.panel.key[7],
431     TYPE_ELEMENT,
432   },
433   {
434     GAME_PANEL_KEY_WHITE,
435     &game.panel.key_white,
436     TYPE_ELEMENT,
437   },
438   {
439     GAME_PANEL_KEY_WHITE_COUNT,
440     &game.panel.key_white_count,
441     TYPE_INTEGER,
442   },
443   {
444     GAME_PANEL_SCORE,
445     &game.panel.score,
446     TYPE_INTEGER,
447   },
448   {
449     GAME_PANEL_HIGHSCORE,
450     &game.panel.highscore,
451     TYPE_INTEGER,
452   },
453   {
454     GAME_PANEL_TIME,
455     &game.panel.time,
456     TYPE_INTEGER,
457   },
458   {
459     GAME_PANEL_TIME_HH,
460     &game.panel.time_hh,
461     TYPE_INTEGER,
462   },
463   {
464     GAME_PANEL_TIME_MM,
465     &game.panel.time_mm,
466     TYPE_INTEGER,
467   },
468   {
469     GAME_PANEL_TIME_SS,
470     &game.panel.time_ss,
471     TYPE_INTEGER,
472   },
473   {
474     GAME_PANEL_SHIELD_NORMAL,
475     &game.panel.shield_normal,
476     TYPE_ELEMENT,
477   },
478   {
479     GAME_PANEL_SHIELD_NORMAL_TIME,
480     &game.panel.shield_normal_time,
481     TYPE_INTEGER,
482   },
483   {
484     GAME_PANEL_SHIELD_DEADLY,
485     &game.panel.shield_deadly,
486     TYPE_ELEMENT,
487   },
488   {
489     GAME_PANEL_SHIELD_DEADLY_TIME,
490     &game.panel.shield_deadly_time,
491     TYPE_INTEGER,
492   },
493   {
494     GAME_PANEL_EXIT,
495     &game.panel.exit,
496     TYPE_ELEMENT,
497   },
498   {
499     GAME_PANEL_EMC_MAGIC_BALL,
500     &game.panel.emc_magic_ball,
501     TYPE_ELEMENT,
502   },
503   {
504     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
505     &game.panel.emc_magic_ball_switch,
506     TYPE_ELEMENT,
507   },
508   {
509     GAME_PANEL_LIGHT_SWITCH,
510     &game.panel.light_switch,
511     TYPE_ELEMENT,
512   },
513   {
514     GAME_PANEL_LIGHT_SWITCH_TIME,
515     &game.panel.light_switch_time,
516     TYPE_INTEGER,
517   },
518   {
519     GAME_PANEL_TIMEGATE_SWITCH,
520     &game.panel.timegate_switch,
521     TYPE_ELEMENT,
522   },
523   {
524     GAME_PANEL_TIMEGATE_SWITCH_TIME,
525     &game.panel.timegate_switch_time,
526     TYPE_INTEGER,
527   },
528   {
529     GAME_PANEL_SWITCHGATE_SWITCH,
530     &game.panel.switchgate_switch,
531     TYPE_ELEMENT,
532   },
533   {
534     GAME_PANEL_EMC_LENSES,
535     &game.panel.emc_lenses,
536     TYPE_ELEMENT,
537   },
538   {
539     GAME_PANEL_EMC_LENSES_TIME,
540     &game.panel.emc_lenses_time,
541     TYPE_INTEGER,
542   },
543   {
544     GAME_PANEL_EMC_MAGNIFIER,
545     &game.panel.emc_magnifier,
546     TYPE_ELEMENT,
547   },
548   {
549     GAME_PANEL_EMC_MAGNIFIER_TIME,
550     &game.panel.emc_magnifier_time,
551     TYPE_INTEGER,
552   },
553   {
554     GAME_PANEL_BALLOON_SWITCH,
555     &game.panel.balloon_switch,
556     TYPE_ELEMENT,
557   },
558   {
559     GAME_PANEL_DYNABOMB_NUMBER,
560     &game.panel.dynabomb_number,
561     TYPE_INTEGER,
562   },
563   {
564     GAME_PANEL_DYNABOMB_SIZE,
565     &game.panel.dynabomb_size,
566     TYPE_INTEGER,
567   },
568   {
569     GAME_PANEL_DYNABOMB_POWER,
570     &game.panel.dynabomb_power,
571     TYPE_ELEMENT,
572   },
573   {
574     GAME_PANEL_PENGUINS,
575     &game.panel.penguins,
576     TYPE_INTEGER,
577   },
578   {
579     GAME_PANEL_SOKOBAN_OBJECTS,
580     &game.panel.sokoban_objects,
581     TYPE_INTEGER,
582   },
583   {
584     GAME_PANEL_SOKOBAN_FIELDS,
585     &game.panel.sokoban_fields,
586     TYPE_INTEGER,
587   },
588   {
589     GAME_PANEL_ROBOT_WHEEL,
590     &game.panel.robot_wheel,
591     TYPE_ELEMENT,
592   },
593   {
594     GAME_PANEL_CONVEYOR_BELT_1,
595     &game.panel.conveyor_belt[0],
596     TYPE_ELEMENT,
597   },
598   {
599     GAME_PANEL_CONVEYOR_BELT_2,
600     &game.panel.conveyor_belt[1],
601     TYPE_ELEMENT,
602   },
603   {
604     GAME_PANEL_CONVEYOR_BELT_3,
605     &game.panel.conveyor_belt[2],
606     TYPE_ELEMENT,
607   },
608   {
609     GAME_PANEL_CONVEYOR_BELT_4,
610     &game.panel.conveyor_belt[3],
611     TYPE_ELEMENT,
612   },
613   {
614     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
615     &game.panel.conveyor_belt_switch[0],
616     TYPE_ELEMENT,
617   },
618   {
619     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
620     &game.panel.conveyor_belt_switch[1],
621     TYPE_ELEMENT,
622   },
623   {
624     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
625     &game.panel.conveyor_belt_switch[2],
626     TYPE_ELEMENT,
627   },
628   {
629     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
630     &game.panel.conveyor_belt_switch[3],
631     TYPE_ELEMENT,
632   },
633   {
634     GAME_PANEL_MAGIC_WALL,
635     &game.panel.magic_wall,
636     TYPE_ELEMENT,
637   },
638   {
639     GAME_PANEL_MAGIC_WALL_TIME,
640     &game.panel.magic_wall_time,
641     TYPE_INTEGER,
642   },
643   {
644     GAME_PANEL_GRAVITY_STATE,
645     &game.panel.gravity_state,
646     TYPE_STRING,
647   },
648   {
649     GAME_PANEL_GRAPHIC_1,
650     &game.panel.graphic[0],
651     TYPE_ELEMENT,
652   },
653   {
654     GAME_PANEL_GRAPHIC_2,
655     &game.panel.graphic[1],
656     TYPE_ELEMENT,
657   },
658   {
659     GAME_PANEL_GRAPHIC_3,
660     &game.panel.graphic[2],
661     TYPE_ELEMENT,
662   },
663   {
664     GAME_PANEL_GRAPHIC_4,
665     &game.panel.graphic[3],
666     TYPE_ELEMENT,
667   },
668   {
669     GAME_PANEL_GRAPHIC_5,
670     &game.panel.graphic[4],
671     TYPE_ELEMENT,
672   },
673   {
674     GAME_PANEL_GRAPHIC_6,
675     &game.panel.graphic[5],
676     TYPE_ELEMENT,
677   },
678   {
679     GAME_PANEL_GRAPHIC_7,
680     &game.panel.graphic[6],
681     TYPE_ELEMENT,
682   },
683   {
684     GAME_PANEL_GRAPHIC_8,
685     &game.panel.graphic[7],
686     TYPE_ELEMENT,
687   },
688   {
689     GAME_PANEL_ELEMENT_1,
690     &game.panel.element[0],
691     TYPE_ELEMENT,
692   },
693   {
694     GAME_PANEL_ELEMENT_2,
695     &game.panel.element[1],
696     TYPE_ELEMENT,
697   },
698   {
699     GAME_PANEL_ELEMENT_3,
700     &game.panel.element[2],
701     TYPE_ELEMENT,
702   },
703   {
704     GAME_PANEL_ELEMENT_4,
705     &game.panel.element[3],
706     TYPE_ELEMENT,
707   },
708   {
709     GAME_PANEL_ELEMENT_5,
710     &game.panel.element[4],
711     TYPE_ELEMENT,
712   },
713   {
714     GAME_PANEL_ELEMENT_6,
715     &game.panel.element[5],
716     TYPE_ELEMENT,
717   },
718   {
719     GAME_PANEL_ELEMENT_7,
720     &game.panel.element[6],
721     TYPE_ELEMENT,
722   },
723   {
724     GAME_PANEL_ELEMENT_8,
725     &game.panel.element[7],
726     TYPE_ELEMENT,
727   },
728   {
729     GAME_PANEL_ELEMENT_COUNT_1,
730     &game.panel.element_count[0],
731     TYPE_INTEGER,
732   },
733   {
734     GAME_PANEL_ELEMENT_COUNT_2,
735     &game.panel.element_count[1],
736     TYPE_INTEGER,
737   },
738   {
739     GAME_PANEL_ELEMENT_COUNT_3,
740     &game.panel.element_count[2],
741     TYPE_INTEGER,
742   },
743   {
744     GAME_PANEL_ELEMENT_COUNT_4,
745     &game.panel.element_count[3],
746     TYPE_INTEGER,
747   },
748   {
749     GAME_PANEL_ELEMENT_COUNT_5,
750     &game.panel.element_count[4],
751     TYPE_INTEGER,
752   },
753   {
754     GAME_PANEL_ELEMENT_COUNT_6,
755     &game.panel.element_count[5],
756     TYPE_INTEGER,
757   },
758   {
759     GAME_PANEL_ELEMENT_COUNT_7,
760     &game.panel.element_count[6],
761     TYPE_INTEGER,
762   },
763   {
764     GAME_PANEL_ELEMENT_COUNT_8,
765     &game.panel.element_count[7],
766     TYPE_INTEGER,
767   },
768   {
769     GAME_PANEL_CE_SCORE_1,
770     &game.panel.ce_score[0],
771     TYPE_INTEGER,
772   },
773   {
774     GAME_PANEL_CE_SCORE_2,
775     &game.panel.ce_score[1],
776     TYPE_INTEGER,
777   },
778   {
779     GAME_PANEL_CE_SCORE_3,
780     &game.panel.ce_score[2],
781     TYPE_INTEGER,
782   },
783   {
784     GAME_PANEL_CE_SCORE_4,
785     &game.panel.ce_score[3],
786     TYPE_INTEGER,
787   },
788   {
789     GAME_PANEL_CE_SCORE_5,
790     &game.panel.ce_score[4],
791     TYPE_INTEGER,
792   },
793   {
794     GAME_PANEL_CE_SCORE_6,
795     &game.panel.ce_score[5],
796     TYPE_INTEGER,
797   },
798   {
799     GAME_PANEL_CE_SCORE_7,
800     &game.panel.ce_score[6],
801     TYPE_INTEGER,
802   },
803   {
804     GAME_PANEL_CE_SCORE_8,
805     &game.panel.ce_score[7],
806     TYPE_INTEGER,
807   },
808   {
809     GAME_PANEL_CE_SCORE_1_ELEMENT,
810     &game.panel.ce_score_element[0],
811     TYPE_ELEMENT,
812   },
813   {
814     GAME_PANEL_CE_SCORE_2_ELEMENT,
815     &game.panel.ce_score_element[1],
816     TYPE_ELEMENT,
817   },
818   {
819     GAME_PANEL_CE_SCORE_3_ELEMENT,
820     &game.panel.ce_score_element[2],
821     TYPE_ELEMENT,
822   },
823   {
824     GAME_PANEL_CE_SCORE_4_ELEMENT,
825     &game.panel.ce_score_element[3],
826     TYPE_ELEMENT,
827   },
828   {
829     GAME_PANEL_CE_SCORE_5_ELEMENT,
830     &game.panel.ce_score_element[4],
831     TYPE_ELEMENT,
832   },
833   {
834     GAME_PANEL_CE_SCORE_6_ELEMENT,
835     &game.panel.ce_score_element[5],
836     TYPE_ELEMENT,
837   },
838   {
839     GAME_PANEL_CE_SCORE_7_ELEMENT,
840     &game.panel.ce_score_element[6],
841     TYPE_ELEMENT,
842   },
843   {
844     GAME_PANEL_CE_SCORE_8_ELEMENT,
845     &game.panel.ce_score_element[7],
846     TYPE_ELEMENT,
847   },
848   {
849     GAME_PANEL_PLAYER_NAME,
850     &game.panel.player_name,
851     TYPE_STRING,
852   },
853   {
854     GAME_PANEL_LEVEL_NAME,
855     &game.panel.level_name,
856     TYPE_STRING,
857   },
858   {
859     GAME_PANEL_LEVEL_AUTHOR,
860     &game.panel.level_author,
861     TYPE_STRING,
862   },
863
864   {
865     -1,
866     NULL,
867     -1,
868   }
869 };
870 #endif
871
872
873 /* values for delayed check of falling and moving elements and for collision */
874 #define CHECK_DELAY_MOVING      3
875 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
876 #define CHECK_DELAY_COLLISION   2
877 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
878
879 /* values for initial player move delay (initial delay counter value) */
880 #define INITIAL_MOVE_DELAY_OFF  -1
881 #define INITIAL_MOVE_DELAY_ON   0
882
883 /* values for player movement speed (which is in fact a delay value) */
884 #define MOVE_DELAY_MIN_SPEED    32
885 #define MOVE_DELAY_NORMAL_SPEED 8
886 #define MOVE_DELAY_HIGH_SPEED   4
887 #define MOVE_DELAY_MAX_SPEED    1
888
889 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
890 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
891
892 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
893 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
894
895 /* values for other actions */
896 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
897 #define MOVE_STEPSIZE_MIN       (1)
898 #define MOVE_STEPSIZE_MAX       (TILEX)
899
900 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
901 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
902
903 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
904
905 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
906                                  RND(element_info[e].push_delay_random))
907 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
908                                  RND(element_info[e].drop_delay_random))
909 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
910                                  RND(element_info[e].move_delay_random))
911 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
912                                     (element_info[e].move_delay_random))
913 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
914                                  RND(element_info[e].ce_value_random_initial))
915 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
916 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
917                                  RND((c)->delay_random * (c)->delay_frames))
918 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
919                                  RND((c)->delay_random))
920
921
922 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
923          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
924
925 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
926         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
927          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
928          (be) + (e) - EL_SELF)
929
930 #define GET_PLAYER_FROM_BITS(p)                                         \
931         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
932
933 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
934         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
935          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
936          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
937          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
938          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
939          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
940          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
941          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
942          (e))
943
944 #define CAN_GROW_INTO(e)                                                \
945         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
946
947 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
948                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
949                                         (condition)))
950
951 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
952                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
953                                         (CAN_MOVE_INTO_ACID(e) &&       \
954                                          Feld[x][y] == EL_ACID) ||      \
955                                         (condition)))
956
957 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
958                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
959                                         (CAN_MOVE_INTO_ACID(e) &&       \
960                                          Feld[x][y] == EL_ACID) ||      \
961                                         (condition)))
962
963 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
964                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
965                                         (condition) ||                  \
966                                         (CAN_MOVE_INTO_ACID(e) &&       \
967                                          Feld[x][y] == EL_ACID) ||      \
968                                         (DONT_COLLIDE_WITH(e) &&        \
969                                          IS_PLAYER(x, y) &&             \
970                                          !PLAYER_ENEMY_PROTECTED(x, y))))
971
972 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
973         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
974
975 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
977
978 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
980
981 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
982         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
983                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
984
985 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
989         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
990
991 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
992         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
993
994 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
995         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
996
997 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
998         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
999
1000 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1002                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1003                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1004                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1005                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1006 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1007         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1008
1009 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1011
1012 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1013         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1014
1015 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1016         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1017                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1018
1019 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1020
1021 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1022                 (!IS_PLAYER(x, y) &&                                    \
1023                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1024
1025 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1026         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1027
1028 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1029 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1030
1031 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1032 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1033 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1034 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1035
1036 /* game button identifiers */
1037 #define GAME_CTRL_ID_STOP               0
1038 #define GAME_CTRL_ID_PAUSE              1
1039 #define GAME_CTRL_ID_PLAY               2
1040 #define SOUND_CTRL_ID_MUSIC             3
1041 #define SOUND_CTRL_ID_LOOPS             4
1042 #define SOUND_CTRL_ID_SIMPLE            5
1043
1044 #define NUM_GAME_BUTTONS                6
1045
1046
1047 /* forward declaration for internal use */
1048
1049 static void CreateField(int, int, int);
1050
1051 static void ResetGfxAnimation(int, int);
1052
1053 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1054 static void AdvanceFrameAndPlayerCounters(int);
1055
1056 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1057 static boolean MovePlayer(struct PlayerInfo *, int, int);
1058 static void ScrollPlayer(struct PlayerInfo *, int);
1059 static void ScrollScreen(struct PlayerInfo *, int);
1060
1061 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1062 static boolean DigFieldByCE(int, int, int);
1063 static boolean SnapField(struct PlayerInfo *, int, int);
1064 static boolean DropElement(struct PlayerInfo *);
1065
1066 static void InitBeltMovement(void);
1067 static void CloseAllOpenTimegates(void);
1068 static void CheckGravityMovement(struct PlayerInfo *);
1069 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1070 static void KillPlayerUnlessEnemyProtected(int, int);
1071 static void KillPlayerUnlessExplosionProtected(int, int);
1072
1073 static void TestIfPlayerTouchesCustomElement(int, int);
1074 static void TestIfElementTouchesCustomElement(int, int);
1075 static void TestIfElementHitsCustomElement(int, int, int);
1076 #if 0
1077 static void TestIfElementSmashesCustomElement(int, int, int);
1078 #endif
1079
1080 static void HandleElementChange(int, int, int);
1081 static void ExecuteCustomElementAction(int, int, int, int);
1082 static boolean ChangeElement(int, int, int, int);
1083
1084 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1085 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1086         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1087 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1088         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1089 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1090         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1091 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1092         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1093
1094 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1095 #define CheckElementChange(x, y, e, te, ev)                             \
1096         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1097 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1098         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1099 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1100         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1101
1102 static void PlayLevelSound(int, int, int);
1103 static void PlayLevelSoundNearest(int, int, int);
1104 static void PlayLevelSoundAction(int, int, int);
1105 static void PlayLevelSoundElementAction(int, int, int, int);
1106 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1107 static void PlayLevelSoundActionIfLoop(int, int, int);
1108 static void StopLevelSoundActionIfLoop(int, int, int);
1109 static void PlayLevelMusic();
1110
1111 static void MapGameButtons();
1112 static void HandleGameButtons(struct GadgetInfo *);
1113
1114 int AmoebeNachbarNr(int, int);
1115 void AmoebeUmwandeln(int, int);
1116 void ContinueMoving(int, int);
1117 void Bang(int, int);
1118 void InitMovDir(int, int);
1119 void InitAmoebaNr(int, int);
1120 int NewHiScore(void);
1121
1122 void TestIfGoodThingHitsBadThing(int, int, int);
1123 void TestIfBadThingHitsGoodThing(int, int, int);
1124 void TestIfPlayerTouchesBadThing(int, int);
1125 void TestIfPlayerRunsIntoBadThing(int, int, int);
1126 void TestIfBadThingTouchesPlayer(int, int);
1127 void TestIfBadThingRunsIntoPlayer(int, int, int);
1128 void TestIfFriendTouchesBadThing(int, int);
1129 void TestIfBadThingTouchesFriend(int, int);
1130 void TestIfBadThingTouchesOtherBadThing(int, int);
1131 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1132
1133 void KillPlayer(struct PlayerInfo *);
1134 void BuryPlayer(struct PlayerInfo *);
1135 void RemovePlayer(struct PlayerInfo *);
1136
1137 static int getInvisibleActiveFromInvisibleElement(int);
1138 static int getInvisibleFromInvisibleActiveElement(int);
1139
1140 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1141
1142 /* for detection of endless loops, caused by custom element programming */
1143 /* (using maximal playfield width x 10 is just a rough approximation) */
1144 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1145
1146 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1147 {                                                                       \
1148   if (recursion_loop_detected)                                          \
1149     return (rc);                                                        \
1150                                                                         \
1151   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1152   {                                                                     \
1153     recursion_loop_detected = TRUE;                                     \
1154     recursion_loop_element = (e);                                       \
1155   }                                                                     \
1156                                                                         \
1157   recursion_loop_depth++;                                               \
1158 }
1159
1160 #define RECURSION_LOOP_DETECTION_END()                                  \
1161 {                                                                       \
1162   recursion_loop_depth--;                                               \
1163 }
1164
1165 static int recursion_loop_depth;
1166 static boolean recursion_loop_detected;
1167 static boolean recursion_loop_element;
1168
1169
1170 /* ------------------------------------------------------------------------- */
1171 /* definition of elements that automatically change to other elements after  */
1172 /* a specified time, eventually calling a function when changing             */
1173 /* ------------------------------------------------------------------------- */
1174
1175 /* forward declaration for changer functions */
1176 static void InitBuggyBase(int, int);
1177 static void WarnBuggyBase(int, int);
1178
1179 static void InitTrap(int, int);
1180 static void ActivateTrap(int, int);
1181 static void ChangeActiveTrap(int, int);
1182
1183 static void InitRobotWheel(int, int);
1184 static void RunRobotWheel(int, int);
1185 static void StopRobotWheel(int, int);
1186
1187 static void InitTimegateWheel(int, int);
1188 static void RunTimegateWheel(int, int);
1189
1190 static void InitMagicBallDelay(int, int);
1191 static void ActivateMagicBall(int, int);
1192
1193 struct ChangingElementInfo
1194 {
1195   int element;
1196   int target_element;
1197   int change_delay;
1198   void (*pre_change_function)(int x, int y);
1199   void (*change_function)(int x, int y);
1200   void (*post_change_function)(int x, int y);
1201 };
1202
1203 static struct ChangingElementInfo change_delay_list[] =
1204 {
1205   {
1206     EL_NUT_BREAKING,
1207     EL_EMERALD,
1208     6,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_PEARL_BREAKING,
1215     EL_EMPTY,
1216     8,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_EXIT_OPENING,
1223     EL_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_EXIT_CLOSING,
1231     EL_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_STEEL_EXIT_OPENING,
1239     EL_STEEL_EXIT_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_STEEL_EXIT_CLOSING,
1247     EL_STEEL_EXIT_CLOSED,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_EM_EXIT_OPENING,
1255     EL_EM_EXIT_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_EM_EXIT_CLOSING,
1263 #if 1
1264     EL_EMPTY,
1265 #else
1266     EL_EM_EXIT_CLOSED,
1267 #endif
1268     29,
1269     NULL,
1270     NULL,
1271     NULL
1272   },
1273   {
1274     EL_EM_STEEL_EXIT_OPENING,
1275     EL_EM_STEEL_EXIT_OPEN,
1276     29,
1277     NULL,
1278     NULL,
1279     NULL
1280   },
1281   {
1282     EL_EM_STEEL_EXIT_CLOSING,
1283 #if 1
1284     EL_STEELWALL,
1285 #else
1286     EL_EM_STEEL_EXIT_CLOSED,
1287 #endif
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SP_EXIT_OPENING,
1295     EL_SP_EXIT_OPEN,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_SP_EXIT_CLOSING,
1303     EL_SP_EXIT_CLOSED,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_SWITCHGATE_OPENING,
1311     EL_SWITCHGATE_OPEN,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317   {
1318     EL_SWITCHGATE_CLOSING,
1319     EL_SWITCHGATE_CLOSED,
1320     29,
1321     NULL,
1322     NULL,
1323     NULL
1324   },
1325   {
1326     EL_TIMEGATE_OPENING,
1327     EL_TIMEGATE_OPEN,
1328     29,
1329     NULL,
1330     NULL,
1331     NULL
1332   },
1333   {
1334     EL_TIMEGATE_CLOSING,
1335     EL_TIMEGATE_CLOSED,
1336     29,
1337     NULL,
1338     NULL,
1339     NULL
1340   },
1341
1342   {
1343     EL_ACID_SPLASH_LEFT,
1344     EL_EMPTY,
1345     8,
1346     NULL,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_ACID_SPLASH_RIGHT,
1352     EL_EMPTY,
1353     8,
1354     NULL,
1355     NULL,
1356     NULL
1357   },
1358   {
1359     EL_SP_BUGGY_BASE,
1360     EL_SP_BUGGY_BASE_ACTIVATING,
1361     0,
1362     InitBuggyBase,
1363     NULL,
1364     NULL
1365   },
1366   {
1367     EL_SP_BUGGY_BASE_ACTIVATING,
1368     EL_SP_BUGGY_BASE_ACTIVE,
1369     0,
1370     InitBuggyBase,
1371     NULL,
1372     NULL
1373   },
1374   {
1375     EL_SP_BUGGY_BASE_ACTIVE,
1376     EL_SP_BUGGY_BASE,
1377     0,
1378     InitBuggyBase,
1379     WarnBuggyBase,
1380     NULL
1381   },
1382   {
1383     EL_TRAP,
1384     EL_TRAP_ACTIVE,
1385     0,
1386     InitTrap,
1387     NULL,
1388     ActivateTrap
1389   },
1390   {
1391     EL_TRAP_ACTIVE,
1392     EL_TRAP,
1393     31,
1394     NULL,
1395     ChangeActiveTrap,
1396     NULL
1397   },
1398   {
1399     EL_ROBOT_WHEEL_ACTIVE,
1400     EL_ROBOT_WHEEL,
1401     0,
1402     InitRobotWheel,
1403     RunRobotWheel,
1404     StopRobotWheel
1405   },
1406   {
1407     EL_TIMEGATE_SWITCH_ACTIVE,
1408     EL_TIMEGATE_SWITCH,
1409     0,
1410     InitTimegateWheel,
1411     RunTimegateWheel,
1412     NULL
1413   },
1414   {
1415     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1416     EL_DC_TIMEGATE_SWITCH,
1417     0,
1418     InitTimegateWheel,
1419     RunTimegateWheel,
1420     NULL
1421   },
1422   {
1423     EL_EMC_MAGIC_BALL_ACTIVE,
1424     EL_EMC_MAGIC_BALL_ACTIVE,
1425     0,
1426     InitMagicBallDelay,
1427     NULL,
1428     ActivateMagicBall
1429   },
1430   {
1431     EL_EMC_SPRING_BUMPER_ACTIVE,
1432     EL_EMC_SPRING_BUMPER,
1433     8,
1434     NULL,
1435     NULL,
1436     NULL
1437   },
1438   {
1439     EL_DIAGONAL_SHRINKING,
1440     EL_UNDEFINED,
1441     0,
1442     NULL,
1443     NULL,
1444     NULL
1445   },
1446   {
1447     EL_DIAGONAL_GROWING,
1448     EL_UNDEFINED,
1449     0,
1450     NULL,
1451     NULL,
1452     NULL,
1453   },
1454
1455   {
1456     EL_UNDEFINED,
1457     EL_UNDEFINED,
1458     -1,
1459     NULL,
1460     NULL,
1461     NULL
1462   }
1463 };
1464
1465 struct
1466 {
1467   int element;
1468   int push_delay_fixed, push_delay_random;
1469 }
1470 push_delay_list[] =
1471 {
1472   { EL_SPRING,                  0, 0 },
1473   { EL_BALLOON,                 0, 0 },
1474
1475   { EL_SOKOBAN_OBJECT,          2, 0 },
1476   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1477   { EL_SATELLITE,               2, 0 },
1478   { EL_SP_DISK_YELLOW,          2, 0 },
1479
1480   { EL_UNDEFINED,               0, 0 },
1481 };
1482
1483 struct
1484 {
1485   int element;
1486   int move_stepsize;
1487 }
1488 move_stepsize_list[] =
1489 {
1490   { EL_AMOEBA_DROP,             2 },
1491   { EL_AMOEBA_DROPPING,         2 },
1492   { EL_QUICKSAND_FILLING,       1 },
1493   { EL_QUICKSAND_EMPTYING,      1 },
1494   { EL_QUICKSAND_FAST_FILLING,  2 },
1495   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1496   { EL_MAGIC_WALL_FILLING,      2 },
1497   { EL_MAGIC_WALL_EMPTYING,     2 },
1498   { EL_BD_MAGIC_WALL_FILLING,   2 },
1499   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1500   { EL_DC_MAGIC_WALL_FILLING,   2 },
1501   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1502
1503   { EL_UNDEFINED,               0 },
1504 };
1505
1506 struct
1507 {
1508   int element;
1509   int count;
1510 }
1511 collect_count_list[] =
1512 {
1513   { EL_EMERALD,                 1 },
1514   { EL_BD_DIAMOND,              1 },
1515   { EL_EMERALD_YELLOW,          1 },
1516   { EL_EMERALD_RED,             1 },
1517   { EL_EMERALD_PURPLE,          1 },
1518   { EL_DIAMOND,                 3 },
1519   { EL_SP_INFOTRON,             1 },
1520   { EL_PEARL,                   5 },
1521   { EL_CRYSTAL,                 8 },
1522
1523   { EL_UNDEFINED,               0 },
1524 };
1525
1526 struct
1527 {
1528   int element;
1529   int direction;
1530 }
1531 access_direction_list[] =
1532 {
1533   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1534   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1535   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1536   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1537   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1538   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1539   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1540   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1541   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1542   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1543   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1544
1545   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1546   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1547   { EL_SP_PORT_UP,                                                   MV_DOWN },
1548   { EL_SP_PORT_DOWN,                                         MV_UP           },
1549   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1550   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1551   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1552   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1553   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1554   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1555   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1556   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1557   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1558   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1559   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1560   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1561   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1562   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1563   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1564
1565   { EL_UNDEFINED,                       MV_NONE                              }
1566 };
1567
1568 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1569
1570 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1571 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1572 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1573                                  IS_JUST_CHANGING(x, y))
1574
1575 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1576
1577 /* static variables for playfield scan mode (scanning forward or backward) */
1578 static int playfield_scan_start_x = 0;
1579 static int playfield_scan_start_y = 0;
1580 static int playfield_scan_delta_x = 1;
1581 static int playfield_scan_delta_y = 1;
1582
1583 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1584                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1585                                      (y) += playfield_scan_delta_y)     \
1586                                 for ((x) = playfield_scan_start_x;      \
1587                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1588                                      (x) += playfield_scan_delta_x)
1589
1590 #ifdef DEBUG
1591 void DEBUG_SetMaximumDynamite()
1592 {
1593   int i;
1594
1595   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1596     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1597       local_player->inventory_element[local_player->inventory_size++] =
1598         EL_DYNAMITE;
1599 }
1600 #endif
1601
1602 static void InitPlayfieldScanModeVars()
1603 {
1604   if (game.use_reverse_scan_direction)
1605   {
1606     playfield_scan_start_x = lev_fieldx - 1;
1607     playfield_scan_start_y = lev_fieldy - 1;
1608
1609     playfield_scan_delta_x = -1;
1610     playfield_scan_delta_y = -1;
1611   }
1612   else
1613   {
1614     playfield_scan_start_x = 0;
1615     playfield_scan_start_y = 0;
1616
1617     playfield_scan_delta_x = 1;
1618     playfield_scan_delta_y = 1;
1619   }
1620 }
1621
1622 static void InitPlayfieldScanMode(int mode)
1623 {
1624   game.use_reverse_scan_direction =
1625     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1626
1627   InitPlayfieldScanModeVars();
1628 }
1629
1630 static int get_move_delay_from_stepsize(int move_stepsize)
1631 {
1632   move_stepsize =
1633     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1634
1635   /* make sure that stepsize value is always a power of 2 */
1636   move_stepsize = (1 << log_2(move_stepsize));
1637
1638   return TILEX / move_stepsize;
1639 }
1640
1641 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1642                                boolean init_game)
1643 {
1644   int player_nr = player->index_nr;
1645   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1646   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1647
1648   /* do no immediately change move delay -- the player might just be moving */
1649   player->move_delay_value_next = move_delay;
1650
1651   /* information if player can move must be set separately */
1652   player->cannot_move = cannot_move;
1653
1654   if (init_game)
1655   {
1656     player->move_delay       = game.initial_move_delay[player_nr];
1657     player->move_delay_value = game.initial_move_delay_value[player_nr];
1658
1659     player->move_delay_value_next = -1;
1660
1661     player->move_delay_reset_counter = 0;
1662   }
1663 }
1664
1665 void GetPlayerConfig()
1666 {
1667   GameFrameDelay = setup.game_frame_delay;
1668
1669   if (!audio.sound_available)
1670     setup.sound_simple = FALSE;
1671
1672   if (!audio.loops_available)
1673     setup.sound_loops = FALSE;
1674
1675   if (!audio.music_available)
1676     setup.sound_music = FALSE;
1677
1678   if (!video.fullscreen_available)
1679     setup.fullscreen = FALSE;
1680
1681   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1682
1683   SetAudioMode(setup.sound);
1684   InitJoysticks();
1685 }
1686
1687 int GetElementFromGroupElement(int element)
1688 {
1689   if (IS_GROUP_ELEMENT(element))
1690   {
1691     struct ElementGroupInfo *group = element_info[element].group;
1692     int last_anim_random_frame = gfx.anim_random_frame;
1693     int element_pos;
1694
1695     if (group->choice_mode == ANIM_RANDOM)
1696       gfx.anim_random_frame = RND(group->num_elements_resolved);
1697
1698     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1699                                     group->choice_mode, 0,
1700                                     group->choice_pos);
1701
1702     if (group->choice_mode == ANIM_RANDOM)
1703       gfx.anim_random_frame = last_anim_random_frame;
1704
1705     group->choice_pos++;
1706
1707     element = group->element_resolved[element_pos];
1708   }
1709
1710   return element;
1711 }
1712
1713 static void InitPlayerField(int x, int y, int element, boolean init_game)
1714 {
1715   if (element == EL_SP_MURPHY)
1716   {
1717     if (init_game)
1718     {
1719       if (stored_player[0].present)
1720       {
1721         Feld[x][y] = EL_SP_MURPHY_CLONE;
1722
1723         return;
1724       }
1725       else
1726       {
1727         stored_player[0].initial_element = element;
1728         stored_player[0].use_murphy = TRUE;
1729
1730         if (!level.use_artwork_element[0])
1731           stored_player[0].artwork_element = EL_SP_MURPHY;
1732       }
1733
1734       Feld[x][y] = EL_PLAYER_1;
1735     }
1736   }
1737
1738   if (init_game)
1739   {
1740     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1741     int jx = player->jx, jy = player->jy;
1742
1743     player->present = TRUE;
1744
1745     player->block_last_field = (element == EL_SP_MURPHY ?
1746                                 level.sp_block_last_field :
1747                                 level.block_last_field);
1748
1749     /* ---------- initialize player's last field block delay --------------- */
1750
1751     /* always start with reliable default value (no adjustment needed) */
1752     player->block_delay_adjustment = 0;
1753
1754     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1755     if (player->block_last_field && element == EL_SP_MURPHY)
1756       player->block_delay_adjustment = 1;
1757
1758     /* special case 2: in game engines before 3.1.1, blocking was different */
1759     if (game.use_block_last_field_bug)
1760       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1761
1762     if (!options.network || player->connected)
1763     {
1764       player->active = TRUE;
1765
1766       /* remove potentially duplicate players */
1767       if (StorePlayer[jx][jy] == Feld[x][y])
1768         StorePlayer[jx][jy] = 0;
1769
1770       StorePlayer[x][y] = Feld[x][y];
1771
1772       if (options.debug)
1773       {
1774         printf("Player %d activated.\n", player->element_nr);
1775         printf("[Local player is %d and currently %s.]\n",
1776                local_player->element_nr,
1777                local_player->active ? "active" : "not active");
1778       }
1779     }
1780
1781     Feld[x][y] = EL_EMPTY;
1782
1783     player->jx = player->last_jx = x;
1784     player->jy = player->last_jy = y;
1785   }
1786
1787 #if USE_PLAYER_REANIMATION
1788   if (!init_game)
1789   {
1790     int player_nr = GET_PLAYER_NR(element);
1791     struct PlayerInfo *player = &stored_player[player_nr];
1792
1793     if (player->active)
1794       player->killed = FALSE;   /* if player was just killed, reanimate him */
1795   }
1796 #endif
1797 }
1798
1799 static void InitField(int x, int y, boolean init_game)
1800 {
1801   int element = Feld[x][y];
1802
1803   switch (element)
1804   {
1805     case EL_SP_MURPHY:
1806     case EL_PLAYER_1:
1807     case EL_PLAYER_2:
1808     case EL_PLAYER_3:
1809     case EL_PLAYER_4:
1810       InitPlayerField(x, y, element, init_game);
1811       break;
1812
1813     case EL_SOKOBAN_FIELD_PLAYER:
1814       element = Feld[x][y] = EL_PLAYER_1;
1815       InitField(x, y, init_game);
1816
1817       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1818       InitField(x, y, init_game);
1819       break;
1820
1821     case EL_SOKOBAN_FIELD_EMPTY:
1822       local_player->sokobanfields_still_needed++;
1823       break;
1824
1825     case EL_STONEBLOCK:
1826       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1827         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1828       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1829         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1830       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1831         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1832       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1833         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1834       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1835         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1836       break;
1837
1838     case EL_BUG:
1839     case EL_BUG_RIGHT:
1840     case EL_BUG_UP:
1841     case EL_BUG_LEFT:
1842     case EL_BUG_DOWN:
1843     case EL_SPACESHIP:
1844     case EL_SPACESHIP_RIGHT:
1845     case EL_SPACESHIP_UP:
1846     case EL_SPACESHIP_LEFT:
1847     case EL_SPACESHIP_DOWN:
1848     case EL_BD_BUTTERFLY:
1849     case EL_BD_BUTTERFLY_RIGHT:
1850     case EL_BD_BUTTERFLY_UP:
1851     case EL_BD_BUTTERFLY_LEFT:
1852     case EL_BD_BUTTERFLY_DOWN:
1853     case EL_BD_FIREFLY:
1854     case EL_BD_FIREFLY_RIGHT:
1855     case EL_BD_FIREFLY_UP:
1856     case EL_BD_FIREFLY_LEFT:
1857     case EL_BD_FIREFLY_DOWN:
1858     case EL_PACMAN_RIGHT:
1859     case EL_PACMAN_UP:
1860     case EL_PACMAN_LEFT:
1861     case EL_PACMAN_DOWN:
1862     case EL_YAMYAM:
1863     case EL_YAMYAM_LEFT:
1864     case EL_YAMYAM_RIGHT:
1865     case EL_YAMYAM_UP:
1866     case EL_YAMYAM_DOWN:
1867     case EL_DARK_YAMYAM:
1868     case EL_ROBOT:
1869     case EL_PACMAN:
1870     case EL_SP_SNIKSNAK:
1871     case EL_SP_ELECTRON:
1872     case EL_MOLE:
1873     case EL_MOLE_LEFT:
1874     case EL_MOLE_RIGHT:
1875     case EL_MOLE_UP:
1876     case EL_MOLE_DOWN:
1877       InitMovDir(x, y);
1878       break;
1879
1880     case EL_AMOEBA_FULL:
1881     case EL_BD_AMOEBA:
1882       InitAmoebaNr(x, y);
1883       break;
1884
1885     case EL_AMOEBA_DROP:
1886       if (y == lev_fieldy - 1)
1887       {
1888         Feld[x][y] = EL_AMOEBA_GROWING;
1889         Store[x][y] = EL_AMOEBA_WET;
1890       }
1891       break;
1892
1893     case EL_DYNAMITE_ACTIVE:
1894     case EL_SP_DISK_RED_ACTIVE:
1895     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1896     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1897     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1899       MovDelay[x][y] = 96;
1900       break;
1901
1902     case EL_EM_DYNAMITE_ACTIVE:
1903       MovDelay[x][y] = 32;
1904       break;
1905
1906     case EL_LAMP:
1907       local_player->lights_still_needed++;
1908       break;
1909
1910     case EL_PENGUIN:
1911       local_player->friends_still_needed++;
1912       break;
1913
1914     case EL_PIG:
1915     case EL_DRAGON:
1916       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1917       break;
1918
1919     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1920     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1921     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1922     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1923     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1924     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1925     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1926     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1927     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1928     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1931       if (init_game)
1932       {
1933         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1934         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1935         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1936
1937         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1938         {
1939           game.belt_dir[belt_nr] = belt_dir;
1940           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1941         }
1942         else    /* more than one switch -- set it like the first switch */
1943         {
1944           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1945         }
1946       }
1947       break;
1948
1949 #if !USE_BOTH_SWITCHGATE_SWITCHES
1950     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1951       if (init_game)
1952         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1953       break;
1954
1955     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1956       if (init_game)
1957         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1958       break;
1959 #endif
1960
1961     case EL_LIGHT_SWITCH_ACTIVE:
1962       if (init_game)
1963         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1964       break;
1965
1966     case EL_INVISIBLE_STEELWALL:
1967     case EL_INVISIBLE_WALL:
1968     case EL_INVISIBLE_SAND:
1969       if (game.light_time_left > 0 ||
1970           game.lenses_time_left > 0)
1971         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1972       break;
1973
1974     case EL_EMC_MAGIC_BALL:
1975       if (game.ball_state)
1976         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1977       break;
1978
1979     case EL_EMC_MAGIC_BALL_SWITCH:
1980       if (game.ball_state)
1981         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1982       break;
1983
1984     case EL_TRIGGER_PLAYER:
1985     case EL_TRIGGER_ELEMENT:
1986     case EL_TRIGGER_CE_VALUE:
1987     case EL_TRIGGER_CE_SCORE:
1988     case EL_SELF:
1989     case EL_ANY_ELEMENT:
1990     case EL_CURRENT_CE_VALUE:
1991     case EL_CURRENT_CE_SCORE:
1992     case EL_PREV_CE_1:
1993     case EL_PREV_CE_2:
1994     case EL_PREV_CE_3:
1995     case EL_PREV_CE_4:
1996     case EL_PREV_CE_5:
1997     case EL_PREV_CE_6:
1998     case EL_PREV_CE_7:
1999     case EL_PREV_CE_8:
2000     case EL_NEXT_CE_1:
2001     case EL_NEXT_CE_2:
2002     case EL_NEXT_CE_3:
2003     case EL_NEXT_CE_4:
2004     case EL_NEXT_CE_5:
2005     case EL_NEXT_CE_6:
2006     case EL_NEXT_CE_7:
2007     case EL_NEXT_CE_8:
2008       /* reference elements should not be used on the playfield */
2009       Feld[x][y] = EL_EMPTY;
2010       break;
2011
2012     default:
2013       if (IS_CUSTOM_ELEMENT(element))
2014       {
2015         if (CAN_MOVE(element))
2016           InitMovDir(x, y);
2017
2018 #if USE_NEW_CUSTOM_VALUE
2019         if (!element_info[element].use_last_ce_value || init_game)
2020           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2021 #endif
2022       }
2023       else if (IS_GROUP_ELEMENT(element))
2024       {
2025         Feld[x][y] = GetElementFromGroupElement(element);
2026
2027         InitField(x, y, init_game);
2028       }
2029
2030       break;
2031   }
2032
2033   if (!init_game)
2034     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2035 }
2036
2037 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2038 {
2039   InitField(x, y, init_game);
2040
2041   /* not needed to call InitMovDir() -- already done by InitField()! */
2042   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2043       CAN_MOVE(Feld[x][y]))
2044     InitMovDir(x, y);
2045 }
2046
2047 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2048 {
2049   int old_element = Feld[x][y];
2050
2051   InitField(x, y, init_game);
2052
2053   /* not needed to call InitMovDir() -- already done by InitField()! */
2054   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2055       CAN_MOVE(old_element) &&
2056       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2057     InitMovDir(x, y);
2058
2059   /* this case is in fact a combination of not less than three bugs:
2060      first, it calls InitMovDir() for elements that can move, although this is
2061      already done by InitField(); then, it checks the element that was at this
2062      field _before_ the call to InitField() (which can change it); lastly, it
2063      was not called for "mole with direction" elements, which were treated as
2064      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2065   */
2066 }
2067
2068 #if 1
2069
2070 static int get_key_element_from_nr(int key_nr)
2071 {
2072   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2073                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2074                           EL_EM_KEY_1 : EL_KEY_1);
2075
2076   return key_base_element + key_nr;
2077 }
2078
2079 static int get_next_dropped_element(struct PlayerInfo *player)
2080 {
2081   return (player->inventory_size > 0 ?
2082           player->inventory_element[player->inventory_size - 1] :
2083           player->inventory_infinite_element != EL_UNDEFINED ?
2084           player->inventory_infinite_element :
2085           player->dynabombs_left > 0 ?
2086           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2087           EL_UNDEFINED);
2088 }
2089
2090 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2091 {
2092   /* pos >= 0: get element from bottom of the stack;
2093      pos <  0: get element from top of the stack */
2094
2095   if (pos < 0)
2096   {
2097     int min_inventory_size = -pos;
2098     int inventory_pos = player->inventory_size - min_inventory_size;
2099     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2100
2101     return (player->inventory_size >= min_inventory_size ?
2102             player->inventory_element[inventory_pos] :
2103             player->inventory_infinite_element != EL_UNDEFINED ?
2104             player->inventory_infinite_element :
2105             player->dynabombs_left >= min_dynabombs_left ?
2106             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2107             EL_UNDEFINED);
2108   }
2109   else
2110   {
2111     int min_dynabombs_left = pos + 1;
2112     int min_inventory_size = pos + 1 - player->dynabombs_left;
2113     int inventory_pos = pos - player->dynabombs_left;
2114
2115     return (player->inventory_infinite_element != EL_UNDEFINED ?
2116             player->inventory_infinite_element :
2117             player->dynabombs_left >= min_dynabombs_left ?
2118             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2119             player->inventory_size >= min_inventory_size ?
2120             player->inventory_element[inventory_pos] :
2121             EL_UNDEFINED);
2122   }
2123 }
2124
2125 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2126 {
2127   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2128   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2129   int compare_result;
2130
2131   if (gpo1->sort_priority != gpo2->sort_priority)
2132     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2133   else
2134     compare_result = gpo1->nr - gpo2->nr;
2135
2136   return compare_result;
2137 }
2138
2139 void InitGameControlValues()
2140 {
2141   int i;
2142
2143   for (i = 0; game_panel_controls[i].nr != -1; i++)
2144   {
2145     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2146     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2147     struct TextPosInfo *pos = gpc->pos;
2148     int nr = gpc->nr;
2149     int type = gpc->type;
2150
2151     if (nr != i)
2152     {
2153       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2154       Error(ERR_EXIT, "this should not happen -- please debug");
2155     }
2156
2157     /* force update of game controls after initialization */
2158     gpc->value = gpc->last_value = -1;
2159     gpc->frame = gpc->last_frame = -1;
2160     gpc->gfx_frame = -1;
2161
2162     /* determine panel value width for later calculation of alignment */
2163     if (type == TYPE_INTEGER || type == TYPE_STRING)
2164     {
2165       pos->width = pos->size * getFontWidth(pos->font);
2166       pos->height = getFontHeight(pos->font);
2167     }
2168     else if (type == TYPE_ELEMENT)
2169     {
2170       pos->width = pos->size;
2171       pos->height = pos->size;
2172     }
2173
2174     /* fill structure for game panel draw order */
2175     gpo->nr = gpc->nr;
2176     gpo->sort_priority = pos->sort_priority;
2177   }
2178
2179   /* sort game panel controls according to sort_priority and control number */
2180   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2181         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2182 }
2183
2184 void UpdatePlayfieldElementCount()
2185 {
2186   boolean use_element_count = FALSE;
2187   int i, j, x, y;
2188
2189   /* first check if it is needed at all to calculate playfield element count */
2190   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2191     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2192       use_element_count = TRUE;
2193
2194   if (!use_element_count)
2195     return;
2196
2197   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2198     element_info[i].element_count = 0;
2199
2200   SCAN_PLAYFIELD(x, y)
2201   {
2202     element_info[Feld[x][y]].element_count++;
2203   }
2204
2205   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2206     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2207       if (IS_IN_GROUP(j, i))
2208         element_info[EL_GROUP_START + i].element_count +=
2209           element_info[j].element_count;
2210 }
2211
2212 void UpdateGameControlValues()
2213 {
2214   int i, k;
2215   int time = (local_player->LevelSolved ?
2216               local_player->LevelSolved_CountingTime :
2217               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2218               level.native_em_level->lev->time :
2219               level.time == 0 ? TimePlayed : TimeLeft);
2220   int score = (local_player->LevelSolved ?
2221                local_player->LevelSolved_CountingScore :
2222                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2223                level.native_em_level->lev->score :
2224                local_player->score);
2225   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226               level.native_em_level->lev->required :
2227               local_player->gems_still_needed);
2228   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229                      level.native_em_level->lev->required > 0 :
2230                      local_player->gems_still_needed > 0 ||
2231                      local_player->sokobanfields_still_needed > 0 ||
2232                      local_player->lights_still_needed > 0);
2233
2234   UpdatePlayfieldElementCount();
2235
2236   /* update game panel control values */
2237
2238   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2239   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2240
2241   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2242   for (i = 0; i < MAX_NUM_KEYS; i++)
2243     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2244   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2245   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2246
2247   if (game.centered_player_nr == -1)
2248   {
2249     for (i = 0; i < MAX_PLAYERS; i++)
2250     {
2251       for (k = 0; k < MAX_NUM_KEYS; k++)
2252       {
2253         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2254         {
2255           if (level.native_em_level->ply[i]->keys & (1 << k))
2256             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2257               get_key_element_from_nr(k);
2258         }
2259         else if (stored_player[i].key[k])
2260           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2261             get_key_element_from_nr(k);
2262       }
2263
2264       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2265         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2266           level.native_em_level->ply[i]->dynamite;
2267       else
2268         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2269           stored_player[i].inventory_size;
2270
2271       if (stored_player[i].num_white_keys > 0)
2272         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2273           EL_DC_KEY_WHITE;
2274
2275       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2276         stored_player[i].num_white_keys;
2277     }
2278   }
2279   else
2280   {
2281     int player_nr = game.centered_player_nr;
2282
2283     for (k = 0; k < MAX_NUM_KEYS; k++)
2284     {
2285       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2286       {
2287         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2288           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2289             get_key_element_from_nr(k);
2290       }
2291       else if (stored_player[player_nr].key[k])
2292         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2293           get_key_element_from_nr(k);
2294     }
2295
2296     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2297       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2298         level.native_em_level->ply[player_nr]->dynamite;
2299     else
2300       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2301         stored_player[player_nr].inventory_size;
2302
2303     if (stored_player[player_nr].num_white_keys > 0)
2304       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2305
2306     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2307       stored_player[player_nr].num_white_keys;
2308   }
2309
2310   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2311   {
2312     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2313       get_inventory_element_from_pos(local_player, i);
2314     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2315       get_inventory_element_from_pos(local_player, -i - 1);
2316   }
2317
2318   game_panel_controls[GAME_PANEL_SCORE].value = score;
2319   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2320
2321   game_panel_controls[GAME_PANEL_TIME].value = time;
2322
2323   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2324   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2325   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2326
2327   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2328     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2329      EL_EMPTY);
2330   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2331     local_player->shield_normal_time_left;
2332   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2333     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2334      EL_EMPTY);
2335   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2336     local_player->shield_deadly_time_left;
2337
2338   game_panel_controls[GAME_PANEL_EXIT].value =
2339     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2340
2341   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2342     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2343   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2344     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2345      EL_EMC_MAGIC_BALL_SWITCH);
2346
2347   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2348     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2349   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2350     game.light_time_left;
2351
2352   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2353     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2354   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2355     game.timegate_time_left;
2356
2357   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2358     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2359
2360   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2361     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2363     game.lenses_time_left;
2364
2365   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2366     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2367   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2368     game.magnify_time_left;
2369
2370   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2371     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2372      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2373      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2374      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2375      EL_BALLOON_SWITCH_NONE);
2376
2377   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2378     local_player->dynabomb_count;
2379   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2380     local_player->dynabomb_size;
2381   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2382     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2383
2384   game_panel_controls[GAME_PANEL_PENGUINS].value =
2385     local_player->friends_still_needed;
2386
2387   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2388     local_player->sokobanfields_still_needed;
2389   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2390     local_player->sokobanfields_still_needed;
2391
2392   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2393     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2394
2395   for (i = 0; i < NUM_BELTS; i++)
2396   {
2397     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2398       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2399        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2400     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2401       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2402   }
2403
2404   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2405     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2406   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2407     game.magic_wall_time_left;
2408
2409 #if USE_PLAYER_GRAVITY
2410   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2411     local_player->gravity;
2412 #else
2413   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2414 #endif
2415
2416   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2417     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2418
2419   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2420     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2421       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2422        game.panel.element[i].id : EL_UNDEFINED);
2423
2424   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2425     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2426       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2427        element_info[game.panel.element_count[i].id].element_count : 0);
2428
2429   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2430     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2431       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2432        element_info[game.panel.ce_score[i].id].collect_score : 0);
2433
2434   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2435     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2436       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2437        element_info[game.panel.ce_score_element[i].id].collect_score :
2438        EL_UNDEFINED);
2439
2440   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2441   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2442   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2443
2444   /* update game panel control frames */
2445
2446   for (i = 0; game_panel_controls[i].nr != -1; i++)
2447   {
2448     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2449
2450     if (gpc->type == TYPE_ELEMENT)
2451     {
2452       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2453       {
2454         int last_anim_random_frame = gfx.anim_random_frame;
2455         int element = gpc->value;
2456         int graphic = el2panelimg(element);
2457
2458         if (gpc->value != gpc->last_value)
2459         {
2460           gpc->gfx_frame = 0;
2461           gpc->gfx_random = INIT_GFX_RANDOM();
2462         }
2463         else
2464         {
2465           gpc->gfx_frame++;
2466
2467           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2468               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2469             gpc->gfx_random = INIT_GFX_RANDOM();
2470         }
2471
2472         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2473           gfx.anim_random_frame = gpc->gfx_random;
2474
2475         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2476           gpc->gfx_frame = element_info[element].collect_score;
2477
2478         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2479                                               gpc->gfx_frame);
2480
2481         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2482           gfx.anim_random_frame = last_anim_random_frame;
2483       }
2484     }
2485   }
2486 }
2487
2488 void DisplayGameControlValues()
2489 {
2490   boolean redraw_panel = FALSE;
2491   int i;
2492
2493   for (i = 0; game_panel_controls[i].nr != -1; i++)
2494   {
2495     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2496
2497     if (PANEL_DEACTIVATED(gpc->pos))
2498       continue;
2499
2500     if (gpc->value == gpc->last_value &&
2501         gpc->frame == gpc->last_frame)
2502       continue;
2503
2504     redraw_panel = TRUE;
2505   }
2506
2507   if (!redraw_panel)
2508     return;
2509
2510   /* copy default game door content to main double buffer */
2511   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2512              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2513
2514   /* redraw game control buttons */
2515 #if 1
2516   RedrawGameButtons();
2517 #else
2518   UnmapGameButtons();
2519   MapGameButtons();
2520 #endif
2521
2522   game_status = GAME_MODE_PSEUDO_PANEL;
2523
2524 #if 1
2525   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2526 #else
2527   for (i = 0; game_panel_controls[i].nr != -1; i++)
2528 #endif
2529   {
2530 #if 1
2531     int nr = game_panel_order[i].nr;
2532     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2533 #else
2534     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2535     int nr = gpc->nr;
2536 #endif
2537     struct TextPosInfo *pos = gpc->pos;
2538     int type = gpc->type;
2539     int value = gpc->value;
2540     int frame = gpc->frame;
2541 #if 0
2542     int last_value = gpc->last_value;
2543     int last_frame = gpc->last_frame;
2544 #endif
2545     int size = pos->size;
2546     int font = pos->font;
2547     boolean draw_masked = pos->draw_masked;
2548     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2549
2550     if (PANEL_DEACTIVATED(pos))
2551       continue;
2552
2553 #if 0
2554     if (value == last_value && frame == last_frame)
2555       continue;
2556 #endif
2557
2558     gpc->last_value = value;
2559     gpc->last_frame = frame;
2560
2561 #if 0
2562     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2563 #endif
2564
2565     if (type == TYPE_INTEGER)
2566     {
2567       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2568           nr == GAME_PANEL_TIME)
2569       {
2570         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2571
2572         if (use_dynamic_size)           /* use dynamic number of digits */
2573         {
2574           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2575           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2576           int size2 = size1 + 1;
2577           int font1 = pos->font;
2578           int font2 = pos->font_alt;
2579
2580           size = (value < value_change ? size1 : size2);
2581           font = (value < value_change ? font1 : font2);
2582
2583 #if 0
2584           /* clear background if value just changed its size (dynamic digits) */
2585           if ((last_value < value_change) != (value < value_change))
2586           {
2587             int width1 = size1 * getFontWidth(font1);
2588             int width2 = size2 * getFontWidth(font2);
2589             int max_width = MAX(width1, width2);
2590             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2591
2592             pos->width = max_width;
2593
2594             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2595                                        max_width, max_height);
2596           }
2597 #endif
2598         }
2599       }
2600
2601 #if 1
2602       /* correct text size if "digits" is zero or less */
2603       if (size <= 0)
2604         size = strlen(int2str(value, size));
2605
2606       /* dynamically correct text alignment */
2607       pos->width = size * getFontWidth(font);
2608 #endif
2609
2610       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2611                   int2str(value, size), font, mask_mode);
2612     }
2613     else if (type == TYPE_ELEMENT)
2614     {
2615       int element, graphic;
2616       Bitmap *src_bitmap;
2617       int src_x, src_y;
2618       int width, height;
2619       int dst_x = PANEL_XPOS(pos);
2620       int dst_y = PANEL_YPOS(pos);
2621
2622 #if 1
2623       if (value != EL_UNDEFINED && value != EL_EMPTY)
2624       {
2625         element = value;
2626         graphic = el2panelimg(value);
2627
2628         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2629
2630 #if 1
2631         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2632           size = TILESIZE;
2633 #endif
2634
2635         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2636                               &src_x, &src_y);
2637
2638         width  = graphic_info[graphic].width  * size / TILESIZE;
2639         height = graphic_info[graphic].height * size / TILESIZE;
2640
2641         if (draw_masked)
2642         {
2643           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2644                         dst_x - src_x, dst_y - src_y);
2645           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2646                            dst_x, dst_y);
2647         }
2648         else
2649         {
2650           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2651                      dst_x, dst_y);
2652         }
2653       }
2654 #else
2655       if (value == EL_UNDEFINED || value == EL_EMPTY)
2656       {
2657         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2658         graphic = el2panelimg(element);
2659
2660         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2661         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2662         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2663       }
2664       else
2665       {
2666         element = value;
2667         graphic = el2panelimg(value);
2668
2669         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2670       }
2671
2672       width  = graphic_info[graphic].width  * size / TILESIZE;
2673       height = graphic_info[graphic].height * size / TILESIZE;
2674
2675       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2676 #endif
2677     }
2678     else if (type == TYPE_STRING)
2679     {
2680       boolean active = (value != 0);
2681       char *state_normal = "off";
2682       char *state_active = "on";
2683       char *state = (active ? state_active : state_normal);
2684       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2685                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2686                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2687                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2688
2689       if (nr == GAME_PANEL_GRAVITY_STATE)
2690       {
2691         int font1 = pos->font;          /* (used for normal state) */
2692         int font2 = pos->font_alt;      /* (used for active state) */
2693 #if 0
2694         int size1 = strlen(state_normal);
2695         int size2 = strlen(state_active);
2696         int width1 = size1 * getFontWidth(font1);
2697         int width2 = size2 * getFontWidth(font2);
2698         int max_width = MAX(width1, width2);
2699         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2700
2701         pos->width = max_width;
2702
2703         /* clear background for values that may have changed its size */
2704         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2705                                    max_width, max_height);
2706 #endif
2707
2708         font = (active ? font2 : font1);
2709       }
2710
2711       if (s != NULL)
2712       {
2713         char *s_cut;
2714
2715 #if 1
2716         if (size <= 0)
2717         {
2718           /* don't truncate output if "chars" is zero or less */
2719           size = strlen(s);
2720
2721           /* dynamically correct text alignment */
2722           pos->width = size * getFontWidth(font);
2723         }
2724 #endif
2725
2726         s_cut = getStringCopyN(s, size);
2727
2728         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2729                     s_cut, font, mask_mode);
2730
2731         free(s_cut);
2732       }
2733     }
2734
2735     redraw_mask |= REDRAW_DOOR_1;
2736   }
2737
2738   game_status = GAME_MODE_PLAYING;
2739 }
2740
2741 void UpdateAndDisplayGameControlValues()
2742 {
2743   if (tape.warp_forward)
2744     return;
2745
2746   UpdateGameControlValues();
2747   DisplayGameControlValues();
2748 }
2749
2750 void DrawGameValue_Emeralds(int value)
2751 {
2752   struct TextPosInfo *pos = &game.panel.gems;
2753 #if 1
2754   int font_nr = pos->font;
2755 #else
2756   int font_nr = FONT_TEXT_2;
2757 #endif
2758   int font_width = getFontWidth(font_nr);
2759   int chars = pos->size;
2760
2761 #if 1
2762   return;       /* !!! USE NEW STUFF !!! */
2763 #endif
2764
2765   if (PANEL_DEACTIVATED(pos))
2766     return;
2767
2768   pos->width = chars * font_width;
2769
2770   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2771 }
2772
2773 void DrawGameValue_Dynamite(int value)
2774 {
2775   struct TextPosInfo *pos = &game.panel.inventory_count;
2776 #if 1
2777   int font_nr = pos->font;
2778 #else
2779   int font_nr = FONT_TEXT_2;
2780 #endif
2781   int font_width = getFontWidth(font_nr);
2782   int chars = pos->size;
2783
2784 #if 1
2785   return;       /* !!! USE NEW STUFF !!! */
2786 #endif
2787
2788   if (PANEL_DEACTIVATED(pos))
2789     return;
2790
2791   pos->width = chars * font_width;
2792
2793   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2794 }
2795
2796 void DrawGameValue_Score(int value)
2797 {
2798   struct TextPosInfo *pos = &game.panel.score;
2799 #if 1
2800   int font_nr = pos->font;
2801 #else
2802   int font_nr = FONT_TEXT_2;
2803 #endif
2804   int font_width = getFontWidth(font_nr);
2805   int chars = pos->size;
2806
2807 #if 1
2808   return;       /* !!! USE NEW STUFF !!! */
2809 #endif
2810
2811   if (PANEL_DEACTIVATED(pos))
2812     return;
2813
2814   pos->width = chars * font_width;
2815
2816   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2817 }
2818
2819 void DrawGameValue_Time(int value)
2820 {
2821   struct TextPosInfo *pos = &game.panel.time;
2822   static int last_value = -1;
2823   int chars1 = 3;
2824   int chars2 = 4;
2825   int chars = pos->size;
2826 #if 1
2827   int font1_nr = pos->font;
2828   int font2_nr = pos->font_alt;
2829 #else
2830   int font1_nr = FONT_TEXT_2;
2831   int font2_nr = FONT_TEXT_1;
2832 #endif
2833   int font_nr = font1_nr;
2834   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2835
2836 #if 1
2837   return;       /* !!! USE NEW STUFF !!! */
2838 #endif
2839
2840   if (PANEL_DEACTIVATED(pos))
2841     return;
2842
2843   if (use_dynamic_chars)                /* use dynamic number of chars */
2844   {
2845     chars   = (value < 1000 ? chars1   : chars2);
2846     font_nr = (value < 1000 ? font1_nr : font2_nr);
2847   }
2848
2849   /* clear background if value just changed its size (dynamic chars only) */
2850   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2851   {
2852     int width1 = chars1 * getFontWidth(font1_nr);
2853     int width2 = chars2 * getFontWidth(font2_nr);
2854     int max_width = MAX(width1, width2);
2855     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2856
2857     pos->width = max_width;
2858
2859     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2860                                max_width, max_height);
2861   }
2862
2863   pos->width = chars * getFontWidth(font_nr);
2864
2865   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2866
2867   last_value = value;
2868 }
2869
2870 void DrawGameValue_Level(int value)
2871 {
2872   struct TextPosInfo *pos = &game.panel.level_number;
2873   int chars1 = 2;
2874   int chars2 = 3;
2875   int chars = pos->size;
2876 #if 1
2877   int font1_nr = pos->font;
2878   int font2_nr = pos->font_alt;
2879 #else
2880   int font1_nr = FONT_TEXT_2;
2881   int font2_nr = FONT_TEXT_1;
2882 #endif
2883   int font_nr = font1_nr;
2884   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2885
2886 #if 1
2887   return;       /* !!! USE NEW STUFF !!! */
2888 #endif
2889
2890   if (PANEL_DEACTIVATED(pos))
2891     return;
2892
2893   if (use_dynamic_chars)                /* use dynamic number of chars */
2894   {
2895     chars   = (level_nr < 100 ? chars1   : chars2);
2896     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2897   }
2898
2899   pos->width = chars * getFontWidth(font_nr);
2900
2901   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2902 }
2903
2904 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2905 {
2906 #if 0
2907   struct TextPosInfo *pos = &game.panel.keys;
2908 #endif
2909 #if 0
2910   int base_key_graphic = EL_KEY_1;
2911 #endif
2912   int i;
2913
2914 #if 1
2915   return;       /* !!! USE NEW STUFF !!! */
2916 #endif
2917
2918 #if 0
2919   if (PANEL_DEACTIVATED(pos))
2920     return;
2921 #endif
2922
2923 #if 0
2924   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2925     base_key_graphic = EL_EM_KEY_1;
2926 #endif
2927
2928 #if 0
2929   pos->width = 4 * MINI_TILEX;
2930 #endif
2931
2932 #if 1
2933   for (i = 0; i < MAX_NUM_KEYS; i++)
2934 #else
2935   /* currently only 4 of 8 possible keys are displayed */
2936   for (i = 0; i < STD_NUM_KEYS; i++)
2937 #endif
2938   {
2939 #if 1
2940     struct TextPosInfo *pos = &game.panel.key[i];
2941 #endif
2942     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2943     int src_y = DOOR_GFX_PAGEY1 + 123;
2944 #if 1
2945     int dst_x = PANEL_XPOS(pos);
2946     int dst_y = PANEL_YPOS(pos);
2947 #else
2948     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2949     int dst_y = PANEL_YPOS(pos);
2950 #endif
2951
2952 #if 1
2953     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2954                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2955                    EL_KEY_1) + i;
2956     int graphic = el2edimg(element);
2957 #endif
2958
2959 #if 1
2960     if (PANEL_DEACTIVATED(pos))
2961       continue;
2962 #endif
2963
2964 #if 0
2965     /* masked blit with tiles from half-size scaled bitmap does not work yet
2966        (no mask bitmap created for these sizes after loading and scaling) --
2967        solution: load without creating mask, scale, then create final mask */
2968
2969     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2970                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2971
2972     if (key[i])
2973     {
2974 #if 0
2975       int graphic = el2edimg(base_key_graphic + i);
2976 #endif
2977       Bitmap *src_bitmap;
2978       int src_x, src_y;
2979
2980       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2981
2982       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2983                     dst_x - src_x, dst_y - src_y);
2984       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2985                        dst_x, dst_y);
2986     }
2987 #else
2988 #if 1
2989     if (key[i])
2990       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2991     else
2992       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2993                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2994 #else
2995     if (key[i])
2996       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2997     else
2998       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2999                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3000 #endif
3001 #endif
3002   }
3003 }
3004
3005 #else
3006
3007 void DrawGameValue_Emeralds(int value)
3008 {
3009   int font_nr = FONT_TEXT_2;
3010   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3011
3012   if (PANEL_DEACTIVATED(game.panel.gems))
3013     return;
3014
3015   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3016 }
3017
3018 void DrawGameValue_Dynamite(int value)
3019 {
3020   int font_nr = FONT_TEXT_2;
3021   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3022
3023   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3024     return;
3025
3026   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3027 }
3028
3029 void DrawGameValue_Score(int value)
3030 {
3031   int font_nr = FONT_TEXT_2;
3032   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3033
3034   if (PANEL_DEACTIVATED(game.panel.score))
3035     return;
3036
3037   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3038 }
3039
3040 void DrawGameValue_Time(int value)
3041 {
3042   int font1_nr = FONT_TEXT_2;
3043 #if 1
3044   int font2_nr = FONT_TEXT_1;
3045 #else
3046   int font2_nr = FONT_LEVEL_NUMBER;
3047 #endif
3048   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3049   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3050
3051   if (PANEL_DEACTIVATED(game.panel.time))
3052     return;
3053
3054   /* clear background if value just changed its size */
3055   if (value == 999 || value == 1000)
3056     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3057
3058   if (value < 1000)
3059     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3060   else
3061     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3062 }
3063
3064 void DrawGameValue_Level(int value)
3065 {
3066   int font1_nr = FONT_TEXT_2;
3067 #if 1
3068   int font2_nr = FONT_TEXT_1;
3069 #else
3070   int font2_nr = FONT_LEVEL_NUMBER;
3071 #endif
3072
3073   if (PANEL_DEACTIVATED(game.panel.level))
3074     return;
3075
3076   if (level_nr < 100)
3077     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3078   else
3079     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3080 }
3081
3082 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3083 {
3084   int base_key_graphic = EL_KEY_1;
3085   int i;
3086
3087   if (PANEL_DEACTIVATED(game.panel.keys))
3088     return;
3089
3090   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3091     base_key_graphic = EL_EM_KEY_1;
3092
3093   /* currently only 4 of 8 possible keys are displayed */
3094   for (i = 0; i < STD_NUM_KEYS; i++)
3095   {
3096     int x = XX_KEYS + i * MINI_TILEX;
3097     int y = YY_KEYS;
3098
3099     if (key[i])
3100       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3101     else
3102       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3103                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3104   }
3105 }
3106
3107 #endif
3108
3109 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3110                        int key_bits)
3111 {
3112   int key[MAX_NUM_KEYS];
3113   int i;
3114
3115   /* prevent EM engine from updating time/score values parallel to GameWon() */
3116   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3117       local_player->LevelSolved)
3118     return;
3119
3120   for (i = 0; i < MAX_NUM_KEYS; i++)
3121     key[i] = key_bits & (1 << i);
3122
3123   DrawGameValue_Level(level_nr);
3124
3125   DrawGameValue_Emeralds(emeralds);
3126   DrawGameValue_Dynamite(dynamite);
3127   DrawGameValue_Score(score);
3128   DrawGameValue_Time(time);
3129
3130   DrawGameValue_Keys(key);
3131 }
3132
3133 void UpdateGameDoorValues()
3134 {
3135   UpdateGameControlValues();
3136 }
3137
3138 void DrawGameDoorValues()
3139 {
3140   DisplayGameControlValues();
3141 }
3142
3143 void DrawGameDoorValues_OLD()
3144 {
3145   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3146   int dynamite_value = 0;
3147   int score_value = (local_player->LevelSolved ? local_player->score_final :
3148                      local_player->score);
3149   int gems_value = local_player->gems_still_needed;
3150   int key_bits = 0;
3151   int i, j;
3152
3153   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3154   {
3155     DrawGameDoorValues_EM();
3156
3157     return;
3158   }
3159
3160   if (game.centered_player_nr == -1)
3161   {
3162     for (i = 0; i < MAX_PLAYERS; i++)
3163     {
3164       for (j = 0; j < MAX_NUM_KEYS; j++)
3165         if (stored_player[i].key[j])
3166           key_bits |= (1 << j);
3167
3168       dynamite_value += stored_player[i].inventory_size;
3169     }
3170   }
3171   else
3172   {
3173     int player_nr = game.centered_player_nr;
3174
3175     for (i = 0; i < MAX_NUM_KEYS; i++)
3176       if (stored_player[player_nr].key[i])
3177         key_bits |= (1 << i);
3178
3179     dynamite_value = stored_player[player_nr].inventory_size;
3180   }
3181
3182   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3183                     key_bits);
3184 }
3185
3186
3187 /*
3188   =============================================================================
3189   InitGameEngine()
3190   -----------------------------------------------------------------------------
3191   initialize game engine due to level / tape version number
3192   =============================================================================
3193 */
3194
3195 static void InitGameEngine()
3196 {
3197   int i, j, k, l, x, y;
3198
3199   /* set game engine from tape file when re-playing, else from level file */
3200   game.engine_version = (tape.playing ? tape.engine_version :
3201                          level.game_version);
3202
3203   /* ---------------------------------------------------------------------- */
3204   /* set flags for bugs and changes according to active game engine version */
3205   /* ---------------------------------------------------------------------- */
3206
3207   /*
3208     Summary of bugfix/change:
3209     Fixed handling for custom elements that change when pushed by the player.
3210
3211     Fixed/changed in version:
3212     3.1.0
3213
3214     Description:
3215     Before 3.1.0, custom elements that "change when pushing" changed directly
3216     after the player started pushing them (until then handled in "DigField()").
3217     Since 3.1.0, these custom elements are not changed until the "pushing"
3218     move of the element is finished (now handled in "ContinueMoving()").
3219
3220     Affected levels/tapes:
3221     The first condition is generally needed for all levels/tapes before version
3222     3.1.0, which might use the old behaviour before it was changed; known tapes
3223     that are affected are some tapes from the level set "Walpurgis Gardens" by
3224     Jamie Cullen.
3225     The second condition is an exception from the above case and is needed for
3226     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3227     above (including some development versions of 3.1.0), but before it was
3228     known that this change would break tapes like the above and was fixed in
3229     3.1.1, so that the changed behaviour was active although the engine version
3230     while recording maybe was before 3.1.0. There is at least one tape that is
3231     affected by this exception, which is the tape for the one-level set "Bug
3232     Machine" by Juergen Bonhagen.
3233   */
3234
3235   game.use_change_when_pushing_bug =
3236     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3237      !(tape.playing &&
3238        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3239        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3240
3241   /*
3242     Summary of bugfix/change:
3243     Fixed handling for blocking the field the player leaves when moving.
3244
3245     Fixed/changed in version:
3246     3.1.1
3247
3248     Description:
3249     Before 3.1.1, when "block last field when moving" was enabled, the field
3250     the player is leaving when moving was blocked for the time of the move,
3251     and was directly unblocked afterwards. This resulted in the last field
3252     being blocked for exactly one less than the number of frames of one player
3253     move. Additionally, even when blocking was disabled, the last field was
3254     blocked for exactly one frame.
3255     Since 3.1.1, due to changes in player movement handling, the last field
3256     is not blocked at all when blocking is disabled. When blocking is enabled,
3257     the last field is blocked for exactly the number of frames of one player
3258     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3259     last field is blocked for exactly one more than the number of frames of
3260     one player move.
3261
3262     Affected levels/tapes:
3263     (!!! yet to be determined -- probably many !!!)
3264   */
3265
3266   game.use_block_last_field_bug =
3267     (game.engine_version < VERSION_IDENT(3,1,1,0));
3268
3269   /*
3270     Summary of bugfix/change:
3271     Changed behaviour of CE changes with multiple changes per single frame.
3272
3273     Fixed/changed in version:
3274     3.2.0-6
3275
3276     Description:
3277     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3278     This resulted in race conditions where CEs seem to behave strange in some
3279     situations (where triggered CE changes were just skipped because there was
3280     already a CE change on that tile in the playfield in that engine frame).
3281     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3282     (The number of changes per frame must be limited in any case, because else
3283     it is easily possible to define CE changes that would result in an infinite
3284     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3285     should be set large enough so that it would only be reached in cases where
3286     the corresponding CE change conditions run into a loop. Therefore, it seems
3287     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3288     maximal number of change pages for custom elements.)
3289
3290     Affected levels/tapes:
3291     Probably many.
3292   */
3293
3294 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3295   game.max_num_changes_per_frame = 1;
3296 #else
3297   game.max_num_changes_per_frame =
3298     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3299 #endif
3300
3301   /* ---------------------------------------------------------------------- */
3302
3303   /* default scan direction: scan playfield from top/left to bottom/right */
3304   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3305
3306   /* dynamically adjust element properties according to game engine version */
3307   InitElementPropertiesEngine(game.engine_version);
3308
3309 #if 0
3310   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3311   printf("          tape version == %06d [%s] [file: %06d]\n",
3312          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3313          tape.file_version);
3314   printf("       => game.engine_version == %06d\n", game.engine_version);
3315 #endif
3316
3317   /* ---------- initialize player's initial move delay --------------------- */
3318
3319   /* dynamically adjust player properties according to level information */
3320   for (i = 0; i < MAX_PLAYERS; i++)
3321     game.initial_move_delay_value[i] =
3322       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3323
3324   /* dynamically adjust player properties according to game engine version */
3325   for (i = 0; i < MAX_PLAYERS; i++)
3326     game.initial_move_delay[i] =
3327       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3328        game.initial_move_delay_value[i] : 0);
3329
3330   /* ---------- initialize player's initial push delay --------------------- */
3331
3332   /* dynamically adjust player properties according to game engine version */
3333   game.initial_push_delay_value =
3334     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3335
3336   /* ---------- initialize changing elements ------------------------------- */
3337
3338   /* initialize changing elements information */
3339   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3340   {
3341     struct ElementInfo *ei = &element_info[i];
3342
3343     /* this pointer might have been changed in the level editor */
3344     ei->change = &ei->change_page[0];
3345
3346     if (!IS_CUSTOM_ELEMENT(i))
3347     {
3348       ei->change->target_element = EL_EMPTY_SPACE;
3349       ei->change->delay_fixed = 0;
3350       ei->change->delay_random = 0;
3351       ei->change->delay_frames = 1;
3352     }
3353
3354     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3355     {
3356       ei->has_change_event[j] = FALSE;
3357
3358       ei->event_page_nr[j] = 0;
3359       ei->event_page[j] = &ei->change_page[0];
3360     }
3361   }
3362
3363   /* add changing elements from pre-defined list */
3364   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3365   {
3366     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3367     struct ElementInfo *ei = &element_info[ch_delay->element];
3368
3369     ei->change->target_element       = ch_delay->target_element;
3370     ei->change->delay_fixed          = ch_delay->change_delay;
3371
3372     ei->change->pre_change_function  = ch_delay->pre_change_function;
3373     ei->change->change_function      = ch_delay->change_function;
3374     ei->change->post_change_function = ch_delay->post_change_function;
3375
3376     ei->change->can_change = TRUE;
3377     ei->change->can_change_or_has_action = TRUE;
3378
3379     ei->has_change_event[CE_DELAY] = TRUE;
3380
3381     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3382     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3383   }
3384
3385   /* ---------- initialize internal run-time variables --------------------- */
3386
3387   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3388   {
3389     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3390
3391     for (j = 0; j < ei->num_change_pages; j++)
3392     {
3393       ei->change_page[j].can_change_or_has_action =
3394         (ei->change_page[j].can_change |
3395          ei->change_page[j].has_action);
3396     }
3397   }
3398
3399   /* add change events from custom element configuration */
3400   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3401   {
3402     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3403
3404     for (j = 0; j < ei->num_change_pages; j++)
3405     {
3406       if (!ei->change_page[j].can_change_or_has_action)
3407         continue;
3408
3409       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3410       {
3411         /* only add event page for the first page found with this event */
3412         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3413         {
3414           ei->has_change_event[k] = TRUE;
3415
3416           ei->event_page_nr[k] = j;
3417           ei->event_page[k] = &ei->change_page[j];
3418         }
3419       }
3420     }
3421   }
3422
3423 #if 1
3424   /* ---------- initialize reference elements in change conditions --------- */
3425
3426   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3427   {
3428     int element = EL_CUSTOM_START + i;
3429     struct ElementInfo *ei = &element_info[element];
3430
3431     for (j = 0; j < ei->num_change_pages; j++)
3432     {
3433       int trigger_element = ei->change_page[j].initial_trigger_element;
3434
3435       if (trigger_element >= EL_PREV_CE_8 &&
3436           trigger_element <= EL_NEXT_CE_8)
3437         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3438
3439       ei->change_page[j].trigger_element = trigger_element;
3440     }
3441   }
3442 #endif
3443
3444   /* ---------- initialize run-time trigger player and element ------------- */
3445
3446   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3447   {
3448     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3449
3450     for (j = 0; j < ei->num_change_pages; j++)
3451     {
3452       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3453       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3454       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3455       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3456       ei->change_page[j].actual_trigger_ce_value = 0;
3457       ei->change_page[j].actual_trigger_ce_score = 0;
3458     }
3459   }
3460
3461   /* ---------- initialize trigger events ---------------------------------- */
3462
3463   /* initialize trigger events information */
3464   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3465     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3466       trigger_events[i][j] = FALSE;
3467
3468   /* add trigger events from element change event properties */
3469   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3470   {
3471     struct ElementInfo *ei = &element_info[i];
3472
3473     for (j = 0; j < ei->num_change_pages; j++)
3474     {
3475       if (!ei->change_page[j].can_change_or_has_action)
3476         continue;
3477
3478       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3479       {
3480         int trigger_element = ei->change_page[j].trigger_element;
3481
3482         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3483         {
3484           if (ei->change_page[j].has_event[k])
3485           {
3486             if (IS_GROUP_ELEMENT(trigger_element))
3487             {
3488               struct ElementGroupInfo *group =
3489                 element_info[trigger_element].group;
3490
3491               for (l = 0; l < group->num_elements_resolved; l++)
3492                 trigger_events[group->element_resolved[l]][k] = TRUE;
3493             }
3494             else if (trigger_element == EL_ANY_ELEMENT)
3495               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3496                 trigger_events[l][k] = TRUE;
3497             else
3498               trigger_events[trigger_element][k] = TRUE;
3499           }
3500         }
3501       }
3502     }
3503   }
3504
3505   /* ---------- initialize push delay -------------------------------------- */
3506
3507   /* initialize push delay values to default */
3508   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3509   {
3510     if (!IS_CUSTOM_ELEMENT(i))
3511     {
3512       /* set default push delay values (corrected since version 3.0.7-1) */
3513       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3514       {
3515         element_info[i].push_delay_fixed = 2;
3516         element_info[i].push_delay_random = 8;
3517       }
3518       else
3519       {
3520         element_info[i].push_delay_fixed = 8;
3521         element_info[i].push_delay_random = 8;
3522       }
3523     }
3524   }
3525
3526   /* set push delay value for certain elements from pre-defined list */
3527   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3528   {
3529     int e = push_delay_list[i].element;
3530
3531     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3532     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3533   }
3534
3535   /* set push delay value for Supaplex elements for newer engine versions */
3536   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3537   {
3538     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3539     {
3540       if (IS_SP_ELEMENT(i))
3541       {
3542         /* set SP push delay to just enough to push under a falling zonk */
3543         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3544
3545         element_info[i].push_delay_fixed  = delay;
3546         element_info[i].push_delay_random = 0;
3547       }
3548     }
3549   }
3550
3551   /* ---------- initialize move stepsize ----------------------------------- */
3552
3553   /* initialize move stepsize values to default */
3554   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3555     if (!IS_CUSTOM_ELEMENT(i))
3556       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3557
3558   /* set move stepsize value for certain elements from pre-defined list */
3559   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3560   {
3561     int e = move_stepsize_list[i].element;
3562
3563     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3564   }
3565
3566   /* ---------- initialize collect score ----------------------------------- */
3567
3568   /* initialize collect score values for custom elements from initial value */
3569   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3570     if (IS_CUSTOM_ELEMENT(i))
3571       element_info[i].collect_score = element_info[i].collect_score_initial;
3572
3573   /* ---------- initialize collect count ----------------------------------- */
3574
3575   /* initialize collect count values for non-custom elements */
3576   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3577     if (!IS_CUSTOM_ELEMENT(i))
3578       element_info[i].collect_count_initial = 0;
3579
3580   /* add collect count values for all elements from pre-defined list */
3581   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3582     element_info[collect_count_list[i].element].collect_count_initial =
3583       collect_count_list[i].count;
3584
3585   /* ---------- initialize access direction -------------------------------- */
3586
3587   /* initialize access direction values to default (access from every side) */
3588   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3589     if (!IS_CUSTOM_ELEMENT(i))
3590       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3591
3592   /* set access direction value for certain elements from pre-defined list */
3593   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3594     element_info[access_direction_list[i].element].access_direction =
3595       access_direction_list[i].direction;
3596
3597   /* ---------- initialize explosion content ------------------------------- */
3598   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3599   {
3600     if (IS_CUSTOM_ELEMENT(i))
3601       continue;
3602
3603     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3604     {
3605       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3606
3607       element_info[i].content.e[x][y] =
3608         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3609          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3610          i == EL_PLAYER_3 ? EL_EMERALD :
3611          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3612          i == EL_MOLE ? EL_EMERALD_RED :
3613          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3614          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3615          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3616          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3617          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3618          i == EL_WALL_EMERALD ? EL_EMERALD :
3619          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3620          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3621          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3622          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3623          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3624          i == EL_WALL_PEARL ? EL_PEARL :
3625          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3626          EL_EMPTY);
3627     }
3628   }
3629
3630   /* ---------- initialize recursion detection ------------------------------ */
3631   recursion_loop_depth = 0;
3632   recursion_loop_detected = FALSE;
3633   recursion_loop_element = EL_UNDEFINED;
3634
3635   /* ---------- initialize graphics engine ---------------------------------- */
3636   game.scroll_delay_value =
3637     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3638      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3639   game.scroll_delay_value =
3640     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3641 }
3642
3643 int get_num_special_action(int element, int action_first, int action_last)
3644 {
3645   int num_special_action = 0;
3646   int i, j;
3647
3648   for (i = action_first; i <= action_last; i++)
3649   {
3650     boolean found = FALSE;
3651
3652     for (j = 0; j < NUM_DIRECTIONS; j++)
3653       if (el_act_dir2img(element, i, j) !=
3654           el_act_dir2img(element, ACTION_DEFAULT, j))
3655         found = TRUE;
3656
3657     if (found)
3658       num_special_action++;
3659     else
3660       break;
3661   }
3662
3663   return num_special_action;
3664 }
3665
3666
3667 /*
3668   =============================================================================
3669   InitGame()
3670   -----------------------------------------------------------------------------
3671   initialize and start new game
3672   =============================================================================
3673 */
3674
3675 void InitGame()
3676 {
3677   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3678   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3679   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3680 #if 0
3681   boolean do_fading = (game_status == GAME_MODE_MAIN);
3682 #endif
3683   int i, j, x, y;
3684
3685   game_status = GAME_MODE_PLAYING;
3686
3687   InitGameEngine();
3688   InitGameControlValues();
3689
3690   /* don't play tapes over network */
3691   network_playing = (options.network && !tape.playing);
3692
3693   for (i = 0; i < MAX_PLAYERS; i++)
3694   {
3695     struct PlayerInfo *player = &stored_player[i];
3696
3697     player->index_nr = i;
3698     player->index_bit = (1 << i);
3699     player->element_nr = EL_PLAYER_1 + i;
3700
3701     player->present = FALSE;
3702     player->active = FALSE;
3703     player->killed = FALSE;
3704
3705     player->action = 0;
3706     player->effective_action = 0;
3707     player->programmed_action = 0;
3708
3709     player->score = 0;
3710     player->score_final = 0;
3711
3712     player->gems_still_needed = level.gems_needed;
3713     player->sokobanfields_still_needed = 0;
3714     player->lights_still_needed = 0;
3715     player->friends_still_needed = 0;
3716
3717     for (j = 0; j < MAX_NUM_KEYS; j++)
3718       player->key[j] = FALSE;
3719
3720     player->num_white_keys = 0;
3721
3722     player->dynabomb_count = 0;
3723     player->dynabomb_size = 1;
3724     player->dynabombs_left = 0;
3725     player->dynabomb_xl = FALSE;
3726
3727     player->MovDir = MV_NONE;
3728     player->MovPos = 0;
3729     player->GfxPos = 0;
3730     player->GfxDir = MV_NONE;
3731     player->GfxAction = ACTION_DEFAULT;
3732     player->Frame = 0;
3733     player->StepFrame = 0;
3734
3735     player->initial_element = player->element_nr;
3736     player->artwork_element =
3737       (level.use_artwork_element[i] ? level.artwork_element[i] :
3738        player->element_nr);
3739     player->use_murphy = FALSE;
3740
3741     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3742     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3743
3744     player->gravity = level.initial_player_gravity[i];
3745
3746     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3747
3748     player->actual_frame_counter = 0;
3749
3750     player->step_counter = 0;
3751
3752     player->last_move_dir = MV_NONE;
3753
3754     player->is_active = FALSE;
3755
3756     player->is_waiting = FALSE;
3757     player->is_moving = FALSE;
3758     player->is_auto_moving = FALSE;
3759     player->is_digging = FALSE;
3760     player->is_snapping = FALSE;
3761     player->is_collecting = FALSE;
3762     player->is_pushing = FALSE;
3763     player->is_switching = FALSE;
3764     player->is_dropping = FALSE;
3765     player->is_dropping_pressed = FALSE;
3766
3767     player->is_bored = FALSE;
3768     player->is_sleeping = FALSE;
3769
3770     player->frame_counter_bored = -1;
3771     player->frame_counter_sleeping = -1;
3772
3773     player->anim_delay_counter = 0;
3774     player->post_delay_counter = 0;
3775
3776     player->dir_waiting = MV_NONE;
3777     player->action_waiting = ACTION_DEFAULT;
3778     player->last_action_waiting = ACTION_DEFAULT;
3779     player->special_action_bored = ACTION_DEFAULT;
3780     player->special_action_sleeping = ACTION_DEFAULT;
3781
3782     player->switch_x = -1;
3783     player->switch_y = -1;
3784
3785     player->drop_x = -1;
3786     player->drop_y = -1;
3787
3788     player->show_envelope = 0;
3789
3790     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3791
3792     player->push_delay       = -1;      /* initialized when pushing starts */
3793     player->push_delay_value = game.initial_push_delay_value;
3794
3795     player->drop_delay = 0;
3796     player->drop_pressed_delay = 0;
3797
3798     player->last_jx = -1;
3799     player->last_jy = -1;
3800     player->jx = -1;
3801     player->jy = -1;
3802
3803     player->shield_normal_time_left = 0;
3804     player->shield_deadly_time_left = 0;
3805
3806     player->inventory_infinite_element = EL_UNDEFINED;
3807     player->inventory_size = 0;
3808
3809     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3810     SnapField(player, 0, 0);
3811
3812     player->LevelSolved = FALSE;
3813     player->GameOver = FALSE;
3814
3815     player->LevelSolved_GameWon = FALSE;
3816     player->LevelSolved_GameEnd = FALSE;
3817     player->LevelSolved_PanelOff = FALSE;
3818     player->LevelSolved_SaveTape = FALSE;
3819     player->LevelSolved_SaveScore = FALSE;
3820     player->LevelSolved_CountingTime = 0;
3821     player->LevelSolved_CountingScore = 0;
3822   }
3823
3824   network_player_action_received = FALSE;
3825
3826 #if defined(NETWORK_AVALIABLE)
3827   /* initial null action */
3828   if (network_playing)
3829     SendToServer_MovePlayer(MV_NONE);
3830 #endif
3831
3832   ZX = ZY = -1;
3833   ExitX = ExitY = -1;
3834
3835   FrameCounter = 0;
3836   TimeFrames = 0;
3837   TimePlayed = 0;
3838   TimeLeft = level.time;
3839   TapeTime = 0;
3840
3841   ScreenMovDir = MV_NONE;
3842   ScreenMovPos = 0;
3843   ScreenGfxPos = 0;
3844
3845   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3846
3847   AllPlayersGone = FALSE;
3848
3849   game.yamyam_content_nr = 0;
3850   game.robot_wheel_active = FALSE;
3851   game.magic_wall_active = FALSE;
3852   game.magic_wall_time_left = 0;
3853   game.light_time_left = 0;
3854   game.timegate_time_left = 0;
3855   game.switchgate_pos = 0;
3856   game.wind_direction = level.wind_direction_initial;
3857
3858 #if !USE_PLAYER_GRAVITY
3859   game.gravity = FALSE;
3860   game.explosions_delayed = TRUE;
3861 #endif
3862
3863   game.lenses_time_left = 0;
3864   game.magnify_time_left = 0;
3865
3866   game.ball_state = level.ball_state_initial;
3867   game.ball_content_nr = 0;
3868
3869   game.envelope_active = FALSE;
3870
3871   /* set focus to local player for network games, else to all players */
3872   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3873   game.centered_player_nr_next = game.centered_player_nr;
3874   game.set_centered_player = FALSE;
3875
3876   if (network_playing && tape.recording)
3877   {
3878     /* store client dependent player focus when recording network games */
3879     tape.centered_player_nr_next = game.centered_player_nr_next;
3880     tape.set_centered_player = TRUE;
3881   }
3882
3883   for (i = 0; i < NUM_BELTS; i++)
3884   {
3885     game.belt_dir[i] = MV_NONE;
3886     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3887   }
3888
3889   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3890     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3891
3892   SCAN_PLAYFIELD(x, y)
3893   {
3894     Feld[x][y] = level.field[x][y];
3895     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3896     ChangeDelay[x][y] = 0;
3897     ChangePage[x][y] = -1;
3898 #if USE_NEW_CUSTOM_VALUE
3899     CustomValue[x][y] = 0;              /* initialized in InitField() */
3900 #endif
3901     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3902     AmoebaNr[x][y] = 0;
3903     WasJustMoving[x][y] = 0;
3904     WasJustFalling[x][y] = 0;
3905     CheckCollision[x][y] = 0;
3906     CheckImpact[x][y] = 0;
3907     Stop[x][y] = FALSE;
3908     Pushed[x][y] = FALSE;
3909
3910     ChangeCount[x][y] = 0;
3911     ChangeEvent[x][y] = -1;
3912
3913     ExplodePhase[x][y] = 0;
3914     ExplodeDelay[x][y] = 0;
3915     ExplodeField[x][y] = EX_TYPE_NONE;
3916
3917     RunnerVisit[x][y] = 0;
3918     PlayerVisit[x][y] = 0;
3919
3920     GfxFrame[x][y] = 0;
3921     GfxRandom[x][y] = INIT_GFX_RANDOM();
3922     GfxElement[x][y] = EL_UNDEFINED;
3923     GfxAction[x][y] = ACTION_DEFAULT;
3924     GfxDir[x][y] = MV_NONE;
3925     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3926   }
3927
3928   SCAN_PLAYFIELD(x, y)
3929   {
3930     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3931       emulate_bd = FALSE;
3932     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3933       emulate_sb = FALSE;
3934     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3935       emulate_sp = FALSE;
3936
3937     InitField(x, y, TRUE);
3938
3939     ResetGfxAnimation(x, y);
3940   }
3941
3942   InitBeltMovement();
3943
3944   for (i = 0; i < MAX_PLAYERS; i++)
3945   {
3946     struct PlayerInfo *player = &stored_player[i];
3947
3948     /* set number of special actions for bored and sleeping animation */
3949     player->num_special_action_bored =
3950       get_num_special_action(player->artwork_element,
3951                              ACTION_BORING_1, ACTION_BORING_LAST);
3952     player->num_special_action_sleeping =
3953       get_num_special_action(player->artwork_element,
3954                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3955   }
3956
3957   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3958                     emulate_sb ? EMU_SOKOBAN :
3959                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3960
3961 #if USE_NEW_ALL_SLIPPERY
3962   /* initialize type of slippery elements */
3963   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3964   {
3965     if (!IS_CUSTOM_ELEMENT(i))
3966     {
3967       /* default: elements slip down either to the left or right randomly */
3968       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3969
3970       /* SP style elements prefer to slip down on the left side */
3971       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3972         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3973
3974       /* BD style elements prefer to slip down on the left side */
3975       if (game.emulation == EMU_BOULDERDASH)
3976         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3977     }
3978   }
3979 #endif
3980
3981   /* initialize explosion and ignition delay */
3982   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3983   {
3984     if (!IS_CUSTOM_ELEMENT(i))
3985     {
3986       int num_phase = 8;
3987       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3988                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3989                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3990       int last_phase = (num_phase + 1) * delay;
3991       int half_phase = (num_phase / 2) * delay;
3992
3993       element_info[i].explosion_delay = last_phase - 1;
3994       element_info[i].ignition_delay = half_phase;
3995
3996       if (i == EL_BLACK_ORB)
3997         element_info[i].ignition_delay = 1;
3998     }
3999
4000 #if 0
4001     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4002       element_info[i].explosion_delay = 1;
4003
4004     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4005       element_info[i].ignition_delay = 1;
4006 #endif
4007   }
4008
4009   /* correct non-moving belts to start moving left */
4010   for (i = 0; i < NUM_BELTS; i++)
4011     if (game.belt_dir[i] == MV_NONE)
4012       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4013
4014   /* check if any connected player was not found in playfield */
4015   for (i = 0; i < MAX_PLAYERS; i++)
4016   {
4017     struct PlayerInfo *player = &stored_player[i];
4018
4019     if (player->connected && !player->present)
4020     {
4021       for (j = 0; j < MAX_PLAYERS; j++)
4022       {
4023         struct PlayerInfo *some_player = &stored_player[j];
4024         int jx = some_player->jx, jy = some_player->jy;
4025
4026         /* assign first free player found that is present in the playfield */
4027         if (some_player->present && !some_player->connected)
4028         {
4029           player->present = TRUE;
4030           player->active = TRUE;
4031
4032           some_player->present = FALSE;
4033           some_player->active = FALSE;
4034
4035           player->initial_element = some_player->initial_element;
4036           player->artwork_element = some_player->artwork_element;
4037
4038           player->block_last_field       = some_player->block_last_field;
4039           player->block_delay_adjustment = some_player->block_delay_adjustment;
4040
4041           StorePlayer[jx][jy] = player->element_nr;
4042           player->jx = player->last_jx = jx;
4043           player->jy = player->last_jy = jy;
4044
4045           break;
4046         }
4047       }
4048     }
4049   }
4050
4051   if (tape.playing)
4052   {
4053     /* when playing a tape, eliminate all players who do not participate */
4054
4055     for (i = 0; i < MAX_PLAYERS; i++)
4056     {
4057       if (stored_player[i].active && !tape.player_participates[i])
4058       {
4059         struct PlayerInfo *player = &stored_player[i];
4060         int jx = player->jx, jy = player->jy;
4061
4062         player->active = FALSE;
4063         StorePlayer[jx][jy] = 0;
4064         Feld[jx][jy] = EL_EMPTY;
4065       }
4066     }
4067   }
4068   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4069   {
4070     /* when in single player mode, eliminate all but the first active player */
4071
4072     for (i = 0; i < MAX_PLAYERS; i++)
4073     {
4074       if (stored_player[i].active)
4075       {
4076         for (j = i + 1; j < MAX_PLAYERS; j++)
4077         {
4078           if (stored_player[j].active)
4079           {
4080             struct PlayerInfo *player = &stored_player[j];
4081             int jx = player->jx, jy = player->jy;
4082
4083             player->active = FALSE;
4084             player->present = FALSE;
4085
4086             StorePlayer[jx][jy] = 0;
4087             Feld[jx][jy] = EL_EMPTY;
4088           }
4089         }
4090       }
4091     }
4092   }
4093
4094   /* when recording the game, store which players take part in the game */
4095   if (tape.recording)
4096   {
4097     for (i = 0; i < MAX_PLAYERS; i++)
4098       if (stored_player[i].active)
4099         tape.player_participates[i] = TRUE;
4100   }
4101
4102   if (options.debug)
4103   {
4104     for (i = 0; i < MAX_PLAYERS; i++)
4105     {
4106       struct PlayerInfo *player = &stored_player[i];
4107
4108       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4109              i+1,
4110              player->present,
4111              player->connected,
4112              player->active);
4113       if (local_player == player)
4114         printf("Player  %d is local player.\n", i+1);
4115     }
4116   }
4117
4118   if (BorderElement == EL_EMPTY)
4119   {
4120     SBX_Left = 0;
4121     SBX_Right = lev_fieldx - SCR_FIELDX;
4122     SBY_Upper = 0;
4123     SBY_Lower = lev_fieldy - SCR_FIELDY;
4124   }
4125   else
4126   {
4127     SBX_Left = -1;
4128     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4129     SBY_Upper = -1;
4130     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4131   }
4132
4133   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4134     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4135
4136   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4137     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4138
4139   /* if local player not found, look for custom element that might create
4140      the player (make some assumptions about the right custom element) */
4141   if (!local_player->present)
4142   {
4143     int start_x = 0, start_y = 0;
4144     int found_rating = 0;
4145     int found_element = EL_UNDEFINED;
4146     int player_nr = local_player->index_nr;
4147
4148     SCAN_PLAYFIELD(x, y)
4149     {
4150       int element = Feld[x][y];
4151       int content;
4152       int xx, yy;
4153       boolean is_player;
4154
4155       if (level.use_start_element[player_nr] &&
4156           level.start_element[player_nr] == element &&
4157           found_rating < 4)
4158       {
4159         start_x = x;
4160         start_y = y;
4161
4162         found_rating = 4;
4163         found_element = element;
4164       }
4165
4166       if (!IS_CUSTOM_ELEMENT(element))
4167         continue;
4168
4169       if (CAN_CHANGE(element))
4170       {
4171         for (i = 0; i < element_info[element].num_change_pages; i++)
4172         {
4173           /* check for player created from custom element as single target */
4174           content = element_info[element].change_page[i].target_element;
4175           is_player = ELEM_IS_PLAYER(content);
4176
4177           if (is_player && (found_rating < 3 ||
4178                             (found_rating == 3 && element < found_element)))
4179           {
4180             start_x = x;
4181             start_y = y;
4182
4183             found_rating = 3;
4184             found_element = element;
4185           }
4186         }
4187       }
4188
4189       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4190       {
4191         /* check for player created from custom element as explosion content */
4192         content = element_info[element].content.e[xx][yy];
4193         is_player = ELEM_IS_PLAYER(content);
4194
4195         if (is_player && (found_rating < 2 ||
4196                           (found_rating == 2 && element < found_element)))
4197         {
4198           start_x = x + xx - 1;
4199           start_y = y + yy - 1;
4200
4201           found_rating = 2;
4202           found_element = element;
4203         }
4204
4205         if (!CAN_CHANGE(element))
4206           continue;
4207
4208         for (i = 0; i < element_info[element].num_change_pages; i++)
4209         {
4210           /* check for player created from custom element as extended target */
4211           content =
4212             element_info[element].change_page[i].target_content.e[xx][yy];
4213
4214           is_player = ELEM_IS_PLAYER(content);
4215
4216           if (is_player && (found_rating < 1 ||
4217                             (found_rating == 1 && element < found_element)))
4218           {
4219             start_x = x + xx - 1;
4220             start_y = y + yy - 1;
4221
4222             found_rating = 1;
4223             found_element = element;
4224           }
4225         }
4226       }
4227     }
4228
4229     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4230                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4231                 start_x - MIDPOSX);
4232
4233     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4234                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4235                 start_y - MIDPOSY);
4236   }
4237   else
4238   {
4239     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4240                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4241                 local_player->jx - MIDPOSX);
4242
4243     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4244                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4245                 local_player->jy - MIDPOSY);
4246   }
4247
4248 #if 0
4249   /* do not use PLAYING mask for fading out from main screen */
4250   game_status = GAME_MODE_MAIN;
4251 #endif
4252
4253   StopAnimation();
4254
4255   if (!game.restart_level)
4256     CloseDoor(DOOR_CLOSE_1);
4257
4258 #if 1
4259   if (level_editor_test_game)
4260     FadeSkipNextFadeIn();
4261   else
4262     FadeSetEnterScreen();
4263 #else
4264   if (level_editor_test_game)
4265     fading = fading_none;
4266   else
4267     fading = menu.destination;
4268 #endif
4269
4270 #if 1
4271   FadeOut(REDRAW_FIELD);
4272 #else
4273   if (do_fading)
4274     FadeOut(REDRAW_FIELD);
4275 #endif
4276
4277 #if 0
4278   game_status = GAME_MODE_PLAYING;
4279 #endif
4280
4281   /* !!! FIX THIS (START) !!! */
4282   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4283   {
4284     InitGameEngine_EM();
4285
4286     /* blit playfield from scroll buffer to normal back buffer for fading in */
4287     BlitScreenToBitmap_EM(backbuffer);
4288   }
4289   else
4290   {
4291     DrawLevel();
4292     DrawAllPlayers();
4293
4294     /* after drawing the level, correct some elements */
4295     if (game.timegate_time_left == 0)
4296       CloseAllOpenTimegates();
4297
4298     /* blit playfield from scroll buffer to normal back buffer for fading in */
4299     if (setup.soft_scrolling)
4300       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4301
4302     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4303   }
4304   /* !!! FIX THIS (END) !!! */
4305
4306 #if 1
4307   FadeIn(REDRAW_FIELD);
4308 #else
4309   if (do_fading)
4310     FadeIn(REDRAW_FIELD);
4311
4312   BackToFront();
4313 #endif
4314
4315   if (!game.restart_level)
4316   {
4317     /* copy default game door content to main double buffer */
4318     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4319                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4320   }
4321
4322   SetPanelBackground();
4323   SetDrawBackgroundMask(REDRAW_DOOR_1);
4324
4325 #if 1
4326   UpdateAndDisplayGameControlValues();
4327 #else
4328   UpdateGameDoorValues();
4329   DrawGameDoorValues();
4330 #endif
4331
4332   if (!game.restart_level)
4333   {
4334     UnmapGameButtons();
4335     UnmapTapeButtons();
4336     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4337     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4338     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4339     MapGameButtons();
4340     MapTapeButtons();
4341
4342     /* copy actual game door content to door double buffer for OpenDoor() */
4343     BlitBitmap(drawto, bitmap_db_door,
4344                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4345
4346     OpenDoor(DOOR_OPEN_ALL);
4347
4348     PlaySound(SND_GAME_STARTING);
4349
4350     if (setup.sound_music)
4351       PlayLevelMusic();
4352
4353     KeyboardAutoRepeatOffUnlessAutoplay();
4354
4355     if (options.debug)
4356     {
4357       for (i = 0; i < MAX_PLAYERS; i++)
4358         printf("Player %d %sactive.\n",
4359                i + 1, (stored_player[i].active ? "" : "not "));
4360     }
4361   }
4362
4363 #if 1
4364   UnmapAllGadgets();
4365
4366   MapGameButtons();
4367   MapTapeButtons();
4368 #endif
4369
4370   game.restart_level = FALSE;
4371 }
4372
4373 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4374 {
4375   /* this is used for non-R'n'D game engines to update certain engine values */
4376
4377   /* needed to determine if sounds are played within the visible screen area */
4378   scroll_x = actual_scroll_x;
4379   scroll_y = actual_scroll_y;
4380 }
4381
4382 void InitMovDir(int x, int y)
4383 {
4384   int i, element = Feld[x][y];
4385   static int xy[4][2] =
4386   {
4387     {  0, +1 },
4388     { +1,  0 },
4389     {  0, -1 },
4390     { -1,  0 }
4391   };
4392   static int direction[3][4] =
4393   {
4394     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4395     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4396     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4397   };
4398
4399   switch (element)
4400   {
4401     case EL_BUG_RIGHT:
4402     case EL_BUG_UP:
4403     case EL_BUG_LEFT:
4404     case EL_BUG_DOWN:
4405       Feld[x][y] = EL_BUG;
4406       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4407       break;
4408
4409     case EL_SPACESHIP_RIGHT:
4410     case EL_SPACESHIP_UP:
4411     case EL_SPACESHIP_LEFT:
4412     case EL_SPACESHIP_DOWN:
4413       Feld[x][y] = EL_SPACESHIP;
4414       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4415       break;
4416
4417     case EL_BD_BUTTERFLY_RIGHT:
4418     case EL_BD_BUTTERFLY_UP:
4419     case EL_BD_BUTTERFLY_LEFT:
4420     case EL_BD_BUTTERFLY_DOWN:
4421       Feld[x][y] = EL_BD_BUTTERFLY;
4422       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4423       break;
4424
4425     case EL_BD_FIREFLY_RIGHT:
4426     case EL_BD_FIREFLY_UP:
4427     case EL_BD_FIREFLY_LEFT:
4428     case EL_BD_FIREFLY_DOWN:
4429       Feld[x][y] = EL_BD_FIREFLY;
4430       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4431       break;
4432
4433     case EL_PACMAN_RIGHT:
4434     case EL_PACMAN_UP:
4435     case EL_PACMAN_LEFT:
4436     case EL_PACMAN_DOWN:
4437       Feld[x][y] = EL_PACMAN;
4438       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4439       break;
4440
4441     case EL_YAMYAM_LEFT:
4442     case EL_YAMYAM_RIGHT:
4443     case EL_YAMYAM_UP:
4444     case EL_YAMYAM_DOWN:
4445       Feld[x][y] = EL_YAMYAM;
4446       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4447       break;
4448
4449     case EL_SP_SNIKSNAK:
4450       MovDir[x][y] = MV_UP;
4451       break;
4452
4453     case EL_SP_ELECTRON:
4454       MovDir[x][y] = MV_LEFT;
4455       break;
4456
4457     case EL_MOLE_LEFT:
4458     case EL_MOLE_RIGHT:
4459     case EL_MOLE_UP:
4460     case EL_MOLE_DOWN:
4461       Feld[x][y] = EL_MOLE;
4462       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4463       break;
4464
4465     default:
4466       if (IS_CUSTOM_ELEMENT(element))
4467       {
4468         struct ElementInfo *ei = &element_info[element];
4469         int move_direction_initial = ei->move_direction_initial;
4470         int move_pattern = ei->move_pattern;
4471
4472         if (move_direction_initial == MV_START_PREVIOUS)
4473         {
4474           if (MovDir[x][y] != MV_NONE)
4475             return;
4476
4477           move_direction_initial = MV_START_AUTOMATIC;
4478         }
4479
4480         if (move_direction_initial == MV_START_RANDOM)
4481           MovDir[x][y] = 1 << RND(4);
4482         else if (move_direction_initial & MV_ANY_DIRECTION)
4483           MovDir[x][y] = move_direction_initial;
4484         else if (move_pattern == MV_ALL_DIRECTIONS ||
4485                  move_pattern == MV_TURNING_LEFT ||
4486                  move_pattern == MV_TURNING_RIGHT ||
4487                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4488                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4489                  move_pattern == MV_TURNING_RANDOM)
4490           MovDir[x][y] = 1 << RND(4);
4491         else if (move_pattern == MV_HORIZONTAL)
4492           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4493         else if (move_pattern == MV_VERTICAL)
4494           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4495         else if (move_pattern & MV_ANY_DIRECTION)
4496           MovDir[x][y] = element_info[element].move_pattern;
4497         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4498                  move_pattern == MV_ALONG_RIGHT_SIDE)
4499         {
4500           /* use random direction as default start direction */
4501           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4502             MovDir[x][y] = 1 << RND(4);
4503
4504           for (i = 0; i < NUM_DIRECTIONS; i++)
4505           {
4506             int x1 = x + xy[i][0];
4507             int y1 = y + xy[i][1];
4508
4509             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4510             {
4511               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4512                 MovDir[x][y] = direction[0][i];
4513               else
4514                 MovDir[x][y] = direction[1][i];
4515
4516               break;
4517             }
4518           }
4519         }                
4520       }
4521       else
4522       {
4523         MovDir[x][y] = 1 << RND(4);
4524
4525         if (element != EL_BUG &&
4526             element != EL_SPACESHIP &&
4527             element != EL_BD_BUTTERFLY &&
4528             element != EL_BD_FIREFLY)
4529           break;
4530
4531         for (i = 0; i < NUM_DIRECTIONS; i++)
4532         {
4533           int x1 = x + xy[i][0];
4534           int y1 = y + xy[i][1];
4535
4536           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4537           {
4538             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4539             {
4540               MovDir[x][y] = direction[0][i];
4541               break;
4542             }
4543             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4544                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4545             {
4546               MovDir[x][y] = direction[1][i];
4547               break;
4548             }
4549           }
4550         }
4551       }
4552       break;
4553   }
4554
4555   GfxDir[x][y] = MovDir[x][y];
4556 }
4557
4558 void InitAmoebaNr(int x, int y)
4559 {
4560   int i;
4561   int group_nr = AmoebeNachbarNr(x, y);
4562
4563   if (group_nr == 0)
4564   {
4565     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4566     {
4567       if (AmoebaCnt[i] == 0)
4568       {
4569         group_nr = i;
4570         break;
4571       }
4572     }
4573   }
4574
4575   AmoebaNr[x][y] = group_nr;
4576   AmoebaCnt[group_nr]++;
4577   AmoebaCnt2[group_nr]++;
4578 }
4579
4580 static void PlayerWins(struct PlayerInfo *player)
4581 {
4582   player->LevelSolved = TRUE;
4583   player->GameOver = TRUE;
4584
4585   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4586                          level.native_em_level->lev->score : player->score);
4587
4588   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4589   player->LevelSolved_CountingScore = player->score_final;
4590 }
4591
4592 void GameWon()
4593 {
4594   static int time, time_final;
4595   static int score, score_final;
4596   static int game_over_delay_1 = 0;
4597   static int game_over_delay_2 = 0;
4598   int game_over_delay_value_1 = 50;
4599   int game_over_delay_value_2 = 50;
4600
4601   if (!local_player->LevelSolved_GameWon)
4602   {
4603     int i;
4604
4605     /* do not start end game actions before the player stops moving (to exit) */
4606     if (local_player->MovPos)
4607       return;
4608
4609     local_player->LevelSolved_GameWon = TRUE;
4610     local_player->LevelSolved_SaveTape = tape.recording;
4611     local_player->LevelSolved_SaveScore = !tape.playing;
4612
4613     if (tape.auto_play)         /* tape might already be stopped here */
4614       tape.auto_play_level_solved = TRUE;
4615
4616 #if 1
4617     TapeStop();
4618 #endif
4619
4620     game_over_delay_1 = game_over_delay_value_1;
4621     game_over_delay_2 = game_over_delay_value_2;
4622
4623     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4624     score = score_final = local_player->score_final;
4625
4626     if (TimeLeft > 0)
4627     {
4628       time_final = 0;
4629       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4630     }
4631     else if (level.time == 0 && TimePlayed < 999)
4632     {
4633       time_final = 999;
4634       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4635     }
4636
4637     local_player->score_final = score_final;
4638
4639     if (level_editor_test_game)
4640     {
4641       time = time_final;
4642       score = score_final;
4643
4644 #if 1
4645       local_player->LevelSolved_CountingTime = time;
4646       local_player->LevelSolved_CountingScore = score;
4647
4648       game_panel_controls[GAME_PANEL_TIME].value = time;
4649       game_panel_controls[GAME_PANEL_SCORE].value = score;
4650
4651       DisplayGameControlValues();
4652 #else
4653       DrawGameValue_Time(time);
4654       DrawGameValue_Score(score);
4655 #endif
4656     }
4657
4658     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4659     {
4660       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4661       {
4662         /* close exit door after last player */
4663         if ((AllPlayersGone &&
4664              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4665               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4666               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4667             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4668             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4669         {
4670           int element = Feld[ExitX][ExitY];
4671
4672 #if 0
4673           if (element == EL_EM_EXIT_OPEN ||
4674               element == EL_EM_STEEL_EXIT_OPEN)
4675           {
4676             Bang(ExitX, ExitY);
4677           }
4678           else
4679 #endif
4680           {
4681             Feld[ExitX][ExitY] =
4682               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4683                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4684                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4685                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4686                EL_EM_STEEL_EXIT_CLOSING);
4687
4688             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4689           }
4690         }
4691
4692         /* player disappears */
4693         DrawLevelField(ExitX, ExitY);
4694       }
4695
4696       for (i = 0; i < MAX_PLAYERS; i++)
4697       {
4698         struct PlayerInfo *player = &stored_player[i];
4699
4700         if (player->present)
4701         {
4702           RemovePlayer(player);
4703
4704           /* player disappears */
4705           DrawLevelField(player->jx, player->jy);
4706         }
4707       }
4708     }
4709
4710     PlaySound(SND_GAME_WINNING);
4711   }
4712
4713   if (game_over_delay_1 > 0)
4714   {
4715     game_over_delay_1--;
4716
4717     return;
4718   }
4719
4720   if (time != time_final)
4721   {
4722     int time_to_go = ABS(time_final - time);
4723     int time_count_dir = (time < time_final ? +1 : -1);
4724     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4725
4726     time  += time_count_steps * time_count_dir;
4727     score += time_count_steps * level.score[SC_TIME_BONUS];
4728
4729 #if 1
4730     local_player->LevelSolved_CountingTime = time;
4731     local_player->LevelSolved_CountingScore = score;
4732
4733     game_panel_controls[GAME_PANEL_TIME].value = time;
4734     game_panel_controls[GAME_PANEL_SCORE].value = score;
4735
4736     DisplayGameControlValues();
4737 #else
4738     DrawGameValue_Time(time);
4739     DrawGameValue_Score(score);
4740 #endif
4741
4742     if (time == time_final)
4743       StopSound(SND_GAME_LEVELTIME_BONUS);
4744     else if (setup.sound_loops)
4745       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4746     else
4747       PlaySound(SND_GAME_LEVELTIME_BONUS);
4748
4749     return;
4750   }
4751
4752   local_player->LevelSolved_PanelOff = TRUE;
4753
4754   if (game_over_delay_2 > 0)
4755   {
4756     game_over_delay_2--;
4757
4758     return;
4759   }
4760
4761 #if 1
4762   GameEnd();
4763 #endif
4764 }
4765
4766 void GameEnd()
4767 {
4768   int hi_pos;
4769   boolean raise_level = FALSE;
4770
4771   local_player->LevelSolved_GameEnd = TRUE;
4772
4773   CloseDoor(DOOR_CLOSE_1);
4774
4775   if (local_player->LevelSolved_SaveTape)
4776   {
4777 #if 0
4778     TapeStop();
4779 #endif
4780
4781 #if 1
4782     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4783 #else
4784     SaveTape(tape.level_nr);            /* ask to save tape */
4785 #endif
4786   }
4787
4788   if (level_editor_test_game)
4789   {
4790     game_status = GAME_MODE_MAIN;
4791
4792 #if 1
4793     DrawAndFadeInMainMenu(REDRAW_FIELD);
4794 #else
4795     DrawMainMenu();
4796 #endif
4797
4798     return;
4799   }
4800
4801   if (!local_player->LevelSolved_SaveScore)
4802   {
4803 #if 1
4804     FadeOut(REDRAW_FIELD);
4805 #endif
4806
4807     game_status = GAME_MODE_MAIN;
4808
4809     DrawAndFadeInMainMenu(REDRAW_FIELD);
4810
4811     return;
4812   }
4813
4814   if (level_nr == leveldir_current->handicap_level)
4815   {
4816     leveldir_current->handicap_level++;
4817     SaveLevelSetup_SeriesInfo();
4818   }
4819
4820   if (level_nr < leveldir_current->last_level)
4821     raise_level = TRUE;                 /* advance to next level */
4822
4823   if ((hi_pos = NewHiScore()) >= 0) 
4824   {
4825     game_status = GAME_MODE_SCORES;
4826
4827     DrawHallOfFame(hi_pos);
4828
4829     if (raise_level)
4830     {
4831       level_nr++;
4832       TapeErase();
4833     }
4834   }
4835   else
4836   {
4837 #if 1
4838     FadeOut(REDRAW_FIELD);
4839 #endif
4840
4841     game_status = GAME_MODE_MAIN;
4842
4843     if (raise_level)
4844     {
4845       level_nr++;
4846       TapeErase();
4847     }
4848
4849     DrawAndFadeInMainMenu(REDRAW_FIELD);
4850   }
4851 }
4852
4853 int NewHiScore()
4854 {
4855   int k, l;
4856   int position = -1;
4857
4858   LoadScore(level_nr);
4859
4860   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4861       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4862     return -1;
4863
4864   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4865   {
4866     if (local_player->score_final > highscore[k].Score)
4867     {
4868       /* player has made it to the hall of fame */
4869
4870       if (k < MAX_SCORE_ENTRIES - 1)
4871       {
4872         int m = MAX_SCORE_ENTRIES - 1;
4873
4874 #ifdef ONE_PER_NAME
4875         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4876           if (strEqual(setup.player_name, highscore[l].Name))
4877             m = l;
4878         if (m == k)     /* player's new highscore overwrites his old one */
4879           goto put_into_list;
4880 #endif
4881
4882         for (l = m; l > k; l--)
4883         {
4884           strcpy(highscore[l].Name, highscore[l - 1].Name);
4885           highscore[l].Score = highscore[l - 1].Score;
4886         }
4887       }
4888
4889 #ifdef ONE_PER_NAME
4890       put_into_list:
4891 #endif
4892       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4893       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4894       highscore[k].Score = local_player->score_final; 
4895       position = k;
4896       break;
4897     }
4898
4899 #ifdef ONE_PER_NAME
4900     else if (!strncmp(setup.player_name, highscore[k].Name,
4901                       MAX_PLAYER_NAME_LEN))
4902       break;    /* player already there with a higher score */
4903 #endif
4904
4905   }
4906
4907   if (position >= 0) 
4908     SaveScore(level_nr);
4909
4910   return position;
4911 }
4912
4913 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4914 {
4915   int element = Feld[x][y];
4916   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4917   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4918   int horiz_move = (dx != 0);
4919   int sign = (horiz_move ? dx : dy);
4920   int step = sign * element_info[element].move_stepsize;
4921
4922   /* special values for move stepsize for spring and things on conveyor belt */
4923   if (horiz_move)
4924   {
4925     if (CAN_FALL(element) &&
4926         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4927       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4928     else if (element == EL_SPRING)
4929       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4930   }
4931
4932   return step;
4933 }
4934
4935 inline static int getElementMoveStepsize(int x, int y)
4936 {
4937   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4938 }
4939
4940 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4941 {
4942   if (player->GfxAction != action || player->GfxDir != dir)
4943   {
4944 #if 0
4945     printf("Player frame reset! (%d => %d, %d => %d)\n",
4946            player->GfxAction, action, player->GfxDir, dir);
4947 #endif
4948
4949     player->GfxAction = action;
4950     player->GfxDir = dir;
4951     player->Frame = 0;
4952     player->StepFrame = 0;
4953   }
4954 }
4955
4956 #if USE_GFX_RESET_GFX_ANIMATION
4957 static void ResetGfxFrame(int x, int y, boolean redraw)
4958 {
4959   int element = Feld[x][y];
4960   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4961   int last_gfx_frame = GfxFrame[x][y];
4962
4963   if (graphic_info[graphic].anim_global_sync)
4964     GfxFrame[x][y] = FrameCounter;
4965   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4966     GfxFrame[x][y] = CustomValue[x][y];
4967   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4968     GfxFrame[x][y] = element_info[element].collect_score;
4969   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4970     GfxFrame[x][y] = ChangeDelay[x][y];
4971
4972   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4973     DrawLevelGraphicAnimation(x, y, graphic);
4974 }
4975 #endif
4976
4977 static void ResetGfxAnimation(int x, int y)
4978 {
4979   GfxAction[x][y] = ACTION_DEFAULT;
4980   GfxDir[x][y] = MovDir[x][y];
4981   GfxFrame[x][y] = 0;
4982
4983 #if USE_GFX_RESET_GFX_ANIMATION
4984   ResetGfxFrame(x, y, FALSE);
4985 #endif
4986 }
4987
4988 static void ResetRandomAnimationValue(int x, int y)
4989 {
4990   GfxRandom[x][y] = INIT_GFX_RANDOM();
4991 }
4992
4993 void InitMovingField(int x, int y, int direction)
4994 {
4995   int element = Feld[x][y];
4996   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4997   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4998   int newx = x + dx;
4999   int newy = y + dy;
5000   boolean is_moving_before, is_moving_after;
5001 #if 0
5002   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5003 #endif
5004
5005   /* check if element was/is moving or being moved before/after mode change */
5006 #if 1
5007 #if 1
5008   is_moving_before = (WasJustMoving[x][y] != 0);
5009 #else
5010   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5011   is_moving_before = WasJustMoving[x][y];
5012 #endif
5013 #else
5014   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5015 #endif
5016   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5017
5018   /* reset animation only for moving elements which change direction of moving
5019      or which just started or stopped moving
5020      (else CEs with property "can move" / "not moving" are reset each frame) */
5021 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5022 #if 1
5023   if (is_moving_before != is_moving_after ||
5024       direction != MovDir[x][y])
5025     ResetGfxAnimation(x, y);
5026 #else
5027   if ((is_moving_before || is_moving_after) && !continues_moving)
5028     ResetGfxAnimation(x, y);
5029 #endif
5030 #else
5031   if (!continues_moving)
5032     ResetGfxAnimation(x, y);
5033 #endif
5034
5035   MovDir[x][y] = direction;
5036   GfxDir[x][y] = direction;
5037
5038 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5039   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5040                      direction == MV_DOWN && CAN_FALL(element) ?
5041                      ACTION_FALLING : ACTION_MOVING);
5042 #else
5043   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5044                      ACTION_FALLING : ACTION_MOVING);
5045 #endif
5046
5047   /* this is needed for CEs with property "can move" / "not moving" */
5048
5049   if (is_moving_after)
5050   {
5051     if (Feld[newx][newy] == EL_EMPTY)
5052       Feld[newx][newy] = EL_BLOCKED;
5053
5054     MovDir[newx][newy] = MovDir[x][y];
5055
5056 #if USE_NEW_CUSTOM_VALUE
5057     CustomValue[newx][newy] = CustomValue[x][y];
5058 #endif
5059
5060     GfxFrame[newx][newy] = GfxFrame[x][y];
5061     GfxRandom[newx][newy] = GfxRandom[x][y];
5062     GfxAction[newx][newy] = GfxAction[x][y];
5063     GfxDir[newx][newy] = GfxDir[x][y];
5064   }
5065 }
5066
5067 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5068 {
5069   int direction = MovDir[x][y];
5070   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5071   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5072
5073   *goes_to_x = newx;
5074   *goes_to_y = newy;
5075 }
5076
5077 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5078 {
5079   int oldx = x, oldy = y;
5080   int direction = MovDir[x][y];
5081
5082   if (direction == MV_LEFT)
5083     oldx++;
5084   else if (direction == MV_RIGHT)
5085     oldx--;
5086   else if (direction == MV_UP)
5087     oldy++;
5088   else if (direction == MV_DOWN)
5089     oldy--;
5090
5091   *comes_from_x = oldx;
5092   *comes_from_y = oldy;
5093 }
5094
5095 int MovingOrBlocked2Element(int x, int y)
5096 {
5097   int element = Feld[x][y];
5098
5099   if (element == EL_BLOCKED)
5100   {
5101     int oldx, oldy;
5102
5103     Blocked2Moving(x, y, &oldx, &oldy);
5104     return Feld[oldx][oldy];
5105   }
5106   else
5107     return element;
5108 }
5109
5110 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5111 {
5112   /* like MovingOrBlocked2Element(), but if element is moving
5113      and (x,y) is the field the moving element is just leaving,
5114      return EL_BLOCKED instead of the element value */
5115   int element = Feld[x][y];
5116
5117   if (IS_MOVING(x, y))
5118   {
5119     if (element == EL_BLOCKED)
5120     {
5121       int oldx, oldy;
5122
5123       Blocked2Moving(x, y, &oldx, &oldy);
5124       return Feld[oldx][oldy];
5125     }
5126     else
5127       return EL_BLOCKED;
5128   }
5129   else
5130     return element;
5131 }
5132
5133 static void RemoveField(int x, int y)
5134 {
5135   Feld[x][y] = EL_EMPTY;
5136
5137   MovPos[x][y] = 0;
5138   MovDir[x][y] = 0;
5139   MovDelay[x][y] = 0;
5140
5141 #if USE_NEW_CUSTOM_VALUE
5142   CustomValue[x][y] = 0;
5143 #endif
5144
5145   AmoebaNr[x][y] = 0;
5146   ChangeDelay[x][y] = 0;
5147   ChangePage[x][y] = -1;
5148   Pushed[x][y] = FALSE;
5149
5150 #if 0
5151   ExplodeField[x][y] = EX_TYPE_NONE;
5152 #endif
5153
5154   GfxElement[x][y] = EL_UNDEFINED;
5155   GfxAction[x][y] = ACTION_DEFAULT;
5156   GfxDir[x][y] = MV_NONE;
5157 #if 0
5158   /* !!! this would prevent the removed tile from being redrawn !!! */
5159   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5160 #endif
5161 }
5162
5163 void RemoveMovingField(int x, int y)
5164 {
5165   int oldx = x, oldy = y, newx = x, newy = y;
5166   int element = Feld[x][y];
5167   int next_element = EL_UNDEFINED;
5168
5169   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5170     return;
5171
5172   if (IS_MOVING(x, y))
5173   {
5174     Moving2Blocked(x, y, &newx, &newy);
5175
5176     if (Feld[newx][newy] != EL_BLOCKED)
5177     {
5178       /* element is moving, but target field is not free (blocked), but
5179          already occupied by something different (example: acid pool);
5180          in this case, only remove the moving field, but not the target */
5181
5182       RemoveField(oldx, oldy);
5183
5184       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5185
5186       TEST_DrawLevelField(oldx, oldy);
5187
5188       return;
5189     }
5190   }
5191   else if (element == EL_BLOCKED)
5192   {
5193     Blocked2Moving(x, y, &oldx, &oldy);
5194     if (!IS_MOVING(oldx, oldy))
5195       return;
5196   }
5197
5198   if (element == EL_BLOCKED &&
5199       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5200        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5201        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5202        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5203        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5204        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5205     next_element = get_next_element(Feld[oldx][oldy]);
5206
5207   RemoveField(oldx, oldy);
5208   RemoveField(newx, newy);
5209
5210   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5211
5212   if (next_element != EL_UNDEFINED)
5213     Feld[oldx][oldy] = next_element;
5214
5215   TEST_DrawLevelField(oldx, oldy);
5216   TEST_DrawLevelField(newx, newy);
5217 }
5218
5219 void DrawDynamite(int x, int y)
5220 {
5221   int sx = SCREENX(x), sy = SCREENY(y);
5222   int graphic = el2img(Feld[x][y]);
5223   int frame;
5224
5225   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5226     return;
5227
5228   if (IS_WALKABLE_INSIDE(Back[x][y]))
5229     return;
5230
5231   if (Back[x][y])
5232     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5233   else if (Store[x][y])
5234     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5235
5236   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5237
5238   if (Back[x][y] || Store[x][y])
5239     DrawGraphicThruMask(sx, sy, graphic, frame);
5240   else
5241     DrawGraphic(sx, sy, graphic, frame);
5242 }
5243
5244 void CheckDynamite(int x, int y)
5245 {
5246   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5247   {
5248     MovDelay[x][y]--;
5249
5250     if (MovDelay[x][y] != 0)
5251     {
5252       DrawDynamite(x, y);
5253       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5254
5255       return;
5256     }
5257   }
5258
5259   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5260
5261   Bang(x, y);
5262 }
5263
5264 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5265 {
5266   boolean num_checked_players = 0;
5267   int i;
5268
5269   for (i = 0; i < MAX_PLAYERS; i++)
5270   {
5271     if (stored_player[i].active)
5272     {
5273       int sx = stored_player[i].jx;
5274       int sy = stored_player[i].jy;
5275
5276       if (num_checked_players == 0)
5277       {
5278         *sx1 = *sx2 = sx;
5279         *sy1 = *sy2 = sy;
5280       }
5281       else
5282       {
5283         *sx1 = MIN(*sx1, sx);
5284         *sy1 = MIN(*sy1, sy);
5285         *sx2 = MAX(*sx2, sx);
5286         *sy2 = MAX(*sy2, sy);
5287       }
5288
5289       num_checked_players++;
5290     }
5291   }
5292 }
5293
5294 static boolean checkIfAllPlayersFitToScreen_RND()
5295 {
5296   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5297
5298   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5299
5300   return (sx2 - sx1 < SCR_FIELDX &&
5301           sy2 - sy1 < SCR_FIELDY);
5302 }
5303
5304 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5305 {
5306   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5307
5308   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5309
5310   *sx = (sx1 + sx2) / 2;
5311   *sy = (sy1 + sy2) / 2;
5312 }
5313
5314 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5315                         boolean center_screen, boolean quick_relocation)
5316 {
5317   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5318   boolean no_delay = (tape.warp_forward);
5319   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5320   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5321
5322   if (quick_relocation)
5323   {
5324     int offset = game.scroll_delay_value;
5325
5326     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5327     {
5328       if (!level.shifted_relocation || center_screen)
5329       {
5330         /* quick relocation (without scrolling), with centering of screen */
5331
5332         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5333                     x > SBX_Right + MIDPOSX ? SBX_Right :
5334                     x - MIDPOSX);
5335
5336         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5337                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5338                     y - MIDPOSY);
5339       }
5340       else
5341       {
5342         /* quick relocation (without scrolling), but do not center screen */
5343
5344         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5345                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5346                                old_x - MIDPOSX);
5347
5348         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5349                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5350                                old_y - MIDPOSY);
5351
5352         int offset_x = x + (scroll_x - center_scroll_x);
5353         int offset_y = y + (scroll_y - center_scroll_y);
5354
5355         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5356                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5357                     offset_x - MIDPOSX);
5358
5359         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5360                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5361                     offset_y - MIDPOSY);
5362       }
5363     }
5364     else
5365     {
5366       /* quick relocation (without scrolling), inside visible screen area */
5367
5368       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5369           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5370         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5371
5372       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5373           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5374         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5375
5376       /* don't scroll over playfield boundaries */
5377       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5378         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5379
5380       /* don't scroll over playfield boundaries */
5381       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5382         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5383     }
5384
5385     RedrawPlayfield(TRUE, 0,0,0,0);
5386   }
5387   else
5388   {
5389 #if 1
5390     int scroll_xx, scroll_yy;
5391
5392     if (!level.shifted_relocation || center_screen)
5393     {
5394       /* visible relocation (with scrolling), with centering of screen */
5395
5396       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5397                    x > SBX_Right + MIDPOSX ? SBX_Right :
5398                    x - MIDPOSX);
5399
5400       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5401                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5402                    y - MIDPOSY);
5403     }
5404     else
5405     {
5406       /* visible relocation (with scrolling), but do not center screen */
5407
5408       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5409                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5410                              old_x - MIDPOSX);
5411
5412       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5413                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5414                              old_y - MIDPOSY);
5415
5416       int offset_x = x + (scroll_x - center_scroll_x);
5417       int offset_y = y + (scroll_y - center_scroll_y);
5418
5419       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5420                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5421                    offset_x - MIDPOSX);
5422
5423       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5424                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5425                    offset_y - MIDPOSY);
5426     }
5427
5428 #else
5429
5430     /* visible relocation (with scrolling), with centering of screen */
5431
5432     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5433                      x > SBX_Right + MIDPOSX ? SBX_Right :
5434                      x - MIDPOSX);
5435
5436     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5437                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5438                      y - MIDPOSY);
5439 #endif
5440
5441     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5442
5443     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5444     {
5445       int dx = 0, dy = 0;
5446       int fx = FX, fy = FY;
5447
5448       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5449       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5450
5451       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5452         break;
5453
5454       scroll_x -= dx;
5455       scroll_y -= dy;
5456
5457       fx += dx * TILEX / 2;
5458       fy += dy * TILEY / 2;
5459
5460       ScrollLevel(dx, dy);
5461       DrawAllPlayers();
5462
5463       /* scroll in two steps of half tile size to make things smoother */
5464       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5465       FlushDisplay();
5466       Delay(wait_delay_value);
5467
5468       /* scroll second step to align at full tile size */
5469       BackToFront();
5470       Delay(wait_delay_value);
5471     }
5472
5473     DrawAllPlayers();
5474     BackToFront();
5475     Delay(wait_delay_value);
5476   }
5477 }
5478
5479 void RelocatePlayer(int jx, int jy, int el_player_raw)
5480 {
5481   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5482   int player_nr = GET_PLAYER_NR(el_player);
5483   struct PlayerInfo *player = &stored_player[player_nr];
5484   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5485   boolean no_delay = (tape.warp_forward);
5486   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5487   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5488   int old_jx = player->jx;
5489   int old_jy = player->jy;
5490   int old_element = Feld[old_jx][old_jy];
5491   int element = Feld[jx][jy];
5492   boolean player_relocated = (old_jx != jx || old_jy != jy);
5493
5494   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5495   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5496   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5497   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5498   int leave_side_horiz = move_dir_horiz;
5499   int leave_side_vert  = move_dir_vert;
5500   int enter_side = enter_side_horiz | enter_side_vert;
5501   int leave_side = leave_side_horiz | leave_side_vert;
5502
5503   if (player->GameOver)         /* do not reanimate dead player */
5504     return;
5505
5506   if (!player_relocated)        /* no need to relocate the player */
5507     return;
5508
5509   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5510   {
5511     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5512     DrawLevelField(jx, jy);
5513   }
5514
5515   if (player->present)
5516   {
5517     while (player->MovPos)
5518     {
5519       ScrollPlayer(player, SCROLL_GO_ON);
5520       ScrollScreen(NULL, SCROLL_GO_ON);
5521
5522       AdvanceFrameAndPlayerCounters(player->index_nr);
5523
5524       DrawPlayer(player);
5525
5526       BackToFront();
5527       Delay(wait_delay_value);
5528     }
5529
5530     DrawPlayer(player);         /* needed here only to cleanup last field */
5531     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5532
5533     player->is_moving = FALSE;
5534   }
5535
5536   if (IS_CUSTOM_ELEMENT(old_element))
5537     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5538                                CE_LEFT_BY_PLAYER,
5539                                player->index_bit, leave_side);
5540
5541   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5542                                       CE_PLAYER_LEAVES_X,
5543                                       player->index_bit, leave_side);
5544
5545   Feld[jx][jy] = el_player;
5546   InitPlayerField(jx, jy, el_player, TRUE);
5547
5548   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5549   {
5550     Feld[jx][jy] = element;
5551     InitField(jx, jy, FALSE);
5552   }
5553
5554   /* only visually relocate centered player */
5555   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5556                      FALSE, level.instant_relocation);
5557
5558   TestIfPlayerTouchesBadThing(jx, jy);
5559   TestIfPlayerTouchesCustomElement(jx, jy);
5560
5561   if (IS_CUSTOM_ELEMENT(element))
5562     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5563                                player->index_bit, enter_side);
5564
5565   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5566                                       player->index_bit, enter_side);
5567 }
5568
5569 void Explode(int ex, int ey, int phase, int mode)
5570 {
5571   int x, y;
5572   int last_phase;
5573   int border_element;
5574
5575   /* !!! eliminate this variable !!! */
5576   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5577
5578   if (game.explosions_delayed)
5579   {
5580     ExplodeField[ex][ey] = mode;
5581     return;
5582   }
5583
5584   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5585   {
5586     int center_element = Feld[ex][ey];
5587     int artwork_element, explosion_element;     /* set these values later */
5588
5589 #if 0
5590     /* --- This is only really needed (and now handled) in "Impact()". --- */
5591     /* do not explode moving elements that left the explode field in time */
5592     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5593         center_element == EL_EMPTY &&
5594         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5595       return;
5596 #endif
5597
5598 #if 0
5599     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5600     if (mode == EX_TYPE_NORMAL ||
5601         mode == EX_TYPE_CENTER ||
5602         mode == EX_TYPE_CROSS)
5603       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5604 #endif
5605
5606     /* remove things displayed in background while burning dynamite */
5607     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5608       Back[ex][ey] = 0;
5609
5610     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5611     {
5612       /* put moving element to center field (and let it explode there) */
5613       center_element = MovingOrBlocked2Element(ex, ey);
5614       RemoveMovingField(ex, ey);
5615       Feld[ex][ey] = center_element;
5616     }
5617
5618     /* now "center_element" is finally determined -- set related values now */
5619     artwork_element = center_element;           /* for custom player artwork */
5620     explosion_element = center_element;         /* for custom player artwork */
5621
5622     if (IS_PLAYER(ex, ey))
5623     {
5624       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5625
5626       artwork_element = stored_player[player_nr].artwork_element;
5627
5628       if (level.use_explosion_element[player_nr])
5629       {
5630         explosion_element = level.explosion_element[player_nr];
5631         artwork_element = explosion_element;
5632       }
5633     }
5634
5635 #if 1
5636     if (mode == EX_TYPE_NORMAL ||
5637         mode == EX_TYPE_CENTER ||
5638         mode == EX_TYPE_CROSS)
5639       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5640 #endif
5641
5642     last_phase = element_info[explosion_element].explosion_delay + 1;
5643
5644     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5645     {
5646       int xx = x - ex + 1;
5647       int yy = y - ey + 1;
5648       int element;
5649
5650       if (!IN_LEV_FIELD(x, y) ||
5651           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5652           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5653         continue;
5654
5655       element = Feld[x][y];
5656
5657       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5658       {
5659         element = MovingOrBlocked2Element(x, y);
5660
5661         if (!IS_EXPLOSION_PROOF(element))
5662           RemoveMovingField(x, y);
5663       }
5664
5665       /* indestructible elements can only explode in center (but not flames) */
5666       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5667                                            mode == EX_TYPE_BORDER)) ||
5668           element == EL_FLAMES)
5669         continue;
5670
5671       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5672          behaviour, for example when touching a yamyam that explodes to rocks
5673          with active deadly shield, a rock is created under the player !!! */
5674       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5675 #if 0
5676       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5677           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5678            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5679 #else
5680       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5681 #endif
5682       {
5683         if (IS_ACTIVE_BOMB(element))
5684         {
5685           /* re-activate things under the bomb like gate or penguin */
5686           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5687           Back[x][y] = 0;
5688         }
5689
5690         continue;
5691       }
5692
5693       /* save walkable background elements while explosion on same tile */
5694       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5695           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5696         Back[x][y] = element;
5697
5698       /* ignite explodable elements reached by other explosion */
5699       if (element == EL_EXPLOSION)
5700         element = Store2[x][y];
5701
5702       if (AmoebaNr[x][y] &&
5703           (element == EL_AMOEBA_FULL ||
5704            element == EL_BD_AMOEBA ||
5705            element == EL_AMOEBA_GROWING))
5706       {
5707         AmoebaCnt[AmoebaNr[x][y]]--;
5708         AmoebaCnt2[AmoebaNr[x][y]]--;
5709       }
5710
5711       RemoveField(x, y);
5712
5713       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5714       {
5715         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5716
5717         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5718
5719         if (PLAYERINFO(ex, ey)->use_murphy)
5720           Store[x][y] = EL_EMPTY;
5721       }
5722
5723       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5724          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5725       else if (ELEM_IS_PLAYER(center_element))
5726         Store[x][y] = EL_EMPTY;
5727       else if (center_element == EL_YAMYAM)
5728         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5729       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5730         Store[x][y] = element_info[center_element].content.e[xx][yy];
5731 #if 1
5732       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5733          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5734          otherwise) -- FIX THIS !!! */
5735       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5736         Store[x][y] = element_info[element].content.e[1][1];
5737 #else
5738       else if (!CAN_EXPLODE(element))
5739         Store[x][y] = element_info[element].content.e[1][1];
5740 #endif
5741       else
5742         Store[x][y] = EL_EMPTY;
5743
5744       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5745           center_element == EL_AMOEBA_TO_DIAMOND)
5746         Store2[x][y] = element;
5747
5748       Feld[x][y] = EL_EXPLOSION;
5749       GfxElement[x][y] = artwork_element;
5750
5751       ExplodePhase[x][y] = 1;
5752       ExplodeDelay[x][y] = last_phase;
5753
5754       Stop[x][y] = TRUE;
5755     }
5756
5757     if (center_element == EL_YAMYAM)
5758       game.yamyam_content_nr =
5759         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5760
5761     return;
5762   }
5763
5764   if (Stop[ex][ey])
5765     return;
5766
5767   x = ex;
5768   y = ey;
5769
5770   if (phase == 1)
5771     GfxFrame[x][y] = 0;         /* restart explosion animation */
5772
5773   last_phase = ExplodeDelay[x][y];
5774
5775   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5776
5777 #ifdef DEBUG
5778
5779   /* activate this even in non-DEBUG version until cause for crash in
5780      getGraphicAnimationFrame() (see below) is found and eliminated */
5781
5782 #endif
5783 #if 1
5784
5785 #if 1
5786   /* this can happen if the player leaves an explosion just in time */
5787   if (GfxElement[x][y] == EL_UNDEFINED)
5788     GfxElement[x][y] = EL_EMPTY;
5789 #else
5790   if (GfxElement[x][y] == EL_UNDEFINED)
5791   {
5792     printf("\n\n");
5793     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5794     printf("Explode(): This should never happen!\n");
5795     printf("\n\n");
5796
5797     GfxElement[x][y] = EL_EMPTY;
5798   }
5799 #endif
5800
5801 #endif
5802
5803   border_element = Store2[x][y];
5804   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5805     border_element = StorePlayer[x][y];
5806
5807   if (phase == element_info[border_element].ignition_delay ||
5808       phase == last_phase)
5809   {
5810     boolean border_explosion = FALSE;
5811
5812     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5813         !PLAYER_EXPLOSION_PROTECTED(x, y))
5814     {
5815       KillPlayerUnlessExplosionProtected(x, y);
5816       border_explosion = TRUE;
5817     }
5818     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5819     {
5820       Feld[x][y] = Store2[x][y];
5821       Store2[x][y] = 0;
5822       Bang(x, y);
5823       border_explosion = TRUE;
5824     }
5825     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5826     {
5827       AmoebeUmwandeln(x, y);
5828       Store2[x][y] = 0;
5829       border_explosion = TRUE;
5830     }
5831
5832     /* if an element just explodes due to another explosion (chain-reaction),
5833        do not immediately end the new explosion when it was the last frame of
5834        the explosion (as it would be done in the following "if"-statement!) */
5835     if (border_explosion && phase == last_phase)
5836       return;
5837   }
5838
5839   if (phase == last_phase)
5840   {
5841     int element;
5842
5843     element = Feld[x][y] = Store[x][y];
5844     Store[x][y] = Store2[x][y] = 0;
5845     GfxElement[x][y] = EL_UNDEFINED;
5846
5847     /* player can escape from explosions and might therefore be still alive */
5848     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5849         element <= EL_PLAYER_IS_EXPLODING_4)
5850     {
5851       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5852       int explosion_element = EL_PLAYER_1 + player_nr;
5853       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5854       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5855
5856       if (level.use_explosion_element[player_nr])
5857         explosion_element = level.explosion_element[player_nr];
5858
5859       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5860                     element_info[explosion_element].content.e[xx][yy]);
5861     }
5862
5863     /* restore probably existing indestructible background element */
5864     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5865       element = Feld[x][y] = Back[x][y];
5866     Back[x][y] = 0;
5867
5868     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5869     GfxDir[x][y] = MV_NONE;
5870     ChangeDelay[x][y] = 0;
5871     ChangePage[x][y] = -1;
5872
5873 #if USE_NEW_CUSTOM_VALUE
5874     CustomValue[x][y] = 0;
5875 #endif
5876
5877     InitField_WithBug2(x, y, FALSE);
5878
5879     TEST_DrawLevelField(x, y);
5880
5881     TestIfElementTouchesCustomElement(x, y);
5882
5883     if (GFX_CRUMBLED(element))
5884       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5885
5886     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5887       StorePlayer[x][y] = 0;
5888
5889     if (ELEM_IS_PLAYER(element))
5890       RelocatePlayer(x, y, element);
5891   }
5892   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5893   {
5894     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5895     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5896
5897     if (phase == delay)
5898       TEST_DrawLevelFieldCrumbledSand(x, y);
5899
5900     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5901     {
5902       DrawLevelElement(x, y, Back[x][y]);
5903       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5904     }
5905     else if (IS_WALKABLE_UNDER(Back[x][y]))
5906     {
5907       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5908       DrawLevelElementThruMask(x, y, Back[x][y]);
5909     }
5910     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5911       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5912   }
5913 }
5914
5915 void DynaExplode(int ex, int ey)
5916 {
5917   int i, j;
5918   int dynabomb_element = Feld[ex][ey];
5919   int dynabomb_size = 1;
5920   boolean dynabomb_xl = FALSE;
5921   struct PlayerInfo *player;
5922   static int xy[4][2] =
5923   {
5924     { 0, -1 },
5925     { -1, 0 },
5926     { +1, 0 },
5927     { 0, +1 }
5928   };
5929
5930   if (IS_ACTIVE_BOMB(dynabomb_element))
5931   {
5932     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5933     dynabomb_size = player->dynabomb_size;
5934     dynabomb_xl = player->dynabomb_xl;
5935     player->dynabombs_left++;
5936   }
5937
5938   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5939
5940   for (i = 0; i < NUM_DIRECTIONS; i++)
5941   {
5942     for (j = 1; j <= dynabomb_size; j++)
5943     {
5944       int x = ex + j * xy[i][0];
5945       int y = ey + j * xy[i][1];
5946       int element;
5947
5948       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5949         break;
5950
5951       element = Feld[x][y];
5952
5953       /* do not restart explosions of fields with active bombs */
5954       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5955         continue;
5956
5957       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5958
5959       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5960           !IS_DIGGABLE(element) && !dynabomb_xl)
5961         break;
5962     }
5963   }
5964 }
5965
5966 void Bang(int x, int y)
5967 {
5968   int element = MovingOrBlocked2Element(x, y);
5969   int explosion_type = EX_TYPE_NORMAL;
5970
5971   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5972   {
5973     struct PlayerInfo *player = PLAYERINFO(x, y);
5974
5975 #if USE_FIX_CE_ACTION_WITH_PLAYER
5976     element = Feld[x][y] = player->initial_element;
5977 #else
5978     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5979                             player->element_nr);
5980 #endif
5981
5982     if (level.use_explosion_element[player->index_nr])
5983     {
5984       int explosion_element = level.explosion_element[player->index_nr];
5985
5986       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5987         explosion_type = EX_TYPE_CROSS;
5988       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5989         explosion_type = EX_TYPE_CENTER;
5990     }
5991   }
5992
5993   switch (element)
5994   {
5995     case EL_BUG:
5996     case EL_SPACESHIP:
5997     case EL_BD_BUTTERFLY:
5998     case EL_BD_FIREFLY:
5999     case EL_YAMYAM:
6000     case EL_DARK_YAMYAM:
6001     case EL_ROBOT:
6002     case EL_PACMAN:
6003     case EL_MOLE:
6004       RaiseScoreElement(element);
6005       break;
6006
6007     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6008     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6009     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6010     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6011     case EL_DYNABOMB_INCREASE_NUMBER:
6012     case EL_DYNABOMB_INCREASE_SIZE:
6013     case EL_DYNABOMB_INCREASE_POWER:
6014       explosion_type = EX_TYPE_DYNA;
6015       break;
6016
6017     case EL_DC_LANDMINE:
6018 #if 0
6019     case EL_EM_EXIT_OPEN:
6020     case EL_EM_STEEL_EXIT_OPEN:
6021 #endif
6022       explosion_type = EX_TYPE_CENTER;
6023       break;
6024
6025     case EL_PENGUIN:
6026     case EL_LAMP:
6027     case EL_LAMP_ACTIVE:
6028     case EL_AMOEBA_TO_DIAMOND:
6029       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6030         explosion_type = EX_TYPE_CENTER;
6031       break;
6032
6033     default:
6034       if (element_info[element].explosion_type == EXPLODES_CROSS)
6035         explosion_type = EX_TYPE_CROSS;
6036       else if (element_info[element].explosion_type == EXPLODES_1X1)
6037         explosion_type = EX_TYPE_CENTER;
6038       break;
6039   }
6040
6041   if (explosion_type == EX_TYPE_DYNA)
6042     DynaExplode(x, y);
6043   else
6044     Explode(x, y, EX_PHASE_START, explosion_type);
6045
6046   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6047 }
6048
6049 void SplashAcid(int x, int y)
6050 {
6051   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6052       (!IN_LEV_FIELD(x - 1, y - 2) ||
6053        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6054     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6055
6056   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6057       (!IN_LEV_FIELD(x + 1, y - 2) ||
6058        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6059     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6060
6061   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6062 }
6063
6064 static void InitBeltMovement()
6065 {
6066   static int belt_base_element[4] =
6067   {
6068     EL_CONVEYOR_BELT_1_LEFT,
6069     EL_CONVEYOR_BELT_2_LEFT,
6070     EL_CONVEYOR_BELT_3_LEFT,
6071     EL_CONVEYOR_BELT_4_LEFT
6072   };
6073   static int belt_base_active_element[4] =
6074   {
6075     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6076     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6077     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6078     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6079   };
6080
6081   int x, y, i, j;
6082
6083   /* set frame order for belt animation graphic according to belt direction */
6084   for (i = 0; i < NUM_BELTS; i++)
6085   {
6086     int belt_nr = i;
6087
6088     for (j = 0; j < NUM_BELT_PARTS; j++)
6089     {
6090       int element = belt_base_active_element[belt_nr] + j;
6091       int graphic_1 = el2img(element);
6092       int graphic_2 = el2panelimg(element);
6093
6094       if (game.belt_dir[i] == MV_LEFT)
6095       {
6096         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6097         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6098       }
6099       else
6100       {
6101         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6102         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6103       }
6104     }
6105   }
6106
6107   SCAN_PLAYFIELD(x, y)
6108   {
6109     int element = Feld[x][y];
6110
6111     for (i = 0; i < NUM_BELTS; i++)
6112     {
6113       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6114       {
6115         int e_belt_nr = getBeltNrFromBeltElement(element);
6116         int belt_nr = i;
6117
6118         if (e_belt_nr == belt_nr)
6119         {
6120           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6121
6122           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6123         }
6124       }
6125     }
6126   }
6127 }
6128
6129 static void ToggleBeltSwitch(int x, int y)
6130 {
6131   static int belt_base_element[4] =
6132   {
6133     EL_CONVEYOR_BELT_1_LEFT,
6134     EL_CONVEYOR_BELT_2_LEFT,
6135     EL_CONVEYOR_BELT_3_LEFT,
6136     EL_CONVEYOR_BELT_4_LEFT
6137   };
6138   static int belt_base_active_element[4] =
6139   {
6140     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6141     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6142     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6143     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6144   };
6145   static int belt_base_switch_element[4] =
6146   {
6147     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6148     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6149     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6150     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6151   };
6152   static int belt_move_dir[4] =
6153   {
6154     MV_LEFT,
6155     MV_NONE,
6156     MV_RIGHT,
6157     MV_NONE,
6158   };
6159
6160   int element = Feld[x][y];
6161   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6162   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6163   int belt_dir = belt_move_dir[belt_dir_nr];
6164   int xx, yy, i;
6165
6166   if (!IS_BELT_SWITCH(element))
6167     return;
6168
6169   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6170   game.belt_dir[belt_nr] = belt_dir;
6171
6172   if (belt_dir_nr == 3)
6173     belt_dir_nr = 1;
6174
6175   /* set frame order for belt animation graphic according to belt direction */
6176   for (i = 0; i < NUM_BELT_PARTS; i++)
6177   {
6178     int element = belt_base_active_element[belt_nr] + i;
6179     int graphic_1 = el2img(element);
6180     int graphic_2 = el2panelimg(element);
6181
6182     if (belt_dir == MV_LEFT)
6183     {
6184       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6185       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6186     }
6187     else
6188     {
6189       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6190       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6191     }
6192   }
6193
6194   SCAN_PLAYFIELD(xx, yy)
6195   {
6196     int element = Feld[xx][yy];
6197
6198     if (IS_BELT_SWITCH(element))
6199     {
6200       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6201
6202       if (e_belt_nr == belt_nr)
6203       {
6204         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6205         TEST_DrawLevelField(xx, yy);
6206       }
6207     }
6208     else if (IS_BELT(element) && belt_dir != MV_NONE)
6209     {
6210       int e_belt_nr = getBeltNrFromBeltElement(element);
6211
6212       if (e_belt_nr == belt_nr)
6213       {
6214         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6215
6216         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6217         TEST_DrawLevelField(xx, yy);
6218       }
6219     }
6220     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6221     {
6222       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6223
6224       if (e_belt_nr == belt_nr)
6225       {
6226         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6227
6228         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6229         TEST_DrawLevelField(xx, yy);
6230       }
6231     }
6232   }
6233 }
6234
6235 static void ToggleSwitchgateSwitch(int x, int y)
6236 {
6237   int xx, yy;
6238
6239   game.switchgate_pos = !game.switchgate_pos;
6240
6241   SCAN_PLAYFIELD(xx, yy)
6242   {
6243     int element = Feld[xx][yy];
6244
6245 #if !USE_BOTH_SWITCHGATE_SWITCHES
6246     if (element == EL_SWITCHGATE_SWITCH_UP ||
6247         element == EL_SWITCHGATE_SWITCH_DOWN)
6248     {
6249       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6250       TEST_DrawLevelField(xx, yy);
6251     }
6252     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6253              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6254     {
6255       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6256       TEST_DrawLevelField(xx, yy);
6257     }
6258 #else
6259     if (element == EL_SWITCHGATE_SWITCH_UP)
6260     {
6261       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6262       TEST_DrawLevelField(xx, yy);
6263     }
6264     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6265     {
6266       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6267       TEST_DrawLevelField(xx, yy);
6268     }
6269     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6270     {
6271       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6272       TEST_DrawLevelField(xx, yy);
6273     }
6274     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6275     {
6276       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6277       TEST_DrawLevelField(xx, yy);
6278     }
6279 #endif
6280     else if (element == EL_SWITCHGATE_OPEN ||
6281              element == EL_SWITCHGATE_OPENING)
6282     {
6283       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6284
6285       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6286     }
6287     else if (element == EL_SWITCHGATE_CLOSED ||
6288              element == EL_SWITCHGATE_CLOSING)
6289     {
6290       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6291
6292       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6293     }
6294   }
6295 }
6296
6297 static int getInvisibleActiveFromInvisibleElement(int element)
6298 {
6299   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6300           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6301           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6302           element);
6303 }
6304
6305 static int getInvisibleFromInvisibleActiveElement(int element)
6306 {
6307   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6308           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6309           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6310           element);
6311 }
6312
6313 static void RedrawAllLightSwitchesAndInvisibleElements()
6314 {
6315   int x, y;
6316
6317   SCAN_PLAYFIELD(x, y)
6318   {
6319     int element = Feld[x][y];
6320
6321     if (element == EL_LIGHT_SWITCH &&
6322         game.light_time_left > 0)
6323     {
6324       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6325       TEST_DrawLevelField(x, y);
6326     }
6327     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6328              game.light_time_left == 0)
6329     {
6330       Feld[x][y] = EL_LIGHT_SWITCH;
6331       TEST_DrawLevelField(x, y);
6332     }
6333     else if (element == EL_EMC_DRIPPER &&
6334              game.light_time_left > 0)
6335     {
6336       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6337       TEST_DrawLevelField(x, y);
6338     }
6339     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6340              game.light_time_left == 0)
6341     {
6342       Feld[x][y] = EL_EMC_DRIPPER;
6343       TEST_DrawLevelField(x, y);
6344     }
6345     else if (element == EL_INVISIBLE_STEELWALL ||
6346              element == EL_INVISIBLE_WALL ||
6347              element == EL_INVISIBLE_SAND)
6348     {
6349       if (game.light_time_left > 0)
6350         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6351
6352       TEST_DrawLevelField(x, y);
6353
6354       /* uncrumble neighbour fields, if needed */
6355       if (element == EL_INVISIBLE_SAND)
6356         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6357     }
6358     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6359              element == EL_INVISIBLE_WALL_ACTIVE ||
6360              element == EL_INVISIBLE_SAND_ACTIVE)
6361     {
6362       if (game.light_time_left == 0)
6363         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6364
6365       TEST_DrawLevelField(x, y);
6366
6367       /* re-crumble neighbour fields, if needed */
6368       if (element == EL_INVISIBLE_SAND)
6369         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6370     }
6371   }
6372 }
6373
6374 static void RedrawAllInvisibleElementsForLenses()
6375 {
6376   int x, y;
6377
6378   SCAN_PLAYFIELD(x, y)
6379   {
6380     int element = Feld[x][y];
6381
6382     if (element == EL_EMC_DRIPPER &&
6383         game.lenses_time_left > 0)
6384     {
6385       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6386       TEST_DrawLevelField(x, y);
6387     }
6388     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6389              game.lenses_time_left == 0)
6390     {
6391       Feld[x][y] = EL_EMC_DRIPPER;
6392       TEST_DrawLevelField(x, y);
6393     }
6394     else if (element == EL_INVISIBLE_STEELWALL ||
6395              element == EL_INVISIBLE_WALL ||
6396              element == EL_INVISIBLE_SAND)
6397     {
6398       if (game.lenses_time_left > 0)
6399         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6400
6401       TEST_DrawLevelField(x, y);
6402
6403       /* uncrumble neighbour fields, if needed */
6404       if (element == EL_INVISIBLE_SAND)
6405         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6406     }
6407     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6408              element == EL_INVISIBLE_WALL_ACTIVE ||
6409              element == EL_INVISIBLE_SAND_ACTIVE)
6410     {
6411       if (game.lenses_time_left == 0)
6412         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6413
6414       TEST_DrawLevelField(x, y);
6415
6416       /* re-crumble neighbour fields, if needed */
6417       if (element == EL_INVISIBLE_SAND)
6418         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6419     }
6420   }
6421 }
6422
6423 static void RedrawAllInvisibleElementsForMagnifier()
6424 {
6425   int x, y;
6426
6427   SCAN_PLAYFIELD(x, y)
6428   {
6429     int element = Feld[x][y];
6430
6431     if (element == EL_EMC_FAKE_GRASS &&
6432         game.magnify_time_left > 0)
6433     {
6434       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6435       TEST_DrawLevelField(x, y);
6436     }
6437     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6438              game.magnify_time_left == 0)
6439     {
6440       Feld[x][y] = EL_EMC_FAKE_GRASS;
6441       TEST_DrawLevelField(x, y);
6442     }
6443     else if (IS_GATE_GRAY(element) &&
6444              game.magnify_time_left > 0)
6445     {
6446       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6447                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6448                     IS_EM_GATE_GRAY(element) ?
6449                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6450                     IS_EMC_GATE_GRAY(element) ?
6451                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6452                     IS_DC_GATE_GRAY(element) ?
6453                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6454                     element);
6455       TEST_DrawLevelField(x, y);
6456     }
6457     else if (IS_GATE_GRAY_ACTIVE(element) &&
6458              game.magnify_time_left == 0)
6459     {
6460       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6461                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6462                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6463                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6464                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6465                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6466                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6467                     EL_DC_GATE_WHITE_GRAY :
6468                     element);
6469       TEST_DrawLevelField(x, y);
6470     }
6471   }
6472 }
6473
6474 static void ToggleLightSwitch(int x, int y)
6475 {
6476   int element = Feld[x][y];
6477
6478   game.light_time_left =
6479     (element == EL_LIGHT_SWITCH ?
6480      level.time_light * FRAMES_PER_SECOND : 0);
6481
6482   RedrawAllLightSwitchesAndInvisibleElements();
6483 }
6484
6485 static void ActivateTimegateSwitch(int x, int y)
6486 {
6487   int xx, yy;
6488
6489   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6490
6491   SCAN_PLAYFIELD(xx, yy)
6492   {
6493     int element = Feld[xx][yy];
6494
6495     if (element == EL_TIMEGATE_CLOSED ||
6496         element == EL_TIMEGATE_CLOSING)
6497     {
6498       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6499       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6500     }
6501
6502     /*
6503     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6504     {
6505       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6506       TEST_DrawLevelField(xx, yy);
6507     }
6508     */
6509
6510   }
6511
6512 #if 1
6513   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6514                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6515 #else
6516   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6517 #endif
6518 }
6519
6520 void Impact(int x, int y)
6521 {
6522   boolean last_line = (y == lev_fieldy - 1);
6523   boolean object_hit = FALSE;
6524   boolean impact = (last_line || object_hit);
6525   int element = Feld[x][y];
6526   int smashed = EL_STEELWALL;
6527
6528   if (!last_line)       /* check if element below was hit */
6529   {
6530     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6531       return;
6532
6533     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6534                                          MovDir[x][y + 1] != MV_DOWN ||
6535                                          MovPos[x][y + 1] <= TILEY / 2));
6536
6537     /* do not smash moving elements that left the smashed field in time */
6538     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6539         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6540       object_hit = FALSE;
6541
6542 #if USE_QUICKSAND_IMPACT_BUGFIX
6543     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6544     {
6545       RemoveMovingField(x, y + 1);
6546       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6547       Feld[x][y + 2] = EL_ROCK;
6548       TEST_DrawLevelField(x, y + 2);
6549
6550       object_hit = TRUE;
6551     }
6552
6553     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6554     {
6555       RemoveMovingField(x, y + 1);
6556       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6557       Feld[x][y + 2] = EL_ROCK;
6558       TEST_DrawLevelField(x, y + 2);
6559
6560       object_hit = TRUE;
6561     }
6562 #endif
6563
6564     if (object_hit)
6565       smashed = MovingOrBlocked2Element(x, y + 1);
6566
6567     impact = (last_line || object_hit);
6568   }
6569
6570   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6571   {
6572     SplashAcid(x, y + 1);
6573     return;
6574   }
6575
6576   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6577   /* only reset graphic animation if graphic really changes after impact */
6578   if (impact &&
6579       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6580   {
6581     ResetGfxAnimation(x, y);
6582     TEST_DrawLevelField(x, y);
6583   }
6584
6585   if (impact && CAN_EXPLODE_IMPACT(element))
6586   {
6587     Bang(x, y);
6588     return;
6589   }
6590   else if (impact && element == EL_PEARL &&
6591            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6592   {
6593     ResetGfxAnimation(x, y);
6594
6595     Feld[x][y] = EL_PEARL_BREAKING;
6596     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6597     return;
6598   }
6599   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6600   {
6601     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6602
6603     return;
6604   }
6605
6606   if (impact && element == EL_AMOEBA_DROP)
6607   {
6608     if (object_hit && IS_PLAYER(x, y + 1))
6609       KillPlayerUnlessEnemyProtected(x, y + 1);
6610     else if (object_hit && smashed == EL_PENGUIN)
6611       Bang(x, y + 1);
6612     else
6613     {
6614       Feld[x][y] = EL_AMOEBA_GROWING;
6615       Store[x][y] = EL_AMOEBA_WET;
6616
6617       ResetRandomAnimationValue(x, y);
6618     }
6619     return;
6620   }
6621
6622   if (object_hit)               /* check which object was hit */
6623   {
6624     if ((CAN_PASS_MAGIC_WALL(element) && 
6625          (smashed == EL_MAGIC_WALL ||
6626           smashed == EL_BD_MAGIC_WALL)) ||
6627         (CAN_PASS_DC_MAGIC_WALL(element) &&
6628          smashed == EL_DC_MAGIC_WALL))
6629     {
6630       int xx, yy;
6631       int activated_magic_wall =
6632         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6633          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6634          EL_DC_MAGIC_WALL_ACTIVE);
6635
6636       /* activate magic wall / mill */
6637       SCAN_PLAYFIELD(xx, yy)
6638       {
6639         if (Feld[xx][yy] == smashed)
6640           Feld[xx][yy] = activated_magic_wall;
6641       }
6642
6643       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6644       game.magic_wall_active = TRUE;
6645
6646       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6647                             SND_MAGIC_WALL_ACTIVATING :
6648                             smashed == EL_BD_MAGIC_WALL ?
6649                             SND_BD_MAGIC_WALL_ACTIVATING :
6650                             SND_DC_MAGIC_WALL_ACTIVATING));
6651     }
6652
6653     if (IS_PLAYER(x, y + 1))
6654     {
6655       if (CAN_SMASH_PLAYER(element))
6656       {
6657         KillPlayerUnlessEnemyProtected(x, y + 1);
6658         return;
6659       }
6660     }
6661     else if (smashed == EL_PENGUIN)
6662     {
6663       if (CAN_SMASH_PLAYER(element))
6664       {
6665         Bang(x, y + 1);
6666         return;
6667       }
6668     }
6669     else if (element == EL_BD_DIAMOND)
6670     {
6671       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6672       {
6673         Bang(x, y + 1);
6674         return;
6675       }
6676     }
6677     else if (((element == EL_SP_INFOTRON ||
6678                element == EL_SP_ZONK) &&
6679               (smashed == EL_SP_SNIKSNAK ||
6680                smashed == EL_SP_ELECTRON ||
6681                smashed == EL_SP_DISK_ORANGE)) ||
6682              (element == EL_SP_INFOTRON &&
6683               smashed == EL_SP_DISK_YELLOW))
6684     {
6685       Bang(x, y + 1);
6686       return;
6687     }
6688     else if (CAN_SMASH_EVERYTHING(element))
6689     {
6690       if (IS_CLASSIC_ENEMY(smashed) ||
6691           CAN_EXPLODE_SMASHED(smashed))
6692       {
6693         Bang(x, y + 1);
6694         return;
6695       }
6696       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6697       {
6698         if (smashed == EL_LAMP ||
6699             smashed == EL_LAMP_ACTIVE)
6700         {
6701           Bang(x, y + 1);
6702           return;
6703         }
6704         else if (smashed == EL_NUT)
6705         {
6706           Feld[x][y + 1] = EL_NUT_BREAKING;
6707           PlayLevelSound(x, y, SND_NUT_BREAKING);
6708           RaiseScoreElement(EL_NUT);
6709           return;
6710         }
6711         else if (smashed == EL_PEARL)
6712         {
6713           ResetGfxAnimation(x, y);
6714
6715           Feld[x][y + 1] = EL_PEARL_BREAKING;
6716           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6717           return;
6718         }
6719         else if (smashed == EL_DIAMOND)
6720         {
6721           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6722           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6723           return;
6724         }
6725         else if (IS_BELT_SWITCH(smashed))
6726         {
6727           ToggleBeltSwitch(x, y + 1);
6728         }
6729         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6730                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6731                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6732                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6733         {
6734           ToggleSwitchgateSwitch(x, y + 1);
6735         }
6736         else if (smashed == EL_LIGHT_SWITCH ||
6737                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6738         {
6739           ToggleLightSwitch(x, y + 1);
6740         }
6741         else
6742         {
6743 #if 0
6744           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6745 #endif
6746
6747           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6748
6749           CheckElementChangeBySide(x, y + 1, smashed, element,
6750                                    CE_SWITCHED, CH_SIDE_TOP);
6751           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6752                                             CH_SIDE_TOP);
6753         }
6754       }
6755       else
6756       {
6757         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6758       }
6759     }
6760   }
6761
6762   /* play sound of magic wall / mill */
6763   if (!last_line &&
6764       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6765        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6766        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6767   {
6768     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6769       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6770     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6771       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6772     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6773       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6774
6775     return;
6776   }
6777
6778   /* play sound of object that hits the ground */
6779   if (last_line || object_hit)
6780     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6781 }
6782
6783 inline static void TurnRoundExt(int x, int y)
6784 {
6785   static struct
6786   {
6787     int dx, dy;
6788   } move_xy[] =
6789   {
6790     {  0,  0 },
6791     { -1,  0 },
6792     { +1,  0 },
6793     {  0,  0 },
6794     {  0, -1 },
6795     {  0,  0 }, { 0, 0 }, { 0, 0 },
6796     {  0, +1 }
6797   };
6798   static struct
6799   {
6800     int left, right, back;
6801   } turn[] =
6802   {
6803     { 0,        0,              0        },
6804     { MV_DOWN,  MV_UP,          MV_RIGHT },
6805     { MV_UP,    MV_DOWN,        MV_LEFT  },
6806     { 0,        0,              0        },
6807     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6808     { 0,        0,              0        },
6809     { 0,        0,              0        },
6810     { 0,        0,              0        },
6811     { MV_RIGHT, MV_LEFT,        MV_UP    }
6812   };
6813
6814   int element = Feld[x][y];
6815   int move_pattern = element_info[element].move_pattern;
6816
6817   int old_move_dir = MovDir[x][y];
6818   int left_dir  = turn[old_move_dir].left;
6819   int right_dir = turn[old_move_dir].right;
6820   int back_dir  = turn[old_move_dir].back;
6821
6822   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6823   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6824   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6825   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6826
6827   int left_x  = x + left_dx,  left_y  = y + left_dy;
6828   int right_x = x + right_dx, right_y = y + right_dy;
6829   int move_x  = x + move_dx,  move_y  = y + move_dy;
6830
6831   int xx, yy;
6832
6833   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6834   {
6835     TestIfBadThingTouchesOtherBadThing(x, y);
6836
6837     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6838       MovDir[x][y] = right_dir;
6839     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6840       MovDir[x][y] = left_dir;
6841
6842     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6843       MovDelay[x][y] = 9;
6844     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6845       MovDelay[x][y] = 1;
6846   }
6847   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6848   {
6849     TestIfBadThingTouchesOtherBadThing(x, y);
6850
6851     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6852       MovDir[x][y] = left_dir;
6853     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6854       MovDir[x][y] = right_dir;
6855
6856     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6857       MovDelay[x][y] = 9;
6858     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6859       MovDelay[x][y] = 1;
6860   }
6861   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6862   {
6863     TestIfBadThingTouchesOtherBadThing(x, y);
6864
6865     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6866       MovDir[x][y] = left_dir;
6867     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6868       MovDir[x][y] = right_dir;
6869
6870     if (MovDir[x][y] != old_move_dir)
6871       MovDelay[x][y] = 9;
6872   }
6873   else if (element == EL_YAMYAM)
6874   {
6875     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6876     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6877
6878     if (can_turn_left && can_turn_right)
6879       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6880     else if (can_turn_left)
6881       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6882     else if (can_turn_right)
6883       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6884     else
6885       MovDir[x][y] = back_dir;
6886
6887     MovDelay[x][y] = 16 + 16 * RND(3);
6888   }
6889   else if (element == EL_DARK_YAMYAM)
6890   {
6891     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6892                                                          left_x, left_y);
6893     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6894                                                          right_x, right_y);
6895
6896     if (can_turn_left && can_turn_right)
6897       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6898     else if (can_turn_left)
6899       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6900     else if (can_turn_right)
6901       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6902     else
6903       MovDir[x][y] = back_dir;
6904
6905     MovDelay[x][y] = 16 + 16 * RND(3);
6906   }
6907   else if (element == EL_PACMAN)
6908   {
6909     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6910     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6911
6912     if (can_turn_left && can_turn_right)
6913       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6914     else if (can_turn_left)
6915       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6916     else if (can_turn_right)
6917       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6918     else
6919       MovDir[x][y] = back_dir;
6920
6921     MovDelay[x][y] = 6 + RND(40);
6922   }
6923   else if (element == EL_PIG)
6924   {
6925     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6926     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6927     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6928     boolean should_turn_left, should_turn_right, should_move_on;
6929     int rnd_value = 24;
6930     int rnd = RND(rnd_value);
6931
6932     should_turn_left = (can_turn_left &&
6933                         (!can_move_on ||
6934                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6935                                                    y + back_dy + left_dy)));
6936     should_turn_right = (can_turn_right &&
6937                          (!can_move_on ||
6938                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6939                                                     y + back_dy + right_dy)));
6940     should_move_on = (can_move_on &&
6941                       (!can_turn_left ||
6942                        !can_turn_right ||
6943                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6944                                                  y + move_dy + left_dy) ||
6945                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6946                                                  y + move_dy + right_dy)));
6947
6948     if (should_turn_left || should_turn_right || should_move_on)
6949     {
6950       if (should_turn_left && should_turn_right && should_move_on)
6951         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6952                         rnd < 2 * rnd_value / 3 ? right_dir :
6953                         old_move_dir);
6954       else if (should_turn_left && should_turn_right)
6955         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6956       else if (should_turn_left && should_move_on)
6957         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6958       else if (should_turn_right && should_move_on)
6959         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6960       else if (should_turn_left)
6961         MovDir[x][y] = left_dir;
6962       else if (should_turn_right)
6963         MovDir[x][y] = right_dir;
6964       else if (should_move_on)
6965         MovDir[x][y] = old_move_dir;
6966     }
6967     else if (can_move_on && rnd > rnd_value / 8)
6968       MovDir[x][y] = old_move_dir;
6969     else if (can_turn_left && can_turn_right)
6970       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6971     else if (can_turn_left && rnd > rnd_value / 8)
6972       MovDir[x][y] = left_dir;
6973     else if (can_turn_right && rnd > rnd_value/8)
6974       MovDir[x][y] = right_dir;
6975     else
6976       MovDir[x][y] = back_dir;
6977
6978     xx = x + move_xy[MovDir[x][y]].dx;
6979     yy = y + move_xy[MovDir[x][y]].dy;
6980
6981     if (!IN_LEV_FIELD(xx, yy) ||
6982         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6983       MovDir[x][y] = old_move_dir;
6984
6985     MovDelay[x][y] = 0;
6986   }
6987   else if (element == EL_DRAGON)
6988   {
6989     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6990     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6991     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6992     int rnd_value = 24;
6993     int rnd = RND(rnd_value);
6994
6995     if (can_move_on && rnd > rnd_value / 8)
6996       MovDir[x][y] = old_move_dir;
6997     else if (can_turn_left && can_turn_right)
6998       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6999     else if (can_turn_left && rnd > rnd_value / 8)
7000       MovDir[x][y] = left_dir;
7001     else if (can_turn_right && rnd > rnd_value / 8)
7002       MovDir[x][y] = right_dir;
7003     else
7004       MovDir[x][y] = back_dir;
7005
7006     xx = x + move_xy[MovDir[x][y]].dx;
7007     yy = y + move_xy[MovDir[x][y]].dy;
7008
7009     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7010       MovDir[x][y] = old_move_dir;
7011
7012     MovDelay[x][y] = 0;
7013   }
7014   else if (element == EL_MOLE)
7015   {
7016     boolean can_move_on =
7017       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7018                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7019                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7020     if (!can_move_on)
7021     {
7022       boolean can_turn_left =
7023         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7024                               IS_AMOEBOID(Feld[left_x][left_y])));
7025
7026       boolean can_turn_right =
7027         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7028                               IS_AMOEBOID(Feld[right_x][right_y])));
7029
7030       if (can_turn_left && can_turn_right)
7031         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7032       else if (can_turn_left)
7033         MovDir[x][y] = left_dir;
7034       else
7035         MovDir[x][y] = right_dir;
7036     }
7037
7038     if (MovDir[x][y] != old_move_dir)
7039       MovDelay[x][y] = 9;
7040   }
7041   else if (element == EL_BALLOON)
7042   {
7043     MovDir[x][y] = game.wind_direction;
7044     MovDelay[x][y] = 0;
7045   }
7046   else if (element == EL_SPRING)
7047   {
7048 #if USE_NEW_SPRING_BUMPER
7049     if (MovDir[x][y] & MV_HORIZONTAL)
7050     {
7051       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7052           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7053       {
7054         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7055         ResetGfxAnimation(move_x, move_y);
7056         TEST_DrawLevelField(move_x, move_y);
7057
7058         MovDir[x][y] = back_dir;
7059       }
7060       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7061                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7062         MovDir[x][y] = MV_NONE;
7063     }
7064 #else
7065     if (MovDir[x][y] & MV_HORIZONTAL &&
7066         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7067          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7068       MovDir[x][y] = MV_NONE;
7069 #endif
7070
7071     MovDelay[x][y] = 0;
7072   }
7073   else if (element == EL_ROBOT ||
7074            element == EL_SATELLITE ||
7075            element == EL_PENGUIN ||
7076            element == EL_EMC_ANDROID)
7077   {
7078     int attr_x = -1, attr_y = -1;
7079
7080     if (AllPlayersGone)
7081     {
7082       attr_x = ExitX;
7083       attr_y = ExitY;
7084     }
7085     else
7086     {
7087       int i;
7088
7089       for (i = 0; i < MAX_PLAYERS; i++)
7090       {
7091         struct PlayerInfo *player = &stored_player[i];
7092         int jx = player->jx, jy = player->jy;
7093
7094         if (!player->active)
7095           continue;
7096
7097         if (attr_x == -1 ||
7098             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7099         {
7100           attr_x = jx;
7101           attr_y = jy;
7102         }
7103       }
7104     }
7105
7106     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7107         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7108          game.engine_version < VERSION_IDENT(3,1,0,0)))
7109     {
7110       attr_x = ZX;
7111       attr_y = ZY;
7112     }
7113
7114     if (element == EL_PENGUIN)
7115     {
7116       int i;
7117       static int xy[4][2] =
7118       {
7119         { 0, -1 },
7120         { -1, 0 },
7121         { +1, 0 },
7122         { 0, +1 }
7123       };
7124
7125       for (i = 0; i < NUM_DIRECTIONS; i++)
7126       {
7127         int ex = x + xy[i][0];
7128         int ey = y + xy[i][1];
7129
7130         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7131                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7132                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7133                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7134         {
7135           attr_x = ex;
7136           attr_y = ey;
7137           break;
7138         }
7139       }
7140     }
7141
7142     MovDir[x][y] = MV_NONE;
7143     if (attr_x < x)
7144       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7145     else if (attr_x > x)
7146       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7147     if (attr_y < y)
7148       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7149     else if (attr_y > y)
7150       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7151
7152     if (element == EL_ROBOT)
7153     {
7154       int newx, newy;
7155
7156       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7157         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7158       Moving2Blocked(x, y, &newx, &newy);
7159
7160       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7161         MovDelay[x][y] = 8 + 8 * !RND(3);
7162       else
7163         MovDelay[x][y] = 16;
7164     }
7165     else if (element == EL_PENGUIN)
7166     {
7167       int newx, newy;
7168
7169       MovDelay[x][y] = 1;
7170
7171       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7172       {
7173         boolean first_horiz = RND(2);
7174         int new_move_dir = MovDir[x][y];
7175
7176         MovDir[x][y] =
7177           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7178         Moving2Blocked(x, y, &newx, &newy);
7179
7180         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7181           return;
7182
7183         MovDir[x][y] =
7184           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7185         Moving2Blocked(x, y, &newx, &newy);
7186
7187         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7188           return;
7189
7190         MovDir[x][y] = old_move_dir;
7191         return;
7192       }
7193     }
7194     else if (element == EL_SATELLITE)
7195     {
7196       int newx, newy;
7197
7198       MovDelay[x][y] = 1;
7199
7200       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7201       {
7202         boolean first_horiz = RND(2);
7203         int new_move_dir = MovDir[x][y];
7204
7205         MovDir[x][y] =
7206           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7207         Moving2Blocked(x, y, &newx, &newy);
7208
7209         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7210           return;
7211
7212         MovDir[x][y] =
7213           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7214         Moving2Blocked(x, y, &newx, &newy);
7215
7216         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7217           return;
7218
7219         MovDir[x][y] = old_move_dir;
7220         return;
7221       }
7222     }
7223     else if (element == EL_EMC_ANDROID)
7224     {
7225       static int check_pos[16] =
7226       {
7227         -1,             /*  0 => (invalid)          */
7228         7,              /*  1 => MV_LEFT            */
7229         3,              /*  2 => MV_RIGHT           */
7230         -1,             /*  3 => (invalid)          */
7231         1,              /*  4 =>            MV_UP   */
7232         0,              /*  5 => MV_LEFT  | MV_UP   */
7233         2,              /*  6 => MV_RIGHT | MV_UP   */
7234         -1,             /*  7 => (invalid)          */
7235         5,              /*  8 =>            MV_DOWN */
7236         6,              /*  9 => MV_LEFT  | MV_DOWN */
7237         4,              /* 10 => MV_RIGHT | MV_DOWN */
7238         -1,             /* 11 => (invalid)          */
7239         -1,             /* 12 => (invalid)          */
7240         -1,             /* 13 => (invalid)          */
7241         -1,             /* 14 => (invalid)          */
7242         -1,             /* 15 => (invalid)          */
7243       };
7244       static struct
7245       {
7246         int dx, dy;
7247         int dir;
7248       } check_xy[8] =
7249       {
7250         { -1, -1,       MV_LEFT  | MV_UP   },
7251         {  0, -1,                  MV_UP   },
7252         { +1, -1,       MV_RIGHT | MV_UP   },
7253         { +1,  0,       MV_RIGHT           },
7254         { +1, +1,       MV_RIGHT | MV_DOWN },
7255         {  0, +1,                  MV_DOWN },
7256         { -1, +1,       MV_LEFT  | MV_DOWN },
7257         { -1,  0,       MV_LEFT            },
7258       };
7259       int start_pos, check_order;
7260       boolean can_clone = FALSE;
7261       int i;
7262
7263       /* check if there is any free field around current position */
7264       for (i = 0; i < 8; i++)
7265       {
7266         int newx = x + check_xy[i].dx;
7267         int newy = y + check_xy[i].dy;
7268
7269         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7270         {
7271           can_clone = TRUE;
7272
7273           break;
7274         }
7275       }
7276
7277       if (can_clone)            /* randomly find an element to clone */
7278       {
7279         can_clone = FALSE;
7280
7281         start_pos = check_pos[RND(8)];
7282         check_order = (RND(2) ? -1 : +1);
7283
7284         for (i = 0; i < 8; i++)
7285         {
7286           int pos_raw = start_pos + i * check_order;
7287           int pos = (pos_raw + 8) % 8;
7288           int newx = x + check_xy[pos].dx;
7289           int newy = y + check_xy[pos].dy;
7290
7291           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7292           {
7293             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7294             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7295
7296             Store[x][y] = Feld[newx][newy];
7297
7298             can_clone = TRUE;
7299
7300             break;
7301           }
7302         }
7303       }
7304
7305       if (can_clone)            /* randomly find a direction to move */
7306       {
7307         can_clone = FALSE;
7308
7309         start_pos = check_pos[RND(8)];
7310         check_order = (RND(2) ? -1 : +1);
7311
7312         for (i = 0; i < 8; i++)
7313         {
7314           int pos_raw = start_pos + i * check_order;
7315           int pos = (pos_raw + 8) % 8;
7316           int newx = x + check_xy[pos].dx;
7317           int newy = y + check_xy[pos].dy;
7318           int new_move_dir = check_xy[pos].dir;
7319
7320           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7321           {
7322             MovDir[x][y] = new_move_dir;
7323             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7324
7325             can_clone = TRUE;
7326
7327             break;
7328           }
7329         }
7330       }
7331
7332       if (can_clone)            /* cloning and moving successful */
7333         return;
7334
7335       /* cannot clone -- try to move towards player */
7336
7337       start_pos = check_pos[MovDir[x][y] & 0x0f];
7338       check_order = (RND(2) ? -1 : +1);
7339
7340       for (i = 0; i < 3; i++)
7341       {
7342         /* first check start_pos, then previous/next or (next/previous) pos */
7343         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7344         int pos = (pos_raw + 8) % 8;
7345         int newx = x + check_xy[pos].dx;
7346         int newy = y + check_xy[pos].dy;
7347         int new_move_dir = check_xy[pos].dir;
7348
7349         if (IS_PLAYER(newx, newy))
7350           break;
7351
7352         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7353         {
7354           MovDir[x][y] = new_move_dir;
7355           MovDelay[x][y] = level.android_move_time * 8 + 1;
7356
7357           break;
7358         }
7359       }
7360     }
7361   }
7362   else if (move_pattern == MV_TURNING_LEFT ||
7363            move_pattern == MV_TURNING_RIGHT ||
7364            move_pattern == MV_TURNING_LEFT_RIGHT ||
7365            move_pattern == MV_TURNING_RIGHT_LEFT ||
7366            move_pattern == MV_TURNING_RANDOM ||
7367            move_pattern == MV_ALL_DIRECTIONS)
7368   {
7369     boolean can_turn_left =
7370       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7371     boolean can_turn_right =
7372       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7373
7374     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7375       return;
7376
7377     if (move_pattern == MV_TURNING_LEFT)
7378       MovDir[x][y] = left_dir;
7379     else if (move_pattern == MV_TURNING_RIGHT)
7380       MovDir[x][y] = right_dir;
7381     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7382       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7383     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7384       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7385     else if (move_pattern == MV_TURNING_RANDOM)
7386       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7387                       can_turn_right && !can_turn_left ? right_dir :
7388                       RND(2) ? left_dir : right_dir);
7389     else if (can_turn_left && can_turn_right)
7390       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7391     else if (can_turn_left)
7392       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7393     else if (can_turn_right)
7394       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7395     else
7396       MovDir[x][y] = back_dir;
7397
7398     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7399   }
7400   else if (move_pattern == MV_HORIZONTAL ||
7401            move_pattern == MV_VERTICAL)
7402   {
7403     if (move_pattern & old_move_dir)
7404       MovDir[x][y] = back_dir;
7405     else if (move_pattern == MV_HORIZONTAL)
7406       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7407     else if (move_pattern == MV_VERTICAL)
7408       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7409
7410     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7411   }
7412   else if (move_pattern & MV_ANY_DIRECTION)
7413   {
7414     MovDir[x][y] = move_pattern;
7415     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7416   }
7417   else if (move_pattern & MV_WIND_DIRECTION)
7418   {
7419     MovDir[x][y] = game.wind_direction;
7420     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7421   }
7422   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7423   {
7424     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7425       MovDir[x][y] = left_dir;
7426     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7427       MovDir[x][y] = right_dir;
7428
7429     if (MovDir[x][y] != old_move_dir)
7430       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7431   }
7432   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7433   {
7434     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7435       MovDir[x][y] = right_dir;
7436     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7437       MovDir[x][y] = left_dir;
7438
7439     if (MovDir[x][y] != old_move_dir)
7440       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7441   }
7442   else if (move_pattern == MV_TOWARDS_PLAYER ||
7443            move_pattern == MV_AWAY_FROM_PLAYER)
7444   {
7445     int attr_x = -1, attr_y = -1;
7446     int newx, newy;
7447     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7448
7449     if (AllPlayersGone)
7450     {
7451       attr_x = ExitX;
7452       attr_y = ExitY;
7453     }
7454     else
7455     {
7456       int i;
7457
7458       for (i = 0; i < MAX_PLAYERS; i++)
7459       {
7460         struct PlayerInfo *player = &stored_player[i];
7461         int jx = player->jx, jy = player->jy;
7462
7463         if (!player->active)
7464           continue;
7465
7466         if (attr_x == -1 ||
7467             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7468         {
7469           attr_x = jx;
7470           attr_y = jy;
7471         }
7472       }
7473     }
7474
7475     MovDir[x][y] = MV_NONE;
7476     if (attr_x < x)
7477       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7478     else if (attr_x > x)
7479       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7480     if (attr_y < y)
7481       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7482     else if (attr_y > y)
7483       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7484
7485     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7486
7487     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7488     {
7489       boolean first_horiz = RND(2);
7490       int new_move_dir = MovDir[x][y];
7491
7492       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7493       {
7494         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7495         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7496
7497         return;
7498       }
7499
7500       MovDir[x][y] =
7501         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7502       Moving2Blocked(x, y, &newx, &newy);
7503
7504       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7505         return;
7506
7507       MovDir[x][y] =
7508         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7509       Moving2Blocked(x, y, &newx, &newy);
7510
7511       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7512         return;
7513
7514       MovDir[x][y] = old_move_dir;
7515     }
7516   }
7517   else if (move_pattern == MV_WHEN_PUSHED ||
7518            move_pattern == MV_WHEN_DROPPED)
7519   {
7520     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7521       MovDir[x][y] = MV_NONE;
7522
7523     MovDelay[x][y] = 0;
7524   }
7525   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7526   {
7527     static int test_xy[7][2] =
7528     {
7529       { 0, -1 },
7530       { -1, 0 },
7531       { +1, 0 },
7532       { 0, +1 },
7533       { 0, -1 },
7534       { -1, 0 },
7535       { +1, 0 },
7536     };
7537     static int test_dir[7] =
7538     {
7539       MV_UP,
7540       MV_LEFT,
7541       MV_RIGHT,
7542       MV_DOWN,
7543       MV_UP,
7544       MV_LEFT,
7545       MV_RIGHT,
7546     };
7547     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7548     int move_preference = -1000000;     /* start with very low preference */
7549     int new_move_dir = MV_NONE;
7550     int start_test = RND(4);
7551     int i;
7552
7553     for (i = 0; i < NUM_DIRECTIONS; i++)
7554     {
7555       int move_dir = test_dir[start_test + i];
7556       int move_dir_preference;
7557
7558       xx = x + test_xy[start_test + i][0];
7559       yy = y + test_xy[start_test + i][1];
7560
7561       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7562           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7563       {
7564         new_move_dir = move_dir;
7565
7566         break;
7567       }
7568
7569       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7570         continue;
7571
7572       move_dir_preference = -1 * RunnerVisit[xx][yy];
7573       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7574         move_dir_preference = PlayerVisit[xx][yy];
7575
7576       if (move_dir_preference > move_preference)
7577       {
7578         /* prefer field that has not been visited for the longest time */
7579         move_preference = move_dir_preference;
7580         new_move_dir = move_dir;
7581       }
7582       else if (move_dir_preference == move_preference &&
7583                move_dir == old_move_dir)
7584       {
7585         /* prefer last direction when all directions are preferred equally */
7586         move_preference = move_dir_preference;
7587         new_move_dir = move_dir;
7588       }
7589     }
7590
7591     MovDir[x][y] = new_move_dir;
7592     if (old_move_dir != new_move_dir)
7593       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7594   }
7595 }
7596
7597 static void TurnRound(int x, int y)
7598 {
7599   int direction = MovDir[x][y];
7600
7601   TurnRoundExt(x, y);
7602
7603   GfxDir[x][y] = MovDir[x][y];
7604
7605   if (direction != MovDir[x][y])
7606     GfxFrame[x][y] = 0;
7607
7608   if (MovDelay[x][y])
7609     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7610
7611   ResetGfxFrame(x, y, FALSE);
7612 }
7613
7614 static boolean JustBeingPushed(int x, int y)
7615 {
7616   int i;
7617
7618   for (i = 0; i < MAX_PLAYERS; i++)
7619   {
7620     struct PlayerInfo *player = &stored_player[i];
7621
7622     if (player->active && player->is_pushing && player->MovPos)
7623     {
7624       int next_jx = player->jx + (player->jx - player->last_jx);
7625       int next_jy = player->jy + (player->jy - player->last_jy);
7626
7627       if (x == next_jx && y == next_jy)
7628         return TRUE;
7629     }
7630   }
7631
7632   return FALSE;
7633 }
7634
7635 void StartMoving(int x, int y)
7636 {
7637   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7638   int element = Feld[x][y];
7639
7640   if (Stop[x][y])
7641     return;
7642
7643   if (MovDelay[x][y] == 0)
7644     GfxAction[x][y] = ACTION_DEFAULT;
7645
7646   if (CAN_FALL(element) && y < lev_fieldy - 1)
7647   {
7648     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7649         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7650       if (JustBeingPushed(x, y))
7651         return;
7652
7653     if (element == EL_QUICKSAND_FULL)
7654     {
7655       if (IS_FREE(x, y + 1))
7656       {
7657         InitMovingField(x, y, MV_DOWN);
7658         started_moving = TRUE;
7659
7660         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7661 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7662         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7663           Store[x][y] = EL_ROCK;
7664 #else
7665         Store[x][y] = EL_ROCK;
7666 #endif
7667
7668         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7669       }
7670       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7671       {
7672         if (!MovDelay[x][y])
7673         {
7674           MovDelay[x][y] = TILEY + 1;
7675
7676           ResetGfxAnimation(x, y);
7677           ResetGfxAnimation(x, y + 1);
7678         }
7679
7680         if (MovDelay[x][y])
7681         {
7682           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7683           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7684
7685           MovDelay[x][y]--;
7686           if (MovDelay[x][y])
7687             return;
7688         }
7689
7690         Feld[x][y] = EL_QUICKSAND_EMPTY;
7691         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7692         Store[x][y + 1] = Store[x][y];
7693         Store[x][y] = 0;
7694
7695         PlayLevelSoundAction(x, y, ACTION_FILLING);
7696       }
7697       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7698       {
7699         if (!MovDelay[x][y])
7700         {
7701           MovDelay[x][y] = TILEY + 1;
7702
7703           ResetGfxAnimation(x, y);
7704           ResetGfxAnimation(x, y + 1);
7705         }
7706
7707         if (MovDelay[x][y])
7708         {
7709           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7710           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7711
7712           MovDelay[x][y]--;
7713           if (MovDelay[x][y])
7714             return;
7715         }
7716
7717         Feld[x][y] = EL_QUICKSAND_EMPTY;
7718         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7719         Store[x][y + 1] = Store[x][y];
7720         Store[x][y] = 0;
7721
7722         PlayLevelSoundAction(x, y, ACTION_FILLING);
7723       }
7724     }
7725     else if (element == EL_QUICKSAND_FAST_FULL)
7726     {
7727       if (IS_FREE(x, y + 1))
7728       {
7729         InitMovingField(x, y, MV_DOWN);
7730         started_moving = TRUE;
7731
7732         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7733 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7734         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7735           Store[x][y] = EL_ROCK;
7736 #else
7737         Store[x][y] = EL_ROCK;
7738 #endif
7739
7740         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7741       }
7742       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7743       {
7744         if (!MovDelay[x][y])
7745         {
7746           MovDelay[x][y] = TILEY + 1;
7747
7748           ResetGfxAnimation(x, y);
7749           ResetGfxAnimation(x, y + 1);
7750         }
7751
7752         if (MovDelay[x][y])
7753         {
7754           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7755           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7756
7757           MovDelay[x][y]--;
7758           if (MovDelay[x][y])
7759             return;
7760         }
7761
7762         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7763         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7764         Store[x][y + 1] = Store[x][y];
7765         Store[x][y] = 0;
7766
7767         PlayLevelSoundAction(x, y, ACTION_FILLING);
7768       }
7769       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7770       {
7771         if (!MovDelay[x][y])
7772         {
7773           MovDelay[x][y] = TILEY + 1;
7774
7775           ResetGfxAnimation(x, y);
7776           ResetGfxAnimation(x, y + 1);
7777         }
7778
7779         if (MovDelay[x][y])
7780         {
7781           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7782           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7783
7784           MovDelay[x][y]--;
7785           if (MovDelay[x][y])
7786             return;
7787         }
7788
7789         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7790         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7791         Store[x][y + 1] = Store[x][y];
7792         Store[x][y] = 0;
7793
7794         PlayLevelSoundAction(x, y, ACTION_FILLING);
7795       }
7796     }
7797     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7798              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7799     {
7800       InitMovingField(x, y, MV_DOWN);
7801       started_moving = TRUE;
7802
7803       Feld[x][y] = EL_QUICKSAND_FILLING;
7804       Store[x][y] = element;
7805
7806       PlayLevelSoundAction(x, y, ACTION_FILLING);
7807     }
7808     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7809              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7810     {
7811       InitMovingField(x, y, MV_DOWN);
7812       started_moving = TRUE;
7813
7814       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7815       Store[x][y] = element;
7816
7817       PlayLevelSoundAction(x, y, ACTION_FILLING);
7818     }
7819     else if (element == EL_MAGIC_WALL_FULL)
7820     {
7821       if (IS_FREE(x, y + 1))
7822       {
7823         InitMovingField(x, y, MV_DOWN);
7824         started_moving = TRUE;
7825
7826         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7827         Store[x][y] = EL_CHANGED(Store[x][y]);
7828       }
7829       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7830       {
7831         if (!MovDelay[x][y])
7832           MovDelay[x][y] = TILEY/4 + 1;
7833
7834         if (MovDelay[x][y])
7835         {
7836           MovDelay[x][y]--;
7837           if (MovDelay[x][y])
7838             return;
7839         }
7840
7841         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7842         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7843         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7844         Store[x][y] = 0;
7845       }
7846     }
7847     else if (element == EL_BD_MAGIC_WALL_FULL)
7848     {
7849       if (IS_FREE(x, y + 1))
7850       {
7851         InitMovingField(x, y, MV_DOWN);
7852         started_moving = TRUE;
7853
7854         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7855         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7856       }
7857       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7858       {
7859         if (!MovDelay[x][y])
7860           MovDelay[x][y] = TILEY/4 + 1;
7861
7862         if (MovDelay[x][y])
7863         {
7864           MovDelay[x][y]--;
7865           if (MovDelay[x][y])
7866             return;
7867         }
7868
7869         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7870         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7871         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7872         Store[x][y] = 0;
7873       }
7874     }
7875     else if (element == EL_DC_MAGIC_WALL_FULL)
7876     {
7877       if (IS_FREE(x, y + 1))
7878       {
7879         InitMovingField(x, y, MV_DOWN);
7880         started_moving = TRUE;
7881
7882         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7883         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7884       }
7885       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7886       {
7887         if (!MovDelay[x][y])
7888           MovDelay[x][y] = TILEY/4 + 1;
7889
7890         if (MovDelay[x][y])
7891         {
7892           MovDelay[x][y]--;
7893           if (MovDelay[x][y])
7894             return;
7895         }
7896
7897         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7898         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7899         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7900         Store[x][y] = 0;
7901       }
7902     }
7903     else if ((CAN_PASS_MAGIC_WALL(element) &&
7904               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7905                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7906              (CAN_PASS_DC_MAGIC_WALL(element) &&
7907               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7908
7909     {
7910       InitMovingField(x, y, MV_DOWN);
7911       started_moving = TRUE;
7912
7913       Feld[x][y] =
7914         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7915          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7916          EL_DC_MAGIC_WALL_FILLING);
7917       Store[x][y] = element;
7918     }
7919     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7920     {
7921       SplashAcid(x, y + 1);
7922
7923       InitMovingField(x, y, MV_DOWN);
7924       started_moving = TRUE;
7925
7926       Store[x][y] = EL_ACID;
7927     }
7928     else if (
7929 #if USE_FIX_IMPACT_COLLISION
7930              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7931               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7932 #else
7933              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7934               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7935 #endif
7936              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7937               CAN_FALL(element) && WasJustFalling[x][y] &&
7938               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7939
7940              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7941               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7942               (Feld[x][y + 1] == EL_BLOCKED)))
7943     {
7944       /* this is needed for a special case not covered by calling "Impact()"
7945          from "ContinueMoving()": if an element moves to a tile directly below
7946          another element which was just falling on that tile (which was empty
7947          in the previous frame), the falling element above would just stop
7948          instead of smashing the element below (in previous version, the above
7949          element was just checked for "moving" instead of "falling", resulting
7950          in incorrect smashes caused by horizontal movement of the above
7951          element; also, the case of the player being the element to smash was
7952          simply not covered here... :-/ ) */
7953
7954       CheckCollision[x][y] = 0;
7955       CheckImpact[x][y] = 0;
7956
7957       Impact(x, y);
7958     }
7959     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7960     {
7961       if (MovDir[x][y] == MV_NONE)
7962       {
7963         InitMovingField(x, y, MV_DOWN);
7964         started_moving = TRUE;
7965       }
7966     }
7967     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7968     {
7969       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7970         MovDir[x][y] = MV_DOWN;
7971
7972       InitMovingField(x, y, MV_DOWN);
7973       started_moving = TRUE;
7974     }
7975     else if (element == EL_AMOEBA_DROP)
7976     {
7977       Feld[x][y] = EL_AMOEBA_GROWING;
7978       Store[x][y] = EL_AMOEBA_WET;
7979     }
7980     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7981               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7982              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7983              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7984     {
7985       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7986                                 (IS_FREE(x - 1, y + 1) ||
7987                                  Feld[x - 1][y + 1] == EL_ACID));
7988       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7989                                 (IS_FREE(x + 1, y + 1) ||
7990                                  Feld[x + 1][y + 1] == EL_ACID));
7991       boolean can_fall_any  = (can_fall_left || can_fall_right);
7992       boolean can_fall_both = (can_fall_left && can_fall_right);
7993       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7994
7995 #if USE_NEW_ALL_SLIPPERY
7996       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7997       {
7998         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7999           can_fall_right = FALSE;
8000         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8001           can_fall_left = FALSE;
8002         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8003           can_fall_right = FALSE;
8004         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8005           can_fall_left = FALSE;
8006
8007         can_fall_any  = (can_fall_left || can_fall_right);
8008         can_fall_both = FALSE;
8009       }
8010 #else
8011       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8012       {
8013         if (slippery_type == SLIPPERY_ONLY_LEFT)
8014           can_fall_right = FALSE;
8015         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8016           can_fall_left = FALSE;
8017         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8018           can_fall_right = FALSE;
8019         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8020           can_fall_left = FALSE;
8021
8022         can_fall_any  = (can_fall_left || can_fall_right);
8023         can_fall_both = (can_fall_left && can_fall_right);
8024       }
8025 #endif
8026
8027 #if USE_NEW_ALL_SLIPPERY
8028 #else
8029 #if USE_NEW_SP_SLIPPERY
8030       /* !!! better use the same properties as for custom elements here !!! */
8031       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8032                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8033       {
8034         can_fall_right = FALSE;         /* slip down on left side */
8035         can_fall_both = FALSE;
8036       }
8037 #endif
8038 #endif
8039
8040 #if USE_NEW_ALL_SLIPPERY
8041       if (can_fall_both)
8042       {
8043         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8044           can_fall_right = FALSE;       /* slip down on left side */
8045         else
8046           can_fall_left = !(can_fall_right = RND(2));
8047
8048         can_fall_both = FALSE;
8049       }
8050 #else
8051       if (can_fall_both)
8052       {
8053         if (game.emulation == EMU_BOULDERDASH ||
8054             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8055           can_fall_right = FALSE;       /* slip down on left side */
8056         else
8057           can_fall_left = !(can_fall_right = RND(2));
8058
8059         can_fall_both = FALSE;
8060       }
8061 #endif
8062
8063       if (can_fall_any)
8064       {
8065         /* if not determined otherwise, prefer left side for slipping down */
8066         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8067         started_moving = TRUE;
8068       }
8069     }
8070 #if 0
8071     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8072 #else
8073     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8074 #endif
8075     {
8076       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8077       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8078       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8079       int belt_dir = game.belt_dir[belt_nr];
8080
8081       if ((belt_dir == MV_LEFT  && left_is_free) ||
8082           (belt_dir == MV_RIGHT && right_is_free))
8083       {
8084         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8085
8086         InitMovingField(x, y, belt_dir);
8087         started_moving = TRUE;
8088
8089         Pushed[x][y] = TRUE;
8090         Pushed[nextx][y] = TRUE;
8091
8092         GfxAction[x][y] = ACTION_DEFAULT;
8093       }
8094       else
8095       {
8096         MovDir[x][y] = 0;       /* if element was moving, stop it */
8097       }
8098     }
8099   }
8100
8101   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8102 #if 0
8103   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8104 #else
8105   if (CAN_MOVE(element) && !started_moving)
8106 #endif
8107   {
8108     int move_pattern = element_info[element].move_pattern;
8109     int newx, newy;
8110
8111 #if 0
8112 #if DEBUG
8113     if (MovDir[x][y] == MV_NONE)
8114     {
8115       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8116              x, y, element, element_info[element].token_name);
8117       printf("StartMoving(): This should never happen!\n");
8118     }
8119 #endif
8120 #endif
8121
8122     Moving2Blocked(x, y, &newx, &newy);
8123
8124     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8125       return;
8126
8127     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8128         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8129     {
8130       WasJustMoving[x][y] = 0;
8131       CheckCollision[x][y] = 0;
8132
8133       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8134
8135       if (Feld[x][y] != element)        /* element has changed */
8136         return;
8137     }
8138
8139     if (!MovDelay[x][y])        /* start new movement phase */
8140     {
8141       /* all objects that can change their move direction after each step
8142          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8143
8144       if (element != EL_YAMYAM &&
8145           element != EL_DARK_YAMYAM &&
8146           element != EL_PACMAN &&
8147           !(move_pattern & MV_ANY_DIRECTION) &&
8148           move_pattern != MV_TURNING_LEFT &&
8149           move_pattern != MV_TURNING_RIGHT &&
8150           move_pattern != MV_TURNING_LEFT_RIGHT &&
8151           move_pattern != MV_TURNING_RIGHT_LEFT &&
8152           move_pattern != MV_TURNING_RANDOM)
8153       {
8154         TurnRound(x, y);
8155
8156         if (MovDelay[x][y] && (element == EL_BUG ||
8157                                element == EL_SPACESHIP ||
8158                                element == EL_SP_SNIKSNAK ||
8159                                element == EL_SP_ELECTRON ||
8160                                element == EL_MOLE))
8161           TEST_DrawLevelField(x, y);
8162       }
8163     }
8164
8165     if (MovDelay[x][y])         /* wait some time before next movement */
8166     {
8167       MovDelay[x][y]--;
8168
8169       if (element == EL_ROBOT ||
8170           element == EL_YAMYAM ||
8171           element == EL_DARK_YAMYAM)
8172       {
8173         DrawLevelElementAnimationIfNeeded(x, y, element);
8174         PlayLevelSoundAction(x, y, ACTION_WAITING);
8175       }
8176       else if (element == EL_SP_ELECTRON)
8177         DrawLevelElementAnimationIfNeeded(x, y, element);
8178       else if (element == EL_DRAGON)
8179       {
8180         int i;
8181         int dir = MovDir[x][y];
8182         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8183         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8184         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8185                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8186                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8187                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8188         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8189
8190         GfxAction[x][y] = ACTION_ATTACKING;
8191
8192         if (IS_PLAYER(x, y))
8193           DrawPlayerField(x, y);
8194         else
8195           TEST_DrawLevelField(x, y);
8196
8197         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8198
8199         for (i = 1; i <= 3; i++)
8200         {
8201           int xx = x + i * dx;
8202           int yy = y + i * dy;
8203           int sx = SCREENX(xx);
8204           int sy = SCREENY(yy);
8205           int flame_graphic = graphic + (i - 1);
8206
8207           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8208             break;
8209
8210           if (MovDelay[x][y])
8211           {
8212             int flamed = MovingOrBlocked2Element(xx, yy);
8213
8214             /* !!! */
8215 #if 0
8216             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8217               Bang(xx, yy);
8218             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8219               RemoveMovingField(xx, yy);
8220             else
8221               RemoveField(xx, yy);
8222 #else
8223             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8224               Bang(xx, yy);
8225             else
8226               RemoveMovingField(xx, yy);
8227 #endif
8228
8229             ChangeDelay[xx][yy] = 0;
8230
8231             Feld[xx][yy] = EL_FLAMES;
8232
8233             if (IN_SCR_FIELD(sx, sy))
8234             {
8235               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8236               DrawGraphic(sx, sy, flame_graphic, frame);
8237             }
8238           }
8239           else
8240           {
8241             if (Feld[xx][yy] == EL_FLAMES)
8242               Feld[xx][yy] = EL_EMPTY;
8243             TEST_DrawLevelField(xx, yy);
8244           }
8245         }
8246       }
8247
8248       if (MovDelay[x][y])       /* element still has to wait some time */
8249       {
8250         PlayLevelSoundAction(x, y, ACTION_WAITING);
8251
8252         return;
8253       }
8254     }
8255
8256     /* now make next step */
8257
8258     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8259
8260     if (DONT_COLLIDE_WITH(element) &&
8261         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8262         !PLAYER_ENEMY_PROTECTED(newx, newy))
8263     {
8264       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8265
8266       return;
8267     }
8268
8269     else if (CAN_MOVE_INTO_ACID(element) &&
8270              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8271              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8272              (MovDir[x][y] == MV_DOWN ||
8273               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8274     {
8275       SplashAcid(newx, newy);
8276       Store[x][y] = EL_ACID;
8277     }
8278     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8279     {
8280       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8281           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8282           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8283           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8284       {
8285         RemoveField(x, y);
8286         TEST_DrawLevelField(x, y);
8287
8288         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8289         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8290           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8291
8292         local_player->friends_still_needed--;
8293         if (!local_player->friends_still_needed &&
8294             !local_player->GameOver && AllPlayersGone)
8295           PlayerWins(local_player);
8296
8297         return;
8298       }
8299       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8300       {
8301         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8302           TEST_DrawLevelField(newx, newy);
8303         else
8304           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8305       }
8306       else if (!IS_FREE(newx, newy))
8307       {
8308         GfxAction[x][y] = ACTION_WAITING;
8309
8310         if (IS_PLAYER(x, y))
8311           DrawPlayerField(x, y);
8312         else
8313           TEST_DrawLevelField(x, y);
8314
8315         return;
8316       }
8317     }
8318     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8319     {
8320       if (IS_FOOD_PIG(Feld[newx][newy]))
8321       {
8322         if (IS_MOVING(newx, newy))
8323           RemoveMovingField(newx, newy);
8324         else
8325         {
8326           Feld[newx][newy] = EL_EMPTY;
8327           TEST_DrawLevelField(newx, newy);
8328         }
8329
8330         PlayLevelSound(x, y, SND_PIG_DIGGING);
8331       }
8332       else if (!IS_FREE(newx, newy))
8333       {
8334         if (IS_PLAYER(x, y))
8335           DrawPlayerField(x, y);
8336         else
8337           TEST_DrawLevelField(x, y);
8338
8339         return;
8340       }
8341     }
8342     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8343     {
8344       if (Store[x][y] != EL_EMPTY)
8345       {
8346         boolean can_clone = FALSE;
8347         int xx, yy;
8348
8349         /* check if element to clone is still there */
8350         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8351         {
8352           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8353           {
8354             can_clone = TRUE;
8355
8356             break;
8357           }
8358         }
8359
8360         /* cannot clone or target field not free anymore -- do not clone */
8361         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8362           Store[x][y] = EL_EMPTY;
8363       }
8364
8365       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8366       {
8367         if (IS_MV_DIAGONAL(MovDir[x][y]))
8368         {
8369           int diagonal_move_dir = MovDir[x][y];
8370           int stored = Store[x][y];
8371           int change_delay = 8;
8372           int graphic;
8373
8374           /* android is moving diagonally */
8375
8376           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8377
8378           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8379           GfxElement[x][y] = EL_EMC_ANDROID;
8380           GfxAction[x][y] = ACTION_SHRINKING;
8381           GfxDir[x][y] = diagonal_move_dir;
8382           ChangeDelay[x][y] = change_delay;
8383
8384           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8385                                    GfxDir[x][y]);
8386
8387           DrawLevelGraphicAnimation(x, y, graphic);
8388           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8389
8390           if (Feld[newx][newy] == EL_ACID)
8391           {
8392             SplashAcid(newx, newy);
8393
8394             return;
8395           }
8396
8397           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8398
8399           Store[newx][newy] = EL_EMC_ANDROID;
8400           GfxElement[newx][newy] = EL_EMC_ANDROID;
8401           GfxAction[newx][newy] = ACTION_GROWING;
8402           GfxDir[newx][newy] = diagonal_move_dir;
8403           ChangeDelay[newx][newy] = change_delay;
8404
8405           graphic = el_act_dir2img(GfxElement[newx][newy],
8406                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8407
8408           DrawLevelGraphicAnimation(newx, newy, graphic);
8409           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8410
8411           return;
8412         }
8413         else
8414         {
8415           Feld[newx][newy] = EL_EMPTY;
8416           TEST_DrawLevelField(newx, newy);
8417
8418           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8419         }
8420       }
8421       else if (!IS_FREE(newx, newy))
8422       {
8423 #if 0
8424         if (IS_PLAYER(x, y))
8425           DrawPlayerField(x, y);
8426         else
8427           TEST_DrawLevelField(x, y);
8428 #endif
8429
8430         return;
8431       }
8432     }
8433     else if (IS_CUSTOM_ELEMENT(element) &&
8434              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8435     {
8436 #if 1
8437       if (!DigFieldByCE(newx, newy, element))
8438         return;
8439 #else
8440       int new_element = Feld[newx][newy];
8441
8442       if (!IS_FREE(newx, newy))
8443       {
8444         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8445                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8446                       ACTION_BREAKING);
8447
8448         /* no element can dig solid indestructible elements */
8449         if (IS_INDESTRUCTIBLE(new_element) &&
8450             !IS_DIGGABLE(new_element) &&
8451             !IS_COLLECTIBLE(new_element))
8452           return;
8453
8454         if (AmoebaNr[newx][newy] &&
8455             (new_element == EL_AMOEBA_FULL ||
8456              new_element == EL_BD_AMOEBA ||
8457              new_element == EL_AMOEBA_GROWING))
8458         {
8459           AmoebaCnt[AmoebaNr[newx][newy]]--;
8460           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8461         }
8462
8463         if (IS_MOVING(newx, newy))
8464           RemoveMovingField(newx, newy);
8465         else
8466         {
8467           RemoveField(newx, newy);
8468           TEST_DrawLevelField(newx, newy);
8469         }
8470
8471         /* if digged element was about to explode, prevent the explosion */
8472         ExplodeField[newx][newy] = EX_TYPE_NONE;
8473
8474         PlayLevelSoundAction(x, y, action);
8475       }
8476
8477       Store[newx][newy] = EL_EMPTY;
8478
8479 #if 1
8480       /* this makes it possible to leave the removed element again */
8481       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8482         Store[newx][newy] = new_element;
8483 #else
8484       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8485       {
8486         int move_leave_element = element_info[element].move_leave_element;
8487
8488         /* this makes it possible to leave the removed element again */
8489         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8490                              new_element : move_leave_element);
8491       }
8492 #endif
8493
8494 #endif
8495
8496       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8497       {
8498         RunnerVisit[x][y] = FrameCounter;
8499         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8500       }
8501     }
8502     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8503     {
8504       if (!IS_FREE(newx, newy))
8505       {
8506         if (IS_PLAYER(x, y))
8507           DrawPlayerField(x, y);
8508         else
8509           TEST_DrawLevelField(x, y);
8510
8511         return;
8512       }
8513       else
8514       {
8515         boolean wanna_flame = !RND(10);
8516         int dx = newx - x, dy = newy - y;
8517         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8518         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8519         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8520                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8521         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8522                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8523
8524         if ((wanna_flame ||
8525              IS_CLASSIC_ENEMY(element1) ||
8526              IS_CLASSIC_ENEMY(element2)) &&
8527             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8528             element1 != EL_FLAMES && element2 != EL_FLAMES)
8529         {
8530           ResetGfxAnimation(x, y);
8531           GfxAction[x][y] = ACTION_ATTACKING;
8532
8533           if (IS_PLAYER(x, y))
8534             DrawPlayerField(x, y);
8535           else
8536             TEST_DrawLevelField(x, y);
8537
8538           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8539
8540           MovDelay[x][y] = 50;
8541
8542           /* !!! */
8543 #if 0
8544           RemoveField(newx, newy);
8545 #endif
8546           Feld[newx][newy] = EL_FLAMES;
8547           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8548           {
8549 #if 0
8550             RemoveField(newx1, newy1);
8551 #endif
8552             Feld[newx1][newy1] = EL_FLAMES;
8553           }
8554           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8555           {
8556 #if 0
8557             RemoveField(newx2, newy2);
8558 #endif
8559             Feld[newx2][newy2] = EL_FLAMES;
8560           }
8561
8562           return;
8563         }
8564       }
8565     }
8566     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8567              Feld[newx][newy] == EL_DIAMOND)
8568     {
8569       if (IS_MOVING(newx, newy))
8570         RemoveMovingField(newx, newy);
8571       else
8572       {
8573         Feld[newx][newy] = EL_EMPTY;
8574         TEST_DrawLevelField(newx, newy);
8575       }
8576
8577       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8578     }
8579     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8580              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8581     {
8582       if (AmoebaNr[newx][newy])
8583       {
8584         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8585         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8586             Feld[newx][newy] == EL_BD_AMOEBA)
8587           AmoebaCnt[AmoebaNr[newx][newy]]--;
8588       }
8589
8590 #if 0
8591       /* !!! test !!! */
8592       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8593       {
8594         RemoveMovingField(newx, newy);
8595       }
8596 #else
8597       if (IS_MOVING(newx, newy))
8598       {
8599         RemoveMovingField(newx, newy);
8600       }
8601 #endif
8602       else
8603       {
8604         Feld[newx][newy] = EL_EMPTY;
8605         TEST_DrawLevelField(newx, newy);
8606       }
8607
8608       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8609     }
8610     else if ((element == EL_PACMAN || element == EL_MOLE)
8611              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8612     {
8613       if (AmoebaNr[newx][newy])
8614       {
8615         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8616         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8617             Feld[newx][newy] == EL_BD_AMOEBA)
8618           AmoebaCnt[AmoebaNr[newx][newy]]--;
8619       }
8620
8621       if (element == EL_MOLE)
8622       {
8623         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8624         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8625
8626         ResetGfxAnimation(x, y);
8627         GfxAction[x][y] = ACTION_DIGGING;
8628         TEST_DrawLevelField(x, y);
8629
8630         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8631
8632         return;                         /* wait for shrinking amoeba */
8633       }
8634       else      /* element == EL_PACMAN */
8635       {
8636         Feld[newx][newy] = EL_EMPTY;
8637         TEST_DrawLevelField(newx, newy);
8638         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8639       }
8640     }
8641     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8642              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8643               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8644     {
8645       /* wait for shrinking amoeba to completely disappear */
8646       return;
8647     }
8648     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8649     {
8650       /* object was running against a wall */
8651
8652       TurnRound(x, y);
8653
8654 #if 0
8655       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8656       if (move_pattern & MV_ANY_DIRECTION &&
8657           move_pattern == MovDir[x][y])
8658       {
8659         int blocking_element =
8660           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8661
8662         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8663                                  MovDir[x][y]);
8664
8665         element = Feld[x][y];   /* element might have changed */
8666       }
8667 #endif
8668
8669       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8670         DrawLevelElementAnimation(x, y, element);
8671
8672       if (DONT_TOUCH(element))
8673         TestIfBadThingTouchesPlayer(x, y);
8674
8675       return;
8676     }
8677
8678     InitMovingField(x, y, MovDir[x][y]);
8679
8680     PlayLevelSoundAction(x, y, ACTION_MOVING);
8681   }
8682
8683   if (MovDir[x][y])
8684     ContinueMoving(x, y);
8685 }
8686
8687 void ContinueMoving(int x, int y)
8688 {
8689   int element = Feld[x][y];
8690   struct ElementInfo *ei = &element_info[element];
8691   int direction = MovDir[x][y];
8692   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8693   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8694   int newx = x + dx, newy = y + dy;
8695   int stored = Store[x][y];
8696   int stored_new = Store[newx][newy];
8697   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8698   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8699   boolean last_line = (newy == lev_fieldy - 1);
8700
8701   MovPos[x][y] += getElementMoveStepsize(x, y);
8702
8703   if (pushed_by_player) /* special case: moving object pushed by player */
8704     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8705
8706   if (ABS(MovPos[x][y]) < TILEX)
8707   {
8708 #if 0
8709     int ee = Feld[x][y];
8710     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8711     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8712
8713     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8714            x, y, ABS(MovPos[x][y]),
8715            ee, gg, ff,
8716            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8717 #endif
8718
8719     TEST_DrawLevelField(x, y);
8720
8721     return;     /* element is still moving */
8722   }
8723
8724   /* element reached destination field */
8725
8726   Feld[x][y] = EL_EMPTY;
8727   Feld[newx][newy] = element;
8728   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8729
8730   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8731   {
8732     element = Feld[newx][newy] = EL_ACID;
8733   }
8734   else if (element == EL_MOLE)
8735   {
8736     Feld[x][y] = EL_SAND;
8737
8738     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8739   }
8740   else if (element == EL_QUICKSAND_FILLING)
8741   {
8742     element = Feld[newx][newy] = get_next_element(element);
8743     Store[newx][newy] = Store[x][y];
8744   }
8745   else if (element == EL_QUICKSAND_EMPTYING)
8746   {
8747     Feld[x][y] = get_next_element(element);
8748     element = Feld[newx][newy] = Store[x][y];
8749   }
8750   else if (element == EL_QUICKSAND_FAST_FILLING)
8751   {
8752     element = Feld[newx][newy] = get_next_element(element);
8753     Store[newx][newy] = Store[x][y];
8754   }
8755   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8756   {
8757     Feld[x][y] = get_next_element(element);
8758     element = Feld[newx][newy] = Store[x][y];
8759   }
8760   else if (element == EL_MAGIC_WALL_FILLING)
8761   {
8762     element = Feld[newx][newy] = get_next_element(element);
8763     if (!game.magic_wall_active)
8764       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8765     Store[newx][newy] = Store[x][y];
8766   }
8767   else if (element == EL_MAGIC_WALL_EMPTYING)
8768   {
8769     Feld[x][y] = get_next_element(element);
8770     if (!game.magic_wall_active)
8771       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8772     element = Feld[newx][newy] = Store[x][y];
8773
8774 #if USE_NEW_CUSTOM_VALUE
8775     InitField(newx, newy, FALSE);
8776 #endif
8777   }
8778   else if (element == EL_BD_MAGIC_WALL_FILLING)
8779   {
8780     element = Feld[newx][newy] = get_next_element(element);
8781     if (!game.magic_wall_active)
8782       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8783     Store[newx][newy] = Store[x][y];
8784   }
8785   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8786   {
8787     Feld[x][y] = get_next_element(element);
8788     if (!game.magic_wall_active)
8789       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8790     element = Feld[newx][newy] = Store[x][y];
8791
8792 #if USE_NEW_CUSTOM_VALUE
8793     InitField(newx, newy, FALSE);
8794 #endif
8795   }
8796   else if (element == EL_DC_MAGIC_WALL_FILLING)
8797   {
8798     element = Feld[newx][newy] = get_next_element(element);
8799     if (!game.magic_wall_active)
8800       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8801     Store[newx][newy] = Store[x][y];
8802   }
8803   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8804   {
8805     Feld[x][y] = get_next_element(element);
8806     if (!game.magic_wall_active)
8807       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8808     element = Feld[newx][newy] = Store[x][y];
8809
8810 #if USE_NEW_CUSTOM_VALUE
8811     InitField(newx, newy, FALSE);
8812 #endif
8813   }
8814   else if (element == EL_AMOEBA_DROPPING)
8815   {
8816     Feld[x][y] = get_next_element(element);
8817     element = Feld[newx][newy] = Store[x][y];
8818   }
8819   else if (element == EL_SOKOBAN_OBJECT)
8820   {
8821     if (Back[x][y])
8822       Feld[x][y] = Back[x][y];
8823
8824     if (Back[newx][newy])
8825       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8826
8827     Back[x][y] = Back[newx][newy] = 0;
8828   }
8829
8830   Store[x][y] = EL_EMPTY;
8831   MovPos[x][y] = 0;
8832   MovDir[x][y] = 0;
8833   MovDelay[x][y] = 0;
8834
8835   MovDelay[newx][newy] = 0;
8836
8837   if (CAN_CHANGE_OR_HAS_ACTION(element))
8838   {
8839     /* copy element change control values to new field */
8840     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8841     ChangePage[newx][newy]  = ChangePage[x][y];
8842     ChangeCount[newx][newy] = ChangeCount[x][y];
8843     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8844   }
8845
8846 #if USE_NEW_CUSTOM_VALUE
8847   CustomValue[newx][newy] = CustomValue[x][y];
8848 #endif
8849
8850   ChangeDelay[x][y] = 0;
8851   ChangePage[x][y] = -1;
8852   ChangeCount[x][y] = 0;
8853   ChangeEvent[x][y] = -1;
8854
8855 #if USE_NEW_CUSTOM_VALUE
8856   CustomValue[x][y] = 0;
8857 #endif
8858
8859   /* copy animation control values to new field */
8860   GfxFrame[newx][newy]  = GfxFrame[x][y];
8861   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8862   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8863   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8864
8865   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8866
8867   /* some elements can leave other elements behind after moving */
8868 #if 1
8869   if (ei->move_leave_element != EL_EMPTY &&
8870       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8871       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8872 #else
8873   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8874       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8875       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8876 #endif
8877   {
8878     int move_leave_element = ei->move_leave_element;
8879
8880 #if 1
8881 #if 1
8882     /* this makes it possible to leave the removed element again */
8883     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8884       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8885 #else
8886     /* this makes it possible to leave the removed element again */
8887     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8888       move_leave_element = stored;
8889 #endif
8890 #else
8891     /* this makes it possible to leave the removed element again */
8892     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8893         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8894       move_leave_element = stored;
8895 #endif
8896
8897     Feld[x][y] = move_leave_element;
8898
8899     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8900       MovDir[x][y] = direction;
8901
8902     InitField(x, y, FALSE);
8903
8904     if (GFX_CRUMBLED(Feld[x][y]))
8905       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8906
8907     if (ELEM_IS_PLAYER(move_leave_element))
8908       RelocatePlayer(x, y, move_leave_element);
8909   }
8910
8911   /* do this after checking for left-behind element */
8912   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8913
8914   if (!CAN_MOVE(element) ||
8915       (CAN_FALL(element) && direction == MV_DOWN &&
8916        (element == EL_SPRING ||
8917         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8918         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8919     GfxDir[x][y] = MovDir[newx][newy] = 0;
8920
8921   TEST_DrawLevelField(x, y);
8922   TEST_DrawLevelField(newx, newy);
8923
8924   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8925
8926   /* prevent pushed element from moving on in pushed direction */
8927   if (pushed_by_player && CAN_MOVE(element) &&
8928       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8929       !(element_info[element].move_pattern & direction))
8930     TurnRound(newx, newy);
8931
8932   /* prevent elements on conveyor belt from moving on in last direction */
8933   if (pushed_by_conveyor && CAN_FALL(element) &&
8934       direction & MV_HORIZONTAL)
8935     MovDir[newx][newy] = 0;
8936
8937   if (!pushed_by_player)
8938   {
8939     int nextx = newx + dx, nexty = newy + dy;
8940     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8941
8942     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8943
8944     if (CAN_FALL(element) && direction == MV_DOWN)
8945       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8946
8947     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8948       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8949
8950 #if USE_FIX_IMPACT_COLLISION
8951     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8952       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8953 #endif
8954   }
8955
8956   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8957   {
8958     TestIfBadThingTouchesPlayer(newx, newy);
8959     TestIfBadThingTouchesFriend(newx, newy);
8960
8961     if (!IS_CUSTOM_ELEMENT(element))
8962       TestIfBadThingTouchesOtherBadThing(newx, newy);
8963   }
8964   else if (element == EL_PENGUIN)
8965     TestIfFriendTouchesBadThing(newx, newy);
8966
8967   if (DONT_GET_HIT_BY(element))
8968   {
8969     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8970   }
8971
8972   /* give the player one last chance (one more frame) to move away */
8973   if (CAN_FALL(element) && direction == MV_DOWN &&
8974       (last_line || (!IS_FREE(x, newy + 1) &&
8975                      (!IS_PLAYER(x, newy + 1) ||
8976                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8977     Impact(x, newy);
8978
8979   if (pushed_by_player && !game.use_change_when_pushing_bug)
8980   {
8981     int push_side = MV_DIR_OPPOSITE(direction);
8982     struct PlayerInfo *player = PLAYERINFO(x, y);
8983
8984     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8985                                player->index_bit, push_side);
8986     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8987                                         player->index_bit, push_side);
8988   }
8989
8990   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8991     MovDelay[newx][newy] = 1;
8992
8993   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8994
8995   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8996
8997 #if 0
8998   if (ChangePage[newx][newy] != -1)             /* delayed change */
8999   {
9000     int page = ChangePage[newx][newy];
9001     struct ElementChangeInfo *change = &ei->change_page[page];
9002
9003     ChangePage[newx][newy] = -1;
9004
9005     if (change->can_change)
9006     {
9007       if (ChangeElement(newx, newy, element, page))
9008       {
9009         if (change->post_change_function)
9010           change->post_change_function(newx, newy);
9011       }
9012     }
9013
9014     if (change->has_action)
9015       ExecuteCustomElementAction(newx, newy, element, page);
9016   }
9017 #endif
9018
9019   TestIfElementHitsCustomElement(newx, newy, direction);
9020   TestIfPlayerTouchesCustomElement(newx, newy);
9021   TestIfElementTouchesCustomElement(newx, newy);
9022
9023   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9024       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9025     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9026                              MV_DIR_OPPOSITE(direction));
9027 }
9028
9029 int AmoebeNachbarNr(int ax, int ay)
9030 {
9031   int i;
9032   int element = Feld[ax][ay];
9033   int group_nr = 0;
9034   static int xy[4][2] =
9035   {
9036     { 0, -1 },
9037     { -1, 0 },
9038     { +1, 0 },
9039     { 0, +1 }
9040   };
9041
9042   for (i = 0; i < NUM_DIRECTIONS; i++)
9043   {
9044     int x = ax + xy[i][0];
9045     int y = ay + xy[i][1];
9046
9047     if (!IN_LEV_FIELD(x, y))
9048       continue;
9049
9050     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9051       group_nr = AmoebaNr[x][y];
9052   }
9053
9054   return group_nr;
9055 }
9056
9057 void AmoebenVereinigen(int ax, int ay)
9058 {
9059   int i, x, y, xx, yy;
9060   int new_group_nr = AmoebaNr[ax][ay];
9061   static int xy[4][2] =
9062   {
9063     { 0, -1 },
9064     { -1, 0 },
9065     { +1, 0 },
9066     { 0, +1 }
9067   };
9068
9069   if (new_group_nr == 0)
9070     return;
9071
9072   for (i = 0; i < NUM_DIRECTIONS; i++)
9073   {
9074     x = ax + xy[i][0];
9075     y = ay + xy[i][1];
9076
9077     if (!IN_LEV_FIELD(x, y))
9078       continue;
9079
9080     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9081          Feld[x][y] == EL_BD_AMOEBA ||
9082          Feld[x][y] == EL_AMOEBA_DEAD) &&
9083         AmoebaNr[x][y] != new_group_nr)
9084     {
9085       int old_group_nr = AmoebaNr[x][y];
9086
9087       if (old_group_nr == 0)
9088         return;
9089
9090       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9091       AmoebaCnt[old_group_nr] = 0;
9092       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9093       AmoebaCnt2[old_group_nr] = 0;
9094
9095       SCAN_PLAYFIELD(xx, yy)
9096       {
9097         if (AmoebaNr[xx][yy] == old_group_nr)
9098           AmoebaNr[xx][yy] = new_group_nr;
9099       }
9100     }
9101   }
9102 }
9103
9104 void AmoebeUmwandeln(int ax, int ay)
9105 {
9106   int i, x, y;
9107
9108   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9109   {
9110     int group_nr = AmoebaNr[ax][ay];
9111
9112 #ifdef DEBUG
9113     if (group_nr == 0)
9114     {
9115       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9116       printf("AmoebeUmwandeln(): This should never happen!\n");
9117       return;
9118     }
9119 #endif
9120
9121     SCAN_PLAYFIELD(x, y)
9122     {
9123       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9124       {
9125         AmoebaNr[x][y] = 0;
9126         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9127       }
9128     }
9129
9130     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9131                             SND_AMOEBA_TURNING_TO_GEM :
9132                             SND_AMOEBA_TURNING_TO_ROCK));
9133     Bang(ax, ay);
9134   }
9135   else
9136   {
9137     static int xy[4][2] =
9138     {
9139       { 0, -1 },
9140       { -1, 0 },
9141       { +1, 0 },
9142       { 0, +1 }
9143     };
9144
9145     for (i = 0; i < NUM_DIRECTIONS; i++)
9146     {
9147       x = ax + xy[i][0];
9148       y = ay + xy[i][1];
9149
9150       if (!IN_LEV_FIELD(x, y))
9151         continue;
9152
9153       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9154       {
9155         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9156                               SND_AMOEBA_TURNING_TO_GEM :
9157                               SND_AMOEBA_TURNING_TO_ROCK));
9158         Bang(x, y);
9159       }
9160     }
9161   }
9162 }
9163
9164 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9165 {
9166   int x, y;
9167   int group_nr = AmoebaNr[ax][ay];
9168   boolean done = FALSE;
9169
9170 #ifdef DEBUG
9171   if (group_nr == 0)
9172   {
9173     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9174     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9175     return;
9176   }
9177 #endif
9178
9179   SCAN_PLAYFIELD(x, y)
9180   {
9181     if (AmoebaNr[x][y] == group_nr &&
9182         (Feld[x][y] == EL_AMOEBA_DEAD ||
9183          Feld[x][y] == EL_BD_AMOEBA ||
9184          Feld[x][y] == EL_AMOEBA_GROWING))
9185     {
9186       AmoebaNr[x][y] = 0;
9187       Feld[x][y] = new_element;
9188       InitField(x, y, FALSE);
9189       TEST_DrawLevelField(x, y);
9190       done = TRUE;
9191     }
9192   }
9193
9194   if (done)
9195     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9196                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9197                             SND_BD_AMOEBA_TURNING_TO_GEM));
9198 }
9199
9200 void AmoebeWaechst(int x, int y)
9201 {
9202   static unsigned long sound_delay = 0;
9203   static unsigned long sound_delay_value = 0;
9204
9205   if (!MovDelay[x][y])          /* start new growing cycle */
9206   {
9207     MovDelay[x][y] = 7;
9208
9209     if (DelayReached(&sound_delay, sound_delay_value))
9210     {
9211       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9212       sound_delay_value = 30;
9213     }
9214   }
9215
9216   if (MovDelay[x][y])           /* wait some time before growing bigger */
9217   {
9218     MovDelay[x][y]--;
9219     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9220     {
9221       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9222                                            6 - MovDelay[x][y]);
9223
9224       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9225     }
9226
9227     if (!MovDelay[x][y])
9228     {
9229       Feld[x][y] = Store[x][y];
9230       Store[x][y] = 0;
9231       TEST_DrawLevelField(x, y);
9232     }
9233   }
9234 }
9235
9236 void AmoebaDisappearing(int x, int y)
9237 {
9238   static unsigned long sound_delay = 0;
9239   static unsigned long sound_delay_value = 0;
9240
9241   if (!MovDelay[x][y])          /* start new shrinking cycle */
9242   {
9243     MovDelay[x][y] = 7;
9244
9245     if (DelayReached(&sound_delay, sound_delay_value))
9246       sound_delay_value = 30;
9247   }
9248
9249   if (MovDelay[x][y])           /* wait some time before shrinking */
9250   {
9251     MovDelay[x][y]--;
9252     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9253     {
9254       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9255                                            6 - MovDelay[x][y]);
9256
9257       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9258     }
9259
9260     if (!MovDelay[x][y])
9261     {
9262       Feld[x][y] = EL_EMPTY;
9263       TEST_DrawLevelField(x, y);
9264
9265       /* don't let mole enter this field in this cycle;
9266          (give priority to objects falling to this field from above) */
9267       Stop[x][y] = TRUE;
9268     }
9269   }
9270 }
9271
9272 void AmoebeAbleger(int ax, int ay)
9273 {
9274   int i;
9275   int element = Feld[ax][ay];
9276   int graphic = el2img(element);
9277   int newax = ax, neway = ay;
9278   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9279   static int xy[4][2] =
9280   {
9281     { 0, -1 },
9282     { -1, 0 },
9283     { +1, 0 },
9284     { 0, +1 }
9285   };
9286
9287   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9288   {
9289     Feld[ax][ay] = EL_AMOEBA_DEAD;
9290     TEST_DrawLevelField(ax, ay);
9291     return;
9292   }
9293
9294   if (IS_ANIMATED(graphic))
9295     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9296
9297   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9298     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9299
9300   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9301   {
9302     MovDelay[ax][ay]--;
9303     if (MovDelay[ax][ay])
9304       return;
9305   }
9306
9307   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9308   {
9309     int start = RND(4);
9310     int x = ax + xy[start][0];
9311     int y = ay + xy[start][1];
9312
9313     if (!IN_LEV_FIELD(x, y))
9314       return;
9315
9316     if (IS_FREE(x, y) ||
9317         CAN_GROW_INTO(Feld[x][y]) ||
9318         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9319         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9320     {
9321       newax = x;
9322       neway = y;
9323     }
9324
9325     if (newax == ax && neway == ay)
9326       return;
9327   }
9328   else                          /* normal or "filled" (BD style) amoeba */
9329   {
9330     int start = RND(4);
9331     boolean waiting_for_player = FALSE;
9332
9333     for (i = 0; i < NUM_DIRECTIONS; i++)
9334     {
9335       int j = (start + i) % 4;
9336       int x = ax + xy[j][0];
9337       int y = ay + xy[j][1];
9338
9339       if (!IN_LEV_FIELD(x, y))
9340         continue;
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         break;
9350       }
9351       else if (IS_PLAYER(x, y))
9352         waiting_for_player = TRUE;
9353     }
9354
9355     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9356     {
9357       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9358       {
9359         Feld[ax][ay] = EL_AMOEBA_DEAD;
9360         TEST_DrawLevelField(ax, ay);
9361         AmoebaCnt[AmoebaNr[ax][ay]]--;
9362
9363         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9364         {
9365           if (element == EL_AMOEBA_FULL)
9366             AmoebeUmwandeln(ax, ay);
9367           else if (element == EL_BD_AMOEBA)
9368             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9369         }
9370       }
9371       return;
9372     }
9373     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9374     {
9375       /* amoeba gets larger by growing in some direction */
9376
9377       int new_group_nr = AmoebaNr[ax][ay];
9378
9379 #ifdef DEBUG
9380   if (new_group_nr == 0)
9381   {
9382     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9383     printf("AmoebeAbleger(): This should never happen!\n");
9384     return;
9385   }
9386 #endif
9387
9388       AmoebaNr[newax][neway] = new_group_nr;
9389       AmoebaCnt[new_group_nr]++;
9390       AmoebaCnt2[new_group_nr]++;
9391
9392       /* if amoeba touches other amoeba(s) after growing, unify them */
9393       AmoebenVereinigen(newax, neway);
9394
9395       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9396       {
9397         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9398         return;
9399       }
9400     }
9401   }
9402
9403   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9404       (neway == lev_fieldy - 1 && newax != ax))
9405   {
9406     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9407     Store[newax][neway] = element;
9408   }
9409   else if (neway == ay || element == EL_EMC_DRIPPER)
9410   {
9411     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9412
9413     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9414   }
9415   else
9416   {
9417     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9418     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9419     Store[ax][ay] = EL_AMOEBA_DROP;
9420     ContinueMoving(ax, ay);
9421     return;
9422   }
9423
9424   TEST_DrawLevelField(newax, neway);
9425 }
9426
9427 void Life(int ax, int ay)
9428 {
9429   int x1, y1, x2, y2;
9430   int life_time = 40;
9431   int element = Feld[ax][ay];
9432   int graphic = el2img(element);
9433   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9434                          level.biomaze);
9435   boolean changed = FALSE;
9436
9437   if (IS_ANIMATED(graphic))
9438     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9439
9440   if (Stop[ax][ay])
9441     return;
9442
9443   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9444     MovDelay[ax][ay] = life_time;
9445
9446   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9447   {
9448     MovDelay[ax][ay]--;
9449     if (MovDelay[ax][ay])
9450       return;
9451   }
9452
9453   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9454   {
9455     int xx = ax+x1, yy = ay+y1;
9456     int nachbarn = 0;
9457
9458     if (!IN_LEV_FIELD(xx, yy))
9459       continue;
9460
9461     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9462     {
9463       int x = xx+x2, y = yy+y2;
9464
9465       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9466         continue;
9467
9468       if (((Feld[x][y] == element ||
9469             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9470            !Stop[x][y]) ||
9471           (IS_FREE(x, y) && Stop[x][y]))
9472         nachbarn++;
9473     }
9474
9475     if (xx == ax && yy == ay)           /* field in the middle */
9476     {
9477       if (nachbarn < life_parameter[0] ||
9478           nachbarn > life_parameter[1])
9479       {
9480         Feld[xx][yy] = EL_EMPTY;
9481         if (!Stop[xx][yy])
9482           TEST_DrawLevelField(xx, yy);
9483         Stop[xx][yy] = TRUE;
9484         changed = TRUE;
9485       }
9486     }
9487     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9488     {                                   /* free border field */
9489       if (nachbarn >= life_parameter[2] &&
9490           nachbarn <= life_parameter[3])
9491       {
9492         Feld[xx][yy] = element;
9493         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9494         if (!Stop[xx][yy])
9495           TEST_DrawLevelField(xx, yy);
9496         Stop[xx][yy] = TRUE;
9497         changed = TRUE;
9498       }
9499     }
9500   }
9501
9502   if (changed)
9503     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9504                    SND_GAME_OF_LIFE_GROWING);
9505 }
9506
9507 static void InitRobotWheel(int x, int y)
9508 {
9509   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9510 }
9511
9512 static void RunRobotWheel(int x, int y)
9513 {
9514   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9515 }
9516
9517 static void StopRobotWheel(int x, int y)
9518 {
9519   if (ZX == x && ZY == y)
9520   {
9521     ZX = ZY = -1;
9522
9523     game.robot_wheel_active = FALSE;
9524   }
9525 }
9526
9527 static void InitTimegateWheel(int x, int y)
9528 {
9529   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9530 }
9531
9532 static void RunTimegateWheel(int x, int y)
9533 {
9534   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9535 }
9536
9537 static void InitMagicBallDelay(int x, int y)
9538 {
9539 #if 1
9540   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9541 #else
9542   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9543 #endif
9544 }
9545
9546 static void ActivateMagicBall(int bx, int by)
9547 {
9548   int x, y;
9549
9550   if (level.ball_random)
9551   {
9552     int pos_border = RND(8);    /* select one of the eight border elements */
9553     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9554     int xx = pos_content % 3;
9555     int yy = pos_content / 3;
9556
9557     x = bx - 1 + xx;
9558     y = by - 1 + yy;
9559
9560     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9561       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9562   }
9563   else
9564   {
9565     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9566     {
9567       int xx = x - bx + 1;
9568       int yy = y - by + 1;
9569
9570       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9571         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9572     }
9573   }
9574
9575   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9576 }
9577
9578 void CheckExit(int x, int y)
9579 {
9580   if (local_player->gems_still_needed > 0 ||
9581       local_player->sokobanfields_still_needed > 0 ||
9582       local_player->lights_still_needed > 0)
9583   {
9584     int element = Feld[x][y];
9585     int graphic = el2img(element);
9586
9587     if (IS_ANIMATED(graphic))
9588       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9589
9590     return;
9591   }
9592
9593   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9594     return;
9595
9596   Feld[x][y] = EL_EXIT_OPENING;
9597
9598   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9599 }
9600
9601 void CheckExitEM(int x, int y)
9602 {
9603   if (local_player->gems_still_needed > 0 ||
9604       local_player->sokobanfields_still_needed > 0 ||
9605       local_player->lights_still_needed > 0)
9606   {
9607     int element = Feld[x][y];
9608     int graphic = el2img(element);
9609
9610     if (IS_ANIMATED(graphic))
9611       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9612
9613     return;
9614   }
9615
9616   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9617     return;
9618
9619   Feld[x][y] = EL_EM_EXIT_OPENING;
9620
9621   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9622 }
9623
9624 void CheckExitSteel(int x, int y)
9625 {
9626   if (local_player->gems_still_needed > 0 ||
9627       local_player->sokobanfields_still_needed > 0 ||
9628       local_player->lights_still_needed > 0)
9629   {
9630     int element = Feld[x][y];
9631     int graphic = el2img(element);
9632
9633     if (IS_ANIMATED(graphic))
9634       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9635
9636     return;
9637   }
9638
9639   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9640     return;
9641
9642   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9643
9644   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9645 }
9646
9647 void CheckExitSteelEM(int x, int y)
9648 {
9649   if (local_player->gems_still_needed > 0 ||
9650       local_player->sokobanfields_still_needed > 0 ||
9651       local_player->lights_still_needed > 0)
9652   {
9653     int element = Feld[x][y];
9654     int graphic = el2img(element);
9655
9656     if (IS_ANIMATED(graphic))
9657       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9658
9659     return;
9660   }
9661
9662   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9663     return;
9664
9665   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9666
9667   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9668 }
9669
9670 void CheckExitSP(int x, int y)
9671 {
9672   if (local_player->gems_still_needed > 0)
9673   {
9674     int element = Feld[x][y];
9675     int graphic = el2img(element);
9676
9677     if (IS_ANIMATED(graphic))
9678       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9679
9680     return;
9681   }
9682
9683   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9684     return;
9685
9686   Feld[x][y] = EL_SP_EXIT_OPENING;
9687
9688   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9689 }
9690
9691 static void CloseAllOpenTimegates()
9692 {
9693   int x, y;
9694
9695   SCAN_PLAYFIELD(x, y)
9696   {
9697     int element = Feld[x][y];
9698
9699     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9700     {
9701       Feld[x][y] = EL_TIMEGATE_CLOSING;
9702
9703       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9704     }
9705   }
9706 }
9707
9708 void DrawTwinkleOnField(int x, int y)
9709 {
9710   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9711     return;
9712
9713   if (Feld[x][y] == EL_BD_DIAMOND)
9714     return;
9715
9716   if (MovDelay[x][y] == 0)      /* next animation frame */
9717     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9718
9719   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9720   {
9721     MovDelay[x][y]--;
9722
9723     DrawLevelElementAnimation(x, y, Feld[x][y]);
9724
9725     if (MovDelay[x][y] != 0)
9726     {
9727       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9728                                            10 - MovDelay[x][y]);
9729
9730       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9731     }
9732   }
9733 }
9734
9735 void MauerWaechst(int x, int y)
9736 {
9737   int delay = 6;
9738
9739   if (!MovDelay[x][y])          /* next animation frame */
9740     MovDelay[x][y] = 3 * delay;
9741
9742   if (MovDelay[x][y])           /* wait some time before next frame */
9743   {
9744     MovDelay[x][y]--;
9745
9746     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9747     {
9748       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9749       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9750
9751       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9752     }
9753
9754     if (!MovDelay[x][y])
9755     {
9756       if (MovDir[x][y] == MV_LEFT)
9757       {
9758         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9759           TEST_DrawLevelField(x - 1, y);
9760       }
9761       else if (MovDir[x][y] == MV_RIGHT)
9762       {
9763         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9764           TEST_DrawLevelField(x + 1, y);
9765       }
9766       else if (MovDir[x][y] == MV_UP)
9767       {
9768         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9769           TEST_DrawLevelField(x, y - 1);
9770       }
9771       else
9772       {
9773         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9774           TEST_DrawLevelField(x, y + 1);
9775       }
9776
9777       Feld[x][y] = Store[x][y];
9778       Store[x][y] = 0;
9779       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9780       TEST_DrawLevelField(x, y);
9781     }
9782   }
9783 }
9784
9785 void MauerAbleger(int ax, int ay)
9786 {
9787   int element = Feld[ax][ay];
9788   int graphic = el2img(element);
9789   boolean oben_frei = FALSE, unten_frei = FALSE;
9790   boolean links_frei = FALSE, rechts_frei = FALSE;
9791   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9792   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9793   boolean new_wall = FALSE;
9794
9795   if (IS_ANIMATED(graphic))
9796     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9797
9798   if (!MovDelay[ax][ay])        /* start building new wall */
9799     MovDelay[ax][ay] = 6;
9800
9801   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9802   {
9803     MovDelay[ax][ay]--;
9804     if (MovDelay[ax][ay])
9805       return;
9806   }
9807
9808   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9809     oben_frei = TRUE;
9810   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9811     unten_frei = TRUE;
9812   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9813     links_frei = TRUE;
9814   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9815     rechts_frei = TRUE;
9816
9817   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9818       element == EL_EXPANDABLE_WALL_ANY)
9819   {
9820     if (oben_frei)
9821     {
9822       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9823       Store[ax][ay-1] = element;
9824       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9825       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9826         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9827                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9828       new_wall = TRUE;
9829     }
9830     if (unten_frei)
9831     {
9832       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9833       Store[ax][ay+1] = element;
9834       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9835       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9836         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9837                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9838       new_wall = TRUE;
9839     }
9840   }
9841
9842   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9843       element == EL_EXPANDABLE_WALL_ANY ||
9844       element == EL_EXPANDABLE_WALL ||
9845       element == EL_BD_EXPANDABLE_WALL)
9846   {
9847     if (links_frei)
9848     {
9849       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9850       Store[ax-1][ay] = element;
9851       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9852       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9853         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9854                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9855       new_wall = TRUE;
9856     }
9857
9858     if (rechts_frei)
9859     {
9860       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9861       Store[ax+1][ay] = element;
9862       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9863       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9864         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9865                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9866       new_wall = TRUE;
9867     }
9868   }
9869
9870   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9871     TEST_DrawLevelField(ax, ay);
9872
9873   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9874     oben_massiv = TRUE;
9875   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9876     unten_massiv = TRUE;
9877   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9878     links_massiv = TRUE;
9879   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9880     rechts_massiv = TRUE;
9881
9882   if (((oben_massiv && unten_massiv) ||
9883        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9884        element == EL_EXPANDABLE_WALL) &&
9885       ((links_massiv && rechts_massiv) ||
9886        element == EL_EXPANDABLE_WALL_VERTICAL))
9887     Feld[ax][ay] = EL_WALL;
9888
9889   if (new_wall)
9890     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9891 }
9892
9893 void MauerAblegerStahl(int ax, int ay)
9894 {
9895   int element = Feld[ax][ay];
9896   int graphic = el2img(element);
9897   boolean oben_frei = FALSE, unten_frei = FALSE;
9898   boolean links_frei = FALSE, rechts_frei = FALSE;
9899   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9900   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9901   boolean new_wall = FALSE;
9902
9903   if (IS_ANIMATED(graphic))
9904     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9905
9906   if (!MovDelay[ax][ay])        /* start building new wall */
9907     MovDelay[ax][ay] = 6;
9908
9909   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9910   {
9911     MovDelay[ax][ay]--;
9912     if (MovDelay[ax][ay])
9913       return;
9914   }
9915
9916   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9917     oben_frei = TRUE;
9918   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9919     unten_frei = TRUE;
9920   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9921     links_frei = TRUE;
9922   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9923     rechts_frei = TRUE;
9924
9925   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9926       element == EL_EXPANDABLE_STEELWALL_ANY)
9927   {
9928     if (oben_frei)
9929     {
9930       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9931       Store[ax][ay-1] = element;
9932       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9933       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9934         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9935                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9936       new_wall = TRUE;
9937     }
9938     if (unten_frei)
9939     {
9940       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9941       Store[ax][ay+1] = element;
9942       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9943       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9944         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9945                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9946       new_wall = TRUE;
9947     }
9948   }
9949
9950   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9951       element == EL_EXPANDABLE_STEELWALL_ANY)
9952   {
9953     if (links_frei)
9954     {
9955       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9956       Store[ax-1][ay] = element;
9957       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9958       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9959         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9960                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9961       new_wall = TRUE;
9962     }
9963
9964     if (rechts_frei)
9965     {
9966       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9967       Store[ax+1][ay] = element;
9968       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9969       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9970         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9971                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9972       new_wall = TRUE;
9973     }
9974   }
9975
9976   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9977     oben_massiv = TRUE;
9978   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9979     unten_massiv = TRUE;
9980   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9981     links_massiv = TRUE;
9982   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9983     rechts_massiv = TRUE;
9984
9985   if (((oben_massiv && unten_massiv) ||
9986        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9987       ((links_massiv && rechts_massiv) ||
9988        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9989     Feld[ax][ay] = EL_STEELWALL;
9990
9991   if (new_wall)
9992     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9993 }
9994
9995 void CheckForDragon(int x, int y)
9996 {
9997   int i, j;
9998   boolean dragon_found = FALSE;
9999   static int xy[4][2] =
10000   {
10001     { 0, -1 },
10002     { -1, 0 },
10003     { +1, 0 },
10004     { 0, +1 }
10005   };
10006
10007   for (i = 0; i < NUM_DIRECTIONS; i++)
10008   {
10009     for (j = 0; j < 4; j++)
10010     {
10011       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10012
10013       if (IN_LEV_FIELD(xx, yy) &&
10014           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10015       {
10016         if (Feld[xx][yy] == EL_DRAGON)
10017           dragon_found = TRUE;
10018       }
10019       else
10020         break;
10021     }
10022   }
10023
10024   if (!dragon_found)
10025   {
10026     for (i = 0; i < NUM_DIRECTIONS; i++)
10027     {
10028       for (j = 0; j < 3; j++)
10029       {
10030         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10031   
10032         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10033         {
10034           Feld[xx][yy] = EL_EMPTY;
10035           TEST_DrawLevelField(xx, yy);
10036         }
10037         else
10038           break;
10039       }
10040     }
10041   }
10042 }
10043
10044 static void InitBuggyBase(int x, int y)
10045 {
10046   int element = Feld[x][y];
10047   int activating_delay = FRAMES_PER_SECOND / 4;
10048
10049   ChangeDelay[x][y] =
10050     (element == EL_SP_BUGGY_BASE ?
10051      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10052      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10053      activating_delay :
10054      element == EL_SP_BUGGY_BASE_ACTIVE ?
10055      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10056 }
10057
10058 static void WarnBuggyBase(int x, int y)
10059 {
10060   int i;
10061   static int xy[4][2] =
10062   {
10063     { 0, -1 },
10064     { -1, 0 },
10065     { +1, 0 },
10066     { 0, +1 }
10067   };
10068
10069   for (i = 0; i < NUM_DIRECTIONS; i++)
10070   {
10071     int xx = x + xy[i][0];
10072     int yy = y + xy[i][1];
10073
10074     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10075     {
10076       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10077
10078       break;
10079     }
10080   }
10081 }
10082
10083 static void InitTrap(int x, int y)
10084 {
10085   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10086 }
10087
10088 static void ActivateTrap(int x, int y)
10089 {
10090   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10091 }
10092
10093 static void ChangeActiveTrap(int x, int y)
10094 {
10095   int graphic = IMG_TRAP_ACTIVE;
10096
10097   /* if new animation frame was drawn, correct crumbled sand border */
10098   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10099     TEST_DrawLevelFieldCrumbledSand(x, y);
10100 }
10101
10102 static int getSpecialActionElement(int element, int number, int base_element)
10103 {
10104   return (element != EL_EMPTY ? element :
10105           number != -1 ? base_element + number - 1 :
10106           EL_EMPTY);
10107 }
10108
10109 static int getModifiedActionNumber(int value_old, int operator, int operand,
10110                                    int value_min, int value_max)
10111 {
10112   int value_new = (operator == CA_MODE_SET      ? operand :
10113                    operator == CA_MODE_ADD      ? value_old + operand :
10114                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10115                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10116                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10117                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10118                    value_old);
10119
10120   return (value_new < value_min ? value_min :
10121           value_new > value_max ? value_max :
10122           value_new);
10123 }
10124
10125 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10126 {
10127   struct ElementInfo *ei = &element_info[element];
10128   struct ElementChangeInfo *change = &ei->change_page[page];
10129   int target_element = change->target_element;
10130   int action_type = change->action_type;
10131   int action_mode = change->action_mode;
10132   int action_arg = change->action_arg;
10133   int i;
10134
10135   if (!change->has_action)
10136     return;
10137
10138   /* ---------- determine action paramater values -------------------------- */
10139
10140   int level_time_value =
10141     (level.time > 0 ? TimeLeft :
10142      TimePlayed);
10143
10144   int action_arg_element =
10145     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10146      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10147      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10148      EL_EMPTY);
10149
10150   int action_arg_direction =
10151     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10152      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10153      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10154      change->actual_trigger_side :
10155      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10156      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10157      MV_NONE);
10158
10159   int action_arg_number_min =
10160     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10161      CA_ARG_MIN);
10162
10163   int action_arg_number_max =
10164     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10165      action_type == CA_SET_LEVEL_GEMS ? 999 :
10166      action_type == CA_SET_LEVEL_TIME ? 9999 :
10167      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10168      action_type == CA_SET_CE_VALUE ? 9999 :
10169      action_type == CA_SET_CE_SCORE ? 9999 :
10170      CA_ARG_MAX);
10171
10172   int action_arg_number_reset =
10173     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10174      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10175      action_type == CA_SET_LEVEL_TIME ? level.time :
10176      action_type == CA_SET_LEVEL_SCORE ? 0 :
10177 #if USE_NEW_CUSTOM_VALUE
10178      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10179 #else
10180      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10181 #endif
10182      action_type == CA_SET_CE_SCORE ? 0 :
10183      0);
10184
10185   int action_arg_number =
10186     (action_arg <= CA_ARG_MAX ? action_arg :
10187      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10188      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10189      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10190      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10191      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10192      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10193 #if USE_NEW_CUSTOM_VALUE
10194      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10195 #else
10196      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10197 #endif
10198      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10199      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10200      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10201      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10202      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10203      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10204      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10205      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10206      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10207      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10208      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10209      -1);
10210
10211   int action_arg_number_old =
10212     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10213      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10214      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10215      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10216      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10217      0);
10218
10219   int action_arg_number_new =
10220     getModifiedActionNumber(action_arg_number_old,
10221                             action_mode, action_arg_number,
10222                             action_arg_number_min, action_arg_number_max);
10223
10224 #if 1
10225   int trigger_player_bits = change->actual_trigger_player_bits;
10226 #else
10227   int trigger_player_bits =
10228     (change->actual_trigger_player >= EL_PLAYER_1 &&
10229      change->actual_trigger_player <= EL_PLAYER_4 ?
10230      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10231      PLAYER_BITS_ANY);
10232 #endif
10233
10234   int action_arg_player_bits =
10235     (action_arg >= CA_ARG_PLAYER_1 &&
10236      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10237      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10238      PLAYER_BITS_ANY);
10239
10240   /* ---------- execute action  -------------------------------------------- */
10241
10242   switch (action_type)
10243   {
10244     case CA_NO_ACTION:
10245     {
10246       return;
10247     }
10248
10249     /* ---------- level actions  ------------------------------------------- */
10250
10251     case CA_RESTART_LEVEL:
10252     {
10253       game.restart_level = TRUE;
10254
10255       break;
10256     }
10257
10258     case CA_SHOW_ENVELOPE:
10259     {
10260       int element = getSpecialActionElement(action_arg_element,
10261                                             action_arg_number, EL_ENVELOPE_1);
10262
10263       if (IS_ENVELOPE(element))
10264         local_player->show_envelope = element;
10265
10266       break;
10267     }
10268
10269     case CA_SET_LEVEL_TIME:
10270     {
10271       if (level.time > 0)       /* only modify limited time value */
10272       {
10273         TimeLeft = action_arg_number_new;
10274
10275 #if 1
10276         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10277
10278         DisplayGameControlValues();
10279 #else
10280         DrawGameValue_Time(TimeLeft);
10281 #endif
10282
10283         if (!TimeLeft && setup.time_limit)
10284           for (i = 0; i < MAX_PLAYERS; i++)
10285             KillPlayer(&stored_player[i]);
10286       }
10287
10288       break;
10289     }
10290
10291     case CA_SET_LEVEL_SCORE:
10292     {
10293       local_player->score = action_arg_number_new;
10294
10295 #if 1
10296       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10297
10298       DisplayGameControlValues();
10299 #else
10300       DrawGameValue_Score(local_player->score);
10301 #endif
10302
10303       break;
10304     }
10305
10306     case CA_SET_LEVEL_GEMS:
10307     {
10308       local_player->gems_still_needed = action_arg_number_new;
10309
10310 #if 1
10311       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10312
10313       DisplayGameControlValues();
10314 #else
10315       DrawGameValue_Emeralds(local_player->gems_still_needed);
10316 #endif
10317
10318       break;
10319     }
10320
10321 #if !USE_PLAYER_GRAVITY
10322     case CA_SET_LEVEL_GRAVITY:
10323     {
10324       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10325                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10326                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10327                       game.gravity);
10328       break;
10329     }
10330 #endif
10331
10332     case CA_SET_LEVEL_WIND:
10333     {
10334       game.wind_direction = action_arg_direction;
10335
10336       break;
10337     }
10338
10339     /* ---------- player actions  ------------------------------------------ */
10340
10341     case CA_MOVE_PLAYER:
10342     {
10343       /* automatically move to the next field in specified direction */
10344       for (i = 0; i < MAX_PLAYERS; i++)
10345         if (trigger_player_bits & (1 << i))
10346           stored_player[i].programmed_action = action_arg_direction;
10347
10348       break;
10349     }
10350
10351     case CA_EXIT_PLAYER:
10352     {
10353       for (i = 0; i < MAX_PLAYERS; i++)
10354         if (action_arg_player_bits & (1 << i))
10355           PlayerWins(&stored_player[i]);
10356
10357       break;
10358     }
10359
10360     case CA_KILL_PLAYER:
10361     {
10362       for (i = 0; i < MAX_PLAYERS; i++)
10363         if (action_arg_player_bits & (1 << i))
10364           KillPlayer(&stored_player[i]);
10365
10366       break;
10367     }
10368
10369     case CA_SET_PLAYER_KEYS:
10370     {
10371       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10372       int element = getSpecialActionElement(action_arg_element,
10373                                             action_arg_number, EL_KEY_1);
10374
10375       if (IS_KEY(element))
10376       {
10377         for (i = 0; i < MAX_PLAYERS; i++)
10378         {
10379           if (trigger_player_bits & (1 << i))
10380           {
10381             stored_player[i].key[KEY_NR(element)] = key_state;
10382
10383             DrawGameDoorValues();
10384           }
10385         }
10386       }
10387
10388       break;
10389     }
10390
10391     case CA_SET_PLAYER_SPEED:
10392     {
10393       for (i = 0; i < MAX_PLAYERS; i++)
10394       {
10395         if (trigger_player_bits & (1 << i))
10396         {
10397           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10398
10399           if (action_arg == CA_ARG_SPEED_FASTER &&
10400               stored_player[i].cannot_move)
10401           {
10402             action_arg_number = STEPSIZE_VERY_SLOW;
10403           }
10404           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10405                    action_arg == CA_ARG_SPEED_FASTER)
10406           {
10407             action_arg_number = 2;
10408             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10409                            CA_MODE_MULTIPLY);
10410           }
10411           else if (action_arg == CA_ARG_NUMBER_RESET)
10412           {
10413             action_arg_number = level.initial_player_stepsize[i];
10414           }
10415
10416           move_stepsize =
10417             getModifiedActionNumber(move_stepsize,
10418                                     action_mode,
10419                                     action_arg_number,
10420                                     action_arg_number_min,
10421                                     action_arg_number_max);
10422
10423           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10424         }
10425       }
10426
10427       break;
10428     }
10429
10430     case CA_SET_PLAYER_SHIELD:
10431     {
10432       for (i = 0; i < MAX_PLAYERS; i++)
10433       {
10434         if (trigger_player_bits & (1 << i))
10435         {
10436           if (action_arg == CA_ARG_SHIELD_OFF)
10437           {
10438             stored_player[i].shield_normal_time_left = 0;
10439             stored_player[i].shield_deadly_time_left = 0;
10440           }
10441           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10442           {
10443             stored_player[i].shield_normal_time_left = 999999;
10444           }
10445           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10446           {
10447             stored_player[i].shield_normal_time_left = 999999;
10448             stored_player[i].shield_deadly_time_left = 999999;
10449           }
10450         }
10451       }
10452
10453       break;
10454     }
10455
10456 #if USE_PLAYER_GRAVITY
10457     case CA_SET_PLAYER_GRAVITY:
10458     {
10459       for (i = 0; i < MAX_PLAYERS; i++)
10460       {
10461         if (trigger_player_bits & (1 << i))
10462         {
10463           stored_player[i].gravity =
10464             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10465              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10466              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10467              stored_player[i].gravity);
10468         }
10469       }
10470
10471       break;
10472     }
10473 #endif
10474
10475     case CA_SET_PLAYER_ARTWORK:
10476     {
10477       for (i = 0; i < MAX_PLAYERS; i++)
10478       {
10479         if (trigger_player_bits & (1 << i))
10480         {
10481           int artwork_element = action_arg_element;
10482
10483           if (action_arg == CA_ARG_ELEMENT_RESET)
10484             artwork_element =
10485               (level.use_artwork_element[i] ? level.artwork_element[i] :
10486                stored_player[i].element_nr);
10487
10488 #if USE_GFX_RESET_PLAYER_ARTWORK
10489           if (stored_player[i].artwork_element != artwork_element)
10490             stored_player[i].Frame = 0;
10491 #endif
10492
10493           stored_player[i].artwork_element = artwork_element;
10494
10495           SetPlayerWaiting(&stored_player[i], FALSE);
10496
10497           /* set number of special actions for bored and sleeping animation */
10498           stored_player[i].num_special_action_bored =
10499             get_num_special_action(artwork_element,
10500                                    ACTION_BORING_1, ACTION_BORING_LAST);
10501           stored_player[i].num_special_action_sleeping =
10502             get_num_special_action(artwork_element,
10503                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10504         }
10505       }
10506
10507       break;
10508     }
10509
10510     /* ---------- CE actions  ---------------------------------------------- */
10511
10512     case CA_SET_CE_VALUE:
10513     {
10514 #if USE_NEW_CUSTOM_VALUE
10515       int last_ce_value = CustomValue[x][y];
10516
10517       CustomValue[x][y] = action_arg_number_new;
10518
10519       if (CustomValue[x][y] != last_ce_value)
10520       {
10521         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10522         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10523
10524         if (CustomValue[x][y] == 0)
10525         {
10526           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10527           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10528         }
10529       }
10530 #endif
10531
10532       break;
10533     }
10534
10535     case CA_SET_CE_SCORE:
10536     {
10537 #if USE_NEW_CUSTOM_VALUE
10538       int last_ce_score = ei->collect_score;
10539
10540       ei->collect_score = action_arg_number_new;
10541
10542       if (ei->collect_score != last_ce_score)
10543       {
10544         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10545         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10546
10547         if (ei->collect_score == 0)
10548         {
10549           int xx, yy;
10550
10551           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10552           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10553
10554           /*
10555             This is a very special case that seems to be a mixture between
10556             CheckElementChange() and CheckTriggeredElementChange(): while
10557             the first one only affects single elements that are triggered
10558             directly, the second one affects multiple elements in the playfield
10559             that are triggered indirectly by another element. This is a third
10560             case: Changing the CE score always affects multiple identical CEs,
10561             so every affected CE must be checked, not only the single CE for
10562             which the CE score was changed in the first place (as every instance
10563             of that CE shares the same CE score, and therefore also can change)!
10564           */
10565           SCAN_PLAYFIELD(xx, yy)
10566           {
10567             if (Feld[xx][yy] == element)
10568               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10569                                  CE_SCORE_GETS_ZERO);
10570           }
10571         }
10572       }
10573 #endif
10574
10575       break;
10576     }
10577
10578     /* ---------- engine actions  ------------------------------------------ */
10579
10580     case CA_SET_ENGINE_SCAN_MODE:
10581     {
10582       InitPlayfieldScanMode(action_arg);
10583
10584       break;
10585     }
10586
10587     default:
10588       break;
10589   }
10590 }
10591
10592 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10593 {
10594   int old_element = Feld[x][y];
10595   int new_element = GetElementFromGroupElement(element);
10596   int previous_move_direction = MovDir[x][y];
10597 #if USE_NEW_CUSTOM_VALUE
10598   int last_ce_value = CustomValue[x][y];
10599 #endif
10600   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10601   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10602   boolean add_player_onto_element = (new_element_is_player &&
10603 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10604                                      /* this breaks SnakeBite when a snake is
10605                                         halfway through a door that closes */
10606                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10607                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10608 #endif
10609                                      IS_WALKABLE(old_element));
10610
10611 #if 0
10612   /* check if element under the player changes from accessible to unaccessible
10613      (needed for special case of dropping element which then changes) */
10614   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10615       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10616   {
10617     Bang(x, y);
10618
10619     return;
10620   }
10621 #endif
10622
10623   if (!add_player_onto_element)
10624   {
10625     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10626       RemoveMovingField(x, y);
10627     else
10628       RemoveField(x, y);
10629
10630     Feld[x][y] = new_element;
10631
10632 #if !USE_GFX_RESET_GFX_ANIMATION
10633     ResetGfxAnimation(x, y);
10634     ResetRandomAnimationValue(x, y);
10635 #endif
10636
10637     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10638       MovDir[x][y] = previous_move_direction;
10639
10640 #if USE_NEW_CUSTOM_VALUE
10641     if (element_info[new_element].use_last_ce_value)
10642       CustomValue[x][y] = last_ce_value;
10643 #endif
10644
10645     InitField_WithBug1(x, y, FALSE);
10646
10647     new_element = Feld[x][y];   /* element may have changed */
10648
10649 #if USE_GFX_RESET_GFX_ANIMATION
10650     ResetGfxAnimation(x, y);
10651     ResetRandomAnimationValue(x, y);
10652 #endif
10653
10654     TEST_DrawLevelField(x, y);
10655
10656     if (GFX_CRUMBLED(new_element))
10657       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10658   }
10659
10660 #if 1
10661   /* check if element under the player changes from accessible to unaccessible
10662      (needed for special case of dropping element which then changes) */
10663   /* (must be checked after creating new element for walkable group elements) */
10664 #if USE_FIX_KILLED_BY_NON_WALKABLE
10665   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10666       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10667   {
10668     Bang(x, y);
10669
10670     return;
10671   }
10672 #else
10673   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10674       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10675   {
10676     Bang(x, y);
10677
10678     return;
10679   }
10680 #endif
10681 #endif
10682
10683   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10684   if (new_element_is_player)
10685     RelocatePlayer(x, y, new_element);
10686
10687   if (is_change)
10688     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10689
10690   TestIfBadThingTouchesPlayer(x, y);
10691   TestIfPlayerTouchesCustomElement(x, y);
10692   TestIfElementTouchesCustomElement(x, y);
10693 }
10694
10695 static void CreateField(int x, int y, int element)
10696 {
10697   CreateFieldExt(x, y, element, FALSE);
10698 }
10699
10700 static void CreateElementFromChange(int x, int y, int element)
10701 {
10702   element = GET_VALID_RUNTIME_ELEMENT(element);
10703
10704 #if USE_STOP_CHANGED_ELEMENTS
10705   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10706   {
10707     int old_element = Feld[x][y];
10708
10709     /* prevent changed element from moving in same engine frame
10710        unless both old and new element can either fall or move */
10711     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10712         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10713       Stop[x][y] = TRUE;
10714   }
10715 #endif
10716
10717   CreateFieldExt(x, y, element, TRUE);
10718 }
10719
10720 static boolean ChangeElement(int x, int y, int element, int page)
10721 {
10722   struct ElementInfo *ei = &element_info[element];
10723   struct ElementChangeInfo *change = &ei->change_page[page];
10724   int ce_value = CustomValue[x][y];
10725   int ce_score = ei->collect_score;
10726   int target_element;
10727   int old_element = Feld[x][y];
10728
10729   /* always use default change event to prevent running into a loop */
10730   if (ChangeEvent[x][y] == -1)
10731     ChangeEvent[x][y] = CE_DELAY;
10732
10733   if (ChangeEvent[x][y] == CE_DELAY)
10734   {
10735     /* reset actual trigger element, trigger player and action element */
10736     change->actual_trigger_element = EL_EMPTY;
10737     change->actual_trigger_player = EL_PLAYER_1;
10738     change->actual_trigger_player_bits = CH_PLAYER_1;
10739     change->actual_trigger_side = CH_SIDE_NONE;
10740     change->actual_trigger_ce_value = 0;
10741     change->actual_trigger_ce_score = 0;
10742   }
10743
10744   /* do not change elements more than a specified maximum number of changes */
10745   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10746     return FALSE;
10747
10748   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10749
10750   if (change->explode)
10751   {
10752     Bang(x, y);
10753
10754     return TRUE;
10755   }
10756
10757   if (change->use_target_content)
10758   {
10759     boolean complete_replace = TRUE;
10760     boolean can_replace[3][3];
10761     int xx, yy;
10762
10763     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10764     {
10765       boolean is_empty;
10766       boolean is_walkable;
10767       boolean is_diggable;
10768       boolean is_collectible;
10769       boolean is_removable;
10770       boolean is_destructible;
10771       int ex = x + xx - 1;
10772       int ey = y + yy - 1;
10773       int content_element = change->target_content.e[xx][yy];
10774       int e;
10775
10776       can_replace[xx][yy] = TRUE;
10777
10778       if (ex == x && ey == y)   /* do not check changing element itself */
10779         continue;
10780
10781       if (content_element == EL_EMPTY_SPACE)
10782       {
10783         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10784
10785         continue;
10786       }
10787
10788       if (!IN_LEV_FIELD(ex, ey))
10789       {
10790         can_replace[xx][yy] = FALSE;
10791         complete_replace = FALSE;
10792
10793         continue;
10794       }
10795
10796       e = Feld[ex][ey];
10797
10798       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10799         e = MovingOrBlocked2Element(ex, ey);
10800
10801       is_empty = (IS_FREE(ex, ey) ||
10802                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10803
10804       is_walkable     = (is_empty || IS_WALKABLE(e));
10805       is_diggable     = (is_empty || IS_DIGGABLE(e));
10806       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10807       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10808       is_removable    = (is_diggable || is_collectible);
10809
10810       can_replace[xx][yy] =
10811         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10812           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10813           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10814           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10815           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10816           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10817          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10818
10819       if (!can_replace[xx][yy])
10820         complete_replace = FALSE;
10821     }
10822
10823     if (!change->only_if_complete || complete_replace)
10824     {
10825       boolean something_has_changed = FALSE;
10826
10827       if (change->only_if_complete && change->use_random_replace &&
10828           RND(100) < change->random_percentage)
10829         return FALSE;
10830
10831       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10832       {
10833         int ex = x + xx - 1;
10834         int ey = y + yy - 1;
10835         int content_element;
10836
10837         if (can_replace[xx][yy] && (!change->use_random_replace ||
10838                                     RND(100) < change->random_percentage))
10839         {
10840           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10841             RemoveMovingField(ex, ey);
10842
10843           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10844
10845           content_element = change->target_content.e[xx][yy];
10846           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10847                                               ce_value, ce_score);
10848
10849           CreateElementFromChange(ex, ey, target_element);
10850
10851           something_has_changed = TRUE;
10852
10853           /* for symmetry reasons, freeze newly created border elements */
10854           if (ex != x || ey != y)
10855             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10856         }
10857       }
10858
10859       if (something_has_changed)
10860       {
10861         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10862         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10863       }
10864     }
10865   }
10866   else
10867   {
10868     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10869                                         ce_value, ce_score);
10870
10871     if (element == EL_DIAGONAL_GROWING ||
10872         element == EL_DIAGONAL_SHRINKING)
10873     {
10874       target_element = Store[x][y];
10875
10876       Store[x][y] = EL_EMPTY;
10877     }
10878
10879     CreateElementFromChange(x, y, target_element);
10880
10881     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10882     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10883   }
10884
10885   /* this uses direct change before indirect change */
10886   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10887
10888   return TRUE;
10889 }
10890
10891 #if USE_NEW_DELAYED_ACTION
10892
10893 static void HandleElementChange(int x, int y, int page)
10894 {
10895   int element = MovingOrBlocked2Element(x, y);
10896   struct ElementInfo *ei = &element_info[element];
10897   struct ElementChangeInfo *change = &ei->change_page[page];
10898
10899 #ifdef DEBUG
10900   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10901       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10902   {
10903     printf("\n\n");
10904     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10905            x, y, element, element_info[element].token_name);
10906     printf("HandleElementChange(): This should never happen!\n");
10907     printf("\n\n");
10908   }
10909 #endif
10910
10911   /* this can happen with classic bombs on walkable, changing elements */
10912   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10913   {
10914 #if 0
10915     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10916       ChangeDelay[x][y] = 0;
10917 #endif
10918
10919     return;
10920   }
10921
10922   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10923   {
10924     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10925
10926     if (change->can_change)
10927     {
10928 #if 1
10929       /* !!! not clear why graphic animation should be reset at all here !!! */
10930       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10931 #if USE_GFX_RESET_WHEN_NOT_MOVING
10932       /* when a custom element is about to change (for example by change delay),
10933          do not reset graphic animation when the custom element is moving */
10934       if (!IS_MOVING(x, y))
10935 #endif
10936       {
10937         ResetGfxAnimation(x, y);
10938         ResetRandomAnimationValue(x, y);
10939       }
10940 #endif
10941
10942       if (change->pre_change_function)
10943         change->pre_change_function(x, y);
10944     }
10945   }
10946
10947   ChangeDelay[x][y]--;
10948
10949   if (ChangeDelay[x][y] != 0)           /* continue element change */
10950   {
10951     if (change->can_change)
10952     {
10953       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10954
10955       if (IS_ANIMATED(graphic))
10956         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10957
10958       if (change->change_function)
10959         change->change_function(x, y);
10960     }
10961   }
10962   else                                  /* finish element change */
10963   {
10964     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10965     {
10966       page = ChangePage[x][y];
10967       ChangePage[x][y] = -1;
10968
10969       change = &ei->change_page[page];
10970     }
10971
10972     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10973     {
10974       ChangeDelay[x][y] = 1;            /* try change after next move step */
10975       ChangePage[x][y] = page;          /* remember page to use for change */
10976
10977       return;
10978     }
10979
10980     if (change->can_change)
10981     {
10982       if (ChangeElement(x, y, element, page))
10983       {
10984         if (change->post_change_function)
10985           change->post_change_function(x, y);
10986       }
10987     }
10988
10989     if (change->has_action)
10990       ExecuteCustomElementAction(x, y, element, page);
10991   }
10992 }
10993
10994 #else
10995
10996 static void HandleElementChange(int x, int y, int page)
10997 {
10998   int element = MovingOrBlocked2Element(x, y);
10999   struct ElementInfo *ei = &element_info[element];
11000   struct ElementChangeInfo *change = &ei->change_page[page];
11001
11002 #ifdef DEBUG
11003   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11004   {
11005     printf("\n\n");
11006     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11007            x, y, element, element_info[element].token_name);
11008     printf("HandleElementChange(): This should never happen!\n");
11009     printf("\n\n");
11010   }
11011 #endif
11012
11013   /* this can happen with classic bombs on walkable, changing elements */
11014   if (!CAN_CHANGE(element))
11015   {
11016 #if 0
11017     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11018       ChangeDelay[x][y] = 0;
11019 #endif
11020
11021     return;
11022   }
11023
11024   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11025   {
11026     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11027
11028     ResetGfxAnimation(x, y);
11029     ResetRandomAnimationValue(x, y);
11030
11031     if (change->pre_change_function)
11032       change->pre_change_function(x, y);
11033   }
11034
11035   ChangeDelay[x][y]--;
11036
11037   if (ChangeDelay[x][y] != 0)           /* continue element change */
11038   {
11039     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11040
11041     if (IS_ANIMATED(graphic))
11042       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11043
11044     if (change->change_function)
11045       change->change_function(x, y);
11046   }
11047   else                                  /* finish element change */
11048   {
11049     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11050     {
11051       page = ChangePage[x][y];
11052       ChangePage[x][y] = -1;
11053
11054       change = &ei->change_page[page];
11055     }
11056
11057     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11058     {
11059       ChangeDelay[x][y] = 1;            /* try change after next move step */
11060       ChangePage[x][y] = page;          /* remember page to use for change */
11061
11062       return;
11063     }
11064
11065     if (ChangeElement(x, y, element, page))
11066     {
11067       if (change->post_change_function)
11068         change->post_change_function(x, y);
11069     }
11070   }
11071 }
11072
11073 #endif
11074
11075 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11076                                               int trigger_element,
11077                                               int trigger_event,
11078                                               int trigger_player,
11079                                               int trigger_side,
11080                                               int trigger_page)
11081 {
11082   boolean change_done_any = FALSE;
11083   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11084   int i;
11085
11086   if (!(trigger_events[trigger_element][trigger_event]))
11087     return FALSE;
11088
11089 #if 0
11090   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11091          trigger_event, recursion_loop_depth, recursion_loop_detected,
11092          recursion_loop_element, EL_NAME(recursion_loop_element));
11093 #endif
11094
11095   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11096
11097   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11098   {
11099     int element = EL_CUSTOM_START + i;
11100     boolean change_done = FALSE;
11101     int p;
11102
11103     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11104         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11105       continue;
11106
11107     for (p = 0; p < element_info[element].num_change_pages; p++)
11108     {
11109       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11110
11111       if (change->can_change_or_has_action &&
11112           change->has_event[trigger_event] &&
11113           change->trigger_side & trigger_side &&
11114           change->trigger_player & trigger_player &&
11115           change->trigger_page & trigger_page_bits &&
11116           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11117       {
11118         change->actual_trigger_element = trigger_element;
11119         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11120         change->actual_trigger_player_bits = trigger_player;
11121         change->actual_trigger_side = trigger_side;
11122         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11123         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11124
11125         if ((change->can_change && !change_done) || change->has_action)
11126         {
11127           int x, y;
11128
11129           SCAN_PLAYFIELD(x, y)
11130           {
11131             if (Feld[x][y] == element)
11132             {
11133               if (change->can_change && !change_done)
11134               {
11135                 ChangeDelay[x][y] = 1;
11136                 ChangeEvent[x][y] = trigger_event;
11137
11138                 HandleElementChange(x, y, p);
11139               }
11140 #if USE_NEW_DELAYED_ACTION
11141               else if (change->has_action)
11142               {
11143                 ExecuteCustomElementAction(x, y, element, p);
11144                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11145               }
11146 #else
11147               if (change->has_action)
11148               {
11149                 ExecuteCustomElementAction(x, y, element, p);
11150                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11151               }
11152 #endif
11153             }
11154           }
11155
11156           if (change->can_change)
11157           {
11158             change_done = TRUE;
11159             change_done_any = TRUE;
11160           }
11161         }
11162       }
11163     }
11164   }
11165
11166   RECURSION_LOOP_DETECTION_END();
11167
11168   return change_done_any;
11169 }
11170
11171 static boolean CheckElementChangeExt(int x, int y,
11172                                      int element,
11173                                      int trigger_element,
11174                                      int trigger_event,
11175                                      int trigger_player,
11176                                      int trigger_side)
11177 {
11178   boolean change_done = FALSE;
11179   int p;
11180
11181   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11182       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11183     return FALSE;
11184
11185   if (Feld[x][y] == EL_BLOCKED)
11186   {
11187     Blocked2Moving(x, y, &x, &y);
11188     element = Feld[x][y];
11189   }
11190
11191 #if 0
11192   /* check if element has already changed */
11193   if (Feld[x][y] != element)
11194     return FALSE;
11195 #else
11196   /* check if element has already changed or is about to change after moving */
11197   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11198        Feld[x][y] != element) ||
11199
11200       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11201        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11202         ChangePage[x][y] != -1)))
11203     return FALSE;
11204 #endif
11205
11206 #if 0
11207   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11208          trigger_event, recursion_loop_depth, recursion_loop_detected,
11209          recursion_loop_element, EL_NAME(recursion_loop_element));
11210 #endif
11211
11212   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11213
11214   for (p = 0; p < element_info[element].num_change_pages; p++)
11215   {
11216     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11217
11218     /* check trigger element for all events where the element that is checked
11219        for changing interacts with a directly adjacent element -- this is
11220        different to element changes that affect other elements to change on the
11221        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11222     boolean check_trigger_element =
11223       (trigger_event == CE_TOUCHING_X ||
11224        trigger_event == CE_HITTING_X ||
11225        trigger_event == CE_HIT_BY_X ||
11226 #if 1
11227        /* this one was forgotten until 3.2.3 */
11228        trigger_event == CE_DIGGING_X);
11229 #endif
11230
11231     if (change->can_change_or_has_action &&
11232         change->has_event[trigger_event] &&
11233         change->trigger_side & trigger_side &&
11234         change->trigger_player & trigger_player &&
11235         (!check_trigger_element ||
11236          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11237     {
11238       change->actual_trigger_element = trigger_element;
11239       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11240       change->actual_trigger_player_bits = trigger_player;
11241       change->actual_trigger_side = trigger_side;
11242       change->actual_trigger_ce_value = CustomValue[x][y];
11243       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11244
11245       /* special case: trigger element not at (x,y) position for some events */
11246       if (check_trigger_element)
11247       {
11248         static struct
11249         {
11250           int dx, dy;
11251         } move_xy[] =
11252           {
11253             {  0,  0 },
11254             { -1,  0 },
11255             { +1,  0 },
11256             {  0,  0 },
11257             {  0, -1 },
11258             {  0,  0 }, { 0, 0 }, { 0, 0 },
11259             {  0, +1 }
11260           };
11261
11262         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11263         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11264
11265         change->actual_trigger_ce_value = CustomValue[xx][yy];
11266         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11267       }
11268
11269       if (change->can_change && !change_done)
11270       {
11271         ChangeDelay[x][y] = 1;
11272         ChangeEvent[x][y] = trigger_event;
11273
11274         HandleElementChange(x, y, p);
11275
11276         change_done = TRUE;
11277       }
11278 #if USE_NEW_DELAYED_ACTION
11279       else if (change->has_action)
11280       {
11281         ExecuteCustomElementAction(x, y, element, p);
11282         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11283       }
11284 #else
11285       if (change->has_action)
11286       {
11287         ExecuteCustomElementAction(x, y, element, p);
11288         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11289       }
11290 #endif
11291     }
11292   }
11293
11294   RECURSION_LOOP_DETECTION_END();
11295
11296   return change_done;
11297 }
11298
11299 static void PlayPlayerSound(struct PlayerInfo *player)
11300 {
11301   int jx = player->jx, jy = player->jy;
11302   int sound_element = player->artwork_element;
11303   int last_action = player->last_action_waiting;
11304   int action = player->action_waiting;
11305
11306   if (player->is_waiting)
11307   {
11308     if (action != last_action)
11309       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11310     else
11311       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11312   }
11313   else
11314   {
11315     if (action != last_action)
11316       StopSound(element_info[sound_element].sound[last_action]);
11317
11318     if (last_action == ACTION_SLEEPING)
11319       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11320   }
11321 }
11322
11323 static void PlayAllPlayersSound()
11324 {
11325   int i;
11326
11327   for (i = 0; i < MAX_PLAYERS; i++)
11328     if (stored_player[i].active)
11329       PlayPlayerSound(&stored_player[i]);
11330 }
11331
11332 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11333 {
11334   boolean last_waiting = player->is_waiting;
11335   int move_dir = player->MovDir;
11336
11337   player->dir_waiting = move_dir;
11338   player->last_action_waiting = player->action_waiting;
11339
11340   if (is_waiting)
11341   {
11342     if (!last_waiting)          /* not waiting -> waiting */
11343     {
11344       player->is_waiting = TRUE;
11345
11346       player->frame_counter_bored =
11347         FrameCounter +
11348         game.player_boring_delay_fixed +
11349         GetSimpleRandom(game.player_boring_delay_random);
11350       player->frame_counter_sleeping =
11351         FrameCounter +
11352         game.player_sleeping_delay_fixed +
11353         GetSimpleRandom(game.player_sleeping_delay_random);
11354
11355       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11356     }
11357
11358     if (game.player_sleeping_delay_fixed +
11359         game.player_sleeping_delay_random > 0 &&
11360         player->anim_delay_counter == 0 &&
11361         player->post_delay_counter == 0 &&
11362         FrameCounter >= player->frame_counter_sleeping)
11363       player->is_sleeping = TRUE;
11364     else if (game.player_boring_delay_fixed +
11365              game.player_boring_delay_random > 0 &&
11366              FrameCounter >= player->frame_counter_bored)
11367       player->is_bored = TRUE;
11368
11369     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11370                               player->is_bored ? ACTION_BORING :
11371                               ACTION_WAITING);
11372
11373     if (player->is_sleeping && player->use_murphy)
11374     {
11375       /* special case for sleeping Murphy when leaning against non-free tile */
11376
11377       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11378           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11379            !IS_MOVING(player->jx - 1, player->jy)))
11380         move_dir = MV_LEFT;
11381       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11382                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11383                 !IS_MOVING(player->jx + 1, player->jy)))
11384         move_dir = MV_RIGHT;
11385       else
11386         player->is_sleeping = FALSE;
11387
11388       player->dir_waiting = move_dir;
11389     }
11390
11391     if (player->is_sleeping)
11392     {
11393       if (player->num_special_action_sleeping > 0)
11394       {
11395         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11396         {
11397           int last_special_action = player->special_action_sleeping;
11398           int num_special_action = player->num_special_action_sleeping;
11399           int special_action =
11400             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11401              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11402              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11403              last_special_action + 1 : ACTION_SLEEPING);
11404           int special_graphic =
11405             el_act_dir2img(player->artwork_element, special_action, move_dir);
11406
11407           player->anim_delay_counter =
11408             graphic_info[special_graphic].anim_delay_fixed +
11409             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11410           player->post_delay_counter =
11411             graphic_info[special_graphic].post_delay_fixed +
11412             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11413
11414           player->special_action_sleeping = special_action;
11415         }
11416
11417         if (player->anim_delay_counter > 0)
11418         {
11419           player->action_waiting = player->special_action_sleeping;
11420           player->anim_delay_counter--;
11421         }
11422         else if (player->post_delay_counter > 0)
11423         {
11424           player->post_delay_counter--;
11425         }
11426       }
11427     }
11428     else if (player->is_bored)
11429     {
11430       if (player->num_special_action_bored > 0)
11431       {
11432         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11433         {
11434           int special_action =
11435             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11436           int special_graphic =
11437             el_act_dir2img(player->artwork_element, special_action, move_dir);
11438
11439           player->anim_delay_counter =
11440             graphic_info[special_graphic].anim_delay_fixed +
11441             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11442           player->post_delay_counter =
11443             graphic_info[special_graphic].post_delay_fixed +
11444             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11445
11446           player->special_action_bored = special_action;
11447         }
11448
11449         if (player->anim_delay_counter > 0)
11450         {
11451           player->action_waiting = player->special_action_bored;
11452           player->anim_delay_counter--;
11453         }
11454         else if (player->post_delay_counter > 0)
11455         {
11456           player->post_delay_counter--;
11457         }
11458       }
11459     }
11460   }
11461   else if (last_waiting)        /* waiting -> not waiting */
11462   {
11463     player->is_waiting = FALSE;
11464     player->is_bored = FALSE;
11465     player->is_sleeping = FALSE;
11466
11467     player->frame_counter_bored = -1;
11468     player->frame_counter_sleeping = -1;
11469
11470     player->anim_delay_counter = 0;
11471     player->post_delay_counter = 0;
11472
11473     player->dir_waiting = player->MovDir;
11474     player->action_waiting = ACTION_DEFAULT;
11475
11476     player->special_action_bored = ACTION_DEFAULT;
11477     player->special_action_sleeping = ACTION_DEFAULT;
11478   }
11479 }
11480
11481 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11482 {
11483   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11484   int left      = player_action & JOY_LEFT;
11485   int right     = player_action & JOY_RIGHT;
11486   int up        = player_action & JOY_UP;
11487   int down      = player_action & JOY_DOWN;
11488   int button1   = player_action & JOY_BUTTON_1;
11489   int button2   = player_action & JOY_BUTTON_2;
11490   int dx        = (left ? -1 : right ? 1 : 0);
11491   int dy        = (up   ? -1 : down  ? 1 : 0);
11492
11493   if (!player->active || tape.pausing)
11494     return 0;
11495
11496   if (player_action)
11497   {
11498     if (button1)
11499       snapped = SnapField(player, dx, dy);
11500     else
11501     {
11502       if (button2)
11503         dropped = DropElement(player);
11504
11505       moved = MovePlayer(player, dx, dy);
11506     }
11507
11508     if (tape.single_step && tape.recording && !tape.pausing)
11509     {
11510       if (button1 || (dropped && !moved))
11511       {
11512         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11513         SnapField(player, 0, 0);                /* stop snapping */
11514       }
11515     }
11516
11517     SetPlayerWaiting(player, FALSE);
11518
11519     return player_action;
11520   }
11521   else
11522   {
11523     /* no actions for this player (no input at player's configured device) */
11524
11525     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11526     SnapField(player, 0, 0);
11527     CheckGravityMovementWhenNotMoving(player);
11528
11529     if (player->MovPos == 0)
11530       SetPlayerWaiting(player, TRUE);
11531
11532     if (player->MovPos == 0)    /* needed for tape.playing */
11533       player->is_moving = FALSE;
11534
11535     player->is_dropping = FALSE;
11536     player->is_dropping_pressed = FALSE;
11537     player->drop_pressed_delay = 0;
11538
11539     return 0;
11540   }
11541 }
11542
11543 static void CheckLevelTime()
11544 {
11545   int i;
11546
11547   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11548   {
11549     if (level.native_em_level->lev->home == 0)  /* all players at home */
11550     {
11551       PlayerWins(local_player);
11552
11553       AllPlayersGone = TRUE;
11554
11555       level.native_em_level->lev->home = -1;
11556     }
11557
11558     if (level.native_em_level->ply[0]->alive == 0 &&
11559         level.native_em_level->ply[1]->alive == 0 &&
11560         level.native_em_level->ply[2]->alive == 0 &&
11561         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11562       AllPlayersGone = TRUE;
11563   }
11564
11565   if (TimeFrames >= FRAMES_PER_SECOND)
11566   {
11567     TimeFrames = 0;
11568     TapeTime++;
11569
11570     for (i = 0; i < MAX_PLAYERS; i++)
11571     {
11572       struct PlayerInfo *player = &stored_player[i];
11573
11574       if (SHIELD_ON(player))
11575       {
11576         player->shield_normal_time_left--;
11577
11578         if (player->shield_deadly_time_left > 0)
11579           player->shield_deadly_time_left--;
11580       }
11581     }
11582
11583     if (!local_player->LevelSolved && !level.use_step_counter)
11584     {
11585       TimePlayed++;
11586
11587       if (TimeLeft > 0)
11588       {
11589         TimeLeft--;
11590
11591         if (TimeLeft <= 10 && setup.time_limit)
11592           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11593
11594 #if 1
11595         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11596
11597         DisplayGameControlValues();
11598 #else
11599         DrawGameValue_Time(TimeLeft);
11600 #endif
11601
11602         if (!TimeLeft && setup.time_limit)
11603         {
11604           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11605             level.native_em_level->lev->killed_out_of_time = TRUE;
11606           else
11607             for (i = 0; i < MAX_PLAYERS; i++)
11608               KillPlayer(&stored_player[i]);
11609         }
11610       }
11611 #if 1
11612       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11613       {
11614         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11615
11616         DisplayGameControlValues();
11617       }
11618 #else
11619       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11620         DrawGameValue_Time(TimePlayed);
11621 #endif
11622
11623       level.native_em_level->lev->time =
11624         (level.time == 0 ? TimePlayed : TimeLeft);
11625     }
11626
11627     if (tape.recording || tape.playing)
11628       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11629   }
11630
11631 #if 1
11632   UpdateAndDisplayGameControlValues();
11633 #else
11634   UpdateGameDoorValues();
11635   DrawGameDoorValues();
11636 #endif
11637 }
11638
11639 void AdvanceFrameAndPlayerCounters(int player_nr)
11640 {
11641   int i;
11642
11643   /* advance frame counters (global frame counter and time frame counter) */
11644   FrameCounter++;
11645   TimeFrames++;
11646
11647   /* advance player counters (counters for move delay, move animation etc.) */
11648   for (i = 0; i < MAX_PLAYERS; i++)
11649   {
11650     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11651     int move_delay_value = stored_player[i].move_delay_value;
11652     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11653
11654     if (!advance_player_counters)       /* not all players may be affected */
11655       continue;
11656
11657 #if USE_NEW_PLAYER_ANIM
11658     if (move_frames == 0)       /* less than one move per game frame */
11659     {
11660       int stepsize = TILEX / move_delay_value;
11661       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11662       int count = (stored_player[i].is_moving ?
11663                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11664
11665       if (count % delay == 0)
11666         move_frames = 1;
11667     }
11668 #endif
11669
11670     stored_player[i].Frame += move_frames;
11671
11672     if (stored_player[i].MovPos != 0)
11673       stored_player[i].StepFrame += move_frames;
11674
11675     if (stored_player[i].move_delay > 0)
11676       stored_player[i].move_delay--;
11677
11678     /* due to bugs in previous versions, counter must count up, not down */
11679     if (stored_player[i].push_delay != -1)
11680       stored_player[i].push_delay++;
11681
11682     if (stored_player[i].drop_delay > 0)
11683       stored_player[i].drop_delay--;
11684
11685     if (stored_player[i].is_dropping_pressed)
11686       stored_player[i].drop_pressed_delay++;
11687   }
11688 }
11689
11690 void StartGameActions(boolean init_network_game, boolean record_tape,
11691                       long random_seed)
11692 {
11693   unsigned long new_random_seed = InitRND(random_seed);
11694
11695   if (record_tape)
11696     TapeStartRecording(new_random_seed);
11697
11698 #if defined(NETWORK_AVALIABLE)
11699   if (init_network_game)
11700   {
11701     SendToServer_StartPlaying();
11702
11703     return;
11704   }
11705 #endif
11706
11707   InitGame();
11708 }
11709
11710 void GameActions()
11711 {
11712   static unsigned long game_frame_delay = 0;
11713   unsigned long game_frame_delay_value;
11714   byte *recorded_player_action;
11715   byte summarized_player_action = 0;
11716   byte tape_action[MAX_PLAYERS];
11717   int i;
11718
11719   /* detect endless loops, caused by custom element programming */
11720   if (recursion_loop_detected && recursion_loop_depth == 0)
11721   {
11722     char *message = getStringCat3("Internal Error ! Element ",
11723                                   EL_NAME(recursion_loop_element),
11724                                   " caused endless loop ! Quit the game ?");
11725
11726     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11727           EL_NAME(recursion_loop_element));
11728
11729     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11730
11731     recursion_loop_detected = FALSE;    /* if game should be continued */
11732
11733     free(message);
11734
11735     return;
11736   }
11737
11738   if (game.restart_level)
11739     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11740
11741   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11742   {
11743     if (level.native_em_level->lev->home == 0)  /* all players at home */
11744     {
11745       PlayerWins(local_player);
11746
11747       AllPlayersGone = TRUE;
11748
11749       level.native_em_level->lev->home = -1;
11750     }
11751
11752     if (level.native_em_level->ply[0]->alive == 0 &&
11753         level.native_em_level->ply[1]->alive == 0 &&
11754         level.native_em_level->ply[2]->alive == 0 &&
11755         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11756       AllPlayersGone = TRUE;
11757   }
11758
11759   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11760     GameWon();
11761
11762   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11763     TapeStop();
11764
11765   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11766     return;
11767
11768   game_frame_delay_value =
11769     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11770
11771   if (tape.playing && tape.warp_forward && !tape.pausing)
11772     game_frame_delay_value = 0;
11773
11774   /* ---------- main game synchronization point ---------- */
11775
11776   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11777
11778   if (network_playing && !network_player_action_received)
11779   {
11780     /* try to get network player actions in time */
11781
11782 #if defined(NETWORK_AVALIABLE)
11783     /* last chance to get network player actions without main loop delay */
11784     HandleNetworking();
11785 #endif
11786
11787     /* game was quit by network peer */
11788     if (game_status != GAME_MODE_PLAYING)
11789       return;
11790
11791     if (!network_player_action_received)
11792       return;           /* failed to get network player actions in time */
11793
11794     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11795   }
11796
11797   if (tape.pausing)
11798     return;
11799
11800   /* at this point we know that we really continue executing the game */
11801
11802   network_player_action_received = FALSE;
11803
11804   /* when playing tape, read previously recorded player input from tape data */
11805   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11806
11807 #if 1
11808   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11809   if (tape.pausing)
11810     return;
11811 #endif
11812
11813   if (tape.set_centered_player)
11814   {
11815     game.centered_player_nr_next = tape.centered_player_nr_next;
11816     game.set_centered_player = TRUE;
11817   }
11818
11819   for (i = 0; i < MAX_PLAYERS; i++)
11820   {
11821     summarized_player_action |= stored_player[i].action;
11822
11823     if (!network_playing)
11824       stored_player[i].effective_action = stored_player[i].action;
11825   }
11826
11827 #if defined(NETWORK_AVALIABLE)
11828   if (network_playing)
11829     SendToServer_MovePlayer(summarized_player_action);
11830 #endif
11831
11832   if (!options.network && !setup.team_mode)
11833     local_player->effective_action = summarized_player_action;
11834
11835   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11836   {
11837     for (i = 0; i < MAX_PLAYERS; i++)
11838       stored_player[i].effective_action =
11839         (i == game.centered_player_nr ? summarized_player_action : 0);
11840   }
11841
11842   if (recorded_player_action != NULL)
11843     for (i = 0; i < MAX_PLAYERS; i++)
11844       stored_player[i].effective_action = recorded_player_action[i];
11845
11846   for (i = 0; i < MAX_PLAYERS; i++)
11847   {
11848     tape_action[i] = stored_player[i].effective_action;
11849
11850     /* (this can only happen in the R'n'D game engine) */
11851     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11852       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11853   }
11854
11855   /* only record actions from input devices, but not programmed actions */
11856   if (tape.recording)
11857     TapeRecordAction(tape_action);
11858
11859   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11860   {
11861     GameActions_EM_Main();
11862   }
11863   else
11864   {
11865     GameActions_RND();
11866   }
11867 }
11868
11869 void GameActions_EM_Main()
11870 {
11871   byte effective_action[MAX_PLAYERS];
11872   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11873   int i;
11874
11875   for (i = 0; i < MAX_PLAYERS; i++)
11876     effective_action[i] = stored_player[i].effective_action;
11877
11878   GameActions_EM(effective_action, warp_mode);
11879
11880   CheckLevelTime();
11881
11882   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11883 }
11884
11885 void GameActions_RND()
11886 {
11887   int magic_wall_x = 0, magic_wall_y = 0;
11888   int i, x, y, element, graphic;
11889
11890   InitPlayfieldScanModeVars();
11891
11892 #if USE_ONE_MORE_CHANGE_PER_FRAME
11893   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11894   {
11895     SCAN_PLAYFIELD(x, y)
11896     {
11897       ChangeCount[x][y] = 0;
11898       ChangeEvent[x][y] = -1;
11899     }
11900   }
11901 #endif
11902
11903   if (game.set_centered_player)
11904   {
11905     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11906
11907     /* switching to "all players" only possible if all players fit to screen */
11908     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11909     {
11910       game.centered_player_nr_next = game.centered_player_nr;
11911       game.set_centered_player = FALSE;
11912     }
11913
11914     /* do not switch focus to non-existing (or non-active) player */
11915     if (game.centered_player_nr_next >= 0 &&
11916         !stored_player[game.centered_player_nr_next].active)
11917     {
11918       game.centered_player_nr_next = game.centered_player_nr;
11919       game.set_centered_player = FALSE;
11920     }
11921   }
11922
11923   if (game.set_centered_player &&
11924       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11925   {
11926     int sx, sy;
11927
11928     if (game.centered_player_nr_next == -1)
11929     {
11930       setScreenCenteredToAllPlayers(&sx, &sy);
11931     }
11932     else
11933     {
11934       sx = stored_player[game.centered_player_nr_next].jx;
11935       sy = stored_player[game.centered_player_nr_next].jy;
11936     }
11937
11938     game.centered_player_nr = game.centered_player_nr_next;
11939     game.set_centered_player = FALSE;
11940
11941     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11942     DrawGameDoorValues();
11943   }
11944
11945   for (i = 0; i < MAX_PLAYERS; i++)
11946   {
11947     int actual_player_action = stored_player[i].effective_action;
11948
11949 #if 1
11950     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11951        - rnd_equinox_tetrachloride 048
11952        - rnd_equinox_tetrachloride_ii 096
11953        - rnd_emanuel_schmieg 002
11954        - doctor_sloan_ww 001, 020
11955     */
11956     if (stored_player[i].MovPos == 0)
11957       CheckGravityMovement(&stored_player[i]);
11958 #endif
11959
11960     /* overwrite programmed action with tape action */
11961     if (stored_player[i].programmed_action)
11962       actual_player_action = stored_player[i].programmed_action;
11963
11964     PlayerActions(&stored_player[i], actual_player_action);
11965
11966     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11967   }
11968
11969   ScrollScreen(NULL, SCROLL_GO_ON);
11970
11971   /* for backwards compatibility, the following code emulates a fixed bug that
11972      occured when pushing elements (causing elements that just made their last
11973      pushing step to already (if possible) make their first falling step in the
11974      same game frame, which is bad); this code is also needed to use the famous
11975      "spring push bug" which is used in older levels and might be wanted to be
11976      used also in newer levels, but in this case the buggy pushing code is only
11977      affecting the "spring" element and no other elements */
11978
11979   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11980   {
11981     for (i = 0; i < MAX_PLAYERS; i++)
11982     {
11983       struct PlayerInfo *player = &stored_player[i];
11984       int x = player->jx;
11985       int y = player->jy;
11986
11987       if (player->active && player->is_pushing && player->is_moving &&
11988           IS_MOVING(x, y) &&
11989           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11990            Feld[x][y] == EL_SPRING))
11991       {
11992         ContinueMoving(x, y);
11993
11994         /* continue moving after pushing (this is actually a bug) */
11995         if (!IS_MOVING(x, y))
11996           Stop[x][y] = FALSE;
11997       }
11998     }
11999   }
12000
12001 #if 0
12002   debug_print_timestamp(0, "start main loop profiling");
12003 #endif
12004
12005   SCAN_PLAYFIELD(x, y)
12006   {
12007     ChangeCount[x][y] = 0;
12008     ChangeEvent[x][y] = -1;
12009
12010     /* this must be handled before main playfield loop */
12011     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12012     {
12013       MovDelay[x][y]--;
12014       if (MovDelay[x][y] <= 0)
12015         RemoveField(x, y);
12016     }
12017
12018 #if USE_NEW_SNAP_DELAY
12019     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12020     {
12021       MovDelay[x][y]--;
12022       if (MovDelay[x][y] <= 0)
12023       {
12024         RemoveField(x, y);
12025         TEST_DrawLevelField(x, y);
12026
12027         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12028       }
12029     }
12030 #endif
12031
12032 #if DEBUG
12033     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12034     {
12035       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12036       printf("GameActions(): This should never happen!\n");
12037
12038       ChangePage[x][y] = -1;
12039     }
12040 #endif
12041
12042     Stop[x][y] = FALSE;
12043     if (WasJustMoving[x][y] > 0)
12044       WasJustMoving[x][y]--;
12045     if (WasJustFalling[x][y] > 0)
12046       WasJustFalling[x][y]--;
12047     if (CheckCollision[x][y] > 0)
12048       CheckCollision[x][y]--;
12049     if (CheckImpact[x][y] > 0)
12050       CheckImpact[x][y]--;
12051
12052     GfxFrame[x][y]++;
12053
12054     /* reset finished pushing action (not done in ContinueMoving() to allow
12055        continuous pushing animation for elements with zero push delay) */
12056     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12057     {
12058       ResetGfxAnimation(x, y);
12059       TEST_DrawLevelField(x, y);
12060     }
12061
12062 #if DEBUG
12063     if (IS_BLOCKED(x, y))
12064     {
12065       int oldx, oldy;
12066
12067       Blocked2Moving(x, y, &oldx, &oldy);
12068       if (!IS_MOVING(oldx, oldy))
12069       {
12070         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12071         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12072         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12073         printf("GameActions(): This should never happen!\n");
12074       }
12075     }
12076 #endif
12077   }
12078
12079 #if 0
12080   debug_print_timestamp(0, "- time for pre-main loop:");
12081 #endif
12082
12083 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12084   SCAN_PLAYFIELD(x, y)
12085   {
12086     element = Feld[x][y];
12087     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12088
12089 #if 1
12090     {
12091 #if 1
12092       int element2 = element;
12093       int graphic2 = graphic;
12094 #else
12095       int element2 = Feld[x][y];
12096       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12097 #endif
12098       int last_gfx_frame = GfxFrame[x][y];
12099
12100       if (graphic_info[graphic2].anim_global_sync)
12101         GfxFrame[x][y] = FrameCounter;
12102       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12103         GfxFrame[x][y] = CustomValue[x][y];
12104       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12105         GfxFrame[x][y] = element_info[element2].collect_score;
12106       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12107         GfxFrame[x][y] = ChangeDelay[x][y];
12108
12109       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12110         DrawLevelGraphicAnimation(x, y, graphic2);
12111     }
12112 #else
12113     ResetGfxFrame(x, y, TRUE);
12114 #endif
12115
12116 #if 1
12117     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12118         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12119       ResetRandomAnimationValue(x, y);
12120 #endif
12121
12122 #if 1
12123     SetRandomAnimationValue(x, y);
12124 #endif
12125
12126 #if 1
12127     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12128 #endif
12129   }
12130 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12131
12132 #if 0
12133   debug_print_timestamp(0, "- time for TEST loop:     -->");
12134 #endif
12135
12136   SCAN_PLAYFIELD(x, y)
12137   {
12138     element = Feld[x][y];
12139     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12140
12141     ResetGfxFrame(x, y, TRUE);
12142
12143     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12144         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12145       ResetRandomAnimationValue(x, y);
12146
12147     SetRandomAnimationValue(x, y);
12148
12149     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12150
12151     if (IS_INACTIVE(element))
12152     {
12153       if (IS_ANIMATED(graphic))
12154         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12155
12156       continue;
12157     }
12158
12159     /* this may take place after moving, so 'element' may have changed */
12160     if (IS_CHANGING(x, y) &&
12161         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12162     {
12163       int page = element_info[element].event_page_nr[CE_DELAY];
12164
12165 #if 1
12166       HandleElementChange(x, y, page);
12167 #else
12168       if (CAN_CHANGE(element))
12169         HandleElementChange(x, y, page);
12170
12171       if (HAS_ACTION(element))
12172         ExecuteCustomElementAction(x, y, element, page);
12173 #endif
12174
12175       element = Feld[x][y];
12176       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12177     }
12178
12179 #if 0   // ---------------------------------------------------------------------
12180
12181     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12182     {
12183       StartMoving(x, y);
12184
12185       element = Feld[x][y];
12186       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12187
12188       if (IS_ANIMATED(graphic) &&
12189           !IS_MOVING(x, y) &&
12190           !Stop[x][y])
12191         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12192
12193       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12194         TEST_DrawTwinkleOnField(x, y);
12195     }
12196     else if (IS_MOVING(x, y))
12197       ContinueMoving(x, y);
12198     else
12199     {
12200       switch (element)
12201       {
12202         case EL_ACID:
12203         case EL_EXIT_OPEN:
12204         case EL_EM_EXIT_OPEN:
12205         case EL_SP_EXIT_OPEN:
12206         case EL_STEEL_EXIT_OPEN:
12207         case EL_EM_STEEL_EXIT_OPEN:
12208         case EL_SP_TERMINAL:
12209         case EL_SP_TERMINAL_ACTIVE:
12210         case EL_EXTRA_TIME:
12211         case EL_SHIELD_NORMAL:
12212         case EL_SHIELD_DEADLY:
12213           if (IS_ANIMATED(graphic))
12214             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12215           break;
12216
12217         case EL_DYNAMITE_ACTIVE:
12218         case EL_EM_DYNAMITE_ACTIVE:
12219         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12220         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12221         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12222         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12223         case EL_SP_DISK_RED_ACTIVE:
12224           CheckDynamite(x, y);
12225           break;
12226
12227         case EL_AMOEBA_GROWING:
12228           AmoebeWaechst(x, y);
12229           break;
12230
12231         case EL_AMOEBA_SHRINKING:
12232           AmoebaDisappearing(x, y);
12233           break;
12234
12235 #if !USE_NEW_AMOEBA_CODE
12236         case EL_AMOEBA_WET:
12237         case EL_AMOEBA_DRY:
12238         case EL_AMOEBA_FULL:
12239         case EL_BD_AMOEBA:
12240         case EL_EMC_DRIPPER:
12241           AmoebeAbleger(x, y);
12242           break;
12243 #endif
12244
12245         case EL_GAME_OF_LIFE:
12246         case EL_BIOMAZE:
12247           Life(x, y);
12248           break;
12249
12250         case EL_EXIT_CLOSED:
12251           CheckExit(x, y);
12252           break;
12253
12254         case EL_EM_EXIT_CLOSED:
12255           CheckExitEM(x, y);
12256           break;
12257
12258         case EL_STEEL_EXIT_CLOSED:
12259           CheckExitSteel(x, y);
12260           break;
12261
12262         case EL_EM_STEEL_EXIT_CLOSED:
12263           CheckExitSteelEM(x, y);
12264           break;
12265
12266         case EL_SP_EXIT_CLOSED:
12267           CheckExitSP(x, y);
12268           break;
12269
12270         case EL_EXPANDABLE_WALL_GROWING:
12271         case EL_EXPANDABLE_STEELWALL_GROWING:
12272           MauerWaechst(x, y);
12273           break;
12274
12275         case EL_EXPANDABLE_WALL:
12276         case EL_EXPANDABLE_WALL_HORIZONTAL:
12277         case EL_EXPANDABLE_WALL_VERTICAL:
12278         case EL_EXPANDABLE_WALL_ANY:
12279         case EL_BD_EXPANDABLE_WALL:
12280           MauerAbleger(x, y);
12281           break;
12282
12283         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12284         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12285         case EL_EXPANDABLE_STEELWALL_ANY:
12286           MauerAblegerStahl(x, y);
12287           break;
12288
12289         case EL_FLAMES:
12290           CheckForDragon(x, y);
12291           break;
12292
12293         case EL_EXPLOSION:
12294           break;
12295
12296         case EL_ELEMENT_SNAPPING:
12297         case EL_DIAGONAL_SHRINKING:
12298         case EL_DIAGONAL_GROWING:
12299         {
12300           graphic =
12301             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12302
12303           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12304           break;
12305         }
12306
12307         default:
12308           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12309             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12310           break;
12311       }
12312     }
12313
12314 #else   // ---------------------------------------------------------------------
12315
12316     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12317     {
12318       StartMoving(x, y);
12319
12320       element = Feld[x][y];
12321       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12322
12323       if (IS_ANIMATED(graphic) &&
12324           !IS_MOVING(x, y) &&
12325           !Stop[x][y])
12326         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12327
12328       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12329         TEST_DrawTwinkleOnField(x, y);
12330     }
12331     else if ((element == EL_ACID ||
12332               element == EL_EXIT_OPEN ||
12333               element == EL_EM_EXIT_OPEN ||
12334               element == EL_SP_EXIT_OPEN ||
12335               element == EL_STEEL_EXIT_OPEN ||
12336               element == EL_EM_STEEL_EXIT_OPEN ||
12337               element == EL_SP_TERMINAL ||
12338               element == EL_SP_TERMINAL_ACTIVE ||
12339               element == EL_EXTRA_TIME ||
12340               element == EL_SHIELD_NORMAL ||
12341               element == EL_SHIELD_DEADLY) &&
12342              IS_ANIMATED(graphic))
12343       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12344     else if (IS_MOVING(x, y))
12345       ContinueMoving(x, y);
12346     else if (IS_ACTIVE_BOMB(element))
12347       CheckDynamite(x, y);
12348     else if (element == EL_AMOEBA_GROWING)
12349       AmoebeWaechst(x, y);
12350     else if (element == EL_AMOEBA_SHRINKING)
12351       AmoebaDisappearing(x, y);
12352
12353 #if !USE_NEW_AMOEBA_CODE
12354     else if (IS_AMOEBALIVE(element))
12355       AmoebeAbleger(x, y);
12356 #endif
12357
12358     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12359       Life(x, y);
12360     else if (element == EL_EXIT_CLOSED)
12361       CheckExit(x, y);
12362     else if (element == EL_EM_EXIT_CLOSED)
12363       CheckExitEM(x, y);
12364     else if (element == EL_STEEL_EXIT_CLOSED)
12365       CheckExitSteel(x, y);
12366     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12367       CheckExitSteelEM(x, y);
12368     else if (element == EL_SP_EXIT_CLOSED)
12369       CheckExitSP(x, y);
12370     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12371              element == EL_EXPANDABLE_STEELWALL_GROWING)
12372       MauerWaechst(x, y);
12373     else if (element == EL_EXPANDABLE_WALL ||
12374              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12375              element == EL_EXPANDABLE_WALL_VERTICAL ||
12376              element == EL_EXPANDABLE_WALL_ANY ||
12377              element == EL_BD_EXPANDABLE_WALL)
12378       MauerAbleger(x, y);
12379     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12380              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12381              element == EL_EXPANDABLE_STEELWALL_ANY)
12382       MauerAblegerStahl(x, y);
12383     else if (element == EL_FLAMES)
12384       CheckForDragon(x, y);
12385     else if (element == EL_EXPLOSION)
12386       ; /* drawing of correct explosion animation is handled separately */
12387     else if (element == EL_ELEMENT_SNAPPING ||
12388              element == EL_DIAGONAL_SHRINKING ||
12389              element == EL_DIAGONAL_GROWING)
12390     {
12391       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12392
12393       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12394     }
12395     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12396       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12397
12398 #endif  // ---------------------------------------------------------------------
12399
12400     if (IS_BELT_ACTIVE(element))
12401       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12402
12403     if (game.magic_wall_active)
12404     {
12405       int jx = local_player->jx, jy = local_player->jy;
12406
12407       /* play the element sound at the position nearest to the player */
12408       if ((element == EL_MAGIC_WALL_FULL ||
12409            element == EL_MAGIC_WALL_ACTIVE ||
12410            element == EL_MAGIC_WALL_EMPTYING ||
12411            element == EL_BD_MAGIC_WALL_FULL ||
12412            element == EL_BD_MAGIC_WALL_ACTIVE ||
12413            element == EL_BD_MAGIC_WALL_EMPTYING ||
12414            element == EL_DC_MAGIC_WALL_FULL ||
12415            element == EL_DC_MAGIC_WALL_ACTIVE ||
12416            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12417           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12418       {
12419         magic_wall_x = x;
12420         magic_wall_y = y;
12421       }
12422     }
12423   }
12424
12425 #if 0
12426   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12427 #endif
12428
12429 #if USE_NEW_AMOEBA_CODE
12430   /* new experimental amoeba growth stuff */
12431   if (!(FrameCounter % 8))
12432   {
12433     static unsigned long random = 1684108901;
12434
12435     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12436     {
12437       x = RND(lev_fieldx);
12438       y = RND(lev_fieldy);
12439       element = Feld[x][y];
12440
12441       if (!IS_PLAYER(x,y) &&
12442           (element == EL_EMPTY ||
12443            CAN_GROW_INTO(element) ||
12444            element == EL_QUICKSAND_EMPTY ||
12445            element == EL_QUICKSAND_FAST_EMPTY ||
12446            element == EL_ACID_SPLASH_LEFT ||
12447            element == EL_ACID_SPLASH_RIGHT))
12448       {
12449         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12450             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12451             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12452             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12453           Feld[x][y] = EL_AMOEBA_DROP;
12454       }
12455
12456       random = random * 129 + 1;
12457     }
12458   }
12459 #endif
12460
12461 #if 0
12462   if (game.explosions_delayed)
12463 #endif
12464   {
12465     game.explosions_delayed = FALSE;
12466
12467     SCAN_PLAYFIELD(x, y)
12468     {
12469       element = Feld[x][y];
12470
12471       if (ExplodeField[x][y])
12472         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12473       else if (element == EL_EXPLOSION)
12474         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12475
12476       ExplodeField[x][y] = EX_TYPE_NONE;
12477     }
12478
12479     game.explosions_delayed = TRUE;
12480   }
12481
12482   if (game.magic_wall_active)
12483   {
12484     if (!(game.magic_wall_time_left % 4))
12485     {
12486       int element = Feld[magic_wall_x][magic_wall_y];
12487
12488       if (element == EL_BD_MAGIC_WALL_FULL ||
12489           element == EL_BD_MAGIC_WALL_ACTIVE ||
12490           element == EL_BD_MAGIC_WALL_EMPTYING)
12491         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12492       else if (element == EL_DC_MAGIC_WALL_FULL ||
12493                element == EL_DC_MAGIC_WALL_ACTIVE ||
12494                element == EL_DC_MAGIC_WALL_EMPTYING)
12495         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12496       else
12497         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12498     }
12499
12500     if (game.magic_wall_time_left > 0)
12501     {
12502       game.magic_wall_time_left--;
12503
12504       if (!game.magic_wall_time_left)
12505       {
12506         SCAN_PLAYFIELD(x, y)
12507         {
12508           element = Feld[x][y];
12509
12510           if (element == EL_MAGIC_WALL_ACTIVE ||
12511               element == EL_MAGIC_WALL_FULL)
12512           {
12513             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12514             TEST_DrawLevelField(x, y);
12515           }
12516           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12517                    element == EL_BD_MAGIC_WALL_FULL)
12518           {
12519             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12520             TEST_DrawLevelField(x, y);
12521           }
12522           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12523                    element == EL_DC_MAGIC_WALL_FULL)
12524           {
12525             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12526             TEST_DrawLevelField(x, y);
12527           }
12528         }
12529
12530         game.magic_wall_active = FALSE;
12531       }
12532     }
12533   }
12534
12535   if (game.light_time_left > 0)
12536   {
12537     game.light_time_left--;
12538
12539     if (game.light_time_left == 0)
12540       RedrawAllLightSwitchesAndInvisibleElements();
12541   }
12542
12543   if (game.timegate_time_left > 0)
12544   {
12545     game.timegate_time_left--;
12546
12547     if (game.timegate_time_left == 0)
12548       CloseAllOpenTimegates();
12549   }
12550
12551   if (game.lenses_time_left > 0)
12552   {
12553     game.lenses_time_left--;
12554
12555     if (game.lenses_time_left == 0)
12556       RedrawAllInvisibleElementsForLenses();
12557   }
12558
12559   if (game.magnify_time_left > 0)
12560   {
12561     game.magnify_time_left--;
12562
12563     if (game.magnify_time_left == 0)
12564       RedrawAllInvisibleElementsForMagnifier();
12565   }
12566
12567   for (i = 0; i < MAX_PLAYERS; i++)
12568   {
12569     struct PlayerInfo *player = &stored_player[i];
12570
12571     if (SHIELD_ON(player))
12572     {
12573       if (player->shield_deadly_time_left)
12574         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12575       else if (player->shield_normal_time_left)
12576         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12577     }
12578   }
12579
12580 #if USE_DELAYED_GFX_REDRAW
12581   SCAN_PLAYFIELD(x, y)
12582   {
12583 #if 1
12584     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12585 #else
12586     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12587         GfxRedraw[x][y] != GFX_REDRAW_NONE)
12588 #endif
12589     {
12590       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12591          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12592
12593       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12594         DrawLevelField(x, y);
12595
12596       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12597         DrawLevelFieldCrumbledSand(x, y);
12598
12599       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12600         DrawLevelFieldCrumbledSandNeighbours(x, y);
12601
12602       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12603         DrawTwinkleOnField(x, y);
12604     }
12605
12606     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12607   }
12608 #endif
12609
12610   CheckLevelTime();
12611
12612   DrawAllPlayers();
12613   PlayAllPlayersSound();
12614
12615   if (options.debug)                    /* calculate frames per second */
12616   {
12617     static unsigned long fps_counter = 0;
12618     static int fps_frames = 0;
12619     unsigned long fps_delay_ms = Counter() - fps_counter;
12620
12621     fps_frames++;
12622
12623     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12624     {
12625       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12626
12627       fps_frames = 0;
12628       fps_counter = Counter();
12629     }
12630
12631     redraw_mask |= REDRAW_FPS;
12632   }
12633
12634   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12635
12636   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12637   {
12638     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12639
12640     local_player->show_envelope = 0;
12641   }
12642
12643 #if 0
12644   debug_print_timestamp(0, "stop main loop profiling ");
12645   printf("----------------------------------------------------------\n");
12646 #endif
12647
12648   /* use random number generator in every frame to make it less predictable */
12649   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12650     RND(1);
12651 }
12652
12653 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12654 {
12655   int min_x = x, min_y = y, max_x = x, max_y = y;
12656   int i;
12657
12658   for (i = 0; i < MAX_PLAYERS; i++)
12659   {
12660     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12661
12662     if (!stored_player[i].active || &stored_player[i] == player)
12663       continue;
12664
12665     min_x = MIN(min_x, jx);
12666     min_y = MIN(min_y, jy);
12667     max_x = MAX(max_x, jx);
12668     max_y = MAX(max_y, jy);
12669   }
12670
12671   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12672 }
12673
12674 static boolean AllPlayersInVisibleScreen()
12675 {
12676   int i;
12677
12678   for (i = 0; i < MAX_PLAYERS; i++)
12679   {
12680     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12681
12682     if (!stored_player[i].active)
12683       continue;
12684
12685     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12686       return FALSE;
12687   }
12688
12689   return TRUE;
12690 }
12691
12692 void ScrollLevel(int dx, int dy)
12693 {
12694 #if 0
12695   /* (directly solved in BlitBitmap() now) */
12696   static Bitmap *bitmap_db_field2 = NULL;
12697   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12698   int x, y;
12699 #else
12700   int x, y;
12701 #endif
12702
12703 #if 0
12704   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12705   /* only horizontal XOR vertical scroll direction allowed */
12706   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12707     return;
12708 #endif
12709
12710 #if 0
12711   /* (directly solved in BlitBitmap() now) */
12712   if (bitmap_db_field2 == NULL)
12713     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12714
12715   /* needed when blitting directly to same bitmap -- should not be needed with
12716      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12717   BlitBitmap(drawto_field, bitmap_db_field2,
12718              FX + TILEX * (dx == -1) - softscroll_offset,
12719              FY + TILEY * (dy == -1) - softscroll_offset,
12720              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12721              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12722              FX + TILEX * (dx == 1) - softscroll_offset,
12723              FY + TILEY * (dy == 1) - softscroll_offset);
12724   BlitBitmap(bitmap_db_field2, drawto_field,
12725              FX + TILEX * (dx == 1) - softscroll_offset,
12726              FY + TILEY * (dy == 1) - softscroll_offset,
12727              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12728              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12729              FX + TILEX * (dx == 1) - softscroll_offset,
12730              FY + TILEY * (dy == 1) - softscroll_offset);
12731
12732 #else
12733
12734 #if 0
12735   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12736   int xsize = (BX2 - BX1 + 1);
12737   int ysize = (BY2 - BY1 + 1);
12738   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12739   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12740   int step  = (start < end ? +1 : -1);
12741
12742   for (i = start; i != end; i += step)
12743   {
12744     BlitBitmap(drawto_field, drawto_field,
12745                FX + TILEX * (dx != 0 ? i + step : 0),
12746                FY + TILEY * (dy != 0 ? i + step : 0),
12747                TILEX * (dx != 0 ? 1 : xsize),
12748                TILEY * (dy != 0 ? 1 : ysize),
12749                FX + TILEX * (dx != 0 ? i : 0),
12750                FY + TILEY * (dy != 0 ? i : 0));
12751   }
12752
12753 #else
12754
12755   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12756
12757   BlitBitmap(drawto_field, drawto_field,
12758              FX + TILEX * (dx == -1) - softscroll_offset,
12759              FY + TILEY * (dy == -1) - softscroll_offset,
12760              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12761              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12762              FX + TILEX * (dx == 1) - softscroll_offset,
12763              FY + TILEY * (dy == 1) - softscroll_offset);
12764 #endif
12765 #endif
12766
12767   if (dx != 0)
12768   {
12769     x = (dx == 1 ? BX1 : BX2);
12770     for (y = BY1; y <= BY2; y++)
12771       DrawScreenField(x, y);
12772   }
12773
12774   if (dy != 0)
12775   {
12776     y = (dy == 1 ? BY1 : BY2);
12777     for (x = BX1; x <= BX2; x++)
12778       DrawScreenField(x, y);
12779   }
12780
12781   redraw_mask |= REDRAW_FIELD;
12782 }
12783
12784 static boolean canFallDown(struct PlayerInfo *player)
12785 {
12786   int jx = player->jx, jy = player->jy;
12787
12788   return (IN_LEV_FIELD(jx, jy + 1) &&
12789           (IS_FREE(jx, jy + 1) ||
12790            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12791           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12792           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12793 }
12794
12795 static boolean canPassField(int x, int y, int move_dir)
12796 {
12797   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12798   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12799   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12800   int nextx = x + dx;
12801   int nexty = y + dy;
12802   int element = Feld[x][y];
12803
12804   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12805           !CAN_MOVE(element) &&
12806           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12807           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12808           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12809 }
12810
12811 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12812 {
12813   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12814   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12815   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12816   int newx = x + dx;
12817   int newy = y + dy;
12818
12819   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12820           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12821           (IS_DIGGABLE(Feld[newx][newy]) ||
12822            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12823            canPassField(newx, newy, move_dir)));
12824 }
12825
12826 static void CheckGravityMovement(struct PlayerInfo *player)
12827 {
12828 #if USE_PLAYER_GRAVITY
12829   if (player->gravity && !player->programmed_action)
12830 #else
12831   if (game.gravity && !player->programmed_action)
12832 #endif
12833   {
12834     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12835     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12836     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12837     int jx = player->jx, jy = player->jy;
12838     boolean player_is_moving_to_valid_field =
12839       (!player_is_snapping &&
12840        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12841         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12842     boolean player_can_fall_down = canFallDown(player);
12843
12844     if (player_can_fall_down &&
12845         !player_is_moving_to_valid_field)
12846       player->programmed_action = MV_DOWN;
12847   }
12848 }
12849
12850 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12851 {
12852   return CheckGravityMovement(player);
12853
12854 #if USE_PLAYER_GRAVITY
12855   if (player->gravity && !player->programmed_action)
12856 #else
12857   if (game.gravity && !player->programmed_action)
12858 #endif
12859   {
12860     int jx = player->jx, jy = player->jy;
12861     boolean field_under_player_is_free =
12862       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12863     boolean player_is_standing_on_valid_field =
12864       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12865        (IS_WALKABLE(Feld[jx][jy]) &&
12866         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12867
12868     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12869       player->programmed_action = MV_DOWN;
12870   }
12871 }
12872
12873 /*
12874   MovePlayerOneStep()
12875   -----------------------------------------------------------------------------
12876   dx, dy:               direction (non-diagonal) to try to move the player to
12877   real_dx, real_dy:     direction as read from input device (can be diagonal)
12878 */
12879
12880 boolean MovePlayerOneStep(struct PlayerInfo *player,
12881                           int dx, int dy, int real_dx, int real_dy)
12882 {
12883   int jx = player->jx, jy = player->jy;
12884   int new_jx = jx + dx, new_jy = jy + dy;
12885 #if !USE_FIXED_DONT_RUN_INTO
12886   int element;
12887 #endif
12888   int can_move;
12889   boolean player_can_move = !player->cannot_move;
12890
12891   if (!player->active || (!dx && !dy))
12892     return MP_NO_ACTION;
12893
12894   player->MovDir = (dx < 0 ? MV_LEFT :
12895                     dx > 0 ? MV_RIGHT :
12896                     dy < 0 ? MV_UP :
12897                     dy > 0 ? MV_DOWN :  MV_NONE);
12898
12899   if (!IN_LEV_FIELD(new_jx, new_jy))
12900     return MP_NO_ACTION;
12901
12902   if (!player_can_move)
12903   {
12904     if (player->MovPos == 0)
12905     {
12906       player->is_moving = FALSE;
12907       player->is_digging = FALSE;
12908       player->is_collecting = FALSE;
12909       player->is_snapping = FALSE;
12910       player->is_pushing = FALSE;
12911     }
12912   }
12913
12914 #if 1
12915   if (!options.network && game.centered_player_nr == -1 &&
12916       !AllPlayersInSight(player, new_jx, new_jy))
12917     return MP_NO_ACTION;
12918 #else
12919   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12920     return MP_NO_ACTION;
12921 #endif
12922
12923 #if !USE_FIXED_DONT_RUN_INTO
12924   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12925
12926   /* (moved to DigField()) */
12927   if (player_can_move && DONT_RUN_INTO(element))
12928   {
12929     if (element == EL_ACID && dx == 0 && dy == 1)
12930     {
12931       SplashAcid(new_jx, new_jy);
12932       Feld[jx][jy] = EL_PLAYER_1;
12933       InitMovingField(jx, jy, MV_DOWN);
12934       Store[jx][jy] = EL_ACID;
12935       ContinueMoving(jx, jy);
12936       BuryPlayer(player);
12937     }
12938     else
12939       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12940
12941     return MP_MOVING;
12942   }
12943 #endif
12944
12945   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12946   if (can_move != MP_MOVING)
12947     return can_move;
12948
12949   /* check if DigField() has caused relocation of the player */
12950   if (player->jx != jx || player->jy != jy)
12951     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12952
12953   StorePlayer[jx][jy] = 0;
12954   player->last_jx = jx;
12955   player->last_jy = jy;
12956   player->jx = new_jx;
12957   player->jy = new_jy;
12958   StorePlayer[new_jx][new_jy] = player->element_nr;
12959
12960   if (player->move_delay_value_next != -1)
12961   {
12962     player->move_delay_value = player->move_delay_value_next;
12963     player->move_delay_value_next = -1;
12964   }
12965
12966   player->MovPos =
12967     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12968
12969   player->step_counter++;
12970
12971   PlayerVisit[jx][jy] = FrameCounter;
12972
12973 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12974   player->is_moving = TRUE;
12975 #endif
12976
12977 #if 1
12978   /* should better be called in MovePlayer(), but this breaks some tapes */
12979   ScrollPlayer(player, SCROLL_INIT);
12980 #endif
12981
12982   return MP_MOVING;
12983 }
12984
12985 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12986 {
12987   int jx = player->jx, jy = player->jy;
12988   int old_jx = jx, old_jy = jy;
12989   int moved = MP_NO_ACTION;
12990
12991   if (!player->active)
12992     return FALSE;
12993
12994   if (!dx && !dy)
12995   {
12996     if (player->MovPos == 0)
12997     {
12998       player->is_moving = FALSE;
12999       player->is_digging = FALSE;
13000       player->is_collecting = FALSE;
13001       player->is_snapping = FALSE;
13002       player->is_pushing = FALSE;
13003     }
13004
13005     return FALSE;
13006   }
13007
13008   if (player->move_delay > 0)
13009     return FALSE;
13010
13011   player->move_delay = -1;              /* set to "uninitialized" value */
13012
13013   /* store if player is automatically moved to next field */
13014   player->is_auto_moving = (player->programmed_action != MV_NONE);
13015
13016   /* remove the last programmed player action */
13017   player->programmed_action = 0;
13018
13019   if (player->MovPos)
13020   {
13021     /* should only happen if pre-1.2 tape recordings are played */
13022     /* this is only for backward compatibility */
13023
13024     int original_move_delay_value = player->move_delay_value;
13025
13026 #if DEBUG
13027     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13028            tape.counter);
13029 #endif
13030
13031     /* scroll remaining steps with finest movement resolution */
13032     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13033
13034     while (player->MovPos)
13035     {
13036       ScrollPlayer(player, SCROLL_GO_ON);
13037       ScrollScreen(NULL, SCROLL_GO_ON);
13038
13039       AdvanceFrameAndPlayerCounters(player->index_nr);
13040
13041       DrawAllPlayers();
13042       BackToFront();
13043     }
13044
13045     player->move_delay_value = original_move_delay_value;
13046   }
13047
13048   player->is_active = FALSE;
13049
13050   if (player->last_move_dir & MV_HORIZONTAL)
13051   {
13052     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13053       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13054   }
13055   else
13056   {
13057     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13058       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13059   }
13060
13061 #if USE_FIXED_BORDER_RUNNING_GFX
13062   if (!moved && !player->is_active)
13063   {
13064     player->is_moving = FALSE;
13065     player->is_digging = FALSE;
13066     player->is_collecting = FALSE;
13067     player->is_snapping = FALSE;
13068     player->is_pushing = FALSE;
13069   }
13070 #endif
13071
13072   jx = player->jx;
13073   jy = player->jy;
13074
13075 #if 1
13076   if (moved & MP_MOVING && !ScreenMovPos &&
13077       (player->index_nr == game.centered_player_nr ||
13078        game.centered_player_nr == -1))
13079 #else
13080   if (moved & MP_MOVING && !ScreenMovPos &&
13081       (player == local_player || !options.network))
13082 #endif
13083   {
13084     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13085     int offset = game.scroll_delay_value;
13086
13087     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13088     {
13089       /* actual player has left the screen -- scroll in that direction */
13090       if (jx != old_jx)         /* player has moved horizontally */
13091         scroll_x += (jx - old_jx);
13092       else                      /* player has moved vertically */
13093         scroll_y += (jy - old_jy);
13094     }
13095     else
13096     {
13097       if (jx != old_jx)         /* player has moved horizontally */
13098       {
13099         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13100             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13101           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13102
13103         /* don't scroll over playfield boundaries */
13104         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13105           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13106
13107         /* don't scroll more than one field at a time */
13108         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13109
13110         /* don't scroll against the player's moving direction */
13111         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13112             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13113           scroll_x = old_scroll_x;
13114       }
13115       else                      /* player has moved vertically */
13116       {
13117         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13118             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13119           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13120
13121         /* don't scroll over playfield boundaries */
13122         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13123           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13124
13125         /* don't scroll more than one field at a time */
13126         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13127
13128         /* don't scroll against the player's moving direction */
13129         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13130             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13131           scroll_y = old_scroll_y;
13132       }
13133     }
13134
13135     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13136     {
13137 #if 1
13138       if (!options.network && game.centered_player_nr == -1 &&
13139           !AllPlayersInVisibleScreen())
13140       {
13141         scroll_x = old_scroll_x;
13142         scroll_y = old_scroll_y;
13143       }
13144       else
13145 #else
13146       if (!options.network && !AllPlayersInVisibleScreen())
13147       {
13148         scroll_x = old_scroll_x;
13149         scroll_y = old_scroll_y;
13150       }
13151       else
13152 #endif
13153       {
13154         ScrollScreen(player, SCROLL_INIT);
13155         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13156       }
13157     }
13158   }
13159
13160   player->StepFrame = 0;
13161
13162   if (moved & MP_MOVING)
13163   {
13164     if (old_jx != jx && old_jy == jy)
13165       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13166     else if (old_jx == jx && old_jy != jy)
13167       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13168
13169     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13170
13171     player->last_move_dir = player->MovDir;
13172     player->is_moving = TRUE;
13173     player->is_snapping = FALSE;
13174     player->is_switching = FALSE;
13175     player->is_dropping = FALSE;
13176     player->is_dropping_pressed = FALSE;
13177     player->drop_pressed_delay = 0;
13178
13179 #if 0
13180     /* should better be called here than above, but this breaks some tapes */
13181     ScrollPlayer(player, SCROLL_INIT);
13182 #endif
13183   }
13184   else
13185   {
13186     CheckGravityMovementWhenNotMoving(player);
13187
13188     player->is_moving = FALSE;
13189
13190     /* at this point, the player is allowed to move, but cannot move right now
13191        (e.g. because of something blocking the way) -- ensure that the player
13192        is also allowed to move in the next frame (in old versions before 3.1.1,
13193        the player was forced to wait again for eight frames before next try) */
13194
13195     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13196       player->move_delay = 0;   /* allow direct movement in the next frame */
13197   }
13198
13199   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13200     player->move_delay = player->move_delay_value;
13201
13202   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13203   {
13204     TestIfPlayerTouchesBadThing(jx, jy);
13205     TestIfPlayerTouchesCustomElement(jx, jy);
13206   }
13207
13208   if (!player->active)
13209     RemovePlayer(player);
13210
13211   return moved;
13212 }
13213
13214 void ScrollPlayer(struct PlayerInfo *player, int mode)
13215 {
13216   int jx = player->jx, jy = player->jy;
13217   int last_jx = player->last_jx, last_jy = player->last_jy;
13218   int move_stepsize = TILEX / player->move_delay_value;
13219
13220 #if USE_NEW_PLAYER_SPEED
13221   if (!player->active)
13222     return;
13223
13224   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13225     return;
13226 #else
13227   if (!player->active || player->MovPos == 0)
13228     return;
13229 #endif
13230
13231   if (mode == SCROLL_INIT)
13232   {
13233     player->actual_frame_counter = FrameCounter;
13234     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13235
13236     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13237         Feld[last_jx][last_jy] == EL_EMPTY)
13238     {
13239       int last_field_block_delay = 0;   /* start with no blocking at all */
13240       int block_delay_adjustment = player->block_delay_adjustment;
13241
13242       /* if player blocks last field, add delay for exactly one move */
13243       if (player->block_last_field)
13244       {
13245         last_field_block_delay += player->move_delay_value;
13246
13247         /* when blocking enabled, prevent moving up despite gravity */
13248 #if USE_PLAYER_GRAVITY
13249         if (player->gravity && player->MovDir == MV_UP)
13250           block_delay_adjustment = -1;
13251 #else
13252         if (game.gravity && player->MovDir == MV_UP)
13253           block_delay_adjustment = -1;
13254 #endif
13255       }
13256
13257       /* add block delay adjustment (also possible when not blocking) */
13258       last_field_block_delay += block_delay_adjustment;
13259
13260       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13261       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13262     }
13263
13264 #if USE_NEW_PLAYER_SPEED
13265     if (player->MovPos != 0)    /* player has not yet reached destination */
13266       return;
13267 #else
13268     return;
13269 #endif
13270   }
13271   else if (!FrameReached(&player->actual_frame_counter, 1))
13272     return;
13273
13274 #if USE_NEW_PLAYER_SPEED
13275   if (player->MovPos != 0)
13276   {
13277     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13278     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13279
13280     /* before DrawPlayer() to draw correct player graphic for this case */
13281     if (player->MovPos == 0)
13282       CheckGravityMovement(player);
13283   }
13284 #else
13285   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13286   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13287
13288   /* before DrawPlayer() to draw correct player graphic for this case */
13289   if (player->MovPos == 0)
13290     CheckGravityMovement(player);
13291 #endif
13292
13293   if (player->MovPos == 0)      /* player reached destination field */
13294   {
13295     if (player->move_delay_reset_counter > 0)
13296     {
13297       player->move_delay_reset_counter--;
13298
13299       if (player->move_delay_reset_counter == 0)
13300       {
13301         /* continue with normal speed after quickly moving through gate */
13302         HALVE_PLAYER_SPEED(player);
13303
13304         /* be able to make the next move without delay */
13305         player->move_delay = 0;
13306       }
13307     }
13308
13309     player->last_jx = jx;
13310     player->last_jy = jy;
13311
13312     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13313         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13314         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13315         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13316         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13317         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13318     {
13319       DrawPlayer(player);       /* needed here only to cleanup last field */
13320       RemovePlayer(player);
13321
13322       if (local_player->friends_still_needed == 0 ||
13323           IS_SP_ELEMENT(Feld[jx][jy]))
13324         PlayerWins(player);
13325     }
13326
13327     /* this breaks one level: "machine", level 000 */
13328     {
13329       int move_direction = player->MovDir;
13330       int enter_side = MV_DIR_OPPOSITE(move_direction);
13331       int leave_side = move_direction;
13332       int old_jx = last_jx;
13333       int old_jy = last_jy;
13334       int old_element = Feld[old_jx][old_jy];
13335       int new_element = Feld[jx][jy];
13336
13337       if (IS_CUSTOM_ELEMENT(old_element))
13338         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13339                                    CE_LEFT_BY_PLAYER,
13340                                    player->index_bit, leave_side);
13341
13342       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13343                                           CE_PLAYER_LEAVES_X,
13344                                           player->index_bit, leave_side);
13345
13346       if (IS_CUSTOM_ELEMENT(new_element))
13347         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13348                                    player->index_bit, enter_side);
13349
13350       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13351                                           CE_PLAYER_ENTERS_X,
13352                                           player->index_bit, enter_side);
13353
13354 #if USE_FIX_CE_ACTION_WITH_PLAYER
13355       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13356                                         CE_MOVE_OF_X, move_direction);
13357 #else
13358       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13359                                         CE_MOVE_OF_X, move_direction);
13360 #endif
13361     }
13362
13363     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13364     {
13365       TestIfPlayerTouchesBadThing(jx, jy);
13366       TestIfPlayerTouchesCustomElement(jx, jy);
13367
13368       /* needed because pushed element has not yet reached its destination,
13369          so it would trigger a change event at its previous field location */
13370       if (!player->is_pushing)
13371         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13372
13373       if (!player->active)
13374         RemovePlayer(player);
13375     }
13376
13377     if (!local_player->LevelSolved && level.use_step_counter)
13378     {
13379       int i;
13380
13381       TimePlayed++;
13382
13383       if (TimeLeft > 0)
13384       {
13385         TimeLeft--;
13386
13387         if (TimeLeft <= 10 && setup.time_limit)
13388           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13389
13390 #if 1
13391         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13392
13393         DisplayGameControlValues();
13394 #else
13395         DrawGameValue_Time(TimeLeft);
13396 #endif
13397
13398         if (!TimeLeft && setup.time_limit)
13399           for (i = 0; i < MAX_PLAYERS; i++)
13400             KillPlayer(&stored_player[i]);
13401       }
13402 #if 1
13403       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13404       {
13405         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13406
13407         DisplayGameControlValues();
13408       }
13409 #else
13410       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13411         DrawGameValue_Time(TimePlayed);
13412 #endif
13413     }
13414
13415     if (tape.single_step && tape.recording && !tape.pausing &&
13416         !player->programmed_action)
13417       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13418   }
13419 }
13420
13421 void ScrollScreen(struct PlayerInfo *player, int mode)
13422 {
13423   static unsigned long screen_frame_counter = 0;
13424
13425   if (mode == SCROLL_INIT)
13426   {
13427     /* set scrolling step size according to actual player's moving speed */
13428     ScrollStepSize = TILEX / player->move_delay_value;
13429
13430     screen_frame_counter = FrameCounter;
13431     ScreenMovDir = player->MovDir;
13432     ScreenMovPos = player->MovPos;
13433     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13434     return;
13435   }
13436   else if (!FrameReached(&screen_frame_counter, 1))
13437     return;
13438
13439   if (ScreenMovPos)
13440   {
13441     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13442     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13443     redraw_mask |= REDRAW_FIELD;
13444   }
13445   else
13446     ScreenMovDir = MV_NONE;
13447 }
13448
13449 void TestIfPlayerTouchesCustomElement(int x, int y)
13450 {
13451   static int xy[4][2] =
13452   {
13453     { 0, -1 },
13454     { -1, 0 },
13455     { +1, 0 },
13456     { 0, +1 }
13457   };
13458   static int trigger_sides[4][2] =
13459   {
13460     /* center side       border side */
13461     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13462     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13463     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13464     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13465   };
13466   static int touch_dir[4] =
13467   {
13468     MV_LEFT | MV_RIGHT,
13469     MV_UP   | MV_DOWN,
13470     MV_UP   | MV_DOWN,
13471     MV_LEFT | MV_RIGHT
13472   };
13473   int center_element = Feld[x][y];      /* should always be non-moving! */
13474   int i;
13475
13476   for (i = 0; i < NUM_DIRECTIONS; i++)
13477   {
13478     int xx = x + xy[i][0];
13479     int yy = y + xy[i][1];
13480     int center_side = trigger_sides[i][0];
13481     int border_side = trigger_sides[i][1];
13482     int border_element;
13483
13484     if (!IN_LEV_FIELD(xx, yy))
13485       continue;
13486
13487     if (IS_PLAYER(x, y))                /* player found at center element */
13488     {
13489       struct PlayerInfo *player = PLAYERINFO(x, y);
13490
13491       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13492         border_element = Feld[xx][yy];          /* may be moving! */
13493       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13494         border_element = Feld[xx][yy];
13495       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13496         border_element = MovingOrBlocked2Element(xx, yy);
13497       else
13498         continue;               /* center and border element do not touch */
13499
13500       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13501                                  player->index_bit, border_side);
13502       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13503                                           CE_PLAYER_TOUCHES_X,
13504                                           player->index_bit, border_side);
13505
13506 #if USE_FIX_CE_ACTION_WITH_PLAYER
13507       {
13508         /* use player element that is initially defined in the level playfield,
13509            not the player element that corresponds to the runtime player number
13510            (example: a level that contains EL_PLAYER_3 as the only player would
13511            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13512         int player_element = PLAYERINFO(x, y)->initial_element;
13513
13514         CheckElementChangeBySide(xx, yy, border_element, player_element,
13515                                  CE_TOUCHING_X, border_side);
13516       }
13517 #endif
13518     }
13519     else if (IS_PLAYER(xx, yy))         /* player found at border element */
13520     {
13521       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13522
13523       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13524       {
13525         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13526           continue;             /* center and border element do not touch */
13527       }
13528
13529       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13530                                  player->index_bit, center_side);
13531       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13532                                           CE_PLAYER_TOUCHES_X,
13533                                           player->index_bit, center_side);
13534
13535 #if USE_FIX_CE_ACTION_WITH_PLAYER
13536       {
13537         /* use player element that is initially defined in the level playfield,
13538            not the player element that corresponds to the runtime player number
13539            (example: a level that contains EL_PLAYER_3 as the only player would
13540            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13541         int player_element = PLAYERINFO(xx, yy)->initial_element;
13542
13543         CheckElementChangeBySide(x, y, center_element, player_element,
13544                                  CE_TOUCHING_X, center_side);
13545       }
13546 #endif
13547
13548       break;
13549     }
13550   }
13551 }
13552
13553 #if USE_ELEMENT_TOUCHING_BUGFIX
13554
13555 void TestIfElementTouchesCustomElement(int x, int y)
13556 {
13557   static int xy[4][2] =
13558   {
13559     { 0, -1 },
13560     { -1, 0 },
13561     { +1, 0 },
13562     { 0, +1 }
13563   };
13564   static int trigger_sides[4][2] =
13565   {
13566     /* center side      border side */
13567     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13568     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13569     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13570     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13571   };
13572   static int touch_dir[4] =
13573   {
13574     MV_LEFT | MV_RIGHT,
13575     MV_UP   | MV_DOWN,
13576     MV_UP   | MV_DOWN,
13577     MV_LEFT | MV_RIGHT
13578   };
13579   boolean change_center_element = FALSE;
13580   int center_element = Feld[x][y];      /* should always be non-moving! */
13581   int border_element_old[NUM_DIRECTIONS];
13582   int i;
13583
13584   for (i = 0; i < NUM_DIRECTIONS; i++)
13585   {
13586     int xx = x + xy[i][0];
13587     int yy = y + xy[i][1];
13588     int border_element;
13589
13590     border_element_old[i] = -1;
13591
13592     if (!IN_LEV_FIELD(xx, yy))
13593       continue;
13594
13595     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13596       border_element = Feld[xx][yy];    /* may be moving! */
13597     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13598       border_element = Feld[xx][yy];
13599     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13600       border_element = MovingOrBlocked2Element(xx, yy);
13601     else
13602       continue;                 /* center and border element do not touch */
13603
13604     border_element_old[i] = border_element;
13605   }
13606
13607   for (i = 0; i < NUM_DIRECTIONS; i++)
13608   {
13609     int xx = x + xy[i][0];
13610     int yy = y + xy[i][1];
13611     int center_side = trigger_sides[i][0];
13612     int border_element = border_element_old[i];
13613
13614     if (border_element == -1)
13615       continue;
13616
13617     /* check for change of border element */
13618     CheckElementChangeBySide(xx, yy, border_element, center_element,
13619                              CE_TOUCHING_X, center_side);
13620
13621     /* (center element cannot be player, so we dont have to check this here) */
13622   }
13623
13624   for (i = 0; i < NUM_DIRECTIONS; i++)
13625   {
13626     int xx = x + xy[i][0];
13627     int yy = y + xy[i][1];
13628     int border_side = trigger_sides[i][1];
13629     int border_element = border_element_old[i];
13630
13631     if (border_element == -1)
13632       continue;
13633
13634     /* check for change of center element (but change it only once) */
13635     if (!change_center_element)
13636       change_center_element =
13637         CheckElementChangeBySide(x, y, center_element, border_element,
13638                                  CE_TOUCHING_X, border_side);
13639
13640 #if USE_FIX_CE_ACTION_WITH_PLAYER
13641     if (IS_PLAYER(xx, yy))
13642     {
13643       /* use player element that is initially defined in the level playfield,
13644          not the player element that corresponds to the runtime player number
13645          (example: a level that contains EL_PLAYER_3 as the only player would
13646          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13647       int player_element = PLAYERINFO(xx, yy)->initial_element;
13648
13649       CheckElementChangeBySide(x, y, center_element, player_element,
13650                                CE_TOUCHING_X, border_side);
13651     }
13652 #endif
13653   }
13654 }
13655
13656 #else
13657
13658 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13659 {
13660   static int xy[4][2] =
13661   {
13662     { 0, -1 },
13663     { -1, 0 },
13664     { +1, 0 },
13665     { 0, +1 }
13666   };
13667   static int trigger_sides[4][2] =
13668   {
13669     /* center side      border side */
13670     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13671     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13672     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13673     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13674   };
13675   static int touch_dir[4] =
13676   {
13677     MV_LEFT | MV_RIGHT,
13678     MV_UP   | MV_DOWN,
13679     MV_UP   | MV_DOWN,
13680     MV_LEFT | MV_RIGHT
13681   };
13682   boolean change_center_element = FALSE;
13683   int center_element = Feld[x][y];      /* should always be non-moving! */
13684   int i;
13685
13686   for (i = 0; i < NUM_DIRECTIONS; i++)
13687   {
13688     int xx = x + xy[i][0];
13689     int yy = y + xy[i][1];
13690     int center_side = trigger_sides[i][0];
13691     int border_side = trigger_sides[i][1];
13692     int border_element;
13693
13694     if (!IN_LEV_FIELD(xx, yy))
13695       continue;
13696
13697     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13698       border_element = Feld[xx][yy];    /* may be moving! */
13699     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13700       border_element = Feld[xx][yy];
13701     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13702       border_element = MovingOrBlocked2Element(xx, yy);
13703     else
13704       continue;                 /* center and border element do not touch */
13705
13706     /* check for change of center element (but change it only once) */
13707     if (!change_center_element)
13708       change_center_element =
13709         CheckElementChangeBySide(x, y, center_element, border_element,
13710                                  CE_TOUCHING_X, border_side);
13711
13712     /* check for change of border element */
13713     CheckElementChangeBySide(xx, yy, border_element, center_element,
13714                              CE_TOUCHING_X, center_side);
13715   }
13716 }
13717
13718 #endif
13719
13720 void TestIfElementHitsCustomElement(int x, int y, int direction)
13721 {
13722   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13723   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13724   int hitx = x + dx, hity = y + dy;
13725   int hitting_element = Feld[x][y];
13726   int touched_element;
13727
13728   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13729     return;
13730
13731   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13732                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13733
13734   if (IN_LEV_FIELD(hitx, hity))
13735   {
13736     int opposite_direction = MV_DIR_OPPOSITE(direction);
13737     int hitting_side = direction;
13738     int touched_side = opposite_direction;
13739     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13740                           MovDir[hitx][hity] != direction ||
13741                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13742
13743     object_hit = TRUE;
13744
13745     if (object_hit)
13746     {
13747       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13748                                CE_HITTING_X, touched_side);
13749
13750       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13751                                CE_HIT_BY_X, hitting_side);
13752
13753       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13754                                CE_HIT_BY_SOMETHING, opposite_direction);
13755
13756 #if USE_FIX_CE_ACTION_WITH_PLAYER
13757       if (IS_PLAYER(hitx, hity))
13758       {
13759         /* use player element that is initially defined in the level playfield,
13760            not the player element that corresponds to the runtime player number
13761            (example: a level that contains EL_PLAYER_3 as the only player would
13762            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13763         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13764
13765         CheckElementChangeBySide(x, y, hitting_element, player_element,
13766                                  CE_HITTING_X, touched_side);
13767       }
13768 #endif
13769     }
13770   }
13771
13772   /* "hitting something" is also true when hitting the playfield border */
13773   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13774                            CE_HITTING_SOMETHING, direction);
13775 }
13776
13777 #if 0
13778 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13779 {
13780   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13781   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13782   int hitx = x + dx, hity = y + dy;
13783   int hitting_element = Feld[x][y];
13784   int touched_element;
13785 #if 0
13786   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13787                         !IS_FREE(hitx, hity) &&
13788                         (!IS_MOVING(hitx, hity) ||
13789                          MovDir[hitx][hity] != direction ||
13790                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13791 #endif
13792
13793   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13794     return;
13795
13796 #if 0
13797   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13798     return;
13799 #endif
13800
13801   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13802                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13803
13804   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13805                            EP_CAN_SMASH_EVERYTHING, direction);
13806
13807   if (IN_LEV_FIELD(hitx, hity))
13808   {
13809     int opposite_direction = MV_DIR_OPPOSITE(direction);
13810     int hitting_side = direction;
13811     int touched_side = opposite_direction;
13812 #if 0
13813     int touched_element = MovingOrBlocked2Element(hitx, hity);
13814 #endif
13815 #if 1
13816     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13817                           MovDir[hitx][hity] != direction ||
13818                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13819
13820     object_hit = TRUE;
13821 #endif
13822
13823     if (object_hit)
13824     {
13825       int i;
13826
13827       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13828                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13829
13830       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13831                                CE_OTHER_IS_SMASHING, touched_side);
13832
13833       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13834                                CE_OTHER_GETS_SMASHED, hitting_side);
13835     }
13836   }
13837 }
13838 #endif
13839
13840 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13841 {
13842   int i, kill_x = -1, kill_y = -1;
13843
13844   int bad_element = -1;
13845   static int test_xy[4][2] =
13846   {
13847     { 0, -1 },
13848     { -1, 0 },
13849     { +1, 0 },
13850     { 0, +1 }
13851   };
13852   static int test_dir[4] =
13853   {
13854     MV_UP,
13855     MV_LEFT,
13856     MV_RIGHT,
13857     MV_DOWN
13858   };
13859
13860   for (i = 0; i < NUM_DIRECTIONS; i++)
13861   {
13862     int test_x, test_y, test_move_dir, test_element;
13863
13864     test_x = good_x + test_xy[i][0];
13865     test_y = good_y + test_xy[i][1];
13866
13867     if (!IN_LEV_FIELD(test_x, test_y))
13868       continue;
13869
13870     test_move_dir =
13871       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13872
13873     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13874
13875     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13876        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13877     */
13878     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13879         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13880     {
13881       kill_x = test_x;
13882       kill_y = test_y;
13883       bad_element = test_element;
13884
13885       break;
13886     }
13887   }
13888
13889   if (kill_x != -1 || kill_y != -1)
13890   {
13891     if (IS_PLAYER(good_x, good_y))
13892     {
13893       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13894
13895       if (player->shield_deadly_time_left > 0 &&
13896           !IS_INDESTRUCTIBLE(bad_element))
13897         Bang(kill_x, kill_y);
13898       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13899         KillPlayer(player);
13900     }
13901     else
13902       Bang(good_x, good_y);
13903   }
13904 }
13905
13906 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13907 {
13908   int i, kill_x = -1, kill_y = -1;
13909   int bad_element = Feld[bad_x][bad_y];
13910   static int test_xy[4][2] =
13911   {
13912     { 0, -1 },
13913     { -1, 0 },
13914     { +1, 0 },
13915     { 0, +1 }
13916   };
13917   static int touch_dir[4] =
13918   {
13919     MV_LEFT | MV_RIGHT,
13920     MV_UP   | MV_DOWN,
13921     MV_UP   | MV_DOWN,
13922     MV_LEFT | MV_RIGHT
13923   };
13924   static int test_dir[4] =
13925   {
13926     MV_UP,
13927     MV_LEFT,
13928     MV_RIGHT,
13929     MV_DOWN
13930   };
13931
13932   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13933     return;
13934
13935   for (i = 0; i < NUM_DIRECTIONS; i++)
13936   {
13937     int test_x, test_y, test_move_dir, test_element;
13938
13939     test_x = bad_x + test_xy[i][0];
13940     test_y = bad_y + test_xy[i][1];
13941
13942     if (!IN_LEV_FIELD(test_x, test_y))
13943       continue;
13944
13945     test_move_dir =
13946       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13947
13948     test_element = Feld[test_x][test_y];
13949
13950     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13951        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13952     */
13953     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13954         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13955     {
13956       /* good thing is player or penguin that does not move away */
13957       if (IS_PLAYER(test_x, test_y))
13958       {
13959         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13960
13961         if (bad_element == EL_ROBOT && player->is_moving)
13962           continue;     /* robot does not kill player if he is moving */
13963
13964         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13965         {
13966           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13967             continue;           /* center and border element do not touch */
13968         }
13969
13970         kill_x = test_x;
13971         kill_y = test_y;
13972
13973         break;
13974       }
13975       else if (test_element == EL_PENGUIN)
13976       {
13977         kill_x = test_x;
13978         kill_y = test_y;
13979
13980         break;
13981       }
13982     }
13983   }
13984
13985   if (kill_x != -1 || kill_y != -1)
13986   {
13987     if (IS_PLAYER(kill_x, kill_y))
13988     {
13989       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13990
13991       if (player->shield_deadly_time_left > 0 &&
13992           !IS_INDESTRUCTIBLE(bad_element))
13993         Bang(bad_x, bad_y);
13994       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13995         KillPlayer(player);
13996     }
13997     else
13998       Bang(kill_x, kill_y);
13999   }
14000 }
14001
14002 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14003 {
14004   int bad_element = Feld[bad_x][bad_y];
14005   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14006   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14007   int test_x = bad_x + dx, test_y = bad_y + dy;
14008   int test_move_dir, test_element;
14009   int kill_x = -1, kill_y = -1;
14010
14011   if (!IN_LEV_FIELD(test_x, test_y))
14012     return;
14013
14014   test_move_dir =
14015     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14016
14017   test_element = Feld[test_x][test_y];
14018
14019   if (test_move_dir != bad_move_dir)
14020   {
14021     /* good thing can be player or penguin that does not move away */
14022     if (IS_PLAYER(test_x, test_y))
14023     {
14024       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14025
14026       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14027          player as being hit when he is moving towards the bad thing, because
14028          the "get hit by" condition would be lost after the player stops) */
14029       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14030         return;         /* player moves away from bad thing */
14031
14032       kill_x = test_x;
14033       kill_y = test_y;
14034     }
14035     else if (test_element == EL_PENGUIN)
14036     {
14037       kill_x = test_x;
14038       kill_y = test_y;
14039     }
14040   }
14041
14042   if (kill_x != -1 || kill_y != -1)
14043   {
14044     if (IS_PLAYER(kill_x, kill_y))
14045     {
14046       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14047
14048       if (player->shield_deadly_time_left > 0 &&
14049           !IS_INDESTRUCTIBLE(bad_element))
14050         Bang(bad_x, bad_y);
14051       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14052         KillPlayer(player);
14053     }
14054     else
14055       Bang(kill_x, kill_y);
14056   }
14057 }
14058
14059 void TestIfPlayerTouchesBadThing(int x, int y)
14060 {
14061   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14062 }
14063
14064 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14065 {
14066   TestIfGoodThingHitsBadThing(x, y, move_dir);
14067 }
14068
14069 void TestIfBadThingTouchesPlayer(int x, int y)
14070 {
14071   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14072 }
14073
14074 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14075 {
14076   TestIfBadThingHitsGoodThing(x, y, move_dir);
14077 }
14078
14079 void TestIfFriendTouchesBadThing(int x, int y)
14080 {
14081   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14082 }
14083
14084 void TestIfBadThingTouchesFriend(int x, int y)
14085 {
14086   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14087 }
14088
14089 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14090 {
14091   int i, kill_x = bad_x, kill_y = bad_y;
14092   static int xy[4][2] =
14093   {
14094     { 0, -1 },
14095     { -1, 0 },
14096     { +1, 0 },
14097     { 0, +1 }
14098   };
14099
14100   for (i = 0; i < NUM_DIRECTIONS; i++)
14101   {
14102     int x, y, element;
14103
14104     x = bad_x + xy[i][0];
14105     y = bad_y + xy[i][1];
14106     if (!IN_LEV_FIELD(x, y))
14107       continue;
14108
14109     element = Feld[x][y];
14110     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14111         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14112     {
14113       kill_x = x;
14114       kill_y = y;
14115       break;
14116     }
14117   }
14118
14119   if (kill_x != bad_x || kill_y != bad_y)
14120     Bang(bad_x, bad_y);
14121 }
14122
14123 void KillPlayer(struct PlayerInfo *player)
14124 {
14125   int jx = player->jx, jy = player->jy;
14126
14127   if (!player->active)
14128     return;
14129
14130   /* the following code was introduced to prevent an infinite loop when calling
14131      -> Bang()
14132      -> CheckTriggeredElementChangeExt()
14133      -> ExecuteCustomElementAction()
14134      -> KillPlayer()
14135      -> (infinitely repeating the above sequence of function calls)
14136      which occurs when killing the player while having a CE with the setting
14137      "kill player X when explosion of <player X>"; the solution using a new
14138      field "player->killed" was chosen for backwards compatibility, although
14139      clever use of the fields "player->active" etc. would probably also work */
14140 #if 1
14141   if (player->killed)
14142     return;
14143 #endif
14144
14145   player->killed = TRUE;
14146
14147   /* remove accessible field at the player's position */
14148   Feld[jx][jy] = EL_EMPTY;
14149
14150   /* deactivate shield (else Bang()/Explode() would not work right) */
14151   player->shield_normal_time_left = 0;
14152   player->shield_deadly_time_left = 0;
14153
14154   Bang(jx, jy);
14155
14156 #if USE_PLAYER_REANIMATION
14157   if (player->killed)           /* player may have been reanimated */
14158     BuryPlayer(player);
14159 #else
14160   BuryPlayer(player);
14161 #endif
14162 }
14163
14164 static void KillPlayerUnlessEnemyProtected(int x, int y)
14165 {
14166   if (!PLAYER_ENEMY_PROTECTED(x, y))
14167     KillPlayer(PLAYERINFO(x, y));
14168 }
14169
14170 static void KillPlayerUnlessExplosionProtected(int x, int y)
14171 {
14172   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14173     KillPlayer(PLAYERINFO(x, y));
14174 }
14175
14176 void BuryPlayer(struct PlayerInfo *player)
14177 {
14178   int jx = player->jx, jy = player->jy;
14179
14180   if (!player->active)
14181     return;
14182
14183   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14184   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14185
14186   player->GameOver = TRUE;
14187   RemovePlayer(player);
14188 }
14189
14190 void RemovePlayer(struct PlayerInfo *player)
14191 {
14192   int jx = player->jx, jy = player->jy;
14193   int i, found = FALSE;
14194
14195   player->present = FALSE;
14196   player->active = FALSE;
14197
14198   if (!ExplodeField[jx][jy])
14199     StorePlayer[jx][jy] = 0;
14200
14201   if (player->is_moving)
14202     TEST_DrawLevelField(player->last_jx, player->last_jy);
14203
14204   for (i = 0; i < MAX_PLAYERS; i++)
14205     if (stored_player[i].active)
14206       found = TRUE;
14207
14208   if (!found)
14209     AllPlayersGone = TRUE;
14210
14211   ExitX = ZX = jx;
14212   ExitY = ZY = jy;
14213 }
14214
14215 #if USE_NEW_SNAP_DELAY
14216 static void setFieldForSnapping(int x, int y, int element, int direction)
14217 {
14218   struct ElementInfo *ei = &element_info[element];
14219   int direction_bit = MV_DIR_TO_BIT(direction);
14220   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14221   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14222                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14223
14224   Feld[x][y] = EL_ELEMENT_SNAPPING;
14225   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14226
14227   ResetGfxAnimation(x, y);
14228
14229   GfxElement[x][y] = element;
14230   GfxAction[x][y] = action;
14231   GfxDir[x][y] = direction;
14232   GfxFrame[x][y] = -1;
14233 }
14234 #endif
14235
14236 /*
14237   =============================================================================
14238   checkDiagonalPushing()
14239   -----------------------------------------------------------------------------
14240   check if diagonal input device direction results in pushing of object
14241   (by checking if the alternative direction is walkable, diggable, ...)
14242   =============================================================================
14243 */
14244
14245 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14246                                     int x, int y, int real_dx, int real_dy)
14247 {
14248   int jx, jy, dx, dy, xx, yy;
14249
14250   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14251     return TRUE;
14252
14253   /* diagonal direction: check alternative direction */
14254   jx = player->jx;
14255   jy = player->jy;
14256   dx = x - jx;
14257   dy = y - jy;
14258   xx = jx + (dx == 0 ? real_dx : 0);
14259   yy = jy + (dy == 0 ? real_dy : 0);
14260
14261   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14262 }
14263
14264 /*
14265   =============================================================================
14266   DigField()
14267   -----------------------------------------------------------------------------
14268   x, y:                 field next to player (non-diagonal) to try to dig to
14269   real_dx, real_dy:     direction as read from input device (can be diagonal)
14270   =============================================================================
14271 */
14272
14273 static int DigField(struct PlayerInfo *player,
14274                     int oldx, int oldy, int x, int y,
14275                     int real_dx, int real_dy, int mode)
14276 {
14277   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14278   boolean player_was_pushing = player->is_pushing;
14279   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14280   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14281   int jx = oldx, jy = oldy;
14282   int dx = x - jx, dy = y - jy;
14283   int nextx = x + dx, nexty = y + dy;
14284   int move_direction = (dx == -1 ? MV_LEFT  :
14285                         dx == +1 ? MV_RIGHT :
14286                         dy == -1 ? MV_UP    :
14287                         dy == +1 ? MV_DOWN  : MV_NONE);
14288   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14289   int dig_side = MV_DIR_OPPOSITE(move_direction);
14290   int old_element = Feld[jx][jy];
14291 #if USE_FIXED_DONT_RUN_INTO
14292   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14293 #else
14294   int element;
14295 #endif
14296   int collect_count;
14297
14298   if (is_player)                /* function can also be called by EL_PENGUIN */
14299   {
14300     if (player->MovPos == 0)
14301     {
14302       player->is_digging = FALSE;
14303       player->is_collecting = FALSE;
14304     }
14305
14306     if (player->MovPos == 0)    /* last pushing move finished */
14307       player->is_pushing = FALSE;
14308
14309     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14310     {
14311       player->is_switching = FALSE;
14312       player->push_delay = -1;
14313
14314       return MP_NO_ACTION;
14315     }
14316   }
14317
14318 #if !USE_FIXED_DONT_RUN_INTO
14319   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14320     return MP_NO_ACTION;
14321 #endif
14322
14323   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14324     old_element = Back[jx][jy];
14325
14326   /* in case of element dropped at player position, check background */
14327   else if (Back[jx][jy] != EL_EMPTY &&
14328            game.engine_version >= VERSION_IDENT(2,2,0,0))
14329     old_element = Back[jx][jy];
14330
14331   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14332     return MP_NO_ACTION;        /* field has no opening in this direction */
14333
14334   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14335     return MP_NO_ACTION;        /* field has no opening in this direction */
14336
14337 #if USE_FIXED_DONT_RUN_INTO
14338   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14339   {
14340     SplashAcid(x, y);
14341
14342     Feld[jx][jy] = player->artwork_element;
14343     InitMovingField(jx, jy, MV_DOWN);
14344     Store[jx][jy] = EL_ACID;
14345     ContinueMoving(jx, jy);
14346     BuryPlayer(player);
14347
14348     return MP_DONT_RUN_INTO;
14349   }
14350 #endif
14351
14352 #if USE_FIXED_DONT_RUN_INTO
14353   if (player_can_move && DONT_RUN_INTO(element))
14354   {
14355     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14356
14357     return MP_DONT_RUN_INTO;
14358   }
14359 #endif
14360
14361 #if USE_FIXED_DONT_RUN_INTO
14362   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14363     return MP_NO_ACTION;
14364 #endif
14365
14366 #if !USE_FIXED_DONT_RUN_INTO
14367   element = Feld[x][y];
14368 #endif
14369
14370   collect_count = element_info[element].collect_count_initial;
14371
14372   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14373     return MP_NO_ACTION;
14374
14375   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14376     player_can_move = player_can_move_or_snap;
14377
14378   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14379       game.engine_version >= VERSION_IDENT(2,2,0,0))
14380   {
14381     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14382                                player->index_bit, dig_side);
14383     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14384                                         player->index_bit, dig_side);
14385
14386     if (element == EL_DC_LANDMINE)
14387       Bang(x, y);
14388
14389     if (Feld[x][y] != element)          /* field changed by snapping */
14390       return MP_ACTION;
14391
14392     return MP_NO_ACTION;
14393   }
14394
14395 #if USE_PLAYER_GRAVITY
14396   if (player->gravity && is_player && !player->is_auto_moving &&
14397       canFallDown(player) && move_direction != MV_DOWN &&
14398       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14399     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14400 #else
14401   if (game.gravity && is_player && !player->is_auto_moving &&
14402       canFallDown(player) && move_direction != MV_DOWN &&
14403       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14404     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14405 #endif
14406
14407   if (player_can_move &&
14408       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14409   {
14410     int sound_element = SND_ELEMENT(element);
14411     int sound_action = ACTION_WALKING;
14412
14413     if (IS_RND_GATE(element))
14414     {
14415       if (!player->key[RND_GATE_NR(element)])
14416         return MP_NO_ACTION;
14417     }
14418     else if (IS_RND_GATE_GRAY(element))
14419     {
14420       if (!player->key[RND_GATE_GRAY_NR(element)])
14421         return MP_NO_ACTION;
14422     }
14423     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14424     {
14425       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14426         return MP_NO_ACTION;
14427     }
14428     else if (element == EL_EXIT_OPEN ||
14429              element == EL_EM_EXIT_OPEN ||
14430              element == EL_STEEL_EXIT_OPEN ||
14431              element == EL_EM_STEEL_EXIT_OPEN ||
14432              element == EL_SP_EXIT_OPEN ||
14433              element == EL_SP_EXIT_OPENING)
14434     {
14435       sound_action = ACTION_PASSING;    /* player is passing exit */
14436     }
14437     else if (element == EL_EMPTY)
14438     {
14439       sound_action = ACTION_MOVING;             /* nothing to walk on */
14440     }
14441
14442     /* play sound from background or player, whatever is available */
14443     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14444       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14445     else
14446       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14447   }
14448   else if (player_can_move &&
14449            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14450   {
14451     if (!ACCESS_FROM(element, opposite_direction))
14452       return MP_NO_ACTION;      /* field not accessible from this direction */
14453
14454     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14455       return MP_NO_ACTION;
14456
14457     if (IS_EM_GATE(element))
14458     {
14459       if (!player->key[EM_GATE_NR(element)])
14460         return MP_NO_ACTION;
14461     }
14462     else if (IS_EM_GATE_GRAY(element))
14463     {
14464       if (!player->key[EM_GATE_GRAY_NR(element)])
14465         return MP_NO_ACTION;
14466     }
14467     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14468     {
14469       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14470         return MP_NO_ACTION;
14471     }
14472     else if (IS_EMC_GATE(element))
14473     {
14474       if (!player->key[EMC_GATE_NR(element)])
14475         return MP_NO_ACTION;
14476     }
14477     else if (IS_EMC_GATE_GRAY(element))
14478     {
14479       if (!player->key[EMC_GATE_GRAY_NR(element)])
14480         return MP_NO_ACTION;
14481     }
14482     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14483     {
14484       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14485         return MP_NO_ACTION;
14486     }
14487     else if (element == EL_DC_GATE_WHITE ||
14488              element == EL_DC_GATE_WHITE_GRAY ||
14489              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14490     {
14491       if (player->num_white_keys == 0)
14492         return MP_NO_ACTION;
14493
14494       player->num_white_keys--;
14495     }
14496     else if (IS_SP_PORT(element))
14497     {
14498       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14499           element == EL_SP_GRAVITY_PORT_RIGHT ||
14500           element == EL_SP_GRAVITY_PORT_UP ||
14501           element == EL_SP_GRAVITY_PORT_DOWN)
14502 #if USE_PLAYER_GRAVITY
14503         player->gravity = !player->gravity;
14504 #else
14505         game.gravity = !game.gravity;
14506 #endif
14507       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14508                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14509                element == EL_SP_GRAVITY_ON_PORT_UP ||
14510                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14511 #if USE_PLAYER_GRAVITY
14512         player->gravity = TRUE;
14513 #else
14514         game.gravity = TRUE;
14515 #endif
14516       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14517                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14518                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14519                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14520 #if USE_PLAYER_GRAVITY
14521         player->gravity = FALSE;
14522 #else
14523         game.gravity = FALSE;
14524 #endif
14525     }
14526
14527     /* automatically move to the next field with double speed */
14528     player->programmed_action = move_direction;
14529
14530     if (player->move_delay_reset_counter == 0)
14531     {
14532       player->move_delay_reset_counter = 2;     /* two double speed steps */
14533
14534       DOUBLE_PLAYER_SPEED(player);
14535     }
14536
14537     PlayLevelSoundAction(x, y, ACTION_PASSING);
14538   }
14539   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14540   {
14541     RemoveField(x, y);
14542
14543     if (mode != DF_SNAP)
14544     {
14545       GfxElement[x][y] = GFX_ELEMENT(element);
14546       player->is_digging = TRUE;
14547     }
14548
14549     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14550
14551     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14552                                         player->index_bit, dig_side);
14553
14554     if (mode == DF_SNAP)
14555     {
14556 #if USE_NEW_SNAP_DELAY
14557       if (level.block_snap_field)
14558         setFieldForSnapping(x, y, element, move_direction);
14559       else
14560         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14561 #else
14562       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14563 #endif
14564
14565       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14566                                           player->index_bit, dig_side);
14567     }
14568   }
14569   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14570   {
14571     RemoveField(x, y);
14572
14573     if (is_player && mode != DF_SNAP)
14574     {
14575       GfxElement[x][y] = element;
14576       player->is_collecting = TRUE;
14577     }
14578
14579     if (element == EL_SPEED_PILL)
14580     {
14581       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14582     }
14583     else if (element == EL_EXTRA_TIME && level.time > 0)
14584     {
14585       TimeLeft += level.extra_time;
14586
14587 #if 1
14588       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14589
14590       DisplayGameControlValues();
14591 #else
14592       DrawGameValue_Time(TimeLeft);
14593 #endif
14594     }
14595     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14596     {
14597       player->shield_normal_time_left += level.shield_normal_time;
14598       if (element == EL_SHIELD_DEADLY)
14599         player->shield_deadly_time_left += level.shield_deadly_time;
14600     }
14601     else if (element == EL_DYNAMITE ||
14602              element == EL_EM_DYNAMITE ||
14603              element == EL_SP_DISK_RED)
14604     {
14605       if (player->inventory_size < MAX_INVENTORY_SIZE)
14606         player->inventory_element[player->inventory_size++] = element;
14607
14608       DrawGameDoorValues();
14609     }
14610     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14611     {
14612       player->dynabomb_count++;
14613       player->dynabombs_left++;
14614     }
14615     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14616     {
14617       player->dynabomb_size++;
14618     }
14619     else if (element == EL_DYNABOMB_INCREASE_POWER)
14620     {
14621       player->dynabomb_xl = TRUE;
14622     }
14623     else if (IS_KEY(element))
14624     {
14625       player->key[KEY_NR(element)] = TRUE;
14626
14627       DrawGameDoorValues();
14628     }
14629     else if (element == EL_DC_KEY_WHITE)
14630     {
14631       player->num_white_keys++;
14632
14633       /* display white keys? */
14634       /* DrawGameDoorValues(); */
14635     }
14636     else if (IS_ENVELOPE(element))
14637     {
14638       player->show_envelope = element;
14639     }
14640     else if (element == EL_EMC_LENSES)
14641     {
14642       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14643
14644       RedrawAllInvisibleElementsForLenses();
14645     }
14646     else if (element == EL_EMC_MAGNIFIER)
14647     {
14648       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14649
14650       RedrawAllInvisibleElementsForMagnifier();
14651     }
14652     else if (IS_DROPPABLE(element) ||
14653              IS_THROWABLE(element))     /* can be collected and dropped */
14654     {
14655       int i;
14656
14657       if (collect_count == 0)
14658         player->inventory_infinite_element = element;
14659       else
14660         for (i = 0; i < collect_count; i++)
14661           if (player->inventory_size < MAX_INVENTORY_SIZE)
14662             player->inventory_element[player->inventory_size++] = element;
14663
14664       DrawGameDoorValues();
14665     }
14666     else if (collect_count > 0)
14667     {
14668       local_player->gems_still_needed -= collect_count;
14669       if (local_player->gems_still_needed < 0)
14670         local_player->gems_still_needed = 0;
14671
14672 #if 1
14673       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14674
14675       DisplayGameControlValues();
14676 #else
14677       DrawGameValue_Emeralds(local_player->gems_still_needed);
14678 #endif
14679     }
14680
14681     RaiseScoreElement(element);
14682     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14683
14684     if (is_player)
14685       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14686                                           player->index_bit, dig_side);
14687
14688     if (mode == DF_SNAP)
14689     {
14690 #if USE_NEW_SNAP_DELAY
14691       if (level.block_snap_field)
14692         setFieldForSnapping(x, y, element, move_direction);
14693       else
14694         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14695 #else
14696       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14697 #endif
14698
14699       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14700                                           player->index_bit, dig_side);
14701     }
14702   }
14703   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14704   {
14705     if (mode == DF_SNAP && element != EL_BD_ROCK)
14706       return MP_NO_ACTION;
14707
14708     if (CAN_FALL(element) && dy)
14709       return MP_NO_ACTION;
14710
14711     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14712         !(element == EL_SPRING && level.use_spring_bug))
14713       return MP_NO_ACTION;
14714
14715     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14716         ((move_direction & MV_VERTICAL &&
14717           ((element_info[element].move_pattern & MV_LEFT &&
14718             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14719            (element_info[element].move_pattern & MV_RIGHT &&
14720             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14721          (move_direction & MV_HORIZONTAL &&
14722           ((element_info[element].move_pattern & MV_UP &&
14723             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14724            (element_info[element].move_pattern & MV_DOWN &&
14725             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14726       return MP_NO_ACTION;
14727
14728     /* do not push elements already moving away faster than player */
14729     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14730         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14731       return MP_NO_ACTION;
14732
14733     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14734     {
14735       if (player->push_delay_value == -1 || !player_was_pushing)
14736         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14737     }
14738     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14739     {
14740       if (player->push_delay_value == -1)
14741         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14742     }
14743     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14744     {
14745       if (!player->is_pushing)
14746         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14747     }
14748
14749     player->is_pushing = TRUE;
14750     player->is_active = TRUE;
14751
14752     if (!(IN_LEV_FIELD(nextx, nexty) &&
14753           (IS_FREE(nextx, nexty) ||
14754            (IS_SB_ELEMENT(element) &&
14755             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14756            (IS_CUSTOM_ELEMENT(element) &&
14757             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14758       return MP_NO_ACTION;
14759
14760     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14761       return MP_NO_ACTION;
14762
14763     if (player->push_delay == -1)       /* new pushing; restart delay */
14764       player->push_delay = 0;
14765
14766     if (player->push_delay < player->push_delay_value &&
14767         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14768         element != EL_SPRING && element != EL_BALLOON)
14769     {
14770       /* make sure that there is no move delay before next try to push */
14771       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14772         player->move_delay = 0;
14773
14774       return MP_NO_ACTION;
14775     }
14776
14777     if (IS_CUSTOM_ELEMENT(element) &&
14778         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14779     {
14780       if (!DigFieldByCE(nextx, nexty, element))
14781         return MP_NO_ACTION;
14782     }
14783
14784     if (IS_SB_ELEMENT(element))
14785     {
14786       if (element == EL_SOKOBAN_FIELD_FULL)
14787       {
14788         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14789         local_player->sokobanfields_still_needed++;
14790       }
14791
14792       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14793       {
14794         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14795         local_player->sokobanfields_still_needed--;
14796       }
14797
14798       Feld[x][y] = EL_SOKOBAN_OBJECT;
14799
14800       if (Back[x][y] == Back[nextx][nexty])
14801         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14802       else if (Back[x][y] != 0)
14803         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14804                                     ACTION_EMPTYING);
14805       else
14806         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14807                                     ACTION_FILLING);
14808
14809       if (local_player->sokobanfields_still_needed == 0 &&
14810           game.emulation == EMU_SOKOBAN)
14811       {
14812         PlayerWins(player);
14813
14814         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14815       }
14816     }
14817     else
14818       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14819
14820     InitMovingField(x, y, move_direction);
14821     GfxAction[x][y] = ACTION_PUSHING;
14822
14823     if (mode == DF_SNAP)
14824       ContinueMoving(x, y);
14825     else
14826       MovPos[x][y] = (dx != 0 ? dx : dy);
14827
14828     Pushed[x][y] = TRUE;
14829     Pushed[nextx][nexty] = TRUE;
14830
14831     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14832       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14833     else
14834       player->push_delay_value = -1;    /* get new value later */
14835
14836     /* check for element change _after_ element has been pushed */
14837     if (game.use_change_when_pushing_bug)
14838     {
14839       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14840                                  player->index_bit, dig_side);
14841       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14842                                           player->index_bit, dig_side);
14843     }
14844   }
14845   else if (IS_SWITCHABLE(element))
14846   {
14847     if (PLAYER_SWITCHING(player, x, y))
14848     {
14849       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14850                                           player->index_bit, dig_side);
14851
14852       return MP_ACTION;
14853     }
14854
14855     player->is_switching = TRUE;
14856     player->switch_x = x;
14857     player->switch_y = y;
14858
14859     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14860
14861     if (element == EL_ROBOT_WHEEL)
14862     {
14863       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14864       ZX = x;
14865       ZY = y;
14866
14867       game.robot_wheel_active = TRUE;
14868
14869       TEST_DrawLevelField(x, y);
14870     }
14871     else if (element == EL_SP_TERMINAL)
14872     {
14873       int xx, yy;
14874
14875       SCAN_PLAYFIELD(xx, yy)
14876       {
14877         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14878           Bang(xx, yy);
14879         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14880           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14881       }
14882     }
14883     else if (IS_BELT_SWITCH(element))
14884     {
14885       ToggleBeltSwitch(x, y);
14886     }
14887     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14888              element == EL_SWITCHGATE_SWITCH_DOWN ||
14889              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14890              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14891     {
14892       ToggleSwitchgateSwitch(x, y);
14893     }
14894     else if (element == EL_LIGHT_SWITCH ||
14895              element == EL_LIGHT_SWITCH_ACTIVE)
14896     {
14897       ToggleLightSwitch(x, y);
14898     }
14899     else if (element == EL_TIMEGATE_SWITCH ||
14900              element == EL_DC_TIMEGATE_SWITCH)
14901     {
14902       ActivateTimegateSwitch(x, y);
14903     }
14904     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14905              element == EL_BALLOON_SWITCH_RIGHT ||
14906              element == EL_BALLOON_SWITCH_UP    ||
14907              element == EL_BALLOON_SWITCH_DOWN  ||
14908              element == EL_BALLOON_SWITCH_NONE  ||
14909              element == EL_BALLOON_SWITCH_ANY)
14910     {
14911       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14912                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14913                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14914                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14915                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14916                              move_direction);
14917     }
14918     else if (element == EL_LAMP)
14919     {
14920       Feld[x][y] = EL_LAMP_ACTIVE;
14921       local_player->lights_still_needed--;
14922
14923       ResetGfxAnimation(x, y);
14924       TEST_DrawLevelField(x, y);
14925     }
14926     else if (element == EL_TIME_ORB_FULL)
14927     {
14928       Feld[x][y] = EL_TIME_ORB_EMPTY;
14929
14930       if (level.time > 0 || level.use_time_orb_bug)
14931       {
14932         TimeLeft += level.time_orb_time;
14933
14934 #if 1
14935         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14936
14937         DisplayGameControlValues();
14938 #else
14939         DrawGameValue_Time(TimeLeft);
14940 #endif
14941       }
14942
14943       ResetGfxAnimation(x, y);
14944       TEST_DrawLevelField(x, y);
14945     }
14946     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14947              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14948     {
14949       int xx, yy;
14950
14951       game.ball_state = !game.ball_state;
14952
14953       SCAN_PLAYFIELD(xx, yy)
14954       {
14955         int e = Feld[xx][yy];
14956
14957         if (game.ball_state)
14958         {
14959           if (e == EL_EMC_MAGIC_BALL)
14960             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14961           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14962             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14963         }
14964         else
14965         {
14966           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14967             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14968           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14969             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14970         }
14971       }
14972     }
14973
14974     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14975                                         player->index_bit, dig_side);
14976
14977     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14978                                         player->index_bit, dig_side);
14979
14980     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14981                                         player->index_bit, dig_side);
14982
14983     return MP_ACTION;
14984   }
14985   else
14986   {
14987     if (!PLAYER_SWITCHING(player, x, y))
14988     {
14989       player->is_switching = TRUE;
14990       player->switch_x = x;
14991       player->switch_y = y;
14992
14993       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14994                                  player->index_bit, dig_side);
14995       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14996                                           player->index_bit, dig_side);
14997
14998       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14999                                  player->index_bit, dig_side);
15000       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15001                                           player->index_bit, dig_side);
15002     }
15003
15004     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15005                                player->index_bit, dig_side);
15006     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15007                                         player->index_bit, dig_side);
15008
15009     return MP_NO_ACTION;
15010   }
15011
15012   player->push_delay = -1;
15013
15014   if (is_player)                /* function can also be called by EL_PENGUIN */
15015   {
15016     if (Feld[x][y] != element)          /* really digged/collected something */
15017     {
15018       player->is_collecting = !player->is_digging;
15019       player->is_active = TRUE;
15020     }
15021   }
15022
15023   return MP_MOVING;
15024 }
15025
15026 static boolean DigFieldByCE(int x, int y, int digging_element)
15027 {
15028   int element = Feld[x][y];
15029
15030   if (!IS_FREE(x, y))
15031   {
15032     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15033                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15034                   ACTION_BREAKING);
15035
15036     /* no element can dig solid indestructible elements */
15037     if (IS_INDESTRUCTIBLE(element) &&
15038         !IS_DIGGABLE(element) &&
15039         !IS_COLLECTIBLE(element))
15040       return FALSE;
15041
15042     if (AmoebaNr[x][y] &&
15043         (element == EL_AMOEBA_FULL ||
15044          element == EL_BD_AMOEBA ||
15045          element == EL_AMOEBA_GROWING))
15046     {
15047       AmoebaCnt[AmoebaNr[x][y]]--;
15048       AmoebaCnt2[AmoebaNr[x][y]]--;
15049     }
15050
15051     if (IS_MOVING(x, y))
15052       RemoveMovingField(x, y);
15053     else
15054     {
15055       RemoveField(x, y);
15056       TEST_DrawLevelField(x, y);
15057     }
15058
15059     /* if digged element was about to explode, prevent the explosion */
15060     ExplodeField[x][y] = EX_TYPE_NONE;
15061
15062     PlayLevelSoundAction(x, y, action);
15063   }
15064
15065   Store[x][y] = EL_EMPTY;
15066
15067 #if 1
15068   /* this makes it possible to leave the removed element again */
15069   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15070     Store[x][y] = element;
15071 #else
15072   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15073   {
15074     int move_leave_element = element_info[digging_element].move_leave_element;
15075
15076     /* this makes it possible to leave the removed element again */
15077     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15078                    element : move_leave_element);
15079   }
15080 #endif
15081
15082   return TRUE;
15083 }
15084
15085 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15086 {
15087   int jx = player->jx, jy = player->jy;
15088   int x = jx + dx, y = jy + dy;
15089   int snap_direction = (dx == -1 ? MV_LEFT  :
15090                         dx == +1 ? MV_RIGHT :
15091                         dy == -1 ? MV_UP    :
15092                         dy == +1 ? MV_DOWN  : MV_NONE);
15093   boolean can_continue_snapping = (level.continuous_snapping &&
15094                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15095
15096   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15097     return FALSE;
15098
15099   if (!player->active || !IN_LEV_FIELD(x, y))
15100     return FALSE;
15101
15102   if (dx && dy)
15103     return FALSE;
15104
15105   if (!dx && !dy)
15106   {
15107     if (player->MovPos == 0)
15108       player->is_pushing = FALSE;
15109
15110     player->is_snapping = FALSE;
15111
15112     if (player->MovPos == 0)
15113     {
15114       player->is_moving = FALSE;
15115       player->is_digging = FALSE;
15116       player->is_collecting = FALSE;
15117     }
15118
15119     return FALSE;
15120   }
15121
15122 #if USE_NEW_CONTINUOUS_SNAPPING
15123   /* prevent snapping with already pressed snap key when not allowed */
15124   if (player->is_snapping && !can_continue_snapping)
15125     return FALSE;
15126 #else
15127   if (player->is_snapping)
15128     return FALSE;
15129 #endif
15130
15131   player->MovDir = snap_direction;
15132
15133   if (player->MovPos == 0)
15134   {
15135     player->is_moving = FALSE;
15136     player->is_digging = FALSE;
15137     player->is_collecting = FALSE;
15138   }
15139
15140   player->is_dropping = FALSE;
15141   player->is_dropping_pressed = FALSE;
15142   player->drop_pressed_delay = 0;
15143
15144   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15145     return FALSE;
15146
15147   player->is_snapping = TRUE;
15148   player->is_active = TRUE;
15149
15150   if (player->MovPos == 0)
15151   {
15152     player->is_moving = FALSE;
15153     player->is_digging = FALSE;
15154     player->is_collecting = FALSE;
15155   }
15156
15157   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15158     TEST_DrawLevelField(player->last_jx, player->last_jy);
15159
15160   TEST_DrawLevelField(x, y);
15161
15162   return TRUE;
15163 }
15164
15165 static boolean DropElement(struct PlayerInfo *player)
15166 {
15167   int old_element, new_element;
15168   int dropx = player->jx, dropy = player->jy;
15169   int drop_direction = player->MovDir;
15170   int drop_side = drop_direction;
15171 #if 1
15172   int drop_element = get_next_dropped_element(player);
15173 #else
15174   int drop_element = (player->inventory_size > 0 ?
15175                       player->inventory_element[player->inventory_size - 1] :
15176                       player->inventory_infinite_element != EL_UNDEFINED ?
15177                       player->inventory_infinite_element :
15178                       player->dynabombs_left > 0 ?
15179                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15180                       EL_UNDEFINED);
15181 #endif
15182
15183   player->is_dropping_pressed = TRUE;
15184
15185   /* do not drop an element on top of another element; when holding drop key
15186      pressed without moving, dropped element must move away before the next
15187      element can be dropped (this is especially important if the next element
15188      is dynamite, which can be placed on background for historical reasons) */
15189   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15190     return MP_ACTION;
15191
15192   if (IS_THROWABLE(drop_element))
15193   {
15194     dropx += GET_DX_FROM_DIR(drop_direction);
15195     dropy += GET_DY_FROM_DIR(drop_direction);
15196
15197     if (!IN_LEV_FIELD(dropx, dropy))
15198       return FALSE;
15199   }
15200
15201   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15202   new_element = drop_element;           /* default: no change when dropping */
15203
15204   /* check if player is active, not moving and ready to drop */
15205   if (!player->active || player->MovPos || player->drop_delay > 0)
15206     return FALSE;
15207
15208   /* check if player has anything that can be dropped */
15209   if (new_element == EL_UNDEFINED)
15210     return FALSE;
15211
15212   /* check if drop key was pressed long enough for EM style dynamite */
15213   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15214     return FALSE;
15215
15216   /* check if anything can be dropped at the current position */
15217   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15218     return FALSE;
15219
15220   /* collected custom elements can only be dropped on empty fields */
15221   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15222     return FALSE;
15223
15224   if (old_element != EL_EMPTY)
15225     Back[dropx][dropy] = old_element;   /* store old element on this field */
15226
15227   ResetGfxAnimation(dropx, dropy);
15228   ResetRandomAnimationValue(dropx, dropy);
15229
15230   if (player->inventory_size > 0 ||
15231       player->inventory_infinite_element != EL_UNDEFINED)
15232   {
15233     if (player->inventory_size > 0)
15234     {
15235       player->inventory_size--;
15236
15237       DrawGameDoorValues();
15238
15239       if (new_element == EL_DYNAMITE)
15240         new_element = EL_DYNAMITE_ACTIVE;
15241       else if (new_element == EL_EM_DYNAMITE)
15242         new_element = EL_EM_DYNAMITE_ACTIVE;
15243       else if (new_element == EL_SP_DISK_RED)
15244         new_element = EL_SP_DISK_RED_ACTIVE;
15245     }
15246
15247     Feld[dropx][dropy] = new_element;
15248
15249     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15250       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15251                           el2img(Feld[dropx][dropy]), 0);
15252
15253     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15254
15255     /* needed if previous element just changed to "empty" in the last frame */
15256     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15257
15258     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15259                                player->index_bit, drop_side);
15260     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15261                                         CE_PLAYER_DROPS_X,
15262                                         player->index_bit, drop_side);
15263
15264     TestIfElementTouchesCustomElement(dropx, dropy);
15265   }
15266   else          /* player is dropping a dyna bomb */
15267   {
15268     player->dynabombs_left--;
15269
15270     Feld[dropx][dropy] = new_element;
15271
15272     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15273       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15274                           el2img(Feld[dropx][dropy]), 0);
15275
15276     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15277   }
15278
15279   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15280     InitField_WithBug1(dropx, dropy, FALSE);
15281
15282   new_element = Feld[dropx][dropy];     /* element might have changed */
15283
15284   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15285       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15286   {
15287     int move_direction, nextx, nexty;
15288
15289     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15290       MovDir[dropx][dropy] = drop_direction;
15291
15292     move_direction = MovDir[dropx][dropy];
15293     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15294     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15295
15296     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15297
15298 #if USE_FIX_IMPACT_COLLISION
15299     /* do not cause impact style collision by dropping elements that can fall */
15300     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15301 #else
15302     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15303 #endif
15304   }
15305
15306   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15307   player->is_dropping = TRUE;
15308
15309   player->drop_pressed_delay = 0;
15310   player->is_dropping_pressed = FALSE;
15311
15312   player->drop_x = dropx;
15313   player->drop_y = dropy;
15314
15315   return TRUE;
15316 }
15317
15318 /* ------------------------------------------------------------------------- */
15319 /* game sound playing functions                                              */
15320 /* ------------------------------------------------------------------------- */
15321
15322 static int *loop_sound_frame = NULL;
15323 static int *loop_sound_volume = NULL;
15324
15325 void InitPlayLevelSound()
15326 {
15327   int num_sounds = getSoundListSize();
15328
15329   checked_free(loop_sound_frame);
15330   checked_free(loop_sound_volume);
15331
15332   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15333   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15334 }
15335
15336 static void PlayLevelSound(int x, int y, int nr)
15337 {
15338   int sx = SCREENX(x), sy = SCREENY(y);
15339   int volume, stereo_position;
15340   int max_distance = 8;
15341   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15342
15343   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15344       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15345     return;
15346
15347   if (!IN_LEV_FIELD(x, y) ||
15348       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15349       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15350     return;
15351
15352   volume = SOUND_MAX_VOLUME;
15353
15354   if (!IN_SCR_FIELD(sx, sy))
15355   {
15356     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15357     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15358
15359     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15360   }
15361
15362   stereo_position = (SOUND_MAX_LEFT +
15363                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15364                      (SCR_FIELDX + 2 * max_distance));
15365
15366   if (IS_LOOP_SOUND(nr))
15367   {
15368     /* This assures that quieter loop sounds do not overwrite louder ones,
15369        while restarting sound volume comparison with each new game frame. */
15370
15371     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15372       return;
15373
15374     loop_sound_volume[nr] = volume;
15375     loop_sound_frame[nr] = FrameCounter;
15376   }
15377
15378   PlaySoundExt(nr, volume, stereo_position, type);
15379 }
15380
15381 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15382 {
15383   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15384                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15385                  y < LEVELY(BY1) ? LEVELY(BY1) :
15386                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15387                  sound_action);
15388 }
15389
15390 static void PlayLevelSoundAction(int x, int y, int action)
15391 {
15392   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15393 }
15394
15395 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15396 {
15397   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15398
15399   if (sound_effect != SND_UNDEFINED)
15400     PlayLevelSound(x, y, sound_effect);
15401 }
15402
15403 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15404                                               int action)
15405 {
15406   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15407
15408   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15409     PlayLevelSound(x, y, sound_effect);
15410 }
15411
15412 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15413 {
15414   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15415
15416   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15417     PlayLevelSound(x, y, sound_effect);
15418 }
15419
15420 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15421 {
15422   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15423
15424   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15425     StopSound(sound_effect);
15426 }
15427
15428 static void PlayLevelMusic()
15429 {
15430   if (levelset.music[level_nr] != MUS_UNDEFINED)
15431     PlayMusic(levelset.music[level_nr]);        /* from config file */
15432   else
15433     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15434 }
15435
15436 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15437 {
15438   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15439   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15440   int x = xx - 1 - offset;
15441   int y = yy - 1 - offset;
15442
15443   switch (sample)
15444   {
15445     case SAMPLE_blank:
15446       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15447       break;
15448
15449     case SAMPLE_roll:
15450       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15451       break;
15452
15453     case SAMPLE_stone:
15454       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15455       break;
15456
15457     case SAMPLE_nut:
15458       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15459       break;
15460
15461     case SAMPLE_crack:
15462       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15463       break;
15464
15465     case SAMPLE_bug:
15466       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15467       break;
15468
15469     case SAMPLE_tank:
15470       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15471       break;
15472
15473     case SAMPLE_android_clone:
15474       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15475       break;
15476
15477     case SAMPLE_android_move:
15478       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15479       break;
15480
15481     case SAMPLE_spring:
15482       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15483       break;
15484
15485     case SAMPLE_slurp:
15486       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15487       break;
15488
15489     case SAMPLE_eater:
15490       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15491       break;
15492
15493     case SAMPLE_eater_eat:
15494       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15495       break;
15496
15497     case SAMPLE_alien:
15498       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15499       break;
15500
15501     case SAMPLE_collect:
15502       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15503       break;
15504
15505     case SAMPLE_diamond:
15506       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15507       break;
15508
15509     case SAMPLE_squash:
15510       /* !!! CHECK THIS !!! */
15511 #if 1
15512       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15513 #else
15514       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15515 #endif
15516       break;
15517
15518     case SAMPLE_wonderfall:
15519       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15520       break;
15521
15522     case SAMPLE_drip:
15523       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15524       break;
15525
15526     case SAMPLE_push:
15527       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15528       break;
15529
15530     case SAMPLE_dirt:
15531       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15532       break;
15533
15534     case SAMPLE_acid:
15535       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15536       break;
15537
15538     case SAMPLE_ball:
15539       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15540       break;
15541
15542     case SAMPLE_grow:
15543       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15544       break;
15545
15546     case SAMPLE_wonder:
15547       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15548       break;
15549
15550     case SAMPLE_door:
15551       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15552       break;
15553
15554     case SAMPLE_exit_open:
15555       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15556       break;
15557
15558     case SAMPLE_exit_leave:
15559       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15560       break;
15561
15562     case SAMPLE_dynamite:
15563       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15564       break;
15565
15566     case SAMPLE_tick:
15567       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15568       break;
15569
15570     case SAMPLE_press:
15571       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15572       break;
15573
15574     case SAMPLE_wheel:
15575       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15576       break;
15577
15578     case SAMPLE_boom:
15579       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15580       break;
15581
15582     case SAMPLE_die:
15583       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15584       break;
15585
15586     case SAMPLE_time:
15587       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15588       break;
15589
15590     default:
15591       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15592       break;
15593   }
15594 }
15595
15596 #if 0
15597 void ChangeTime(int value)
15598 {
15599   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15600
15601   *time += value;
15602
15603   /* EMC game engine uses value from time counter of RND game engine */
15604   level.native_em_level->lev->time = *time;
15605
15606   DrawGameValue_Time(*time);
15607 }
15608
15609 void RaiseScore(int value)
15610 {
15611   /* EMC game engine and RND game engine have separate score counters */
15612   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15613                 &level.native_em_level->lev->score : &local_player->score);
15614
15615   *score += value;
15616
15617   DrawGameValue_Score(*score);
15618 }
15619 #endif
15620
15621 void RaiseScore(int value)
15622 {
15623   local_player->score += value;
15624
15625 #if 1
15626   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15627
15628   DisplayGameControlValues();
15629 #else
15630   DrawGameValue_Score(local_player->score);
15631 #endif
15632 }
15633
15634 void RaiseScoreElement(int element)
15635 {
15636   switch (element)
15637   {
15638     case EL_EMERALD:
15639     case EL_BD_DIAMOND:
15640     case EL_EMERALD_YELLOW:
15641     case EL_EMERALD_RED:
15642     case EL_EMERALD_PURPLE:
15643     case EL_SP_INFOTRON:
15644       RaiseScore(level.score[SC_EMERALD]);
15645       break;
15646     case EL_DIAMOND:
15647       RaiseScore(level.score[SC_DIAMOND]);
15648       break;
15649     case EL_CRYSTAL:
15650       RaiseScore(level.score[SC_CRYSTAL]);
15651       break;
15652     case EL_PEARL:
15653       RaiseScore(level.score[SC_PEARL]);
15654       break;
15655     case EL_BUG:
15656     case EL_BD_BUTTERFLY:
15657     case EL_SP_ELECTRON:
15658       RaiseScore(level.score[SC_BUG]);
15659       break;
15660     case EL_SPACESHIP:
15661     case EL_BD_FIREFLY:
15662     case EL_SP_SNIKSNAK:
15663       RaiseScore(level.score[SC_SPACESHIP]);
15664       break;
15665     case EL_YAMYAM:
15666     case EL_DARK_YAMYAM:
15667       RaiseScore(level.score[SC_YAMYAM]);
15668       break;
15669     case EL_ROBOT:
15670       RaiseScore(level.score[SC_ROBOT]);
15671       break;
15672     case EL_PACMAN:
15673       RaiseScore(level.score[SC_PACMAN]);
15674       break;
15675     case EL_NUT:
15676       RaiseScore(level.score[SC_NUT]);
15677       break;
15678     case EL_DYNAMITE:
15679     case EL_EM_DYNAMITE:
15680     case EL_SP_DISK_RED:
15681     case EL_DYNABOMB_INCREASE_NUMBER:
15682     case EL_DYNABOMB_INCREASE_SIZE:
15683     case EL_DYNABOMB_INCREASE_POWER:
15684       RaiseScore(level.score[SC_DYNAMITE]);
15685       break;
15686     case EL_SHIELD_NORMAL:
15687     case EL_SHIELD_DEADLY:
15688       RaiseScore(level.score[SC_SHIELD]);
15689       break;
15690     case EL_EXTRA_TIME:
15691       RaiseScore(level.extra_time_score);
15692       break;
15693     case EL_KEY_1:
15694     case EL_KEY_2:
15695     case EL_KEY_3:
15696     case EL_KEY_4:
15697     case EL_EM_KEY_1:
15698     case EL_EM_KEY_2:
15699     case EL_EM_KEY_3:
15700     case EL_EM_KEY_4:
15701     case EL_EMC_KEY_5:
15702     case EL_EMC_KEY_6:
15703     case EL_EMC_KEY_7:
15704     case EL_EMC_KEY_8:
15705     case EL_DC_KEY_WHITE:
15706       RaiseScore(level.score[SC_KEY]);
15707       break;
15708     default:
15709       RaiseScore(element_info[element].collect_score);
15710       break;
15711   }
15712 }
15713
15714 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15715 {
15716   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15717   {
15718 #if defined(NETWORK_AVALIABLE)
15719     if (options.network)
15720       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15721     else
15722 #endif
15723     {
15724       if (quick_quit)
15725       {
15726 #if 1
15727
15728 #if 1
15729         FadeSkipNextFadeIn();
15730 #else
15731         fading = fading_none;
15732 #endif
15733
15734 #else
15735         OpenDoor(DOOR_CLOSE_1);
15736 #endif
15737
15738         game_status = GAME_MODE_MAIN;
15739
15740 #if 1
15741         DrawAndFadeInMainMenu(REDRAW_FIELD);
15742 #else
15743         DrawMainMenu();
15744 #endif
15745       }
15746       else
15747       {
15748 #if 0
15749         FadeOut(REDRAW_FIELD);
15750 #endif
15751
15752         game_status = GAME_MODE_MAIN;
15753
15754         DrawAndFadeInMainMenu(REDRAW_FIELD);
15755       }
15756     }
15757   }
15758   else          /* continue playing the game */
15759   {
15760     if (tape.playing && tape.deactivate_display)
15761       TapeDeactivateDisplayOff(TRUE);
15762
15763     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15764
15765     if (tape.playing && tape.deactivate_display)
15766       TapeDeactivateDisplayOn();
15767   }
15768 }
15769
15770 void RequestQuitGame(boolean ask_if_really_quit)
15771 {
15772   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15773   boolean skip_request = AllPlayersGone || quick_quit;
15774
15775   RequestQuitGameExt(skip_request, quick_quit,
15776                      "Do you really want to quit the game ?");
15777 }
15778
15779
15780 /* ------------------------------------------------------------------------- */
15781 /* random generator functions                                                */
15782 /* ------------------------------------------------------------------------- */
15783
15784 unsigned int InitEngineRandom_RND(long seed)
15785 {
15786   game.num_random_calls = 0;
15787
15788 #if 0
15789   unsigned int rnd_seed = InitEngineRandom(seed);
15790
15791   printf("::: START RND: %d\n", rnd_seed);
15792
15793   return rnd_seed;
15794 #else
15795
15796   return InitEngineRandom(seed);
15797
15798 #endif
15799
15800 }
15801
15802 unsigned int RND(int max)
15803 {
15804   if (max > 0)
15805   {
15806     game.num_random_calls++;
15807
15808     return GetEngineRandom(max);
15809   }
15810
15811   return 0;
15812 }
15813
15814
15815 /* ------------------------------------------------------------------------- */
15816 /* game engine snapshot handling functions                                   */
15817 /* ------------------------------------------------------------------------- */
15818
15819 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15820
15821 struct EngineSnapshotInfo
15822 {
15823   /* runtime values for custom element collect score */
15824   int collect_score[NUM_CUSTOM_ELEMENTS];
15825
15826   /* runtime values for group element choice position */
15827   int choice_pos[NUM_GROUP_ELEMENTS];
15828
15829   /* runtime values for belt position animations */
15830   int belt_graphic[4 * NUM_BELT_PARTS];
15831   int belt_anim_mode[4 * NUM_BELT_PARTS];
15832 };
15833
15834 struct EngineSnapshotNodeInfo
15835 {
15836   void *buffer_orig;
15837   void *buffer_copy;
15838   int size;
15839 };
15840
15841 static struct EngineSnapshotInfo engine_snapshot_rnd;
15842 static ListNode *engine_snapshot_list = NULL;
15843 static char *snapshot_level_identifier = NULL;
15844 static int snapshot_level_nr = -1;
15845
15846 void FreeEngineSnapshot()
15847 {
15848   while (engine_snapshot_list != NULL)
15849     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15850                        checked_free);
15851
15852   setString(&snapshot_level_identifier, NULL);
15853   snapshot_level_nr = -1;
15854 }
15855
15856 static void SaveEngineSnapshotValues_RND()
15857 {
15858   static int belt_base_active_element[4] =
15859   {
15860     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15861     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15862     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15863     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15864   };
15865   int i, j;
15866
15867   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15868   {
15869     int element = EL_CUSTOM_START + i;
15870
15871     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15872   }
15873
15874   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15875   {
15876     int element = EL_GROUP_START + i;
15877
15878     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15879   }
15880
15881   for (i = 0; i < 4; i++)
15882   {
15883     for (j = 0; j < NUM_BELT_PARTS; j++)
15884     {
15885       int element = belt_base_active_element[i] + j;
15886       int graphic = el2img(element);
15887       int anim_mode = graphic_info[graphic].anim_mode;
15888
15889       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15890       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15891     }
15892   }
15893 }
15894
15895 static void LoadEngineSnapshotValues_RND()
15896 {
15897   unsigned long num_random_calls = game.num_random_calls;
15898   int i, j;
15899
15900   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15901   {
15902     int element = EL_CUSTOM_START + i;
15903
15904     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15905   }
15906
15907   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15908   {
15909     int element = EL_GROUP_START + i;
15910
15911     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15912   }
15913
15914   for (i = 0; i < 4; i++)
15915   {
15916     for (j = 0; j < NUM_BELT_PARTS; j++)
15917     {
15918       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15919       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15920
15921       graphic_info[graphic].anim_mode = anim_mode;
15922     }
15923   }
15924
15925   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15926   {
15927     InitRND(tape.random_seed);
15928     for (i = 0; i < num_random_calls; i++)
15929       RND(1);
15930   }
15931
15932   if (game.num_random_calls != num_random_calls)
15933   {
15934     Error(ERR_INFO, "number of random calls out of sync");
15935     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15936     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15937     Error(ERR_EXIT, "this should not happen -- please debug");
15938   }
15939 }
15940
15941 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15942 {
15943   struct EngineSnapshotNodeInfo *bi =
15944     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15945
15946   bi->buffer_orig = buffer;
15947   bi->buffer_copy = checked_malloc(size);
15948   bi->size = size;
15949
15950   memcpy(bi->buffer_copy, buffer, size);
15951
15952   addNodeToList(&engine_snapshot_list, NULL, bi);
15953 }
15954
15955 void SaveEngineSnapshot()
15956 {
15957   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15958
15959   if (level_editor_test_game)   /* do not save snapshots from editor */
15960     return;
15961
15962   /* copy some special values to a structure better suited for the snapshot */
15963
15964   SaveEngineSnapshotValues_RND();
15965   SaveEngineSnapshotValues_EM();
15966
15967   /* save values stored in special snapshot structure */
15968
15969   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15970   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15971
15972   /* save further RND engine values */
15973
15974   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15975   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15976   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15977
15978   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15979   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15980   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15981   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15982
15983   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15984   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15985   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15986   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15987   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15988
15989   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15990   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15991   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15992
15993   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15994
15995   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15996
15997   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15998   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15999
16000   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16001   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16002   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16003   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16004   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16005   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16006   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16007   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16008   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16009   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16010   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16011   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16012   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16013   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16014   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16015   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16016   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16017   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16018
16019   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16020   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16021
16022   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16023   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16024   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16025
16026   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16027   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16028
16029   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16030   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16031   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16032   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16033   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16034
16035   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16036   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16037
16038   /* save level identification information */
16039
16040   setString(&snapshot_level_identifier, leveldir_current->identifier);
16041   snapshot_level_nr = level_nr;
16042
16043 #if 0
16044   ListNode *node = engine_snapshot_list;
16045   int num_bytes = 0;
16046
16047   while (node != NULL)
16048   {
16049     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16050
16051     node = node->next;
16052   }
16053
16054   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16055 #endif
16056 }
16057
16058 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16059 {
16060   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16061 }
16062
16063 void LoadEngineSnapshot()
16064 {
16065   ListNode *node = engine_snapshot_list;
16066
16067   if (engine_snapshot_list == NULL)
16068     return;
16069
16070   while (node != NULL)
16071   {
16072     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16073
16074     node = node->next;
16075   }
16076
16077   /* restore special values from snapshot structure */
16078
16079   LoadEngineSnapshotValues_RND();
16080   LoadEngineSnapshotValues_EM();
16081 }
16082
16083 boolean CheckEngineSnapshot()
16084 {
16085   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16086           snapshot_level_nr == level_nr);
16087 }
16088
16089
16090 /* ---------- new game button stuff ---------------------------------------- */
16091
16092 /* graphic position values for game buttons */
16093 #define GAME_BUTTON_XSIZE       30
16094 #define GAME_BUTTON_YSIZE       30
16095 #define GAME_BUTTON_XPOS        5
16096 #define GAME_BUTTON_YPOS        215
16097 #define SOUND_BUTTON_XPOS       5
16098 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16099
16100 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16101 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16102 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16103 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16104 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16105 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16106
16107 static struct
16108 {
16109   int *x, *y;
16110   int gd_x, gd_y;
16111   int gadget_id;
16112   char *infotext;
16113 } gamebutton_info[NUM_GAME_BUTTONS] =
16114 {
16115 #if 1
16116   {
16117     &game.button.stop.x,        &game.button.stop.y,
16118     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16119     GAME_CTRL_ID_STOP,
16120     "stop game"
16121   },
16122   {
16123     &game.button.pause.x,       &game.button.pause.y,
16124     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16125     GAME_CTRL_ID_PAUSE,
16126     "pause game"
16127   },
16128   {
16129     &game.button.play.x,        &game.button.play.y,
16130     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16131     GAME_CTRL_ID_PLAY,
16132     "play game"
16133   },
16134   {
16135     &game.button.sound_music.x, &game.button.sound_music.y,
16136     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16137     SOUND_CTRL_ID_MUSIC,
16138     "background music on/off"
16139   },
16140   {
16141     &game.button.sound_loops.x, &game.button.sound_loops.y,
16142     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16143     SOUND_CTRL_ID_LOOPS,
16144     "sound loops on/off"
16145   },
16146   {
16147     &game.button.sound_simple.x,&game.button.sound_simple.y,
16148     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16149     SOUND_CTRL_ID_SIMPLE,
16150     "normal sounds on/off"
16151   }
16152 #else
16153   {
16154     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16155     GAME_CTRL_ID_STOP,
16156     "stop game"
16157   },
16158   {
16159     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16160     GAME_CTRL_ID_PAUSE,
16161     "pause game"
16162   },
16163   {
16164     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16165     GAME_CTRL_ID_PLAY,
16166     "play game"
16167   },
16168   {
16169     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16170     SOUND_CTRL_ID_MUSIC,
16171     "background music on/off"
16172   },
16173   {
16174     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16175     SOUND_CTRL_ID_LOOPS,
16176     "sound loops on/off"
16177   },
16178   {
16179     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16180     SOUND_CTRL_ID_SIMPLE,
16181     "normal sounds on/off"
16182   }
16183 #endif
16184 };
16185
16186 void CreateGameButtons()
16187 {
16188   int i;
16189
16190   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16191   {
16192     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16193     struct GadgetInfo *gi;
16194     int button_type;
16195     boolean checked;
16196     unsigned long event_mask;
16197     int x, y;
16198     int gd_xoffset, gd_yoffset;
16199     int gd_x1, gd_x2, gd_y1, gd_y2;
16200     int id = i;
16201
16202     x = DX + *gamebutton_info[i].x;
16203     y = DY + *gamebutton_info[i].y;
16204     gd_xoffset = gamebutton_info[i].gd_x;
16205     gd_yoffset = gamebutton_info[i].gd_y;
16206     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16207     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16208
16209     if (id == GAME_CTRL_ID_STOP ||
16210         id == GAME_CTRL_ID_PAUSE ||
16211         id == GAME_CTRL_ID_PLAY)
16212     {
16213       button_type = GD_TYPE_NORMAL_BUTTON;
16214       checked = FALSE;
16215       event_mask = GD_EVENT_RELEASED;
16216       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16217       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16218     }
16219     else
16220     {
16221       button_type = GD_TYPE_CHECK_BUTTON;
16222       checked =
16223         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16224          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16225          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16226       event_mask = GD_EVENT_PRESSED;
16227       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16228       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16229     }
16230
16231     gi = CreateGadget(GDI_CUSTOM_ID, id,
16232                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16233 #if 1
16234                       GDI_X, x,
16235                       GDI_Y, y,
16236 #else
16237                       GDI_X, DX + gd_xoffset,
16238                       GDI_Y, DY + gd_yoffset,
16239 #endif
16240                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16241                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16242                       GDI_TYPE, button_type,
16243                       GDI_STATE, GD_BUTTON_UNPRESSED,
16244                       GDI_CHECKED, checked,
16245                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16246                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16247                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16248                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16249                       GDI_DIRECT_DRAW, FALSE,
16250                       GDI_EVENT_MASK, event_mask,
16251                       GDI_CALLBACK_ACTION, HandleGameButtons,
16252                       GDI_END);
16253
16254     if (gi == NULL)
16255       Error(ERR_EXIT, "cannot create gadget");
16256
16257     game_gadget[id] = gi;
16258   }
16259 }
16260
16261 void FreeGameButtons()
16262 {
16263   int i;
16264
16265   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16266     FreeGadget(game_gadget[i]);
16267 }
16268
16269 static void MapGameButtons()
16270 {
16271   int i;
16272
16273   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16274     MapGadget(game_gadget[i]);
16275 }
16276
16277 void UnmapGameButtons()
16278 {
16279   int i;
16280
16281   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16282     UnmapGadget(game_gadget[i]);
16283 }
16284
16285 void RedrawGameButtons()
16286 {
16287   int i;
16288
16289   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16290     RedrawGadget(game_gadget[i]);
16291 }
16292
16293 static void HandleGameButtons(struct GadgetInfo *gi)
16294 {
16295   int id = gi->custom_id;
16296
16297   if (game_status != GAME_MODE_PLAYING)
16298     return;
16299
16300   switch (id)
16301   {
16302     case GAME_CTRL_ID_STOP:
16303       if (tape.playing)
16304         TapeStop();
16305       else
16306         RequestQuitGame(TRUE);
16307       break;
16308
16309     case GAME_CTRL_ID_PAUSE:
16310       if (options.network)
16311       {
16312 #if defined(NETWORK_AVALIABLE)
16313         if (tape.pausing)
16314           SendToServer_ContinuePlaying();
16315         else
16316           SendToServer_PausePlaying();
16317 #endif
16318       }
16319       else
16320         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16321       break;
16322
16323     case GAME_CTRL_ID_PLAY:
16324       if (tape.pausing)
16325       {
16326 #if defined(NETWORK_AVALIABLE)
16327         if (options.network)
16328           SendToServer_ContinuePlaying();
16329         else
16330 #endif
16331         {
16332           tape.pausing = FALSE;
16333           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16334         }
16335       }
16336       break;
16337
16338     case SOUND_CTRL_ID_MUSIC:
16339       if (setup.sound_music)
16340       { 
16341         setup.sound_music = FALSE;
16342         FadeMusic();
16343       }
16344       else if (audio.music_available)
16345       { 
16346         setup.sound = setup.sound_music = TRUE;
16347
16348         SetAudioMode(setup.sound);
16349
16350         PlayLevelMusic();
16351       }
16352       break;
16353
16354     case SOUND_CTRL_ID_LOOPS:
16355       if (setup.sound_loops)
16356         setup.sound_loops = FALSE;
16357       else if (audio.loops_available)
16358       {
16359         setup.sound = setup.sound_loops = TRUE;
16360         SetAudioMode(setup.sound);
16361       }
16362       break;
16363
16364     case SOUND_CTRL_ID_SIMPLE:
16365       if (setup.sound_simple)
16366         setup.sound_simple = FALSE;
16367       else if (audio.sound_available)
16368       {
16369         setup.sound = setup.sound_simple = TRUE;
16370         SetAudioMode(setup.sound);
16371       }
16372       break;
16373
16374     default:
16375       break;
16376   }
16377 }