rnd-20080204-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
63
64 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
65
66 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
67
68 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
69
70 #if USE_DELAYED_GFX_REDRAW
71 #define TEST_DrawLevelField(x, y)                               \
72         GfxRedraw[x][y] |= GFX_REDRAW_TILE
73 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
75 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
77 #define TEST_DrawTwinkleOnField(x, y)                           \
78         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
79 #else
80 #define TEST_DrawLevelField(x, y)                               \
81              DrawLevelField(x, y)
82 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
83              DrawLevelFieldCrumbledSand(x, y)
84 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
85              DrawLevelFieldCrumbledSandNeighbours(x, y)
86 #define TEST_DrawTwinkleOnField(x, y)                           \
87              DrawTwinkleOnField(x, y)
88 #endif
89
90
91 /* for DigField() */
92 #define DF_NO_PUSH              0
93 #define DF_DIG                  1
94 #define DF_SNAP                 2
95
96 /* for MovePlayer() */
97 #define MP_NO_ACTION            0
98 #define MP_MOVING               1
99 #define MP_ACTION               2
100 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
101
102 /* for ScrollPlayer() */
103 #define SCROLL_INIT             0
104 #define SCROLL_GO_ON            1
105
106 /* for Bang()/Explode() */
107 #define EX_PHASE_START          0
108 #define EX_TYPE_NONE            0
109 #define EX_TYPE_NORMAL          (1 << 0)
110 #define EX_TYPE_CENTER          (1 << 1)
111 #define EX_TYPE_BORDER          (1 << 2)
112 #define EX_TYPE_CROSS           (1 << 3)
113 #define EX_TYPE_DYNA            (1 << 4)
114 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
115
116 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
117 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
118 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
119 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
120
121 /* special positions in the game control window (relative to control window) */
122 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
123 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
124 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
125 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
126 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
127 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
128 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
129 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
130 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
131 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
132 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
133 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
134 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
135 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
136 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
137 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
138
139 /* special positions in the game control window (relative to main window) */
140 #define DX_LEVEL1               (DX + XX_LEVEL1)
141 #define DX_LEVEL2               (DX + XX_LEVEL2)
142 #define DX_LEVEL                (DX + XX_LEVEL)
143 #define DY_LEVEL                (DY + YY_LEVEL)
144 #define DX_EMERALDS             (DX + XX_EMERALDS)
145 #define DY_EMERALDS             (DY + YY_EMERALDS)
146 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
147 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
148 #define DX_KEYS                 (DX + XX_KEYS)
149 #define DY_KEYS                 (DY + YY_KEYS)
150 #define DX_SCORE                (DX + XX_SCORE)
151 #define DY_SCORE                (DY + YY_SCORE)
152 #define DX_TIME1                (DX + XX_TIME1)
153 #define DX_TIME2                (DX + XX_TIME2)
154 #define DX_TIME                 (DX + XX_TIME)
155 #define DY_TIME                 (DY + YY_TIME)
156
157 #if 1
158 /* game panel display and control definitions */
159
160 #define GAME_PANEL_LEVEL_NUMBER                 0
161 #define GAME_PANEL_GEMS                         1
162 #define GAME_PANEL_INVENTORY_COUNT              2
163 #define GAME_PANEL_INVENTORY_FIRST_1            3
164 #define GAME_PANEL_INVENTORY_FIRST_2            4
165 #define GAME_PANEL_INVENTORY_FIRST_3            5
166 #define GAME_PANEL_INVENTORY_FIRST_4            6
167 #define GAME_PANEL_INVENTORY_FIRST_5            7
168 #define GAME_PANEL_INVENTORY_FIRST_6            8
169 #define GAME_PANEL_INVENTORY_FIRST_7            9
170 #define GAME_PANEL_INVENTORY_FIRST_8            10
171 #define GAME_PANEL_INVENTORY_LAST_1             11
172 #define GAME_PANEL_INVENTORY_LAST_2             12
173 #define GAME_PANEL_INVENTORY_LAST_3             13
174 #define GAME_PANEL_INVENTORY_LAST_4             14
175 #define GAME_PANEL_INVENTORY_LAST_5             15
176 #define GAME_PANEL_INVENTORY_LAST_6             16
177 #define GAME_PANEL_INVENTORY_LAST_7             17
178 #define GAME_PANEL_INVENTORY_LAST_8             18
179 #define GAME_PANEL_KEY_1                        19
180 #define GAME_PANEL_KEY_2                        20
181 #define GAME_PANEL_KEY_3                        21
182 #define GAME_PANEL_KEY_4                        22
183 #define GAME_PANEL_KEY_5                        23
184 #define GAME_PANEL_KEY_6                        24
185 #define GAME_PANEL_KEY_7                        25
186 #define GAME_PANEL_KEY_8                        26
187 #define GAME_PANEL_KEY_WHITE                    27
188 #define GAME_PANEL_KEY_WHITE_COUNT              28
189 #define GAME_PANEL_SCORE                        29
190 #define GAME_PANEL_HIGHSCORE                    30
191 #define GAME_PANEL_TIME                         31
192 #define GAME_PANEL_TIME_HH                      32
193 #define GAME_PANEL_TIME_MM                      33
194 #define GAME_PANEL_TIME_SS                      34
195 #define GAME_PANEL_SHIELD_NORMAL                35
196 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
197 #define GAME_PANEL_SHIELD_DEADLY                37
198 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
199 #define GAME_PANEL_EXIT                         39
200 #define GAME_PANEL_EMC_MAGIC_BALL               40
201 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
202 #define GAME_PANEL_LIGHT_SWITCH                 42
203 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
204 #define GAME_PANEL_TIMEGATE_SWITCH              44
205 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
206 #define GAME_PANEL_SWITCHGATE_SWITCH            46
207 #define GAME_PANEL_EMC_LENSES                   47
208 #define GAME_PANEL_EMC_LENSES_TIME              48
209 #define GAME_PANEL_EMC_MAGNIFIER                49
210 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
211 #define GAME_PANEL_BALLOON_SWITCH               51
212 #define GAME_PANEL_DYNABOMB_NUMBER              52
213 #define GAME_PANEL_DYNABOMB_SIZE                53
214 #define GAME_PANEL_DYNABOMB_POWER               54
215 #define GAME_PANEL_PENGUINS                     55
216 #define GAME_PANEL_SOKOBAN_OBJECTS              56
217 #define GAME_PANEL_SOKOBAN_FIELDS               57
218 #define GAME_PANEL_ROBOT_WHEEL                  58
219 #define GAME_PANEL_CONVEYOR_BELT_1              59
220 #define GAME_PANEL_CONVEYOR_BELT_2              60
221 #define GAME_PANEL_CONVEYOR_BELT_3              61
222 #define GAME_PANEL_CONVEYOR_BELT_4              62
223 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
224 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
225 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
226 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
227 #define GAME_PANEL_MAGIC_WALL                   67
228 #define GAME_PANEL_MAGIC_WALL_TIME              68
229 #define GAME_PANEL_GRAVITY_STATE                69
230 #define GAME_PANEL_GRAPHIC_1                    70
231 #define GAME_PANEL_GRAPHIC_2                    71
232 #define GAME_PANEL_GRAPHIC_3                    72
233 #define GAME_PANEL_GRAPHIC_4                    73
234 #define GAME_PANEL_GRAPHIC_5                    74
235 #define GAME_PANEL_GRAPHIC_6                    75
236 #define GAME_PANEL_GRAPHIC_7                    76
237 #define GAME_PANEL_GRAPHIC_8                    77
238 #define GAME_PANEL_ELEMENT_1                    78
239 #define GAME_PANEL_ELEMENT_2                    79
240 #define GAME_PANEL_ELEMENT_3                    80
241 #define GAME_PANEL_ELEMENT_4                    81
242 #define GAME_PANEL_ELEMENT_5                    82
243 #define GAME_PANEL_ELEMENT_6                    83
244 #define GAME_PANEL_ELEMENT_7                    84
245 #define GAME_PANEL_ELEMENT_8                    85
246 #define GAME_PANEL_ELEMENT_COUNT_1              86
247 #define GAME_PANEL_ELEMENT_COUNT_2              87
248 #define GAME_PANEL_ELEMENT_COUNT_3              88
249 #define GAME_PANEL_ELEMENT_COUNT_4              89
250 #define GAME_PANEL_ELEMENT_COUNT_5              90
251 #define GAME_PANEL_ELEMENT_COUNT_6              91
252 #define GAME_PANEL_ELEMENT_COUNT_7              92
253 #define GAME_PANEL_ELEMENT_COUNT_8              93
254 #define GAME_PANEL_CE_SCORE_1                   94
255 #define GAME_PANEL_CE_SCORE_2                   95
256 #define GAME_PANEL_CE_SCORE_3                   96
257 #define GAME_PANEL_CE_SCORE_4                   97
258 #define GAME_PANEL_CE_SCORE_5                   98
259 #define GAME_PANEL_CE_SCORE_6                   99
260 #define GAME_PANEL_CE_SCORE_7                   100
261 #define GAME_PANEL_CE_SCORE_8                   101
262 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
263 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
264 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
265 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
266 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
267 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
268 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
269 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
270 #define GAME_PANEL_PLAYER_NAME                  110
271 #define GAME_PANEL_LEVEL_NAME                   111
272 #define GAME_PANEL_LEVEL_AUTHOR                 112
273
274 #define NUM_GAME_PANEL_CONTROLS                 113
275
276 struct GamePanelOrderInfo
277 {
278   int nr;
279   int sort_priority;
280 };
281
282 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
283
284 struct GamePanelControlInfo
285 {
286   int nr;
287
288   struct TextPosInfo *pos;
289   int type;
290
291   int value, last_value;
292   int frame, last_frame;
293   int gfx_frame;
294   int gfx_random;
295 };
296
297 static struct GamePanelControlInfo game_panel_controls[] =
298 {
299   {
300     GAME_PANEL_LEVEL_NUMBER,
301     &game.panel.level_number,
302     TYPE_INTEGER,
303   },
304   {
305     GAME_PANEL_GEMS,
306     &game.panel.gems,
307     TYPE_INTEGER,
308   },
309   {
310     GAME_PANEL_INVENTORY_COUNT,
311     &game.panel.inventory_count,
312     TYPE_INTEGER,
313   },
314   {
315     GAME_PANEL_INVENTORY_FIRST_1,
316     &game.panel.inventory_first[0],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_FIRST_2,
321     &game.panel.inventory_first[1],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_INVENTORY_FIRST_3,
326     &game.panel.inventory_first[2],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_INVENTORY_FIRST_4,
331     &game.panel.inventory_first[3],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_INVENTORY_FIRST_5,
336     &game.panel.inventory_first[4],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_INVENTORY_FIRST_6,
341     &game.panel.inventory_first[5],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_INVENTORY_FIRST_7,
346     &game.panel.inventory_first[6],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_INVENTORY_FIRST_8,
351     &game.panel.inventory_first[7],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_INVENTORY_LAST_1,
356     &game.panel.inventory_last[0],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_INVENTORY_LAST_2,
361     &game.panel.inventory_last[1],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_INVENTORY_LAST_3,
366     &game.panel.inventory_last[2],
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_INVENTORY_LAST_4,
371     &game.panel.inventory_last[3],
372     TYPE_ELEMENT,
373   },
374   {
375     GAME_PANEL_INVENTORY_LAST_5,
376     &game.panel.inventory_last[4],
377     TYPE_ELEMENT,
378   },
379   {
380     GAME_PANEL_INVENTORY_LAST_6,
381     &game.panel.inventory_last[5],
382     TYPE_ELEMENT,
383   },
384   {
385     GAME_PANEL_INVENTORY_LAST_7,
386     &game.panel.inventory_last[6],
387     TYPE_ELEMENT,
388   },
389   {
390     GAME_PANEL_INVENTORY_LAST_8,
391     &game.panel.inventory_last[7],
392     TYPE_ELEMENT,
393   },
394   {
395     GAME_PANEL_KEY_1,
396     &game.panel.key[0],
397     TYPE_ELEMENT,
398   },
399   {
400     GAME_PANEL_KEY_2,
401     &game.panel.key[1],
402     TYPE_ELEMENT,
403   },
404   {
405     GAME_PANEL_KEY_3,
406     &game.panel.key[2],
407     TYPE_ELEMENT,
408   },
409   {
410     GAME_PANEL_KEY_4,
411     &game.panel.key[3],
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_KEY_5,
416     &game.panel.key[4],
417     TYPE_ELEMENT,
418   },
419   {
420     GAME_PANEL_KEY_6,
421     &game.panel.key[5],
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_KEY_7,
426     &game.panel.key[6],
427     TYPE_ELEMENT,
428   },
429   {
430     GAME_PANEL_KEY_8,
431     &game.panel.key[7],
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_KEY_WHITE,
436     &game.panel.key_white,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_KEY_WHITE_COUNT,
441     &game.panel.key_white_count,
442     TYPE_INTEGER,
443   },
444   {
445     GAME_PANEL_SCORE,
446     &game.panel.score,
447     TYPE_INTEGER,
448   },
449   {
450     GAME_PANEL_HIGHSCORE,
451     &game.panel.highscore,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIME,
456     &game.panel.time,
457     TYPE_INTEGER,
458   },
459   {
460     GAME_PANEL_TIME_HH,
461     &game.panel.time_hh,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_TIME_MM,
466     &game.panel.time_mm,
467     TYPE_INTEGER,
468   },
469   {
470     GAME_PANEL_TIME_SS,
471     &game.panel.time_ss,
472     TYPE_INTEGER,
473   },
474   {
475     GAME_PANEL_SHIELD_NORMAL,
476     &game.panel.shield_normal,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_SHIELD_NORMAL_TIME,
481     &game.panel.shield_normal_time,
482     TYPE_INTEGER,
483   },
484   {
485     GAME_PANEL_SHIELD_DEADLY,
486     &game.panel.shield_deadly,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_PANEL_SHIELD_DEADLY_TIME,
491     &game.panel.shield_deadly_time,
492     TYPE_INTEGER,
493   },
494   {
495     GAME_PANEL_EXIT,
496     &game.panel.exit,
497     TYPE_ELEMENT,
498   },
499   {
500     GAME_PANEL_EMC_MAGIC_BALL,
501     &game.panel.emc_magic_ball,
502     TYPE_ELEMENT,
503   },
504   {
505     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
506     &game.panel.emc_magic_ball_switch,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_LIGHT_SWITCH,
511     &game.panel.light_switch,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_PANEL_LIGHT_SWITCH_TIME,
516     &game.panel.light_switch_time,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_TIMEGATE_SWITCH,
521     &game.panel.timegate_switch,
522     TYPE_ELEMENT,
523   },
524   {
525     GAME_PANEL_TIMEGATE_SWITCH_TIME,
526     &game.panel.timegate_switch_time,
527     TYPE_INTEGER,
528   },
529   {
530     GAME_PANEL_SWITCHGATE_SWITCH,
531     &game.panel.switchgate_switch,
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_EMC_LENSES,
536     &game.panel.emc_lenses,
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_EMC_LENSES_TIME,
541     &game.panel.emc_lenses_time,
542     TYPE_INTEGER,
543   },
544   {
545     GAME_PANEL_EMC_MAGNIFIER,
546     &game.panel.emc_magnifier,
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_EMC_MAGNIFIER_TIME,
551     &game.panel.emc_magnifier_time,
552     TYPE_INTEGER,
553   },
554   {
555     GAME_PANEL_BALLOON_SWITCH,
556     &game.panel.balloon_switch,
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_DYNABOMB_NUMBER,
561     &game.panel.dynabomb_number,
562     TYPE_INTEGER,
563   },
564   {
565     GAME_PANEL_DYNABOMB_SIZE,
566     &game.panel.dynabomb_size,
567     TYPE_INTEGER,
568   },
569   {
570     GAME_PANEL_DYNABOMB_POWER,
571     &game.panel.dynabomb_power,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_PENGUINS,
576     &game.panel.penguins,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_SOKOBAN_OBJECTS,
581     &game.panel.sokoban_objects,
582     TYPE_INTEGER,
583   },
584   {
585     GAME_PANEL_SOKOBAN_FIELDS,
586     &game.panel.sokoban_fields,
587     TYPE_INTEGER,
588   },
589   {
590     GAME_PANEL_ROBOT_WHEEL,
591     &game.panel.robot_wheel,
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_1,
596     &game.panel.conveyor_belt[0],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_2,
601     &game.panel.conveyor_belt[1],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_CONVEYOR_BELT_3,
606     &game.panel.conveyor_belt[2],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_CONVEYOR_BELT_4,
611     &game.panel.conveyor_belt[3],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
616     &game.panel.conveyor_belt_switch[0],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
621     &game.panel.conveyor_belt_switch[1],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
626     &game.panel.conveyor_belt_switch[2],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
631     &game.panel.conveyor_belt_switch[3],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_MAGIC_WALL,
636     &game.panel.magic_wall,
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_MAGIC_WALL_TIME,
641     &game.panel.magic_wall_time,
642     TYPE_INTEGER,
643   },
644   {
645     GAME_PANEL_GRAVITY_STATE,
646     &game.panel.gravity_state,
647     TYPE_STRING,
648   },
649   {
650     GAME_PANEL_GRAPHIC_1,
651     &game.panel.graphic[0],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_2,
656     &game.panel.graphic[1],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_GRAPHIC_3,
661     &game.panel.graphic[2],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_GRAPHIC_4,
666     &game.panel.graphic[3],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_GRAPHIC_5,
671     &game.panel.graphic[4],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_GRAPHIC_6,
676     &game.panel.graphic[5],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_GRAPHIC_7,
681     &game.panel.graphic[6],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_GRAPHIC_8,
686     &game.panel.graphic[7],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_1,
691     &game.panel.element[0],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_2,
696     &game.panel.element[1],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_3,
701     &game.panel.element[2],
702     TYPE_ELEMENT,
703   },
704   {
705     GAME_PANEL_ELEMENT_4,
706     &game.panel.element[3],
707     TYPE_ELEMENT,
708   },
709   {
710     GAME_PANEL_ELEMENT_5,
711     &game.panel.element[4],
712     TYPE_ELEMENT,
713   },
714   {
715     GAME_PANEL_ELEMENT_6,
716     &game.panel.element[5],
717     TYPE_ELEMENT,
718   },
719   {
720     GAME_PANEL_ELEMENT_7,
721     &game.panel.element[6],
722     TYPE_ELEMENT,
723   },
724   {
725     GAME_PANEL_ELEMENT_8,
726     &game.panel.element[7],
727     TYPE_ELEMENT,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_1,
731     &game.panel.element_count[0],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_2,
736     &game.panel.element_count[1],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_ELEMENT_COUNT_3,
741     &game.panel.element_count[2],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_ELEMENT_COUNT_4,
746     &game.panel.element_count[3],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_ELEMENT_COUNT_5,
751     &game.panel.element_count[4],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_ELEMENT_COUNT_6,
756     &game.panel.element_count[5],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_ELEMENT_COUNT_7,
761     &game.panel.element_count[6],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_ELEMENT_COUNT_8,
766     &game.panel.element_count[7],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_1,
771     &game.panel.ce_score[0],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_2,
776     &game.panel.ce_score[1],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_3,
781     &game.panel.ce_score[2],
782     TYPE_INTEGER,
783   },
784   {
785     GAME_PANEL_CE_SCORE_4,
786     &game.panel.ce_score[3],
787     TYPE_INTEGER,
788   },
789   {
790     GAME_PANEL_CE_SCORE_5,
791     &game.panel.ce_score[4],
792     TYPE_INTEGER,
793   },
794   {
795     GAME_PANEL_CE_SCORE_6,
796     &game.panel.ce_score[5],
797     TYPE_INTEGER,
798   },
799   {
800     GAME_PANEL_CE_SCORE_7,
801     &game.panel.ce_score[6],
802     TYPE_INTEGER,
803   },
804   {
805     GAME_PANEL_CE_SCORE_8,
806     &game.panel.ce_score[7],
807     TYPE_INTEGER,
808   },
809   {
810     GAME_PANEL_CE_SCORE_1_ELEMENT,
811     &game.panel.ce_score_element[0],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_2_ELEMENT,
816     &game.panel.ce_score_element[1],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_CE_SCORE_3_ELEMENT,
821     &game.panel.ce_score_element[2],
822     TYPE_ELEMENT,
823   },
824   {
825     GAME_PANEL_CE_SCORE_4_ELEMENT,
826     &game.panel.ce_score_element[3],
827     TYPE_ELEMENT,
828   },
829   {
830     GAME_PANEL_CE_SCORE_5_ELEMENT,
831     &game.panel.ce_score_element[4],
832     TYPE_ELEMENT,
833   },
834   {
835     GAME_PANEL_CE_SCORE_6_ELEMENT,
836     &game.panel.ce_score_element[5],
837     TYPE_ELEMENT,
838   },
839   {
840     GAME_PANEL_CE_SCORE_7_ELEMENT,
841     &game.panel.ce_score_element[6],
842     TYPE_ELEMENT,
843   },
844   {
845     GAME_PANEL_CE_SCORE_8_ELEMENT,
846     &game.panel.ce_score_element[7],
847     TYPE_ELEMENT,
848   },
849   {
850     GAME_PANEL_PLAYER_NAME,
851     &game.panel.player_name,
852     TYPE_STRING,
853   },
854   {
855     GAME_PANEL_LEVEL_NAME,
856     &game.panel.level_name,
857     TYPE_STRING,
858   },
859   {
860     GAME_PANEL_LEVEL_AUTHOR,
861     &game.panel.level_author,
862     TYPE_STRING,
863   },
864
865   {
866     -1,
867     NULL,
868     -1,
869   }
870 };
871 #endif
872
873
874 /* values for delayed check of falling and moving elements and for collision */
875 #define CHECK_DELAY_MOVING      3
876 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
877 #define CHECK_DELAY_COLLISION   2
878 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
879
880 /* values for initial player move delay (initial delay counter value) */
881 #define INITIAL_MOVE_DELAY_OFF  -1
882 #define INITIAL_MOVE_DELAY_ON   0
883
884 /* values for player movement speed (which is in fact a delay value) */
885 #define MOVE_DELAY_MIN_SPEED    32
886 #define MOVE_DELAY_NORMAL_SPEED 8
887 #define MOVE_DELAY_HIGH_SPEED   4
888 #define MOVE_DELAY_MAX_SPEED    1
889
890 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
891 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
892
893 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
894 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
895
896 /* values for other actions */
897 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
898 #define MOVE_STEPSIZE_MIN       (1)
899 #define MOVE_STEPSIZE_MAX       (TILEX)
900
901 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
902 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
903
904 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
905
906 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
907                                  RND(element_info[e].push_delay_random))
908 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
909                                  RND(element_info[e].drop_delay_random))
910 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
911                                  RND(element_info[e].move_delay_random))
912 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
913                                     (element_info[e].move_delay_random))
914 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
915                                  RND(element_info[e].ce_value_random_initial))
916 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
917 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
918                                  RND((c)->delay_random * (c)->delay_frames))
919 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
920                                  RND((c)->delay_random))
921
922
923 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
924          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
925
926 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
927         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
928          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
929          (be) + (e) - EL_SELF)
930
931 #define GET_PLAYER_FROM_BITS(p)                                         \
932         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
933
934 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
935         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
936          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
937          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
938          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
939          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
940          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
941          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
942          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
943          (e))
944
945 #define CAN_GROW_INTO(e)                                                \
946         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
947
948 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
949                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
950                                         (condition)))
951
952 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
953                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
954                                         (CAN_MOVE_INTO_ACID(e) &&       \
955                                          Feld[x][y] == EL_ACID) ||      \
956                                         (condition)))
957
958 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
959                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
960                                         (CAN_MOVE_INTO_ACID(e) &&       \
961                                          Feld[x][y] == EL_ACID) ||      \
962                                         (condition)))
963
964 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
965                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
966                                         (condition) ||                  \
967                                         (CAN_MOVE_INTO_ACID(e) &&       \
968                                          Feld[x][y] == EL_ACID) ||      \
969                                         (DONT_COLLIDE_WITH(e) &&        \
970                                          IS_PLAYER(x, y) &&             \
971                                          !PLAYER_ENEMY_PROTECTED(x, y))))
972
973 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
975
976 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
977         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
978
979 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
981
982 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
983         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
984                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
985
986 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
987         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
988
989 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
990         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
991
992 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
993         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
994
995 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
996         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
997
998 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
999         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1000
1001 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1002         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1003                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1004                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1005                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1006                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1007 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1008         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1009
1010 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1011         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1012
1013 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1014         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1015
1016 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1017         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1018                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1019
1020 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1021
1022 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1023                 (!IS_PLAYER(x, y) &&                                    \
1024                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1025
1026 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1027         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1028
1029 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1030 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1031
1032 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1033 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1034 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1035 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1036
1037 /* game button identifiers */
1038 #define GAME_CTRL_ID_STOP               0
1039 #define GAME_CTRL_ID_PAUSE              1
1040 #define GAME_CTRL_ID_PLAY               2
1041 #define SOUND_CTRL_ID_MUSIC             3
1042 #define SOUND_CTRL_ID_LOOPS             4
1043 #define SOUND_CTRL_ID_SIMPLE            5
1044
1045 #define NUM_GAME_BUTTONS                6
1046
1047
1048 /* forward declaration for internal use */
1049
1050 static void CreateField(int, int, int);
1051
1052 static void ResetGfxAnimation(int, int);
1053
1054 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1055 static void AdvanceFrameAndPlayerCounters(int);
1056
1057 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1058 static boolean MovePlayer(struct PlayerInfo *, int, int);
1059 static void ScrollPlayer(struct PlayerInfo *, int);
1060 static void ScrollScreen(struct PlayerInfo *, int);
1061
1062 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1063 static boolean DigFieldByCE(int, int, int);
1064 static boolean SnapField(struct PlayerInfo *, int, int);
1065 static boolean DropElement(struct PlayerInfo *);
1066
1067 static void InitBeltMovement(void);
1068 static void CloseAllOpenTimegates(void);
1069 static void CheckGravityMovement(struct PlayerInfo *);
1070 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1071 static void KillPlayerUnlessEnemyProtected(int, int);
1072 static void KillPlayerUnlessExplosionProtected(int, int);
1073
1074 static void TestIfPlayerTouchesCustomElement(int, int);
1075 static void TestIfElementTouchesCustomElement(int, int);
1076 static void TestIfElementHitsCustomElement(int, int, int);
1077 #if 0
1078 static void TestIfElementSmashesCustomElement(int, int, int);
1079 #endif
1080
1081 static void HandleElementChange(int, int, int);
1082 static void ExecuteCustomElementAction(int, int, int, int);
1083 static boolean ChangeElement(int, int, int, int);
1084
1085 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1086 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1087         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1088 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1089         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1090 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1091         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1092 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1093         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1094
1095 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1096 #define CheckElementChange(x, y, e, te, ev)                             \
1097         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1098 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1099         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1100 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1101         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1102
1103 static void PlayLevelSound(int, int, int);
1104 static void PlayLevelSoundNearest(int, int, int);
1105 static void PlayLevelSoundAction(int, int, int);
1106 static void PlayLevelSoundElementAction(int, int, int, int);
1107 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1108 static void PlayLevelSoundActionIfLoop(int, int, int);
1109 static void StopLevelSoundActionIfLoop(int, int, int);
1110 static void PlayLevelMusic();
1111
1112 static void MapGameButtons();
1113 static void HandleGameButtons(struct GadgetInfo *);
1114
1115 int AmoebeNachbarNr(int, int);
1116 void AmoebeUmwandeln(int, int);
1117 void ContinueMoving(int, int);
1118 void Bang(int, int);
1119 void InitMovDir(int, int);
1120 void InitAmoebaNr(int, int);
1121 int NewHiScore(void);
1122
1123 void TestIfGoodThingHitsBadThing(int, int, int);
1124 void TestIfBadThingHitsGoodThing(int, int, int);
1125 void TestIfPlayerTouchesBadThing(int, int);
1126 void TestIfPlayerRunsIntoBadThing(int, int, int);
1127 void TestIfBadThingTouchesPlayer(int, int);
1128 void TestIfBadThingRunsIntoPlayer(int, int, int);
1129 void TestIfFriendTouchesBadThing(int, int);
1130 void TestIfBadThingTouchesFriend(int, int);
1131 void TestIfBadThingTouchesOtherBadThing(int, int);
1132 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1133
1134 void KillPlayer(struct PlayerInfo *);
1135 void BuryPlayer(struct PlayerInfo *);
1136 void RemovePlayer(struct PlayerInfo *);
1137
1138 static int getInvisibleActiveFromInvisibleElement(int);
1139 static int getInvisibleFromInvisibleActiveElement(int);
1140
1141 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1142
1143 /* for detection of endless loops, caused by custom element programming */
1144 /* (using maximal playfield width x 10 is just a rough approximation) */
1145 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1146
1147 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1148 {                                                                       \
1149   if (recursion_loop_detected)                                          \
1150     return (rc);                                                        \
1151                                                                         \
1152   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1153   {                                                                     \
1154     recursion_loop_detected = TRUE;                                     \
1155     recursion_loop_element = (e);                                       \
1156   }                                                                     \
1157                                                                         \
1158   recursion_loop_depth++;                                               \
1159 }
1160
1161 #define RECURSION_LOOP_DETECTION_END()                                  \
1162 {                                                                       \
1163   recursion_loop_depth--;                                               \
1164 }
1165
1166 static int recursion_loop_depth;
1167 static boolean recursion_loop_detected;
1168 static boolean recursion_loop_element;
1169
1170
1171 /* ------------------------------------------------------------------------- */
1172 /* definition of elements that automatically change to other elements after  */
1173 /* a specified time, eventually calling a function when changing             */
1174 /* ------------------------------------------------------------------------- */
1175
1176 /* forward declaration for changer functions */
1177 static void InitBuggyBase(int, int);
1178 static void WarnBuggyBase(int, int);
1179
1180 static void InitTrap(int, int);
1181 static void ActivateTrap(int, int);
1182 static void ChangeActiveTrap(int, int);
1183
1184 static void InitRobotWheel(int, int);
1185 static void RunRobotWheel(int, int);
1186 static void StopRobotWheel(int, int);
1187
1188 static void InitTimegateWheel(int, int);
1189 static void RunTimegateWheel(int, int);
1190
1191 static void InitMagicBallDelay(int, int);
1192 static void ActivateMagicBall(int, int);
1193
1194 struct ChangingElementInfo
1195 {
1196   int element;
1197   int target_element;
1198   int change_delay;
1199   void (*pre_change_function)(int x, int y);
1200   void (*change_function)(int x, int y);
1201   void (*post_change_function)(int x, int y);
1202 };
1203
1204 static struct ChangingElementInfo change_delay_list[] =
1205 {
1206   {
1207     EL_NUT_BREAKING,
1208     EL_EMERALD,
1209     6,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_PEARL_BREAKING,
1216     EL_EMPTY,
1217     8,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_EXIT_OPENING,
1224     EL_EXIT_OPEN,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_EXIT_CLOSING,
1232     EL_EXIT_CLOSED,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_STEEL_EXIT_OPENING,
1240     EL_STEEL_EXIT_OPEN,
1241     29,
1242     NULL,
1243     NULL,
1244     NULL
1245   },
1246   {
1247     EL_STEEL_EXIT_CLOSING,
1248     EL_STEEL_EXIT_CLOSED,
1249     29,
1250     NULL,
1251     NULL,
1252     NULL
1253   },
1254   {
1255     EL_EM_EXIT_OPENING,
1256     EL_EM_EXIT_OPEN,
1257     29,
1258     NULL,
1259     NULL,
1260     NULL
1261   },
1262   {
1263     EL_EM_EXIT_CLOSING,
1264 #if 1
1265     EL_EMPTY,
1266 #else
1267     EL_EM_EXIT_CLOSED,
1268 #endif
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_OPENING,
1276     EL_EM_STEEL_EXIT_OPEN,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_EM_STEEL_EXIT_CLOSING,
1284 #if 1
1285     EL_STEELWALL,
1286 #else
1287     EL_EM_STEEL_EXIT_CLOSED,
1288 #endif
1289     29,
1290     NULL,
1291     NULL,
1292     NULL
1293   },
1294   {
1295     EL_SP_EXIT_OPENING,
1296     EL_SP_EXIT_OPEN,
1297     29,
1298     NULL,
1299     NULL,
1300     NULL
1301   },
1302   {
1303     EL_SP_EXIT_CLOSING,
1304     EL_SP_EXIT_CLOSED,
1305     29,
1306     NULL,
1307     NULL,
1308     NULL
1309   },
1310   {
1311     EL_SWITCHGATE_OPENING,
1312     EL_SWITCHGATE_OPEN,
1313     29,
1314     NULL,
1315     NULL,
1316     NULL
1317   },
1318   {
1319     EL_SWITCHGATE_CLOSING,
1320     EL_SWITCHGATE_CLOSED,
1321     29,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_TIMEGATE_OPENING,
1328     EL_TIMEGATE_OPEN,
1329     29,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_TIMEGATE_CLOSING,
1336     EL_TIMEGATE_CLOSED,
1337     29,
1338     NULL,
1339     NULL,
1340     NULL
1341   },
1342
1343   {
1344     EL_ACID_SPLASH_LEFT,
1345     EL_EMPTY,
1346     8,
1347     NULL,
1348     NULL,
1349     NULL
1350   },
1351   {
1352     EL_ACID_SPLASH_RIGHT,
1353     EL_EMPTY,
1354     8,
1355     NULL,
1356     NULL,
1357     NULL
1358   },
1359   {
1360     EL_SP_BUGGY_BASE,
1361     EL_SP_BUGGY_BASE_ACTIVATING,
1362     0,
1363     InitBuggyBase,
1364     NULL,
1365     NULL
1366   },
1367   {
1368     EL_SP_BUGGY_BASE_ACTIVATING,
1369     EL_SP_BUGGY_BASE_ACTIVE,
1370     0,
1371     InitBuggyBase,
1372     NULL,
1373     NULL
1374   },
1375   {
1376     EL_SP_BUGGY_BASE_ACTIVE,
1377     EL_SP_BUGGY_BASE,
1378     0,
1379     InitBuggyBase,
1380     WarnBuggyBase,
1381     NULL
1382   },
1383   {
1384     EL_TRAP,
1385     EL_TRAP_ACTIVE,
1386     0,
1387     InitTrap,
1388     NULL,
1389     ActivateTrap
1390   },
1391   {
1392     EL_TRAP_ACTIVE,
1393     EL_TRAP,
1394     31,
1395     NULL,
1396     ChangeActiveTrap,
1397     NULL
1398   },
1399   {
1400     EL_ROBOT_WHEEL_ACTIVE,
1401     EL_ROBOT_WHEEL,
1402     0,
1403     InitRobotWheel,
1404     RunRobotWheel,
1405     StopRobotWheel
1406   },
1407   {
1408     EL_TIMEGATE_SWITCH_ACTIVE,
1409     EL_TIMEGATE_SWITCH,
1410     0,
1411     InitTimegateWheel,
1412     RunTimegateWheel,
1413     NULL
1414   },
1415   {
1416     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1417     EL_DC_TIMEGATE_SWITCH,
1418     0,
1419     InitTimegateWheel,
1420     RunTimegateWheel,
1421     NULL
1422   },
1423   {
1424     EL_EMC_MAGIC_BALL_ACTIVE,
1425     EL_EMC_MAGIC_BALL_ACTIVE,
1426     0,
1427     InitMagicBallDelay,
1428     NULL,
1429     ActivateMagicBall
1430   },
1431   {
1432     EL_EMC_SPRING_BUMPER_ACTIVE,
1433     EL_EMC_SPRING_BUMPER,
1434     8,
1435     NULL,
1436     NULL,
1437     NULL
1438   },
1439   {
1440     EL_DIAGONAL_SHRINKING,
1441     EL_UNDEFINED,
1442     0,
1443     NULL,
1444     NULL,
1445     NULL
1446   },
1447   {
1448     EL_DIAGONAL_GROWING,
1449     EL_UNDEFINED,
1450     0,
1451     NULL,
1452     NULL,
1453     NULL,
1454   },
1455
1456   {
1457     EL_UNDEFINED,
1458     EL_UNDEFINED,
1459     -1,
1460     NULL,
1461     NULL,
1462     NULL
1463   }
1464 };
1465
1466 struct
1467 {
1468   int element;
1469   int push_delay_fixed, push_delay_random;
1470 }
1471 push_delay_list[] =
1472 {
1473   { EL_SPRING,                  0, 0 },
1474   { EL_BALLOON,                 0, 0 },
1475
1476   { EL_SOKOBAN_OBJECT,          2, 0 },
1477   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1478   { EL_SATELLITE,               2, 0 },
1479   { EL_SP_DISK_YELLOW,          2, 0 },
1480
1481   { EL_UNDEFINED,               0, 0 },
1482 };
1483
1484 struct
1485 {
1486   int element;
1487   int move_stepsize;
1488 }
1489 move_stepsize_list[] =
1490 {
1491   { EL_AMOEBA_DROP,             2 },
1492   { EL_AMOEBA_DROPPING,         2 },
1493   { EL_QUICKSAND_FILLING,       1 },
1494   { EL_QUICKSAND_EMPTYING,      1 },
1495   { EL_QUICKSAND_FAST_FILLING,  2 },
1496   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1497   { EL_MAGIC_WALL_FILLING,      2 },
1498   { EL_MAGIC_WALL_EMPTYING,     2 },
1499   { EL_BD_MAGIC_WALL_FILLING,   2 },
1500   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1501   { EL_DC_MAGIC_WALL_FILLING,   2 },
1502   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1503
1504   { EL_UNDEFINED,               0 },
1505 };
1506
1507 struct
1508 {
1509   int element;
1510   int count;
1511 }
1512 collect_count_list[] =
1513 {
1514   { EL_EMERALD,                 1 },
1515   { EL_BD_DIAMOND,              1 },
1516   { EL_EMERALD_YELLOW,          1 },
1517   { EL_EMERALD_RED,             1 },
1518   { EL_EMERALD_PURPLE,          1 },
1519   { EL_DIAMOND,                 3 },
1520   { EL_SP_INFOTRON,             1 },
1521   { EL_PEARL,                   5 },
1522   { EL_CRYSTAL,                 8 },
1523
1524   { EL_UNDEFINED,               0 },
1525 };
1526
1527 struct
1528 {
1529   int element;
1530   int direction;
1531 }
1532 access_direction_list[] =
1533 {
1534   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1535   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1536   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1537   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1538   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1539   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1540   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1541   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1542   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1543   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1544   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1545
1546   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1547   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1548   { EL_SP_PORT_UP,                                                   MV_DOWN },
1549   { EL_SP_PORT_DOWN,                                         MV_UP           },
1550   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1551   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1552   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1553   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1554   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1555   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1556   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1557   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1558   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1559   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1560   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1561   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1562   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1563   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1564   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1565
1566   { EL_UNDEFINED,                       MV_NONE                              }
1567 };
1568
1569 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1570
1571 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1572 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1573 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1574                                  IS_JUST_CHANGING(x, y))
1575
1576 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1577
1578 /* static variables for playfield scan mode (scanning forward or backward) */
1579 static int playfield_scan_start_x = 0;
1580 static int playfield_scan_start_y = 0;
1581 static int playfield_scan_delta_x = 1;
1582 static int playfield_scan_delta_y = 1;
1583
1584 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1585                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1586                                      (y) += playfield_scan_delta_y)     \
1587                                 for ((x) = playfield_scan_start_x;      \
1588                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1589                                      (x) += playfield_scan_delta_x)
1590
1591 #ifdef DEBUG
1592 void DEBUG_SetMaximumDynamite()
1593 {
1594   int i;
1595
1596   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1597     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1598       local_player->inventory_element[local_player->inventory_size++] =
1599         EL_DYNAMITE;
1600 }
1601 #endif
1602
1603 static void InitPlayfieldScanModeVars()
1604 {
1605   if (game.use_reverse_scan_direction)
1606   {
1607     playfield_scan_start_x = lev_fieldx - 1;
1608     playfield_scan_start_y = lev_fieldy - 1;
1609
1610     playfield_scan_delta_x = -1;
1611     playfield_scan_delta_y = -1;
1612   }
1613   else
1614   {
1615     playfield_scan_start_x = 0;
1616     playfield_scan_start_y = 0;
1617
1618     playfield_scan_delta_x = 1;
1619     playfield_scan_delta_y = 1;
1620   }
1621 }
1622
1623 static void InitPlayfieldScanMode(int mode)
1624 {
1625   game.use_reverse_scan_direction =
1626     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1627
1628   InitPlayfieldScanModeVars();
1629 }
1630
1631 static int get_move_delay_from_stepsize(int move_stepsize)
1632 {
1633   move_stepsize =
1634     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1635
1636   /* make sure that stepsize value is always a power of 2 */
1637   move_stepsize = (1 << log_2(move_stepsize));
1638
1639   return TILEX / move_stepsize;
1640 }
1641
1642 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1643                                boolean init_game)
1644 {
1645   int player_nr = player->index_nr;
1646   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1647   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1648
1649   /* do no immediately change move delay -- the player might just be moving */
1650   player->move_delay_value_next = move_delay;
1651
1652   /* information if player can move must be set separately */
1653   player->cannot_move = cannot_move;
1654
1655   if (init_game)
1656   {
1657     player->move_delay       = game.initial_move_delay[player_nr];
1658     player->move_delay_value = game.initial_move_delay_value[player_nr];
1659
1660     player->move_delay_value_next = -1;
1661
1662     player->move_delay_reset_counter = 0;
1663   }
1664 }
1665
1666 void GetPlayerConfig()
1667 {
1668   GameFrameDelay = setup.game_frame_delay;
1669
1670   if (!audio.sound_available)
1671     setup.sound_simple = FALSE;
1672
1673   if (!audio.loops_available)
1674     setup.sound_loops = FALSE;
1675
1676   if (!audio.music_available)
1677     setup.sound_music = FALSE;
1678
1679   if (!video.fullscreen_available)
1680     setup.fullscreen = FALSE;
1681
1682   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1683
1684   SetAudioMode(setup.sound);
1685   InitJoysticks();
1686 }
1687
1688 int GetElementFromGroupElement(int element)
1689 {
1690   if (IS_GROUP_ELEMENT(element))
1691   {
1692     struct ElementGroupInfo *group = element_info[element].group;
1693     int last_anim_random_frame = gfx.anim_random_frame;
1694     int element_pos;
1695
1696     if (group->choice_mode == ANIM_RANDOM)
1697       gfx.anim_random_frame = RND(group->num_elements_resolved);
1698
1699     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1700                                     group->choice_mode, 0,
1701                                     group->choice_pos);
1702
1703     if (group->choice_mode == ANIM_RANDOM)
1704       gfx.anim_random_frame = last_anim_random_frame;
1705
1706     group->choice_pos++;
1707
1708     element = group->element_resolved[element_pos];
1709   }
1710
1711   return element;
1712 }
1713
1714 static void InitPlayerField(int x, int y, int element, boolean init_game)
1715 {
1716   if (element == EL_SP_MURPHY)
1717   {
1718     if (init_game)
1719     {
1720       if (stored_player[0].present)
1721       {
1722         Feld[x][y] = EL_SP_MURPHY_CLONE;
1723
1724         return;
1725       }
1726       else
1727       {
1728         stored_player[0].initial_element = element;
1729         stored_player[0].use_murphy = TRUE;
1730
1731         if (!level.use_artwork_element[0])
1732           stored_player[0].artwork_element = EL_SP_MURPHY;
1733       }
1734
1735       Feld[x][y] = EL_PLAYER_1;
1736     }
1737   }
1738
1739   if (init_game)
1740   {
1741     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1742     int jx = player->jx, jy = player->jy;
1743
1744     player->present = TRUE;
1745
1746     player->block_last_field = (element == EL_SP_MURPHY ?
1747                                 level.sp_block_last_field :
1748                                 level.block_last_field);
1749
1750     /* ---------- initialize player's last field block delay --------------- */
1751
1752     /* always start with reliable default value (no adjustment needed) */
1753     player->block_delay_adjustment = 0;
1754
1755     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1756     if (player->block_last_field && element == EL_SP_MURPHY)
1757       player->block_delay_adjustment = 1;
1758
1759     /* special case 2: in game engines before 3.1.1, blocking was different */
1760     if (game.use_block_last_field_bug)
1761       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1762
1763     if (!options.network || player->connected)
1764     {
1765       player->active = TRUE;
1766
1767       /* remove potentially duplicate players */
1768       if (StorePlayer[jx][jy] == Feld[x][y])
1769         StorePlayer[jx][jy] = 0;
1770
1771       StorePlayer[x][y] = Feld[x][y];
1772
1773       if (options.debug)
1774       {
1775         printf("Player %d activated.\n", player->element_nr);
1776         printf("[Local player is %d and currently %s.]\n",
1777                local_player->element_nr,
1778                local_player->active ? "active" : "not active");
1779       }
1780     }
1781
1782     Feld[x][y] = EL_EMPTY;
1783
1784     player->jx = player->last_jx = x;
1785     player->jy = player->last_jy = y;
1786   }
1787
1788 #if USE_PLAYER_REANIMATION
1789   if (!init_game)
1790   {
1791     int player_nr = GET_PLAYER_NR(element);
1792     struct PlayerInfo *player = &stored_player[player_nr];
1793
1794     if (player->active)
1795       player->killed = FALSE;   /* if player was just killed, reanimate him */
1796   }
1797 #endif
1798 }
1799
1800 static void InitField(int x, int y, boolean init_game)
1801 {
1802   int element = Feld[x][y];
1803
1804   switch (element)
1805   {
1806     case EL_SP_MURPHY:
1807     case EL_PLAYER_1:
1808     case EL_PLAYER_2:
1809     case EL_PLAYER_3:
1810     case EL_PLAYER_4:
1811       InitPlayerField(x, y, element, init_game);
1812       break;
1813
1814     case EL_SOKOBAN_FIELD_PLAYER:
1815       element = Feld[x][y] = EL_PLAYER_1;
1816       InitField(x, y, init_game);
1817
1818       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1819       InitField(x, y, init_game);
1820       break;
1821
1822     case EL_SOKOBAN_FIELD_EMPTY:
1823       local_player->sokobanfields_still_needed++;
1824       break;
1825
1826     case EL_STONEBLOCK:
1827       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1828         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1829       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1830         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1831       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1837       break;
1838
1839     case EL_BUG:
1840     case EL_BUG_RIGHT:
1841     case EL_BUG_UP:
1842     case EL_BUG_LEFT:
1843     case EL_BUG_DOWN:
1844     case EL_SPACESHIP:
1845     case EL_SPACESHIP_RIGHT:
1846     case EL_SPACESHIP_UP:
1847     case EL_SPACESHIP_LEFT:
1848     case EL_SPACESHIP_DOWN:
1849     case EL_BD_BUTTERFLY:
1850     case EL_BD_BUTTERFLY_RIGHT:
1851     case EL_BD_BUTTERFLY_UP:
1852     case EL_BD_BUTTERFLY_LEFT:
1853     case EL_BD_BUTTERFLY_DOWN:
1854     case EL_BD_FIREFLY:
1855     case EL_BD_FIREFLY_RIGHT:
1856     case EL_BD_FIREFLY_UP:
1857     case EL_BD_FIREFLY_LEFT:
1858     case EL_BD_FIREFLY_DOWN:
1859     case EL_PACMAN_RIGHT:
1860     case EL_PACMAN_UP:
1861     case EL_PACMAN_LEFT:
1862     case EL_PACMAN_DOWN:
1863     case EL_YAMYAM:
1864     case EL_YAMYAM_LEFT:
1865     case EL_YAMYAM_RIGHT:
1866     case EL_YAMYAM_UP:
1867     case EL_YAMYAM_DOWN:
1868     case EL_DARK_YAMYAM:
1869     case EL_ROBOT:
1870     case EL_PACMAN:
1871     case EL_SP_SNIKSNAK:
1872     case EL_SP_ELECTRON:
1873     case EL_MOLE:
1874     case EL_MOLE_LEFT:
1875     case EL_MOLE_RIGHT:
1876     case EL_MOLE_UP:
1877     case EL_MOLE_DOWN:
1878       InitMovDir(x, y);
1879       break;
1880
1881     case EL_AMOEBA_FULL:
1882     case EL_BD_AMOEBA:
1883       InitAmoebaNr(x, y);
1884       break;
1885
1886     case EL_AMOEBA_DROP:
1887       if (y == lev_fieldy - 1)
1888       {
1889         Feld[x][y] = EL_AMOEBA_GROWING;
1890         Store[x][y] = EL_AMOEBA_WET;
1891       }
1892       break;
1893
1894     case EL_DYNAMITE_ACTIVE:
1895     case EL_SP_DISK_RED_ACTIVE:
1896     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1897     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1898     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1899     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1900       MovDelay[x][y] = 96;
1901       break;
1902
1903     case EL_EM_DYNAMITE_ACTIVE:
1904       MovDelay[x][y] = 32;
1905       break;
1906
1907     case EL_LAMP:
1908       local_player->lights_still_needed++;
1909       break;
1910
1911     case EL_PENGUIN:
1912       local_player->friends_still_needed++;
1913       break;
1914
1915     case EL_PIG:
1916     case EL_DRAGON:
1917       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1918       break;
1919
1920     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1921     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1922     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1923     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1924     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1925     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1926     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1927     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1928     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1929     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1930     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1931     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1932       if (init_game)
1933       {
1934         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1935         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1936         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1937
1938         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1939         {
1940           game.belt_dir[belt_nr] = belt_dir;
1941           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1942         }
1943         else    /* more than one switch -- set it like the first switch */
1944         {
1945           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1946         }
1947       }
1948       break;
1949
1950 #if !USE_BOTH_SWITCHGATE_SWITCHES
1951     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1952       if (init_game)
1953         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1954       break;
1955
1956     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1957       if (init_game)
1958         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1959       break;
1960 #endif
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_state)
1977         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_state)
1982         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       /* reference elements should not be used on the playfield */
2010       Feld[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019 #if USE_NEW_CUSTOM_VALUE
2020         if (!element_info[element].use_last_ce_value || init_game)
2021           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2022 #endif
2023       }
2024       else if (IS_GROUP_ELEMENT(element))
2025       {
2026         Feld[x][y] = GetElementFromGroupElement(element);
2027
2028         InitField(x, y, init_game);
2029       }
2030
2031       break;
2032   }
2033
2034   if (!init_game)
2035     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2036 }
2037
2038 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2039 {
2040   InitField(x, y, init_game);
2041
2042   /* not needed to call InitMovDir() -- already done by InitField()! */
2043   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044       CAN_MOVE(Feld[x][y]))
2045     InitMovDir(x, y);
2046 }
2047
2048 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2049 {
2050   int old_element = Feld[x][y];
2051
2052   InitField(x, y, init_game);
2053
2054   /* not needed to call InitMovDir() -- already done by InitField()! */
2055   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2056       CAN_MOVE(old_element) &&
2057       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2058     InitMovDir(x, y);
2059
2060   /* this case is in fact a combination of not less than three bugs:
2061      first, it calls InitMovDir() for elements that can move, although this is
2062      already done by InitField(); then, it checks the element that was at this
2063      field _before_ the call to InitField() (which can change it); lastly, it
2064      was not called for "mole with direction" elements, which were treated as
2065      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2066   */
2067 }
2068
2069 #if 1
2070
2071 static int get_key_element_from_nr(int key_nr)
2072 {
2073   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2074                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2075                           EL_EM_KEY_1 : EL_KEY_1);
2076
2077   return key_base_element + key_nr;
2078 }
2079
2080 static int get_next_dropped_element(struct PlayerInfo *player)
2081 {
2082   return (player->inventory_size > 0 ?
2083           player->inventory_element[player->inventory_size - 1] :
2084           player->inventory_infinite_element != EL_UNDEFINED ?
2085           player->inventory_infinite_element :
2086           player->dynabombs_left > 0 ?
2087           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2088           EL_UNDEFINED);
2089 }
2090
2091 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2092 {
2093   /* pos >= 0: get element from bottom of the stack;
2094      pos <  0: get element from top of the stack */
2095
2096   if (pos < 0)
2097   {
2098     int min_inventory_size = -pos;
2099     int inventory_pos = player->inventory_size - min_inventory_size;
2100     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2101
2102     return (player->inventory_size >= min_inventory_size ?
2103             player->inventory_element[inventory_pos] :
2104             player->inventory_infinite_element != EL_UNDEFINED ?
2105             player->inventory_infinite_element :
2106             player->dynabombs_left >= min_dynabombs_left ?
2107             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2108             EL_UNDEFINED);
2109   }
2110   else
2111   {
2112     int min_dynabombs_left = pos + 1;
2113     int min_inventory_size = pos + 1 - player->dynabombs_left;
2114     int inventory_pos = pos - player->dynabombs_left;
2115
2116     return (player->inventory_infinite_element != EL_UNDEFINED ?
2117             player->inventory_infinite_element :
2118             player->dynabombs_left >= min_dynabombs_left ?
2119             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2120             player->inventory_size >= min_inventory_size ?
2121             player->inventory_element[inventory_pos] :
2122             EL_UNDEFINED);
2123   }
2124 }
2125
2126 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2127 {
2128   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2129   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2130   int compare_result;
2131
2132   if (gpo1->sort_priority != gpo2->sort_priority)
2133     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2134   else
2135     compare_result = gpo1->nr - gpo2->nr;
2136
2137   return compare_result;
2138 }
2139
2140 void InitGameControlValues()
2141 {
2142   int i;
2143
2144   for (i = 0; game_panel_controls[i].nr != -1; i++)
2145   {
2146     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2147     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2148     struct TextPosInfo *pos = gpc->pos;
2149     int nr = gpc->nr;
2150     int type = gpc->type;
2151
2152     if (nr != i)
2153     {
2154       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2155       Error(ERR_EXIT, "this should not happen -- please debug");
2156     }
2157
2158     /* force update of game controls after initialization */
2159     gpc->value = gpc->last_value = -1;
2160     gpc->frame = gpc->last_frame = -1;
2161     gpc->gfx_frame = -1;
2162
2163     /* determine panel value width for later calculation of alignment */
2164     if (type == TYPE_INTEGER || type == TYPE_STRING)
2165     {
2166       pos->width = pos->size * getFontWidth(pos->font);
2167       pos->height = getFontHeight(pos->font);
2168     }
2169     else if (type == TYPE_ELEMENT)
2170     {
2171       pos->width = pos->size;
2172       pos->height = pos->size;
2173     }
2174
2175     /* fill structure for game panel draw order */
2176     gpo->nr = gpc->nr;
2177     gpo->sort_priority = pos->sort_priority;
2178   }
2179
2180   /* sort game panel controls according to sort_priority and control number */
2181   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2182         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2183 }
2184
2185 void UpdatePlayfieldElementCount()
2186 {
2187   boolean use_element_count = FALSE;
2188   int i, j, x, y;
2189
2190   /* first check if it is needed at all to calculate playfield element count */
2191   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2192     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2193       use_element_count = TRUE;
2194
2195   if (!use_element_count)
2196     return;
2197
2198   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2199     element_info[i].element_count = 0;
2200
2201   SCAN_PLAYFIELD(x, y)
2202   {
2203     element_info[Feld[x][y]].element_count++;
2204   }
2205
2206   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2207     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2208       if (IS_IN_GROUP(j, i))
2209         element_info[EL_GROUP_START + i].element_count +=
2210           element_info[j].element_count;
2211 }
2212
2213 void UpdateGameControlValues()
2214 {
2215   int i, k;
2216   int time = (local_player->LevelSolved ?
2217               local_player->LevelSolved_CountingTime :
2218               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2219               level.native_em_level->lev->time :
2220               level.time == 0 ? TimePlayed : TimeLeft);
2221   int score = (local_player->LevelSolved ?
2222                local_player->LevelSolved_CountingScore :
2223                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224                level.native_em_level->lev->score :
2225                local_player->score);
2226   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2227               level.native_em_level->lev->required :
2228               local_player->gems_still_needed);
2229   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2230                      level.native_em_level->lev->required > 0 :
2231                      local_player->gems_still_needed > 0 ||
2232                      local_player->sokobanfields_still_needed > 0 ||
2233                      local_player->lights_still_needed > 0);
2234
2235   UpdatePlayfieldElementCount();
2236
2237   /* update game panel control values */
2238
2239   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2240   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2241
2242   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2243   for (i = 0; i < MAX_NUM_KEYS; i++)
2244     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2245   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2246   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2247
2248   if (game.centered_player_nr == -1)
2249   {
2250     for (i = 0; i < MAX_PLAYERS; i++)
2251     {
2252       for (k = 0; k < MAX_NUM_KEYS; k++)
2253       {
2254         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2255         {
2256           if (level.native_em_level->ply[i]->keys & (1 << k))
2257             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2258               get_key_element_from_nr(k);
2259         }
2260         else if (stored_player[i].key[k])
2261           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2262             get_key_element_from_nr(k);
2263       }
2264
2265       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2266         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2267           level.native_em_level->ply[i]->dynamite;
2268       else
2269         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2270           stored_player[i].inventory_size;
2271
2272       if (stored_player[i].num_white_keys > 0)
2273         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2274           EL_DC_KEY_WHITE;
2275
2276       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2277         stored_player[i].num_white_keys;
2278     }
2279   }
2280   else
2281   {
2282     int player_nr = game.centered_player_nr;
2283
2284     for (k = 0; k < MAX_NUM_KEYS; k++)
2285     {
2286       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2287       {
2288         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2289           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290             get_key_element_from_nr(k);
2291       }
2292       else if (stored_player[player_nr].key[k])
2293         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294           get_key_element_from_nr(k);
2295     }
2296
2297     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2298       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2299         level.native_em_level->ply[player_nr]->dynamite;
2300     else
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         stored_player[player_nr].inventory_size;
2303
2304     if (stored_player[player_nr].num_white_keys > 0)
2305       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2306
2307     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2308       stored_player[player_nr].num_white_keys;
2309   }
2310
2311   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2312   {
2313     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2314       get_inventory_element_from_pos(local_player, i);
2315     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2316       get_inventory_element_from_pos(local_player, -i - 1);
2317   }
2318
2319   game_panel_controls[GAME_PANEL_SCORE].value = score;
2320   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2321
2322   game_panel_controls[GAME_PANEL_TIME].value = time;
2323
2324   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2325   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2326   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2327
2328   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2329     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2330      EL_EMPTY);
2331   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2332     local_player->shield_normal_time_left;
2333   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2334     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2335      EL_EMPTY);
2336   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2337     local_player->shield_deadly_time_left;
2338
2339   game_panel_controls[GAME_PANEL_EXIT].value =
2340     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2341
2342   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2343     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2344   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2345     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2346      EL_EMC_MAGIC_BALL_SWITCH);
2347
2348   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2349     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2350   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2351     game.light_time_left;
2352
2353   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2354     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2355   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2356     game.timegate_time_left;
2357
2358   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2359     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2360
2361   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2362     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2364     game.lenses_time_left;
2365
2366   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2367     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2368   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2369     game.magnify_time_left;
2370
2371   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2372     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2373      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2374      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2375      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2376      EL_BALLOON_SWITCH_NONE);
2377
2378   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2379     local_player->dynabomb_count;
2380   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2381     local_player->dynabomb_size;
2382   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2383     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2384
2385   game_panel_controls[GAME_PANEL_PENGUINS].value =
2386     local_player->friends_still_needed;
2387
2388   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2389     local_player->sokobanfields_still_needed;
2390   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2391     local_player->sokobanfields_still_needed;
2392
2393   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2394     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2395
2396   for (i = 0; i < NUM_BELTS; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2399       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2400        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2401     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2402       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2403   }
2404
2405   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2406     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2407   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2408     game.magic_wall_time_left;
2409
2410 #if USE_PLAYER_GRAVITY
2411   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2412     local_player->gravity;
2413 #else
2414   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2415 #endif
2416
2417   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2418     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2419
2420   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2421     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2422       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2423        game.panel.element[i].id : EL_UNDEFINED);
2424
2425   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2426     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2427       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2428        element_info[game.panel.element_count[i].id].element_count : 0);
2429
2430   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2431     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2432       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2433        element_info[game.panel.ce_score[i].id].collect_score : 0);
2434
2435   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2436     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2437       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2438        element_info[game.panel.ce_score_element[i].id].collect_score :
2439        EL_UNDEFINED);
2440
2441   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2442   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2443   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2444
2445   /* update game panel control frames */
2446
2447   for (i = 0; game_panel_controls[i].nr != -1; i++)
2448   {
2449     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2450
2451     if (gpc->type == TYPE_ELEMENT)
2452     {
2453       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2454       {
2455         int last_anim_random_frame = gfx.anim_random_frame;
2456         int element = gpc->value;
2457         int graphic = el2panelimg(element);
2458
2459         if (gpc->value != gpc->last_value)
2460         {
2461           gpc->gfx_frame = 0;
2462           gpc->gfx_random = INIT_GFX_RANDOM();
2463         }
2464         else
2465         {
2466           gpc->gfx_frame++;
2467
2468           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2469               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2470             gpc->gfx_random = INIT_GFX_RANDOM();
2471         }
2472
2473         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2474           gfx.anim_random_frame = gpc->gfx_random;
2475
2476         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2477           gpc->gfx_frame = element_info[element].collect_score;
2478
2479         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2480                                               gpc->gfx_frame);
2481
2482         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2483           gfx.anim_random_frame = last_anim_random_frame;
2484       }
2485     }
2486   }
2487 }
2488
2489 void DisplayGameControlValues()
2490 {
2491   boolean redraw_panel = FALSE;
2492   int i;
2493
2494   for (i = 0; game_panel_controls[i].nr != -1; i++)
2495   {
2496     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2497
2498     if (PANEL_DEACTIVATED(gpc->pos))
2499       continue;
2500
2501     if (gpc->value == gpc->last_value &&
2502         gpc->frame == gpc->last_frame)
2503       continue;
2504
2505     redraw_panel = TRUE;
2506   }
2507
2508   if (!redraw_panel)
2509     return;
2510
2511   /* copy default game door content to main double buffer */
2512   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2513              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2514
2515   /* redraw game control buttons */
2516 #if 1
2517   RedrawGameButtons();
2518 #else
2519   UnmapGameButtons();
2520   MapGameButtons();
2521 #endif
2522
2523   game_status = GAME_MODE_PSEUDO_PANEL;
2524
2525 #if 1
2526   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2527 #else
2528   for (i = 0; game_panel_controls[i].nr != -1; i++)
2529 #endif
2530   {
2531 #if 1
2532     int nr = game_panel_order[i].nr;
2533     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2534 #else
2535     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2536     int nr = gpc->nr;
2537 #endif
2538     struct TextPosInfo *pos = gpc->pos;
2539     int type = gpc->type;
2540     int value = gpc->value;
2541     int frame = gpc->frame;
2542 #if 0
2543     int last_value = gpc->last_value;
2544     int last_frame = gpc->last_frame;
2545 #endif
2546     int size = pos->size;
2547     int font = pos->font;
2548     boolean draw_masked = pos->draw_masked;
2549     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2550
2551     if (PANEL_DEACTIVATED(pos))
2552       continue;
2553
2554 #if 0
2555     if (value == last_value && frame == last_frame)
2556       continue;
2557 #endif
2558
2559     gpc->last_value = value;
2560     gpc->last_frame = frame;
2561
2562 #if 0
2563     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2564 #endif
2565
2566     if (type == TYPE_INTEGER)
2567     {
2568       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2569           nr == GAME_PANEL_TIME)
2570       {
2571         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2572
2573         if (use_dynamic_size)           /* use dynamic number of digits */
2574         {
2575           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2576           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2577           int size2 = size1 + 1;
2578           int font1 = pos->font;
2579           int font2 = pos->font_alt;
2580
2581           size = (value < value_change ? size1 : size2);
2582           font = (value < value_change ? font1 : font2);
2583
2584 #if 0
2585           /* clear background if value just changed its size (dynamic digits) */
2586           if ((last_value < value_change) != (value < value_change))
2587           {
2588             int width1 = size1 * getFontWidth(font1);
2589             int width2 = size2 * getFontWidth(font2);
2590             int max_width = MAX(width1, width2);
2591             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2592
2593             pos->width = max_width;
2594
2595             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2596                                        max_width, max_height);
2597           }
2598 #endif
2599         }
2600       }
2601
2602 #if 1
2603       /* correct text size if "digits" is zero or less */
2604       if (size <= 0)
2605         size = strlen(int2str(value, size));
2606
2607       /* dynamically correct text alignment */
2608       pos->width = size * getFontWidth(font);
2609 #endif
2610
2611       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2612                   int2str(value, size), font, mask_mode);
2613     }
2614     else if (type == TYPE_ELEMENT)
2615     {
2616       int element, graphic;
2617       Bitmap *src_bitmap;
2618       int src_x, src_y;
2619       int width, height;
2620       int dst_x = PANEL_XPOS(pos);
2621       int dst_y = PANEL_YPOS(pos);
2622
2623 #if 1
2624       if (value != EL_UNDEFINED && value != EL_EMPTY)
2625       {
2626         element = value;
2627         graphic = el2panelimg(value);
2628
2629         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2630
2631 #if 1
2632         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2633           size = TILESIZE;
2634 #endif
2635
2636         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2637                               &src_x, &src_y);
2638
2639         width  = graphic_info[graphic].width  * size / TILESIZE;
2640         height = graphic_info[graphic].height * size / TILESIZE;
2641
2642         if (draw_masked)
2643         {
2644           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2645                         dst_x - src_x, dst_y - src_y);
2646           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2647                            dst_x, dst_y);
2648         }
2649         else
2650         {
2651           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2652                      dst_x, dst_y);
2653         }
2654       }
2655 #else
2656       if (value == EL_UNDEFINED || value == EL_EMPTY)
2657       {
2658         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2659         graphic = el2panelimg(element);
2660
2661         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2662         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2663         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2664       }
2665       else
2666       {
2667         element = value;
2668         graphic = el2panelimg(value);
2669
2670         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2671       }
2672
2673       width  = graphic_info[graphic].width  * size / TILESIZE;
2674       height = graphic_info[graphic].height * size / TILESIZE;
2675
2676       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2677 #endif
2678     }
2679     else if (type == TYPE_STRING)
2680     {
2681       boolean active = (value != 0);
2682       char *state_normal = "off";
2683       char *state_active = "on";
2684       char *state = (active ? state_active : state_normal);
2685       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2686                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2687                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2688                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2689
2690       if (nr == GAME_PANEL_GRAVITY_STATE)
2691       {
2692         int font1 = pos->font;          /* (used for normal state) */
2693         int font2 = pos->font_alt;      /* (used for active state) */
2694 #if 0
2695         int size1 = strlen(state_normal);
2696         int size2 = strlen(state_active);
2697         int width1 = size1 * getFontWidth(font1);
2698         int width2 = size2 * getFontWidth(font2);
2699         int max_width = MAX(width1, width2);
2700         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2701
2702         pos->width = max_width;
2703
2704         /* clear background for values that may have changed its size */
2705         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2706                                    max_width, max_height);
2707 #endif
2708
2709         font = (active ? font2 : font1);
2710       }
2711
2712       if (s != NULL)
2713       {
2714         char *s_cut;
2715
2716 #if 1
2717         if (size <= 0)
2718         {
2719           /* don't truncate output if "chars" is zero or less */
2720           size = strlen(s);
2721
2722           /* dynamically correct text alignment */
2723           pos->width = size * getFontWidth(font);
2724         }
2725 #endif
2726
2727         s_cut = getStringCopyN(s, size);
2728
2729         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2730                     s_cut, font, mask_mode);
2731
2732         free(s_cut);
2733       }
2734     }
2735
2736     redraw_mask |= REDRAW_DOOR_1;
2737   }
2738
2739   game_status = GAME_MODE_PLAYING;
2740 }
2741
2742 void UpdateAndDisplayGameControlValues()
2743 {
2744   if (tape.warp_forward)
2745     return;
2746
2747   UpdateGameControlValues();
2748   DisplayGameControlValues();
2749 }
2750
2751 void DrawGameValue_Emeralds(int value)
2752 {
2753   struct TextPosInfo *pos = &game.panel.gems;
2754 #if 1
2755   int font_nr = pos->font;
2756 #else
2757   int font_nr = FONT_TEXT_2;
2758 #endif
2759   int font_width = getFontWidth(font_nr);
2760   int chars = pos->size;
2761
2762 #if 1
2763   return;       /* !!! USE NEW STUFF !!! */
2764 #endif
2765
2766   if (PANEL_DEACTIVATED(pos))
2767     return;
2768
2769   pos->width = chars * font_width;
2770
2771   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2772 }
2773
2774 void DrawGameValue_Dynamite(int value)
2775 {
2776   struct TextPosInfo *pos = &game.panel.inventory_count;
2777 #if 1
2778   int font_nr = pos->font;
2779 #else
2780   int font_nr = FONT_TEXT_2;
2781 #endif
2782   int font_width = getFontWidth(font_nr);
2783   int chars = pos->size;
2784
2785 #if 1
2786   return;       /* !!! USE NEW STUFF !!! */
2787 #endif
2788
2789   if (PANEL_DEACTIVATED(pos))
2790     return;
2791
2792   pos->width = chars * font_width;
2793
2794   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2795 }
2796
2797 void DrawGameValue_Score(int value)
2798 {
2799   struct TextPosInfo *pos = &game.panel.score;
2800 #if 1
2801   int font_nr = pos->font;
2802 #else
2803   int font_nr = FONT_TEXT_2;
2804 #endif
2805   int font_width = getFontWidth(font_nr);
2806   int chars = pos->size;
2807
2808 #if 1
2809   return;       /* !!! USE NEW STUFF !!! */
2810 #endif
2811
2812   if (PANEL_DEACTIVATED(pos))
2813     return;
2814
2815   pos->width = chars * font_width;
2816
2817   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2818 }
2819
2820 void DrawGameValue_Time(int value)
2821 {
2822   struct TextPosInfo *pos = &game.panel.time;
2823   static int last_value = -1;
2824   int chars1 = 3;
2825   int chars2 = 4;
2826   int chars = pos->size;
2827 #if 1
2828   int font1_nr = pos->font;
2829   int font2_nr = pos->font_alt;
2830 #else
2831   int font1_nr = FONT_TEXT_2;
2832   int font2_nr = FONT_TEXT_1;
2833 #endif
2834   int font_nr = font1_nr;
2835   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2836
2837 #if 1
2838   return;       /* !!! USE NEW STUFF !!! */
2839 #endif
2840
2841   if (PANEL_DEACTIVATED(pos))
2842     return;
2843
2844   if (use_dynamic_chars)                /* use dynamic number of chars */
2845   {
2846     chars   = (value < 1000 ? chars1   : chars2);
2847     font_nr = (value < 1000 ? font1_nr : font2_nr);
2848   }
2849
2850   /* clear background if value just changed its size (dynamic chars only) */
2851   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2852   {
2853     int width1 = chars1 * getFontWidth(font1_nr);
2854     int width2 = chars2 * getFontWidth(font2_nr);
2855     int max_width = MAX(width1, width2);
2856     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2857
2858     pos->width = max_width;
2859
2860     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2861                                max_width, max_height);
2862   }
2863
2864   pos->width = chars * getFontWidth(font_nr);
2865
2866   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2867
2868   last_value = value;
2869 }
2870
2871 void DrawGameValue_Level(int value)
2872 {
2873   struct TextPosInfo *pos = &game.panel.level_number;
2874   int chars1 = 2;
2875   int chars2 = 3;
2876   int chars = pos->size;
2877 #if 1
2878   int font1_nr = pos->font;
2879   int font2_nr = pos->font_alt;
2880 #else
2881   int font1_nr = FONT_TEXT_2;
2882   int font2_nr = FONT_TEXT_1;
2883 #endif
2884   int font_nr = font1_nr;
2885   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2886
2887 #if 1
2888   return;       /* !!! USE NEW STUFF !!! */
2889 #endif
2890
2891   if (PANEL_DEACTIVATED(pos))
2892     return;
2893
2894   if (use_dynamic_chars)                /* use dynamic number of chars */
2895   {
2896     chars   = (level_nr < 100 ? chars1   : chars2);
2897     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2898   }
2899
2900   pos->width = chars * getFontWidth(font_nr);
2901
2902   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2903 }
2904
2905 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2906 {
2907 #if 0
2908   struct TextPosInfo *pos = &game.panel.keys;
2909 #endif
2910 #if 0
2911   int base_key_graphic = EL_KEY_1;
2912 #endif
2913   int i;
2914
2915 #if 1
2916   return;       /* !!! USE NEW STUFF !!! */
2917 #endif
2918
2919 #if 0
2920   if (PANEL_DEACTIVATED(pos))
2921     return;
2922 #endif
2923
2924 #if 0
2925   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2926     base_key_graphic = EL_EM_KEY_1;
2927 #endif
2928
2929 #if 0
2930   pos->width = 4 * MINI_TILEX;
2931 #endif
2932
2933 #if 1
2934   for (i = 0; i < MAX_NUM_KEYS; i++)
2935 #else
2936   /* currently only 4 of 8 possible keys are displayed */
2937   for (i = 0; i < STD_NUM_KEYS; i++)
2938 #endif
2939   {
2940 #if 1
2941     struct TextPosInfo *pos = &game.panel.key[i];
2942 #endif
2943     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2944     int src_y = DOOR_GFX_PAGEY1 + 123;
2945 #if 1
2946     int dst_x = PANEL_XPOS(pos);
2947     int dst_y = PANEL_YPOS(pos);
2948 #else
2949     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2950     int dst_y = PANEL_YPOS(pos);
2951 #endif
2952
2953 #if 1
2954     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2955                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2956                    EL_KEY_1) + i;
2957     int graphic = el2edimg(element);
2958 #endif
2959
2960 #if 1
2961     if (PANEL_DEACTIVATED(pos))
2962       continue;
2963 #endif
2964
2965 #if 0
2966     /* masked blit with tiles from half-size scaled bitmap does not work yet
2967        (no mask bitmap created for these sizes after loading and scaling) --
2968        solution: load without creating mask, scale, then create final mask */
2969
2970     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2971                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2972
2973     if (key[i])
2974     {
2975 #if 0
2976       int graphic = el2edimg(base_key_graphic + i);
2977 #endif
2978       Bitmap *src_bitmap;
2979       int src_x, src_y;
2980
2981       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2982
2983       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2984                     dst_x - src_x, dst_y - src_y);
2985       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2986                        dst_x, dst_y);
2987     }
2988 #else
2989 #if 1
2990     if (key[i])
2991       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2992     else
2993       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2994                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2995 #else
2996     if (key[i])
2997       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2998     else
2999       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3000                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3001 #endif
3002 #endif
3003   }
3004 }
3005
3006 #else
3007
3008 void DrawGameValue_Emeralds(int value)
3009 {
3010   int font_nr = FONT_TEXT_2;
3011   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3012
3013   if (PANEL_DEACTIVATED(game.panel.gems))
3014     return;
3015
3016   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3017 }
3018
3019 void DrawGameValue_Dynamite(int value)
3020 {
3021   int font_nr = FONT_TEXT_2;
3022   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3023
3024   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3025     return;
3026
3027   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3028 }
3029
3030 void DrawGameValue_Score(int value)
3031 {
3032   int font_nr = FONT_TEXT_2;
3033   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3034
3035   if (PANEL_DEACTIVATED(game.panel.score))
3036     return;
3037
3038   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3039 }
3040
3041 void DrawGameValue_Time(int value)
3042 {
3043   int font1_nr = FONT_TEXT_2;
3044 #if 1
3045   int font2_nr = FONT_TEXT_1;
3046 #else
3047   int font2_nr = FONT_LEVEL_NUMBER;
3048 #endif
3049   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3050   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3051
3052   if (PANEL_DEACTIVATED(game.panel.time))
3053     return;
3054
3055   /* clear background if value just changed its size */
3056   if (value == 999 || value == 1000)
3057     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3058
3059   if (value < 1000)
3060     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3061   else
3062     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3063 }
3064
3065 void DrawGameValue_Level(int value)
3066 {
3067   int font1_nr = FONT_TEXT_2;
3068 #if 1
3069   int font2_nr = FONT_TEXT_1;
3070 #else
3071   int font2_nr = FONT_LEVEL_NUMBER;
3072 #endif
3073
3074   if (PANEL_DEACTIVATED(game.panel.level))
3075     return;
3076
3077   if (level_nr < 100)
3078     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3079   else
3080     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3081 }
3082
3083 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3084 {
3085   int base_key_graphic = EL_KEY_1;
3086   int i;
3087
3088   if (PANEL_DEACTIVATED(game.panel.keys))
3089     return;
3090
3091   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3092     base_key_graphic = EL_EM_KEY_1;
3093
3094   /* currently only 4 of 8 possible keys are displayed */
3095   for (i = 0; i < STD_NUM_KEYS; i++)
3096   {
3097     int x = XX_KEYS + i * MINI_TILEX;
3098     int y = YY_KEYS;
3099
3100     if (key[i])
3101       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3102     else
3103       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3104                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3105   }
3106 }
3107
3108 #endif
3109
3110 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3111                        int key_bits)
3112 {
3113   int key[MAX_NUM_KEYS];
3114   int i;
3115
3116   /* prevent EM engine from updating time/score values parallel to GameWon() */
3117   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3118       local_player->LevelSolved)
3119     return;
3120
3121   for (i = 0; i < MAX_NUM_KEYS; i++)
3122     key[i] = key_bits & (1 << i);
3123
3124   DrawGameValue_Level(level_nr);
3125
3126   DrawGameValue_Emeralds(emeralds);
3127   DrawGameValue_Dynamite(dynamite);
3128   DrawGameValue_Score(score);
3129   DrawGameValue_Time(time);
3130
3131   DrawGameValue_Keys(key);
3132 }
3133
3134 void UpdateGameDoorValues()
3135 {
3136   UpdateGameControlValues();
3137 }
3138
3139 void DrawGameDoorValues()
3140 {
3141   DisplayGameControlValues();
3142 }
3143
3144 void DrawGameDoorValues_OLD()
3145 {
3146   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3147   int dynamite_value = 0;
3148   int score_value = (local_player->LevelSolved ? local_player->score_final :
3149                      local_player->score);
3150   int gems_value = local_player->gems_still_needed;
3151   int key_bits = 0;
3152   int i, j;
3153
3154   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3155   {
3156     DrawGameDoorValues_EM();
3157
3158     return;
3159   }
3160
3161   if (game.centered_player_nr == -1)
3162   {
3163     for (i = 0; i < MAX_PLAYERS; i++)
3164     {
3165       for (j = 0; j < MAX_NUM_KEYS; j++)
3166         if (stored_player[i].key[j])
3167           key_bits |= (1 << j);
3168
3169       dynamite_value += stored_player[i].inventory_size;
3170     }
3171   }
3172   else
3173   {
3174     int player_nr = game.centered_player_nr;
3175
3176     for (i = 0; i < MAX_NUM_KEYS; i++)
3177       if (stored_player[player_nr].key[i])
3178         key_bits |= (1 << i);
3179
3180     dynamite_value = stored_player[player_nr].inventory_size;
3181   }
3182
3183   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3184                     key_bits);
3185 }
3186
3187
3188 /*
3189   =============================================================================
3190   InitGameEngine()
3191   -----------------------------------------------------------------------------
3192   initialize game engine due to level / tape version number
3193   =============================================================================
3194 */
3195
3196 static void InitGameEngine()
3197 {
3198   int i, j, k, l, x, y;
3199
3200   /* set game engine from tape file when re-playing, else from level file */
3201   game.engine_version = (tape.playing ? tape.engine_version :
3202                          level.game_version);
3203
3204   /* ---------------------------------------------------------------------- */
3205   /* set flags for bugs and changes according to active game engine version */
3206   /* ---------------------------------------------------------------------- */
3207
3208   /*
3209     Summary of bugfix/change:
3210     Fixed handling for custom elements that change when pushed by the player.
3211
3212     Fixed/changed in version:
3213     3.1.0
3214
3215     Description:
3216     Before 3.1.0, custom elements that "change when pushing" changed directly
3217     after the player started pushing them (until then handled in "DigField()").
3218     Since 3.1.0, these custom elements are not changed until the "pushing"
3219     move of the element is finished (now handled in "ContinueMoving()").
3220
3221     Affected levels/tapes:
3222     The first condition is generally needed for all levels/tapes before version
3223     3.1.0, which might use the old behaviour before it was changed; known tapes
3224     that are affected are some tapes from the level set "Walpurgis Gardens" by
3225     Jamie Cullen.
3226     The second condition is an exception from the above case and is needed for
3227     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3228     above (including some development versions of 3.1.0), but before it was
3229     known that this change would break tapes like the above and was fixed in
3230     3.1.1, so that the changed behaviour was active although the engine version
3231     while recording maybe was before 3.1.0. There is at least one tape that is
3232     affected by this exception, which is the tape for the one-level set "Bug
3233     Machine" by Juergen Bonhagen.
3234   */
3235
3236   game.use_change_when_pushing_bug =
3237     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3238      !(tape.playing &&
3239        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3240        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3241
3242   /*
3243     Summary of bugfix/change:
3244     Fixed handling for blocking the field the player leaves when moving.
3245
3246     Fixed/changed in version:
3247     3.1.1
3248
3249     Description:
3250     Before 3.1.1, when "block last field when moving" was enabled, the field
3251     the player is leaving when moving was blocked for the time of the move,
3252     and was directly unblocked afterwards. This resulted in the last field
3253     being blocked for exactly one less than the number of frames of one player
3254     move. Additionally, even when blocking was disabled, the last field was
3255     blocked for exactly one frame.
3256     Since 3.1.1, due to changes in player movement handling, the last field
3257     is not blocked at all when blocking is disabled. When blocking is enabled,
3258     the last field is blocked for exactly the number of frames of one player
3259     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3260     last field is blocked for exactly one more than the number of frames of
3261     one player move.
3262
3263     Affected levels/tapes:
3264     (!!! yet to be determined -- probably many !!!)
3265   */
3266
3267   game.use_block_last_field_bug =
3268     (game.engine_version < VERSION_IDENT(3,1,1,0));
3269
3270   /*
3271     Summary of bugfix/change:
3272     Changed behaviour of CE changes with multiple changes per single frame.
3273
3274     Fixed/changed in version:
3275     3.2.0-6
3276
3277     Description:
3278     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3279     This resulted in race conditions where CEs seem to behave strange in some
3280     situations (where triggered CE changes were just skipped because there was
3281     already a CE change on that tile in the playfield in that engine frame).
3282     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3283     (The number of changes per frame must be limited in any case, because else
3284     it is easily possible to define CE changes that would result in an infinite
3285     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3286     should be set large enough so that it would only be reached in cases where
3287     the corresponding CE change conditions run into a loop. Therefore, it seems
3288     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3289     maximal number of change pages for custom elements.)
3290
3291     Affected levels/tapes:
3292     Probably many.
3293   */
3294
3295 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3296   game.max_num_changes_per_frame = 1;
3297 #else
3298   game.max_num_changes_per_frame =
3299     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3300 #endif
3301
3302   /* ---------------------------------------------------------------------- */
3303
3304   /* default scan direction: scan playfield from top/left to bottom/right */
3305   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3306
3307   /* dynamically adjust element properties according to game engine version */
3308   InitElementPropertiesEngine(game.engine_version);
3309
3310 #if 0
3311   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3312   printf("          tape version == %06d [%s] [file: %06d]\n",
3313          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3314          tape.file_version);
3315   printf("       => game.engine_version == %06d\n", game.engine_version);
3316 #endif
3317
3318   /* ---------- initialize player's initial move delay --------------------- */
3319
3320   /* dynamically adjust player properties according to level information */
3321   for (i = 0; i < MAX_PLAYERS; i++)
3322     game.initial_move_delay_value[i] =
3323       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3324
3325   /* dynamically adjust player properties according to game engine version */
3326   for (i = 0; i < MAX_PLAYERS; i++)
3327     game.initial_move_delay[i] =
3328       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3329        game.initial_move_delay_value[i] : 0);
3330
3331   /* ---------- initialize player's initial push delay --------------------- */
3332
3333   /* dynamically adjust player properties according to game engine version */
3334   game.initial_push_delay_value =
3335     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3336
3337   /* ---------- initialize changing elements ------------------------------- */
3338
3339   /* initialize changing elements information */
3340   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3341   {
3342     struct ElementInfo *ei = &element_info[i];
3343
3344     /* this pointer might have been changed in the level editor */
3345     ei->change = &ei->change_page[0];
3346
3347     if (!IS_CUSTOM_ELEMENT(i))
3348     {
3349       ei->change->target_element = EL_EMPTY_SPACE;
3350       ei->change->delay_fixed = 0;
3351       ei->change->delay_random = 0;
3352       ei->change->delay_frames = 1;
3353     }
3354
3355     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3356     {
3357       ei->has_change_event[j] = FALSE;
3358
3359       ei->event_page_nr[j] = 0;
3360       ei->event_page[j] = &ei->change_page[0];
3361     }
3362   }
3363
3364   /* add changing elements from pre-defined list */
3365   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3366   {
3367     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3368     struct ElementInfo *ei = &element_info[ch_delay->element];
3369
3370     ei->change->target_element       = ch_delay->target_element;
3371     ei->change->delay_fixed          = ch_delay->change_delay;
3372
3373     ei->change->pre_change_function  = ch_delay->pre_change_function;
3374     ei->change->change_function      = ch_delay->change_function;
3375     ei->change->post_change_function = ch_delay->post_change_function;
3376
3377     ei->change->can_change = TRUE;
3378     ei->change->can_change_or_has_action = TRUE;
3379
3380     ei->has_change_event[CE_DELAY] = TRUE;
3381
3382     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3383     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3384   }
3385
3386   /* ---------- initialize internal run-time variables --------------------- */
3387
3388   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3389   {
3390     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3391
3392     for (j = 0; j < ei->num_change_pages; j++)
3393     {
3394       ei->change_page[j].can_change_or_has_action =
3395         (ei->change_page[j].can_change |
3396          ei->change_page[j].has_action);
3397     }
3398   }
3399
3400   /* add change events from custom element configuration */
3401   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3402   {
3403     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3404
3405     for (j = 0; j < ei->num_change_pages; j++)
3406     {
3407       if (!ei->change_page[j].can_change_or_has_action)
3408         continue;
3409
3410       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3411       {
3412         /* only add event page for the first page found with this event */
3413         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3414         {
3415           ei->has_change_event[k] = TRUE;
3416
3417           ei->event_page_nr[k] = j;
3418           ei->event_page[k] = &ei->change_page[j];
3419         }
3420       }
3421     }
3422   }
3423
3424 #if 1
3425   /* ---------- initialize reference elements in change conditions --------- */
3426
3427   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3428   {
3429     int element = EL_CUSTOM_START + i;
3430     struct ElementInfo *ei = &element_info[element];
3431
3432     for (j = 0; j < ei->num_change_pages; j++)
3433     {
3434       int trigger_element = ei->change_page[j].initial_trigger_element;
3435
3436       if (trigger_element >= EL_PREV_CE_8 &&
3437           trigger_element <= EL_NEXT_CE_8)
3438         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3439
3440       ei->change_page[j].trigger_element = trigger_element;
3441     }
3442   }
3443 #endif
3444
3445   /* ---------- initialize run-time trigger player and element ------------- */
3446
3447   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3448   {
3449     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3450
3451     for (j = 0; j < ei->num_change_pages; j++)
3452     {
3453       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3454       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3455       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3456       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3457       ei->change_page[j].actual_trigger_ce_value = 0;
3458       ei->change_page[j].actual_trigger_ce_score = 0;
3459     }
3460   }
3461
3462   /* ---------- initialize trigger events ---------------------------------- */
3463
3464   /* initialize trigger events information */
3465   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3466     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3467       trigger_events[i][j] = FALSE;
3468
3469   /* add trigger events from element change event properties */
3470   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3471   {
3472     struct ElementInfo *ei = &element_info[i];
3473
3474     for (j = 0; j < ei->num_change_pages; j++)
3475     {
3476       if (!ei->change_page[j].can_change_or_has_action)
3477         continue;
3478
3479       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3480       {
3481         int trigger_element = ei->change_page[j].trigger_element;
3482
3483         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3484         {
3485           if (ei->change_page[j].has_event[k])
3486           {
3487             if (IS_GROUP_ELEMENT(trigger_element))
3488             {
3489               struct ElementGroupInfo *group =
3490                 element_info[trigger_element].group;
3491
3492               for (l = 0; l < group->num_elements_resolved; l++)
3493                 trigger_events[group->element_resolved[l]][k] = TRUE;
3494             }
3495             else if (trigger_element == EL_ANY_ELEMENT)
3496               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3497                 trigger_events[l][k] = TRUE;
3498             else
3499               trigger_events[trigger_element][k] = TRUE;
3500           }
3501         }
3502       }
3503     }
3504   }
3505
3506   /* ---------- initialize push delay -------------------------------------- */
3507
3508   /* initialize push delay values to default */
3509   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3510   {
3511     if (!IS_CUSTOM_ELEMENT(i))
3512     {
3513       /* set default push delay values (corrected since version 3.0.7-1) */
3514       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3515       {
3516         element_info[i].push_delay_fixed = 2;
3517         element_info[i].push_delay_random = 8;
3518       }
3519       else
3520       {
3521         element_info[i].push_delay_fixed = 8;
3522         element_info[i].push_delay_random = 8;
3523       }
3524     }
3525   }
3526
3527   /* set push delay value for certain elements from pre-defined list */
3528   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3529   {
3530     int e = push_delay_list[i].element;
3531
3532     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3533     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3534   }
3535
3536   /* set push delay value for Supaplex elements for newer engine versions */
3537   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3538   {
3539     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3540     {
3541       if (IS_SP_ELEMENT(i))
3542       {
3543         /* set SP push delay to just enough to push under a falling zonk */
3544         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3545
3546         element_info[i].push_delay_fixed  = delay;
3547         element_info[i].push_delay_random = 0;
3548       }
3549     }
3550   }
3551
3552   /* ---------- initialize move stepsize ----------------------------------- */
3553
3554   /* initialize move stepsize values to default */
3555   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3556     if (!IS_CUSTOM_ELEMENT(i))
3557       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3558
3559   /* set move stepsize value for certain elements from pre-defined list */
3560   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3561   {
3562     int e = move_stepsize_list[i].element;
3563
3564     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3565   }
3566
3567   /* ---------- initialize collect score ----------------------------------- */
3568
3569   /* initialize collect score values for custom elements from initial value */
3570   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3571     if (IS_CUSTOM_ELEMENT(i))
3572       element_info[i].collect_score = element_info[i].collect_score_initial;
3573
3574   /* ---------- initialize collect count ----------------------------------- */
3575
3576   /* initialize collect count values for non-custom elements */
3577   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3578     if (!IS_CUSTOM_ELEMENT(i))
3579       element_info[i].collect_count_initial = 0;
3580
3581   /* add collect count values for all elements from pre-defined list */
3582   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3583     element_info[collect_count_list[i].element].collect_count_initial =
3584       collect_count_list[i].count;
3585
3586   /* ---------- initialize access direction -------------------------------- */
3587
3588   /* initialize access direction values to default (access from every side) */
3589   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3590     if (!IS_CUSTOM_ELEMENT(i))
3591       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3592
3593   /* set access direction value for certain elements from pre-defined list */
3594   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3595     element_info[access_direction_list[i].element].access_direction =
3596       access_direction_list[i].direction;
3597
3598   /* ---------- initialize explosion content ------------------------------- */
3599   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3600   {
3601     if (IS_CUSTOM_ELEMENT(i))
3602       continue;
3603
3604     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3605     {
3606       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3607
3608       element_info[i].content.e[x][y] =
3609         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3610          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3611          i == EL_PLAYER_3 ? EL_EMERALD :
3612          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3613          i == EL_MOLE ? EL_EMERALD_RED :
3614          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3615          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3616          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3617          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3618          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3619          i == EL_WALL_EMERALD ? EL_EMERALD :
3620          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3621          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3622          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3623          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3624          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3625          i == EL_WALL_PEARL ? EL_PEARL :
3626          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3627          EL_EMPTY);
3628     }
3629   }
3630
3631   /* ---------- initialize recursion detection ------------------------------ */
3632   recursion_loop_depth = 0;
3633   recursion_loop_detected = FALSE;
3634   recursion_loop_element = EL_UNDEFINED;
3635
3636   /* ---------- initialize graphics engine ---------------------------------- */
3637   game.scroll_delay_value =
3638     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3639      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3640   game.scroll_delay_value =
3641     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3642 }
3643
3644 int get_num_special_action(int element, int action_first, int action_last)
3645 {
3646   int num_special_action = 0;
3647   int i, j;
3648
3649   for (i = action_first; i <= action_last; i++)
3650   {
3651     boolean found = FALSE;
3652
3653     for (j = 0; j < NUM_DIRECTIONS; j++)
3654       if (el_act_dir2img(element, i, j) !=
3655           el_act_dir2img(element, ACTION_DEFAULT, j))
3656         found = TRUE;
3657
3658     if (found)
3659       num_special_action++;
3660     else
3661       break;
3662   }
3663
3664   return num_special_action;
3665 }
3666
3667
3668 /*
3669   =============================================================================
3670   InitGame()
3671   -----------------------------------------------------------------------------
3672   initialize and start new game
3673   =============================================================================
3674 */
3675
3676 void InitGame()
3677 {
3678   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3679   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3680   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3681 #if 0
3682   boolean do_fading = (game_status == GAME_MODE_MAIN);
3683 #endif
3684 #if 1
3685   int initial_move_dir = MV_DOWN;
3686 #else
3687   int initial_move_dir = MV_NONE;
3688 #endif
3689   int i, j, x, y;
3690
3691   game_status = GAME_MODE_PLAYING;
3692
3693   InitGameEngine();
3694   InitGameControlValues();
3695
3696   /* don't play tapes over network */
3697   network_playing = (options.network && !tape.playing);
3698
3699   for (i = 0; i < MAX_PLAYERS; i++)
3700   {
3701     struct PlayerInfo *player = &stored_player[i];
3702
3703     player->index_nr = i;
3704     player->index_bit = (1 << i);
3705     player->element_nr = EL_PLAYER_1 + i;
3706
3707     player->present = FALSE;
3708     player->active = FALSE;
3709     player->killed = FALSE;
3710
3711     player->action = 0;
3712     player->effective_action = 0;
3713     player->programmed_action = 0;
3714
3715     player->score = 0;
3716     player->score_final = 0;
3717
3718     player->gems_still_needed = level.gems_needed;
3719     player->sokobanfields_still_needed = 0;
3720     player->lights_still_needed = 0;
3721     player->friends_still_needed = 0;
3722
3723     for (j = 0; j < MAX_NUM_KEYS; j++)
3724       player->key[j] = FALSE;
3725
3726     player->num_white_keys = 0;
3727
3728     player->dynabomb_count = 0;
3729     player->dynabomb_size = 1;
3730     player->dynabombs_left = 0;
3731     player->dynabomb_xl = FALSE;
3732
3733     player->MovDir = initial_move_dir;
3734     player->MovPos = 0;
3735     player->GfxPos = 0;
3736     player->GfxDir = initial_move_dir;
3737     player->GfxAction = ACTION_DEFAULT;
3738     player->Frame = 0;
3739     player->StepFrame = 0;
3740
3741     player->initial_element = player->element_nr;
3742     player->artwork_element =
3743       (level.use_artwork_element[i] ? level.artwork_element[i] :
3744        player->element_nr);
3745     player->use_murphy = FALSE;
3746
3747     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3748     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3749
3750     player->gravity = level.initial_player_gravity[i];
3751
3752     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3753
3754     player->actual_frame_counter = 0;
3755
3756     player->step_counter = 0;
3757
3758     player->last_move_dir = initial_move_dir;
3759
3760     player->is_active = FALSE;
3761
3762     player->is_waiting = FALSE;
3763     player->is_moving = FALSE;
3764     player->is_auto_moving = FALSE;
3765     player->is_digging = FALSE;
3766     player->is_snapping = FALSE;
3767     player->is_collecting = FALSE;
3768     player->is_pushing = FALSE;
3769     player->is_switching = FALSE;
3770     player->is_dropping = FALSE;
3771     player->is_dropping_pressed = FALSE;
3772
3773     player->is_bored = FALSE;
3774     player->is_sleeping = FALSE;
3775
3776     player->frame_counter_bored = -1;
3777     player->frame_counter_sleeping = -1;
3778
3779     player->anim_delay_counter = 0;
3780     player->post_delay_counter = 0;
3781
3782     player->dir_waiting = initial_move_dir;
3783     player->action_waiting = ACTION_DEFAULT;
3784     player->last_action_waiting = ACTION_DEFAULT;
3785     player->special_action_bored = ACTION_DEFAULT;
3786     player->special_action_sleeping = ACTION_DEFAULT;
3787
3788     player->switch_x = -1;
3789     player->switch_y = -1;
3790
3791     player->drop_x = -1;
3792     player->drop_y = -1;
3793
3794     player->show_envelope = 0;
3795
3796     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3797
3798     player->push_delay       = -1;      /* initialized when pushing starts */
3799     player->push_delay_value = game.initial_push_delay_value;
3800
3801     player->drop_delay = 0;
3802     player->drop_pressed_delay = 0;
3803
3804     player->last_jx = -1;
3805     player->last_jy = -1;
3806     player->jx = -1;
3807     player->jy = -1;
3808
3809     player->shield_normal_time_left = 0;
3810     player->shield_deadly_time_left = 0;
3811
3812     player->inventory_infinite_element = EL_UNDEFINED;
3813     player->inventory_size = 0;
3814
3815     if (level.use_initial_inventory[i])
3816     {
3817       for (j = 0; j < level.initial_inventory_size[i]; j++)
3818       {
3819         int element = level.initial_inventory_content[i][j];
3820         int collect_count = element_info[element].collect_count_initial;
3821         int k;
3822
3823         if (!IS_CUSTOM_ELEMENT(element))
3824           collect_count = 1;
3825
3826         if (collect_count == 0)
3827           player->inventory_infinite_element = element;
3828         else
3829           for (k = 0; k < collect_count; k++)
3830             if (player->inventory_size < MAX_INVENTORY_SIZE)
3831               player->inventory_element[player->inventory_size++] = element;
3832       }
3833     }
3834
3835     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3836     SnapField(player, 0, 0);
3837
3838     player->LevelSolved = FALSE;
3839     player->GameOver = FALSE;
3840
3841     player->LevelSolved_GameWon = FALSE;
3842     player->LevelSolved_GameEnd = FALSE;
3843     player->LevelSolved_PanelOff = FALSE;
3844     player->LevelSolved_SaveTape = FALSE;
3845     player->LevelSolved_SaveScore = FALSE;
3846     player->LevelSolved_CountingTime = 0;
3847     player->LevelSolved_CountingScore = 0;
3848   }
3849
3850   network_player_action_received = FALSE;
3851
3852 #if defined(NETWORK_AVALIABLE)
3853   /* initial null action */
3854   if (network_playing)
3855     SendToServer_MovePlayer(MV_NONE);
3856 #endif
3857
3858   ZX = ZY = -1;
3859   ExitX = ExitY = -1;
3860
3861   FrameCounter = 0;
3862   TimeFrames = 0;
3863   TimePlayed = 0;
3864   TimeLeft = level.time;
3865   TapeTime = 0;
3866
3867   ScreenMovDir = MV_NONE;
3868   ScreenMovPos = 0;
3869   ScreenGfxPos = 0;
3870
3871   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3872
3873   AllPlayersGone = FALSE;
3874
3875   game.yamyam_content_nr = 0;
3876   game.robot_wheel_active = FALSE;
3877   game.magic_wall_active = FALSE;
3878   game.magic_wall_time_left = 0;
3879   game.light_time_left = 0;
3880   game.timegate_time_left = 0;
3881   game.switchgate_pos = 0;
3882   game.wind_direction = level.wind_direction_initial;
3883
3884 #if !USE_PLAYER_GRAVITY
3885   game.gravity = FALSE;
3886   game.explosions_delayed = TRUE;
3887 #endif
3888
3889   game.lenses_time_left = 0;
3890   game.magnify_time_left = 0;
3891
3892   game.ball_state = level.ball_state_initial;
3893   game.ball_content_nr = 0;
3894
3895   game.envelope_active = FALSE;
3896
3897   /* set focus to local player for network games, else to all players */
3898   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3899   game.centered_player_nr_next = game.centered_player_nr;
3900   game.set_centered_player = FALSE;
3901
3902   if (network_playing && tape.recording)
3903   {
3904     /* store client dependent player focus when recording network games */
3905     tape.centered_player_nr_next = game.centered_player_nr_next;
3906     tape.set_centered_player = TRUE;
3907   }
3908
3909   for (i = 0; i < NUM_BELTS; i++)
3910   {
3911     game.belt_dir[i] = MV_NONE;
3912     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3913   }
3914
3915   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3916     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3917
3918   SCAN_PLAYFIELD(x, y)
3919   {
3920     Feld[x][y] = level.field[x][y];
3921     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3922     ChangeDelay[x][y] = 0;
3923     ChangePage[x][y] = -1;
3924 #if USE_NEW_CUSTOM_VALUE
3925     CustomValue[x][y] = 0;              /* initialized in InitField() */
3926 #endif
3927     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3928     AmoebaNr[x][y] = 0;
3929     WasJustMoving[x][y] = 0;
3930     WasJustFalling[x][y] = 0;
3931     CheckCollision[x][y] = 0;
3932     CheckImpact[x][y] = 0;
3933     Stop[x][y] = FALSE;
3934     Pushed[x][y] = FALSE;
3935
3936     ChangeCount[x][y] = 0;
3937     ChangeEvent[x][y] = -1;
3938
3939     ExplodePhase[x][y] = 0;
3940     ExplodeDelay[x][y] = 0;
3941     ExplodeField[x][y] = EX_TYPE_NONE;
3942
3943     RunnerVisit[x][y] = 0;
3944     PlayerVisit[x][y] = 0;
3945
3946     GfxFrame[x][y] = 0;
3947     GfxRandom[x][y] = INIT_GFX_RANDOM();
3948     GfxElement[x][y] = EL_UNDEFINED;
3949     GfxAction[x][y] = ACTION_DEFAULT;
3950     GfxDir[x][y] = MV_NONE;
3951     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3952   }
3953
3954   SCAN_PLAYFIELD(x, y)
3955   {
3956     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3957       emulate_bd = FALSE;
3958     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3959       emulate_sb = FALSE;
3960     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3961       emulate_sp = FALSE;
3962
3963     InitField(x, y, TRUE);
3964
3965     ResetGfxAnimation(x, y);
3966   }
3967
3968   InitBeltMovement();
3969
3970   for (i = 0; i < MAX_PLAYERS; i++)
3971   {
3972     struct PlayerInfo *player = &stored_player[i];
3973
3974     /* set number of special actions for bored and sleeping animation */
3975     player->num_special_action_bored =
3976       get_num_special_action(player->artwork_element,
3977                              ACTION_BORING_1, ACTION_BORING_LAST);
3978     player->num_special_action_sleeping =
3979       get_num_special_action(player->artwork_element,
3980                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3981   }
3982
3983   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3984                     emulate_sb ? EMU_SOKOBAN :
3985                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3986
3987 #if USE_NEW_ALL_SLIPPERY
3988   /* initialize type of slippery elements */
3989   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3990   {
3991     if (!IS_CUSTOM_ELEMENT(i))
3992     {
3993       /* default: elements slip down either to the left or right randomly */
3994       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3995
3996       /* SP style elements prefer to slip down on the left side */
3997       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3998         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3999
4000       /* BD style elements prefer to slip down on the left side */
4001       if (game.emulation == EMU_BOULDERDASH)
4002         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4003     }
4004   }
4005 #endif
4006
4007   /* initialize explosion and ignition delay */
4008   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4009   {
4010     if (!IS_CUSTOM_ELEMENT(i))
4011     {
4012       int num_phase = 8;
4013       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4014                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4015                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4016       int last_phase = (num_phase + 1) * delay;
4017       int half_phase = (num_phase / 2) * delay;
4018
4019       element_info[i].explosion_delay = last_phase - 1;
4020       element_info[i].ignition_delay = half_phase;
4021
4022       if (i == EL_BLACK_ORB)
4023         element_info[i].ignition_delay = 1;
4024     }
4025
4026 #if 0
4027     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4028       element_info[i].explosion_delay = 1;
4029
4030     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4031       element_info[i].ignition_delay = 1;
4032 #endif
4033   }
4034
4035   /* correct non-moving belts to start moving left */
4036   for (i = 0; i < NUM_BELTS; i++)
4037     if (game.belt_dir[i] == MV_NONE)
4038       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4039
4040   /* check if any connected player was not found in playfield */
4041   for (i = 0; i < MAX_PLAYERS; i++)
4042   {
4043     struct PlayerInfo *player = &stored_player[i];
4044
4045     if (player->connected && !player->present)
4046     {
4047       for (j = 0; j < MAX_PLAYERS; j++)
4048       {
4049         struct PlayerInfo *some_player = &stored_player[j];
4050         int jx = some_player->jx, jy = some_player->jy;
4051
4052         /* assign first free player found that is present in the playfield */
4053         if (some_player->present && !some_player->connected)
4054         {
4055           player->present = TRUE;
4056           player->active = TRUE;
4057
4058           some_player->present = FALSE;
4059           some_player->active = FALSE;
4060
4061           player->initial_element = some_player->initial_element;
4062           player->artwork_element = some_player->artwork_element;
4063
4064           player->block_last_field       = some_player->block_last_field;
4065           player->block_delay_adjustment = some_player->block_delay_adjustment;
4066
4067           StorePlayer[jx][jy] = player->element_nr;
4068           player->jx = player->last_jx = jx;
4069           player->jy = player->last_jy = jy;
4070
4071           break;
4072         }
4073       }
4074     }
4075   }
4076
4077   if (tape.playing)
4078   {
4079     /* when playing a tape, eliminate all players who do not participate */
4080
4081     for (i = 0; i < MAX_PLAYERS; i++)
4082     {
4083       if (stored_player[i].active && !tape.player_participates[i])
4084       {
4085         struct PlayerInfo *player = &stored_player[i];
4086         int jx = player->jx, jy = player->jy;
4087
4088         player->active = FALSE;
4089         StorePlayer[jx][jy] = 0;
4090         Feld[jx][jy] = EL_EMPTY;
4091       }
4092     }
4093   }
4094   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4095   {
4096     /* when in single player mode, eliminate all but the first active player */
4097
4098     for (i = 0; i < MAX_PLAYERS; i++)
4099     {
4100       if (stored_player[i].active)
4101       {
4102         for (j = i + 1; j < MAX_PLAYERS; j++)
4103         {
4104           if (stored_player[j].active)
4105           {
4106             struct PlayerInfo *player = &stored_player[j];
4107             int jx = player->jx, jy = player->jy;
4108
4109             player->active = FALSE;
4110             player->present = FALSE;
4111
4112             StorePlayer[jx][jy] = 0;
4113             Feld[jx][jy] = EL_EMPTY;
4114           }
4115         }
4116       }
4117     }
4118   }
4119
4120   /* when recording the game, store which players take part in the game */
4121   if (tape.recording)
4122   {
4123     for (i = 0; i < MAX_PLAYERS; i++)
4124       if (stored_player[i].active)
4125         tape.player_participates[i] = TRUE;
4126   }
4127
4128   if (options.debug)
4129   {
4130     for (i = 0; i < MAX_PLAYERS; i++)
4131     {
4132       struct PlayerInfo *player = &stored_player[i];
4133
4134       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4135              i+1,
4136              player->present,
4137              player->connected,
4138              player->active);
4139       if (local_player == player)
4140         printf("Player  %d is local player.\n", i+1);
4141     }
4142   }
4143
4144   if (BorderElement == EL_EMPTY)
4145   {
4146     SBX_Left = 0;
4147     SBX_Right = lev_fieldx - SCR_FIELDX;
4148     SBY_Upper = 0;
4149     SBY_Lower = lev_fieldy - SCR_FIELDY;
4150   }
4151   else
4152   {
4153     SBX_Left = -1;
4154     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4155     SBY_Upper = -1;
4156     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4157   }
4158
4159   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4160     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4161
4162   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4163     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4164
4165   /* if local player not found, look for custom element that might create
4166      the player (make some assumptions about the right custom element) */
4167   if (!local_player->present)
4168   {
4169     int start_x = 0, start_y = 0;
4170     int found_rating = 0;
4171     int found_element = EL_UNDEFINED;
4172     int player_nr = local_player->index_nr;
4173
4174     SCAN_PLAYFIELD(x, y)
4175     {
4176       int element = Feld[x][y];
4177       int content;
4178       int xx, yy;
4179       boolean is_player;
4180
4181       if (level.use_start_element[player_nr] &&
4182           level.start_element[player_nr] == element &&
4183           found_rating < 4)
4184       {
4185         start_x = x;
4186         start_y = y;
4187
4188         found_rating = 4;
4189         found_element = element;
4190       }
4191
4192       if (!IS_CUSTOM_ELEMENT(element))
4193         continue;
4194
4195       if (CAN_CHANGE(element))
4196       {
4197         for (i = 0; i < element_info[element].num_change_pages; i++)
4198         {
4199           /* check for player created from custom element as single target */
4200           content = element_info[element].change_page[i].target_element;
4201           is_player = ELEM_IS_PLAYER(content);
4202
4203           if (is_player && (found_rating < 3 ||
4204                             (found_rating == 3 && element < found_element)))
4205           {
4206             start_x = x;
4207             start_y = y;
4208
4209             found_rating = 3;
4210             found_element = element;
4211           }
4212         }
4213       }
4214
4215       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4216       {
4217         /* check for player created from custom element as explosion content */
4218         content = element_info[element].content.e[xx][yy];
4219         is_player = ELEM_IS_PLAYER(content);
4220
4221         if (is_player && (found_rating < 2 ||
4222                           (found_rating == 2 && element < found_element)))
4223         {
4224           start_x = x + xx - 1;
4225           start_y = y + yy - 1;
4226
4227           found_rating = 2;
4228           found_element = element;
4229         }
4230
4231         if (!CAN_CHANGE(element))
4232           continue;
4233
4234         for (i = 0; i < element_info[element].num_change_pages; i++)
4235         {
4236           /* check for player created from custom element as extended target */
4237           content =
4238             element_info[element].change_page[i].target_content.e[xx][yy];
4239
4240           is_player = ELEM_IS_PLAYER(content);
4241
4242           if (is_player && (found_rating < 1 ||
4243                             (found_rating == 1 && element < found_element)))
4244           {
4245             start_x = x + xx - 1;
4246             start_y = y + yy - 1;
4247
4248             found_rating = 1;
4249             found_element = element;
4250           }
4251         }
4252       }
4253     }
4254
4255     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4256                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4257                 start_x - MIDPOSX);
4258
4259     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4260                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4261                 start_y - MIDPOSY);
4262   }
4263   else
4264   {
4265     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4266                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4267                 local_player->jx - MIDPOSX);
4268
4269     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4270                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4271                 local_player->jy - MIDPOSY);
4272   }
4273
4274 #if 0
4275   /* do not use PLAYING mask for fading out from main screen */
4276   game_status = GAME_MODE_MAIN;
4277 #endif
4278
4279   StopAnimation();
4280
4281   if (!game.restart_level)
4282     CloseDoor(DOOR_CLOSE_1);
4283
4284 #if 1
4285   if (level_editor_test_game)
4286     FadeSkipNextFadeIn();
4287   else
4288     FadeSetEnterScreen();
4289 #else
4290   if (level_editor_test_game)
4291     fading = fading_none;
4292   else
4293     fading = menu.destination;
4294 #endif
4295
4296 #if 1
4297   FadeOut(REDRAW_FIELD);
4298 #else
4299   if (do_fading)
4300     FadeOut(REDRAW_FIELD);
4301 #endif
4302
4303 #if 0
4304   game_status = GAME_MODE_PLAYING;
4305 #endif
4306
4307   /* !!! FIX THIS (START) !!! */
4308   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4309   {
4310     InitGameEngine_EM();
4311
4312     /* blit playfield from scroll buffer to normal back buffer for fading in */
4313     BlitScreenToBitmap_EM(backbuffer);
4314   }
4315   else
4316   {
4317     DrawLevel();
4318     DrawAllPlayers();
4319
4320     /* after drawing the level, correct some elements */
4321     if (game.timegate_time_left == 0)
4322       CloseAllOpenTimegates();
4323
4324     /* blit playfield from scroll buffer to normal back buffer for fading in */
4325     if (setup.soft_scrolling)
4326       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4327
4328     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4329   }
4330   /* !!! FIX THIS (END) !!! */
4331
4332 #if 1
4333   FadeIn(REDRAW_FIELD);
4334 #else
4335   if (do_fading)
4336     FadeIn(REDRAW_FIELD);
4337
4338   BackToFront();
4339 #endif
4340
4341   if (!game.restart_level)
4342   {
4343     /* copy default game door content to main double buffer */
4344     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4345                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4346   }
4347
4348   SetPanelBackground();
4349   SetDrawBackgroundMask(REDRAW_DOOR_1);
4350
4351 #if 1
4352   UpdateAndDisplayGameControlValues();
4353 #else
4354   UpdateGameDoorValues();
4355   DrawGameDoorValues();
4356 #endif
4357
4358   if (!game.restart_level)
4359   {
4360     UnmapGameButtons();
4361     UnmapTapeButtons();
4362     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4363     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4364     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4365     MapGameButtons();
4366     MapTapeButtons();
4367
4368     /* copy actual game door content to door double buffer for OpenDoor() */
4369     BlitBitmap(drawto, bitmap_db_door,
4370                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4371
4372     OpenDoor(DOOR_OPEN_ALL);
4373
4374     PlaySound(SND_GAME_STARTING);
4375
4376     if (setup.sound_music)
4377       PlayLevelMusic();
4378
4379     KeyboardAutoRepeatOffUnlessAutoplay();
4380
4381     if (options.debug)
4382     {
4383       for (i = 0; i < MAX_PLAYERS; i++)
4384         printf("Player %d %sactive.\n",
4385                i + 1, (stored_player[i].active ? "" : "not "));
4386     }
4387   }
4388
4389 #if 1
4390   UnmapAllGadgets();
4391
4392   MapGameButtons();
4393   MapTapeButtons();
4394 #endif
4395
4396   game.restart_level = FALSE;
4397 }
4398
4399 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4400 {
4401   /* this is used for non-R'n'D game engines to update certain engine values */
4402
4403   /* needed to determine if sounds are played within the visible screen area */
4404   scroll_x = actual_scroll_x;
4405   scroll_y = actual_scroll_y;
4406 }
4407
4408 void InitMovDir(int x, int y)
4409 {
4410   int i, element = Feld[x][y];
4411   static int xy[4][2] =
4412   {
4413     {  0, +1 },
4414     { +1,  0 },
4415     {  0, -1 },
4416     { -1,  0 }
4417   };
4418   static int direction[3][4] =
4419   {
4420     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4421     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4422     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4423   };
4424
4425   switch (element)
4426   {
4427     case EL_BUG_RIGHT:
4428     case EL_BUG_UP:
4429     case EL_BUG_LEFT:
4430     case EL_BUG_DOWN:
4431       Feld[x][y] = EL_BUG;
4432       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4433       break;
4434
4435     case EL_SPACESHIP_RIGHT:
4436     case EL_SPACESHIP_UP:
4437     case EL_SPACESHIP_LEFT:
4438     case EL_SPACESHIP_DOWN:
4439       Feld[x][y] = EL_SPACESHIP;
4440       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4441       break;
4442
4443     case EL_BD_BUTTERFLY_RIGHT:
4444     case EL_BD_BUTTERFLY_UP:
4445     case EL_BD_BUTTERFLY_LEFT:
4446     case EL_BD_BUTTERFLY_DOWN:
4447       Feld[x][y] = EL_BD_BUTTERFLY;
4448       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4449       break;
4450
4451     case EL_BD_FIREFLY_RIGHT:
4452     case EL_BD_FIREFLY_UP:
4453     case EL_BD_FIREFLY_LEFT:
4454     case EL_BD_FIREFLY_DOWN:
4455       Feld[x][y] = EL_BD_FIREFLY;
4456       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4457       break;
4458
4459     case EL_PACMAN_RIGHT:
4460     case EL_PACMAN_UP:
4461     case EL_PACMAN_LEFT:
4462     case EL_PACMAN_DOWN:
4463       Feld[x][y] = EL_PACMAN;
4464       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4465       break;
4466
4467     case EL_YAMYAM_LEFT:
4468     case EL_YAMYAM_RIGHT:
4469     case EL_YAMYAM_UP:
4470     case EL_YAMYAM_DOWN:
4471       Feld[x][y] = EL_YAMYAM;
4472       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4473       break;
4474
4475     case EL_SP_SNIKSNAK:
4476       MovDir[x][y] = MV_UP;
4477       break;
4478
4479     case EL_SP_ELECTRON:
4480       MovDir[x][y] = MV_LEFT;
4481       break;
4482
4483     case EL_MOLE_LEFT:
4484     case EL_MOLE_RIGHT:
4485     case EL_MOLE_UP:
4486     case EL_MOLE_DOWN:
4487       Feld[x][y] = EL_MOLE;
4488       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4489       break;
4490
4491     default:
4492       if (IS_CUSTOM_ELEMENT(element))
4493       {
4494         struct ElementInfo *ei = &element_info[element];
4495         int move_direction_initial = ei->move_direction_initial;
4496         int move_pattern = ei->move_pattern;
4497
4498         if (move_direction_initial == MV_START_PREVIOUS)
4499         {
4500           if (MovDir[x][y] != MV_NONE)
4501             return;
4502
4503           move_direction_initial = MV_START_AUTOMATIC;
4504         }
4505
4506         if (move_direction_initial == MV_START_RANDOM)
4507           MovDir[x][y] = 1 << RND(4);
4508         else if (move_direction_initial & MV_ANY_DIRECTION)
4509           MovDir[x][y] = move_direction_initial;
4510         else if (move_pattern == MV_ALL_DIRECTIONS ||
4511                  move_pattern == MV_TURNING_LEFT ||
4512                  move_pattern == MV_TURNING_RIGHT ||
4513                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4514                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4515                  move_pattern == MV_TURNING_RANDOM)
4516           MovDir[x][y] = 1 << RND(4);
4517         else if (move_pattern == MV_HORIZONTAL)
4518           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4519         else if (move_pattern == MV_VERTICAL)
4520           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4521         else if (move_pattern & MV_ANY_DIRECTION)
4522           MovDir[x][y] = element_info[element].move_pattern;
4523         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4524                  move_pattern == MV_ALONG_RIGHT_SIDE)
4525         {
4526           /* use random direction as default start direction */
4527           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4528             MovDir[x][y] = 1 << RND(4);
4529
4530           for (i = 0; i < NUM_DIRECTIONS; i++)
4531           {
4532             int x1 = x + xy[i][0];
4533             int y1 = y + xy[i][1];
4534
4535             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4536             {
4537               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4538                 MovDir[x][y] = direction[0][i];
4539               else
4540                 MovDir[x][y] = direction[1][i];
4541
4542               break;
4543             }
4544           }
4545         }                
4546       }
4547       else
4548       {
4549         MovDir[x][y] = 1 << RND(4);
4550
4551         if (element != EL_BUG &&
4552             element != EL_SPACESHIP &&
4553             element != EL_BD_BUTTERFLY &&
4554             element != EL_BD_FIREFLY)
4555           break;
4556
4557         for (i = 0; i < NUM_DIRECTIONS; i++)
4558         {
4559           int x1 = x + xy[i][0];
4560           int y1 = y + xy[i][1];
4561
4562           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4563           {
4564             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4565             {
4566               MovDir[x][y] = direction[0][i];
4567               break;
4568             }
4569             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4570                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4571             {
4572               MovDir[x][y] = direction[1][i];
4573               break;
4574             }
4575           }
4576         }
4577       }
4578       break;
4579   }
4580
4581   GfxDir[x][y] = MovDir[x][y];
4582 }
4583
4584 void InitAmoebaNr(int x, int y)
4585 {
4586   int i;
4587   int group_nr = AmoebeNachbarNr(x, y);
4588
4589   if (group_nr == 0)
4590   {
4591     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4592     {
4593       if (AmoebaCnt[i] == 0)
4594       {
4595         group_nr = i;
4596         break;
4597       }
4598     }
4599   }
4600
4601   AmoebaNr[x][y] = group_nr;
4602   AmoebaCnt[group_nr]++;
4603   AmoebaCnt2[group_nr]++;
4604 }
4605
4606 static void PlayerWins(struct PlayerInfo *player)
4607 {
4608   player->LevelSolved = TRUE;
4609   player->GameOver = TRUE;
4610
4611   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4612                          level.native_em_level->lev->score : player->score);
4613
4614   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4615   player->LevelSolved_CountingScore = player->score_final;
4616 }
4617
4618 void GameWon()
4619 {
4620   static int time, time_final;
4621   static int score, score_final;
4622   static int game_over_delay_1 = 0;
4623   static int game_over_delay_2 = 0;
4624   int game_over_delay_value_1 = 50;
4625   int game_over_delay_value_2 = 50;
4626
4627   if (!local_player->LevelSolved_GameWon)
4628   {
4629     int i;
4630
4631     /* do not start end game actions before the player stops moving (to exit) */
4632     if (local_player->MovPos)
4633       return;
4634
4635     local_player->LevelSolved_GameWon = TRUE;
4636     local_player->LevelSolved_SaveTape = tape.recording;
4637     local_player->LevelSolved_SaveScore = !tape.playing;
4638
4639     if (tape.auto_play)         /* tape might already be stopped here */
4640       tape.auto_play_level_solved = TRUE;
4641
4642 #if 1
4643     TapeStop();
4644 #endif
4645
4646     game_over_delay_1 = game_over_delay_value_1;
4647     game_over_delay_2 = game_over_delay_value_2;
4648
4649     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4650     score = score_final = local_player->score_final;
4651
4652     if (TimeLeft > 0)
4653     {
4654       time_final = 0;
4655       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4656     }
4657     else if (level.time == 0 && TimePlayed < 999)
4658     {
4659       time_final = 999;
4660       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4661     }
4662
4663     local_player->score_final = score_final;
4664
4665     if (level_editor_test_game)
4666     {
4667       time = time_final;
4668       score = score_final;
4669
4670 #if 1
4671       local_player->LevelSolved_CountingTime = time;
4672       local_player->LevelSolved_CountingScore = score;
4673
4674       game_panel_controls[GAME_PANEL_TIME].value = time;
4675       game_panel_controls[GAME_PANEL_SCORE].value = score;
4676
4677       DisplayGameControlValues();
4678 #else
4679       DrawGameValue_Time(time);
4680       DrawGameValue_Score(score);
4681 #endif
4682     }
4683
4684     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4685     {
4686       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4687       {
4688         /* close exit door after last player */
4689         if ((AllPlayersGone &&
4690              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4691               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4692               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4693             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4694             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4695         {
4696           int element = Feld[ExitX][ExitY];
4697
4698 #if 0
4699           if (element == EL_EM_EXIT_OPEN ||
4700               element == EL_EM_STEEL_EXIT_OPEN)
4701           {
4702             Bang(ExitX, ExitY);
4703           }
4704           else
4705 #endif
4706           {
4707             Feld[ExitX][ExitY] =
4708               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4709                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4710                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4711                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4712                EL_EM_STEEL_EXIT_CLOSING);
4713
4714             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4715           }
4716         }
4717
4718         /* player disappears */
4719         DrawLevelField(ExitX, ExitY);
4720       }
4721
4722       for (i = 0; i < MAX_PLAYERS; i++)
4723       {
4724         struct PlayerInfo *player = &stored_player[i];
4725
4726         if (player->present)
4727         {
4728           RemovePlayer(player);
4729
4730           /* player disappears */
4731           DrawLevelField(player->jx, player->jy);
4732         }
4733       }
4734     }
4735
4736     PlaySound(SND_GAME_WINNING);
4737   }
4738
4739   if (game_over_delay_1 > 0)
4740   {
4741     game_over_delay_1--;
4742
4743     return;
4744   }
4745
4746   if (time != time_final)
4747   {
4748     int time_to_go = ABS(time_final - time);
4749     int time_count_dir = (time < time_final ? +1 : -1);
4750     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4751
4752     time  += time_count_steps * time_count_dir;
4753     score += time_count_steps * level.score[SC_TIME_BONUS];
4754
4755 #if 1
4756     local_player->LevelSolved_CountingTime = time;
4757     local_player->LevelSolved_CountingScore = score;
4758
4759     game_panel_controls[GAME_PANEL_TIME].value = time;
4760     game_panel_controls[GAME_PANEL_SCORE].value = score;
4761
4762     DisplayGameControlValues();
4763 #else
4764     DrawGameValue_Time(time);
4765     DrawGameValue_Score(score);
4766 #endif
4767
4768     if (time == time_final)
4769       StopSound(SND_GAME_LEVELTIME_BONUS);
4770     else if (setup.sound_loops)
4771       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4772     else
4773       PlaySound(SND_GAME_LEVELTIME_BONUS);
4774
4775     return;
4776   }
4777
4778   local_player->LevelSolved_PanelOff = TRUE;
4779
4780   if (game_over_delay_2 > 0)
4781   {
4782     game_over_delay_2--;
4783
4784     return;
4785   }
4786
4787 #if 1
4788   GameEnd();
4789 #endif
4790 }
4791
4792 void GameEnd()
4793 {
4794   int hi_pos;
4795   boolean raise_level = FALSE;
4796
4797   local_player->LevelSolved_GameEnd = TRUE;
4798
4799   CloseDoor(DOOR_CLOSE_1);
4800
4801   if (local_player->LevelSolved_SaveTape)
4802   {
4803 #if 0
4804     TapeStop();
4805 #endif
4806
4807 #if 1
4808     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4809 #else
4810     SaveTape(tape.level_nr);            /* ask to save tape */
4811 #endif
4812   }
4813
4814   if (level_editor_test_game)
4815   {
4816     game_status = GAME_MODE_MAIN;
4817
4818 #if 1
4819     DrawAndFadeInMainMenu(REDRAW_FIELD);
4820 #else
4821     DrawMainMenu();
4822 #endif
4823
4824     return;
4825   }
4826
4827   if (!local_player->LevelSolved_SaveScore)
4828   {
4829 #if 1
4830     FadeOut(REDRAW_FIELD);
4831 #endif
4832
4833     game_status = GAME_MODE_MAIN;
4834
4835     DrawAndFadeInMainMenu(REDRAW_FIELD);
4836
4837     return;
4838   }
4839
4840   if (level_nr == leveldir_current->handicap_level)
4841   {
4842     leveldir_current->handicap_level++;
4843     SaveLevelSetup_SeriesInfo();
4844   }
4845
4846   if (level_nr < leveldir_current->last_level)
4847     raise_level = TRUE;                 /* advance to next level */
4848
4849   if ((hi_pos = NewHiScore()) >= 0) 
4850   {
4851     game_status = GAME_MODE_SCORES;
4852
4853     DrawHallOfFame(hi_pos);
4854
4855     if (raise_level)
4856     {
4857       level_nr++;
4858       TapeErase();
4859     }
4860   }
4861   else
4862   {
4863 #if 1
4864     FadeOut(REDRAW_FIELD);
4865 #endif
4866
4867     game_status = GAME_MODE_MAIN;
4868
4869     if (raise_level)
4870     {
4871       level_nr++;
4872       TapeErase();
4873     }
4874
4875     DrawAndFadeInMainMenu(REDRAW_FIELD);
4876   }
4877 }
4878
4879 int NewHiScore()
4880 {
4881   int k, l;
4882   int position = -1;
4883
4884   LoadScore(level_nr);
4885
4886   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4887       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4888     return -1;
4889
4890   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4891   {
4892     if (local_player->score_final > highscore[k].Score)
4893     {
4894       /* player has made it to the hall of fame */
4895
4896       if (k < MAX_SCORE_ENTRIES - 1)
4897       {
4898         int m = MAX_SCORE_ENTRIES - 1;
4899
4900 #ifdef ONE_PER_NAME
4901         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4902           if (strEqual(setup.player_name, highscore[l].Name))
4903             m = l;
4904         if (m == k)     /* player's new highscore overwrites his old one */
4905           goto put_into_list;
4906 #endif
4907
4908         for (l = m; l > k; l--)
4909         {
4910           strcpy(highscore[l].Name, highscore[l - 1].Name);
4911           highscore[l].Score = highscore[l - 1].Score;
4912         }
4913       }
4914
4915 #ifdef ONE_PER_NAME
4916       put_into_list:
4917 #endif
4918       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4919       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4920       highscore[k].Score = local_player->score_final; 
4921       position = k;
4922       break;
4923     }
4924
4925 #ifdef ONE_PER_NAME
4926     else if (!strncmp(setup.player_name, highscore[k].Name,
4927                       MAX_PLAYER_NAME_LEN))
4928       break;    /* player already there with a higher score */
4929 #endif
4930
4931   }
4932
4933   if (position >= 0) 
4934     SaveScore(level_nr);
4935
4936   return position;
4937 }
4938
4939 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4940 {
4941   int element = Feld[x][y];
4942   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4943   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4944   int horiz_move = (dx != 0);
4945   int sign = (horiz_move ? dx : dy);
4946   int step = sign * element_info[element].move_stepsize;
4947
4948   /* special values for move stepsize for spring and things on conveyor belt */
4949   if (horiz_move)
4950   {
4951     if (CAN_FALL(element) &&
4952         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4953       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4954     else if (element == EL_SPRING)
4955       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4956   }
4957
4958   return step;
4959 }
4960
4961 inline static int getElementMoveStepsize(int x, int y)
4962 {
4963   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4964 }
4965
4966 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4967 {
4968   if (player->GfxAction != action || player->GfxDir != dir)
4969   {
4970 #if 0
4971     printf("Player frame reset! (%d => %d, %d => %d)\n",
4972            player->GfxAction, action, player->GfxDir, dir);
4973 #endif
4974
4975     player->GfxAction = action;
4976     player->GfxDir = dir;
4977     player->Frame = 0;
4978     player->StepFrame = 0;
4979   }
4980 }
4981
4982 #if USE_GFX_RESET_GFX_ANIMATION
4983 static void ResetGfxFrame(int x, int y, boolean redraw)
4984 {
4985   int element = Feld[x][y];
4986   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4987   int last_gfx_frame = GfxFrame[x][y];
4988
4989   if (graphic_info[graphic].anim_global_sync)
4990     GfxFrame[x][y] = FrameCounter;
4991   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4992     GfxFrame[x][y] = CustomValue[x][y];
4993   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4994     GfxFrame[x][y] = element_info[element].collect_score;
4995   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4996     GfxFrame[x][y] = ChangeDelay[x][y];
4997
4998   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4999     DrawLevelGraphicAnimation(x, y, graphic);
5000 }
5001 #endif
5002
5003 static void ResetGfxAnimation(int x, int y)
5004 {
5005   GfxAction[x][y] = ACTION_DEFAULT;
5006   GfxDir[x][y] = MovDir[x][y];
5007   GfxFrame[x][y] = 0;
5008
5009 #if USE_GFX_RESET_GFX_ANIMATION
5010   ResetGfxFrame(x, y, FALSE);
5011 #endif
5012 }
5013
5014 static void ResetRandomAnimationValue(int x, int y)
5015 {
5016   GfxRandom[x][y] = INIT_GFX_RANDOM();
5017 }
5018
5019 void InitMovingField(int x, int y, int direction)
5020 {
5021   int element = Feld[x][y];
5022   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5023   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5024   int newx = x + dx;
5025   int newy = y + dy;
5026   boolean is_moving_before, is_moving_after;
5027 #if 0
5028   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5029 #endif
5030
5031   /* check if element was/is moving or being moved before/after mode change */
5032 #if 1
5033 #if 1
5034   is_moving_before = (WasJustMoving[x][y] != 0);
5035 #else
5036   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5037   is_moving_before = WasJustMoving[x][y];
5038 #endif
5039 #else
5040   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5041 #endif
5042   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5043
5044   /* reset animation only for moving elements which change direction of moving
5045      or which just started or stopped moving
5046      (else CEs with property "can move" / "not moving" are reset each frame) */
5047 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5048 #if 1
5049   if (is_moving_before != is_moving_after ||
5050       direction != MovDir[x][y])
5051     ResetGfxAnimation(x, y);
5052 #else
5053   if ((is_moving_before || is_moving_after) && !continues_moving)
5054     ResetGfxAnimation(x, y);
5055 #endif
5056 #else
5057   if (!continues_moving)
5058     ResetGfxAnimation(x, y);
5059 #endif
5060
5061   MovDir[x][y] = direction;
5062   GfxDir[x][y] = direction;
5063
5064 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5065   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5066                      direction == MV_DOWN && CAN_FALL(element) ?
5067                      ACTION_FALLING : ACTION_MOVING);
5068 #else
5069   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5070                      ACTION_FALLING : ACTION_MOVING);
5071 #endif
5072
5073   /* this is needed for CEs with property "can move" / "not moving" */
5074
5075   if (is_moving_after)
5076   {
5077     if (Feld[newx][newy] == EL_EMPTY)
5078       Feld[newx][newy] = EL_BLOCKED;
5079
5080     MovDir[newx][newy] = MovDir[x][y];
5081
5082 #if USE_NEW_CUSTOM_VALUE
5083     CustomValue[newx][newy] = CustomValue[x][y];
5084 #endif
5085
5086     GfxFrame[newx][newy] = GfxFrame[x][y];
5087     GfxRandom[newx][newy] = GfxRandom[x][y];
5088     GfxAction[newx][newy] = GfxAction[x][y];
5089     GfxDir[newx][newy] = GfxDir[x][y];
5090   }
5091 }
5092
5093 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5094 {
5095   int direction = MovDir[x][y];
5096   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5097   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5098
5099   *goes_to_x = newx;
5100   *goes_to_y = newy;
5101 }
5102
5103 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5104 {
5105   int oldx = x, oldy = y;
5106   int direction = MovDir[x][y];
5107
5108   if (direction == MV_LEFT)
5109     oldx++;
5110   else if (direction == MV_RIGHT)
5111     oldx--;
5112   else if (direction == MV_UP)
5113     oldy++;
5114   else if (direction == MV_DOWN)
5115     oldy--;
5116
5117   *comes_from_x = oldx;
5118   *comes_from_y = oldy;
5119 }
5120
5121 int MovingOrBlocked2Element(int x, int y)
5122 {
5123   int element = Feld[x][y];
5124
5125   if (element == EL_BLOCKED)
5126   {
5127     int oldx, oldy;
5128
5129     Blocked2Moving(x, y, &oldx, &oldy);
5130     return Feld[oldx][oldy];
5131   }
5132   else
5133     return element;
5134 }
5135
5136 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5137 {
5138   /* like MovingOrBlocked2Element(), but if element is moving
5139      and (x,y) is the field the moving element is just leaving,
5140      return EL_BLOCKED instead of the element value */
5141   int element = Feld[x][y];
5142
5143   if (IS_MOVING(x, y))
5144   {
5145     if (element == EL_BLOCKED)
5146     {
5147       int oldx, oldy;
5148
5149       Blocked2Moving(x, y, &oldx, &oldy);
5150       return Feld[oldx][oldy];
5151     }
5152     else
5153       return EL_BLOCKED;
5154   }
5155   else
5156     return element;
5157 }
5158
5159 static void RemoveField(int x, int y)
5160 {
5161   Feld[x][y] = EL_EMPTY;
5162
5163   MovPos[x][y] = 0;
5164   MovDir[x][y] = 0;
5165   MovDelay[x][y] = 0;
5166
5167 #if USE_NEW_CUSTOM_VALUE
5168   CustomValue[x][y] = 0;
5169 #endif
5170
5171   AmoebaNr[x][y] = 0;
5172   ChangeDelay[x][y] = 0;
5173   ChangePage[x][y] = -1;
5174   Pushed[x][y] = FALSE;
5175
5176 #if 0
5177   ExplodeField[x][y] = EX_TYPE_NONE;
5178 #endif
5179
5180   GfxElement[x][y] = EL_UNDEFINED;
5181   GfxAction[x][y] = ACTION_DEFAULT;
5182   GfxDir[x][y] = MV_NONE;
5183 #if 0
5184   /* !!! this would prevent the removed tile from being redrawn !!! */
5185   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5186 #endif
5187 }
5188
5189 void RemoveMovingField(int x, int y)
5190 {
5191   int oldx = x, oldy = y, newx = x, newy = y;
5192   int element = Feld[x][y];
5193   int next_element = EL_UNDEFINED;
5194
5195   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5196     return;
5197
5198   if (IS_MOVING(x, y))
5199   {
5200     Moving2Blocked(x, y, &newx, &newy);
5201
5202     if (Feld[newx][newy] != EL_BLOCKED)
5203     {
5204       /* element is moving, but target field is not free (blocked), but
5205          already occupied by something different (example: acid pool);
5206          in this case, only remove the moving field, but not the target */
5207
5208       RemoveField(oldx, oldy);
5209
5210       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5211
5212       TEST_DrawLevelField(oldx, oldy);
5213
5214       return;
5215     }
5216   }
5217   else if (element == EL_BLOCKED)
5218   {
5219     Blocked2Moving(x, y, &oldx, &oldy);
5220     if (!IS_MOVING(oldx, oldy))
5221       return;
5222   }
5223
5224   if (element == EL_BLOCKED &&
5225       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5226        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5227        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5228        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5229        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5230        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5231     next_element = get_next_element(Feld[oldx][oldy]);
5232
5233   RemoveField(oldx, oldy);
5234   RemoveField(newx, newy);
5235
5236   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5237
5238   if (next_element != EL_UNDEFINED)
5239     Feld[oldx][oldy] = next_element;
5240
5241   TEST_DrawLevelField(oldx, oldy);
5242   TEST_DrawLevelField(newx, newy);
5243 }
5244
5245 void DrawDynamite(int x, int y)
5246 {
5247   int sx = SCREENX(x), sy = SCREENY(y);
5248   int graphic = el2img(Feld[x][y]);
5249   int frame;
5250
5251   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5252     return;
5253
5254   if (IS_WALKABLE_INSIDE(Back[x][y]))
5255     return;
5256
5257   if (Back[x][y])
5258     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5259   else if (Store[x][y])
5260     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5261
5262   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5263
5264   if (Back[x][y] || Store[x][y])
5265     DrawGraphicThruMask(sx, sy, graphic, frame);
5266   else
5267     DrawGraphic(sx, sy, graphic, frame);
5268 }
5269
5270 void CheckDynamite(int x, int y)
5271 {
5272   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5273   {
5274     MovDelay[x][y]--;
5275
5276     if (MovDelay[x][y] != 0)
5277     {
5278       DrawDynamite(x, y);
5279       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5280
5281       return;
5282     }
5283   }
5284
5285   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5286
5287   Bang(x, y);
5288 }
5289
5290 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5291 {
5292   boolean num_checked_players = 0;
5293   int i;
5294
5295   for (i = 0; i < MAX_PLAYERS; i++)
5296   {
5297     if (stored_player[i].active)
5298     {
5299       int sx = stored_player[i].jx;
5300       int sy = stored_player[i].jy;
5301
5302       if (num_checked_players == 0)
5303       {
5304         *sx1 = *sx2 = sx;
5305         *sy1 = *sy2 = sy;
5306       }
5307       else
5308       {
5309         *sx1 = MIN(*sx1, sx);
5310         *sy1 = MIN(*sy1, sy);
5311         *sx2 = MAX(*sx2, sx);
5312         *sy2 = MAX(*sy2, sy);
5313       }
5314
5315       num_checked_players++;
5316     }
5317   }
5318 }
5319
5320 static boolean checkIfAllPlayersFitToScreen_RND()
5321 {
5322   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5323
5324   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5325
5326   return (sx2 - sx1 < SCR_FIELDX &&
5327           sy2 - sy1 < SCR_FIELDY);
5328 }
5329
5330 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5331 {
5332   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5333
5334   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5335
5336   *sx = (sx1 + sx2) / 2;
5337   *sy = (sy1 + sy2) / 2;
5338 }
5339
5340 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5341                         boolean center_screen, boolean quick_relocation)
5342 {
5343   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5344   boolean no_delay = (tape.warp_forward);
5345   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5346   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5347
5348   if (quick_relocation)
5349   {
5350     int offset = game.scroll_delay_value;
5351
5352     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5353     {
5354       if (!level.shifted_relocation || center_screen)
5355       {
5356         /* quick relocation (without scrolling), with centering of screen */
5357
5358         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5359                     x > SBX_Right + MIDPOSX ? SBX_Right :
5360                     x - MIDPOSX);
5361
5362         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5363                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5364                     y - MIDPOSY);
5365       }
5366       else
5367       {
5368         /* quick relocation (without scrolling), but do not center screen */
5369
5370         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5371                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5372                                old_x - MIDPOSX);
5373
5374         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5375                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5376                                old_y - MIDPOSY);
5377
5378         int offset_x = x + (scroll_x - center_scroll_x);
5379         int offset_y = y + (scroll_y - center_scroll_y);
5380
5381         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5382                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5383                     offset_x - MIDPOSX);
5384
5385         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5386                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5387                     offset_y - MIDPOSY);
5388       }
5389     }
5390     else
5391     {
5392       /* quick relocation (without scrolling), inside visible screen area */
5393
5394       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5395           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5396         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5397
5398       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5399           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5400         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5401
5402       /* don't scroll over playfield boundaries */
5403       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5404         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5405
5406       /* don't scroll over playfield boundaries */
5407       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5408         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5409     }
5410
5411     RedrawPlayfield(TRUE, 0,0,0,0);
5412   }
5413   else
5414   {
5415 #if 1
5416     int scroll_xx, scroll_yy;
5417
5418     if (!level.shifted_relocation || center_screen)
5419     {
5420       /* visible relocation (with scrolling), with centering of screen */
5421
5422       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5423                    x > SBX_Right + MIDPOSX ? SBX_Right :
5424                    x - MIDPOSX);
5425
5426       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5427                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5428                    y - MIDPOSY);
5429     }
5430     else
5431     {
5432       /* visible relocation (with scrolling), but do not center screen */
5433
5434       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5435                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5436                              old_x - MIDPOSX);
5437
5438       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5439                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5440                              old_y - MIDPOSY);
5441
5442       int offset_x = x + (scroll_x - center_scroll_x);
5443       int offset_y = y + (scroll_y - center_scroll_y);
5444
5445       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5446                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5447                    offset_x - MIDPOSX);
5448
5449       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5450                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5451                    offset_y - MIDPOSY);
5452     }
5453
5454 #else
5455
5456     /* visible relocation (with scrolling), with centering of screen */
5457
5458     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5459                      x > SBX_Right + MIDPOSX ? SBX_Right :
5460                      x - MIDPOSX);
5461
5462     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5463                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5464                      y - MIDPOSY);
5465 #endif
5466
5467     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5468
5469     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5470     {
5471       int dx = 0, dy = 0;
5472       int fx = FX, fy = FY;
5473
5474       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5475       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5476
5477       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5478         break;
5479
5480       scroll_x -= dx;
5481       scroll_y -= dy;
5482
5483       fx += dx * TILEX / 2;
5484       fy += dy * TILEY / 2;
5485
5486       ScrollLevel(dx, dy);
5487       DrawAllPlayers();
5488
5489       /* scroll in two steps of half tile size to make things smoother */
5490       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5491       FlushDisplay();
5492       Delay(wait_delay_value);
5493
5494       /* scroll second step to align at full tile size */
5495       BackToFront();
5496       Delay(wait_delay_value);
5497     }
5498
5499     DrawAllPlayers();
5500     BackToFront();
5501     Delay(wait_delay_value);
5502   }
5503 }
5504
5505 void RelocatePlayer(int jx, int jy, int el_player_raw)
5506 {
5507   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5508   int player_nr = GET_PLAYER_NR(el_player);
5509   struct PlayerInfo *player = &stored_player[player_nr];
5510   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5511   boolean no_delay = (tape.warp_forward);
5512   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5513   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5514   int old_jx = player->jx;
5515   int old_jy = player->jy;
5516   int old_element = Feld[old_jx][old_jy];
5517   int element = Feld[jx][jy];
5518   boolean player_relocated = (old_jx != jx || old_jy != jy);
5519
5520   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5521   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5522   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5523   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5524   int leave_side_horiz = move_dir_horiz;
5525   int leave_side_vert  = move_dir_vert;
5526   int enter_side = enter_side_horiz | enter_side_vert;
5527   int leave_side = leave_side_horiz | leave_side_vert;
5528
5529   if (player->GameOver)         /* do not reanimate dead player */
5530     return;
5531
5532   if (!player_relocated)        /* no need to relocate the player */
5533     return;
5534
5535   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5536   {
5537     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5538     DrawLevelField(jx, jy);
5539   }
5540
5541   if (player->present)
5542   {
5543     while (player->MovPos)
5544     {
5545       ScrollPlayer(player, SCROLL_GO_ON);
5546       ScrollScreen(NULL, SCROLL_GO_ON);
5547
5548       AdvanceFrameAndPlayerCounters(player->index_nr);
5549
5550       DrawPlayer(player);
5551
5552       BackToFront();
5553       Delay(wait_delay_value);
5554     }
5555
5556     DrawPlayer(player);         /* needed here only to cleanup last field */
5557     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5558
5559     player->is_moving = FALSE;
5560   }
5561
5562   if (IS_CUSTOM_ELEMENT(old_element))
5563     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5564                                CE_LEFT_BY_PLAYER,
5565                                player->index_bit, leave_side);
5566
5567   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5568                                       CE_PLAYER_LEAVES_X,
5569                                       player->index_bit, leave_side);
5570
5571   Feld[jx][jy] = el_player;
5572   InitPlayerField(jx, jy, el_player, TRUE);
5573
5574   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5575   {
5576     Feld[jx][jy] = element;
5577     InitField(jx, jy, FALSE);
5578   }
5579
5580   /* only visually relocate centered player */
5581   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5582                      FALSE, level.instant_relocation);
5583
5584   TestIfPlayerTouchesBadThing(jx, jy);
5585   TestIfPlayerTouchesCustomElement(jx, jy);
5586
5587   if (IS_CUSTOM_ELEMENT(element))
5588     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5589                                player->index_bit, enter_side);
5590
5591   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5592                                       player->index_bit, enter_side);
5593 }
5594
5595 void Explode(int ex, int ey, int phase, int mode)
5596 {
5597   int x, y;
5598   int last_phase;
5599   int border_element;
5600
5601   /* !!! eliminate this variable !!! */
5602   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5603
5604   if (game.explosions_delayed)
5605   {
5606     ExplodeField[ex][ey] = mode;
5607     return;
5608   }
5609
5610   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5611   {
5612     int center_element = Feld[ex][ey];
5613     int artwork_element, explosion_element;     /* set these values later */
5614
5615 #if 0
5616     /* --- This is only really needed (and now handled) in "Impact()". --- */
5617     /* do not explode moving elements that left the explode field in time */
5618     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5619         center_element == EL_EMPTY &&
5620         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5621       return;
5622 #endif
5623
5624 #if 0
5625     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5626     if (mode == EX_TYPE_NORMAL ||
5627         mode == EX_TYPE_CENTER ||
5628         mode == EX_TYPE_CROSS)
5629       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5630 #endif
5631
5632     /* remove things displayed in background while burning dynamite */
5633     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5634       Back[ex][ey] = 0;
5635
5636     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5637     {
5638       /* put moving element to center field (and let it explode there) */
5639       center_element = MovingOrBlocked2Element(ex, ey);
5640       RemoveMovingField(ex, ey);
5641       Feld[ex][ey] = center_element;
5642     }
5643
5644     /* now "center_element" is finally determined -- set related values now */
5645     artwork_element = center_element;           /* for custom player artwork */
5646     explosion_element = center_element;         /* for custom player artwork */
5647
5648     if (IS_PLAYER(ex, ey))
5649     {
5650       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5651
5652       artwork_element = stored_player[player_nr].artwork_element;
5653
5654       if (level.use_explosion_element[player_nr])
5655       {
5656         explosion_element = level.explosion_element[player_nr];
5657         artwork_element = explosion_element;
5658       }
5659     }
5660
5661 #if 1
5662     if (mode == EX_TYPE_NORMAL ||
5663         mode == EX_TYPE_CENTER ||
5664         mode == EX_TYPE_CROSS)
5665       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5666 #endif
5667
5668     last_phase = element_info[explosion_element].explosion_delay + 1;
5669
5670     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5671     {
5672       int xx = x - ex + 1;
5673       int yy = y - ey + 1;
5674       int element;
5675
5676       if (!IN_LEV_FIELD(x, y) ||
5677           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5678           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5679         continue;
5680
5681       element = Feld[x][y];
5682
5683       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5684       {
5685         element = MovingOrBlocked2Element(x, y);
5686
5687         if (!IS_EXPLOSION_PROOF(element))
5688           RemoveMovingField(x, y);
5689       }
5690
5691       /* indestructible elements can only explode in center (but not flames) */
5692       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5693                                            mode == EX_TYPE_BORDER)) ||
5694           element == EL_FLAMES)
5695         continue;
5696
5697       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5698          behaviour, for example when touching a yamyam that explodes to rocks
5699          with active deadly shield, a rock is created under the player !!! */
5700       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5701 #if 0
5702       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5703           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5704            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5705 #else
5706       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5707 #endif
5708       {
5709         if (IS_ACTIVE_BOMB(element))
5710         {
5711           /* re-activate things under the bomb like gate or penguin */
5712           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5713           Back[x][y] = 0;
5714         }
5715
5716         continue;
5717       }
5718
5719       /* save walkable background elements while explosion on same tile */
5720       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5721           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5722         Back[x][y] = element;
5723
5724       /* ignite explodable elements reached by other explosion */
5725       if (element == EL_EXPLOSION)
5726         element = Store2[x][y];
5727
5728       if (AmoebaNr[x][y] &&
5729           (element == EL_AMOEBA_FULL ||
5730            element == EL_BD_AMOEBA ||
5731            element == EL_AMOEBA_GROWING))
5732       {
5733         AmoebaCnt[AmoebaNr[x][y]]--;
5734         AmoebaCnt2[AmoebaNr[x][y]]--;
5735       }
5736
5737       RemoveField(x, y);
5738
5739       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5740       {
5741         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5742
5743         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5744
5745         if (PLAYERINFO(ex, ey)->use_murphy)
5746           Store[x][y] = EL_EMPTY;
5747       }
5748
5749       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5750          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5751       else if (ELEM_IS_PLAYER(center_element))
5752         Store[x][y] = EL_EMPTY;
5753       else if (center_element == EL_YAMYAM)
5754         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5755       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5756         Store[x][y] = element_info[center_element].content.e[xx][yy];
5757 #if 1
5758       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5759          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5760          otherwise) -- FIX THIS !!! */
5761       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5762         Store[x][y] = element_info[element].content.e[1][1];
5763 #else
5764       else if (!CAN_EXPLODE(element))
5765         Store[x][y] = element_info[element].content.e[1][1];
5766 #endif
5767       else
5768         Store[x][y] = EL_EMPTY;
5769
5770       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5771           center_element == EL_AMOEBA_TO_DIAMOND)
5772         Store2[x][y] = element;
5773
5774       Feld[x][y] = EL_EXPLOSION;
5775       GfxElement[x][y] = artwork_element;
5776
5777       ExplodePhase[x][y] = 1;
5778       ExplodeDelay[x][y] = last_phase;
5779
5780       Stop[x][y] = TRUE;
5781     }
5782
5783     if (center_element == EL_YAMYAM)
5784       game.yamyam_content_nr =
5785         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5786
5787     return;
5788   }
5789
5790   if (Stop[ex][ey])
5791     return;
5792
5793   x = ex;
5794   y = ey;
5795
5796   if (phase == 1)
5797     GfxFrame[x][y] = 0;         /* restart explosion animation */
5798
5799   last_phase = ExplodeDelay[x][y];
5800
5801   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5802
5803 #ifdef DEBUG
5804
5805   /* activate this even in non-DEBUG version until cause for crash in
5806      getGraphicAnimationFrame() (see below) is found and eliminated */
5807
5808 #endif
5809 #if 1
5810
5811 #if 1
5812   /* this can happen if the player leaves an explosion just in time */
5813   if (GfxElement[x][y] == EL_UNDEFINED)
5814     GfxElement[x][y] = EL_EMPTY;
5815 #else
5816   if (GfxElement[x][y] == EL_UNDEFINED)
5817   {
5818     printf("\n\n");
5819     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5820     printf("Explode(): This should never happen!\n");
5821     printf("\n\n");
5822
5823     GfxElement[x][y] = EL_EMPTY;
5824   }
5825 #endif
5826
5827 #endif
5828
5829   border_element = Store2[x][y];
5830   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5831     border_element = StorePlayer[x][y];
5832
5833   if (phase == element_info[border_element].ignition_delay ||
5834       phase == last_phase)
5835   {
5836     boolean border_explosion = FALSE;
5837
5838     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5839         !PLAYER_EXPLOSION_PROTECTED(x, y))
5840     {
5841       KillPlayerUnlessExplosionProtected(x, y);
5842       border_explosion = TRUE;
5843     }
5844     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5845     {
5846       Feld[x][y] = Store2[x][y];
5847       Store2[x][y] = 0;
5848       Bang(x, y);
5849       border_explosion = TRUE;
5850     }
5851     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5852     {
5853       AmoebeUmwandeln(x, y);
5854       Store2[x][y] = 0;
5855       border_explosion = TRUE;
5856     }
5857
5858     /* if an element just explodes due to another explosion (chain-reaction),
5859        do not immediately end the new explosion when it was the last frame of
5860        the explosion (as it would be done in the following "if"-statement!) */
5861     if (border_explosion && phase == last_phase)
5862       return;
5863   }
5864
5865   if (phase == last_phase)
5866   {
5867     int element;
5868
5869     element = Feld[x][y] = Store[x][y];
5870     Store[x][y] = Store2[x][y] = 0;
5871     GfxElement[x][y] = EL_UNDEFINED;
5872
5873     /* player can escape from explosions and might therefore be still alive */
5874     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5875         element <= EL_PLAYER_IS_EXPLODING_4)
5876     {
5877       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5878       int explosion_element = EL_PLAYER_1 + player_nr;
5879       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5880       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5881
5882       if (level.use_explosion_element[player_nr])
5883         explosion_element = level.explosion_element[player_nr];
5884
5885       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5886                     element_info[explosion_element].content.e[xx][yy]);
5887     }
5888
5889     /* restore probably existing indestructible background element */
5890     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5891       element = Feld[x][y] = Back[x][y];
5892     Back[x][y] = 0;
5893
5894     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5895     GfxDir[x][y] = MV_NONE;
5896     ChangeDelay[x][y] = 0;
5897     ChangePage[x][y] = -1;
5898
5899 #if USE_NEW_CUSTOM_VALUE
5900     CustomValue[x][y] = 0;
5901 #endif
5902
5903     InitField_WithBug2(x, y, FALSE);
5904
5905     TEST_DrawLevelField(x, y);
5906
5907     TestIfElementTouchesCustomElement(x, y);
5908
5909     if (GFX_CRUMBLED(element))
5910       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5911
5912     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5913       StorePlayer[x][y] = 0;
5914
5915     if (ELEM_IS_PLAYER(element))
5916       RelocatePlayer(x, y, element);
5917   }
5918   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5919   {
5920     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5921     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5922
5923     if (phase == delay)
5924       TEST_DrawLevelFieldCrumbledSand(x, y);
5925
5926     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5927     {
5928       DrawLevelElement(x, y, Back[x][y]);
5929       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5930     }
5931     else if (IS_WALKABLE_UNDER(Back[x][y]))
5932     {
5933       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5934       DrawLevelElementThruMask(x, y, Back[x][y]);
5935     }
5936     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5937       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5938   }
5939 }
5940
5941 void DynaExplode(int ex, int ey)
5942 {
5943   int i, j;
5944   int dynabomb_element = Feld[ex][ey];
5945   int dynabomb_size = 1;
5946   boolean dynabomb_xl = FALSE;
5947   struct PlayerInfo *player;
5948   static int xy[4][2] =
5949   {
5950     { 0, -1 },
5951     { -1, 0 },
5952     { +1, 0 },
5953     { 0, +1 }
5954   };
5955
5956   if (IS_ACTIVE_BOMB(dynabomb_element))
5957   {
5958     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5959     dynabomb_size = player->dynabomb_size;
5960     dynabomb_xl = player->dynabomb_xl;
5961     player->dynabombs_left++;
5962   }
5963
5964   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5965
5966   for (i = 0; i < NUM_DIRECTIONS; i++)
5967   {
5968     for (j = 1; j <= dynabomb_size; j++)
5969     {
5970       int x = ex + j * xy[i][0];
5971       int y = ey + j * xy[i][1];
5972       int element;
5973
5974       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5975         break;
5976
5977       element = Feld[x][y];
5978
5979       /* do not restart explosions of fields with active bombs */
5980       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5981         continue;
5982
5983       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5984
5985       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5986           !IS_DIGGABLE(element) && !dynabomb_xl)
5987         break;
5988     }
5989   }
5990 }
5991
5992 void Bang(int x, int y)
5993 {
5994   int element = MovingOrBlocked2Element(x, y);
5995   int explosion_type = EX_TYPE_NORMAL;
5996
5997   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5998   {
5999     struct PlayerInfo *player = PLAYERINFO(x, y);
6000
6001 #if USE_FIX_CE_ACTION_WITH_PLAYER
6002     element = Feld[x][y] = player->initial_element;
6003 #else
6004     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6005                             player->element_nr);
6006 #endif
6007
6008     if (level.use_explosion_element[player->index_nr])
6009     {
6010       int explosion_element = level.explosion_element[player->index_nr];
6011
6012       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6013         explosion_type = EX_TYPE_CROSS;
6014       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6015         explosion_type = EX_TYPE_CENTER;
6016     }
6017   }
6018
6019   switch (element)
6020   {
6021     case EL_BUG:
6022     case EL_SPACESHIP:
6023     case EL_BD_BUTTERFLY:
6024     case EL_BD_FIREFLY:
6025     case EL_YAMYAM:
6026     case EL_DARK_YAMYAM:
6027     case EL_ROBOT:
6028     case EL_PACMAN:
6029     case EL_MOLE:
6030       RaiseScoreElement(element);
6031       break;
6032
6033     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6034     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6035     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6036     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6037     case EL_DYNABOMB_INCREASE_NUMBER:
6038     case EL_DYNABOMB_INCREASE_SIZE:
6039     case EL_DYNABOMB_INCREASE_POWER:
6040       explosion_type = EX_TYPE_DYNA;
6041       break;
6042
6043     case EL_DC_LANDMINE:
6044 #if 0
6045     case EL_EM_EXIT_OPEN:
6046     case EL_EM_STEEL_EXIT_OPEN:
6047 #endif
6048       explosion_type = EX_TYPE_CENTER;
6049       break;
6050
6051     case EL_PENGUIN:
6052     case EL_LAMP:
6053     case EL_LAMP_ACTIVE:
6054     case EL_AMOEBA_TO_DIAMOND:
6055       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6056         explosion_type = EX_TYPE_CENTER;
6057       break;
6058
6059     default:
6060       if (element_info[element].explosion_type == EXPLODES_CROSS)
6061         explosion_type = EX_TYPE_CROSS;
6062       else if (element_info[element].explosion_type == EXPLODES_1X1)
6063         explosion_type = EX_TYPE_CENTER;
6064       break;
6065   }
6066
6067   if (explosion_type == EX_TYPE_DYNA)
6068     DynaExplode(x, y);
6069   else
6070     Explode(x, y, EX_PHASE_START, explosion_type);
6071
6072   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6073 }
6074
6075 void SplashAcid(int x, int y)
6076 {
6077   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6078       (!IN_LEV_FIELD(x - 1, y - 2) ||
6079        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6080     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6081
6082   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6083       (!IN_LEV_FIELD(x + 1, y - 2) ||
6084        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6085     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6086
6087   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6088 }
6089
6090 static void InitBeltMovement()
6091 {
6092   static int belt_base_element[4] =
6093   {
6094     EL_CONVEYOR_BELT_1_LEFT,
6095     EL_CONVEYOR_BELT_2_LEFT,
6096     EL_CONVEYOR_BELT_3_LEFT,
6097     EL_CONVEYOR_BELT_4_LEFT
6098   };
6099   static int belt_base_active_element[4] =
6100   {
6101     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6102     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6103     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6104     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6105   };
6106
6107   int x, y, i, j;
6108
6109   /* set frame order for belt animation graphic according to belt direction */
6110   for (i = 0; i < NUM_BELTS; i++)
6111   {
6112     int belt_nr = i;
6113
6114     for (j = 0; j < NUM_BELT_PARTS; j++)
6115     {
6116       int element = belt_base_active_element[belt_nr] + j;
6117       int graphic_1 = el2img(element);
6118       int graphic_2 = el2panelimg(element);
6119
6120       if (game.belt_dir[i] == MV_LEFT)
6121       {
6122         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6123         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6124       }
6125       else
6126       {
6127         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6128         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6129       }
6130     }
6131   }
6132
6133   SCAN_PLAYFIELD(x, y)
6134   {
6135     int element = Feld[x][y];
6136
6137     for (i = 0; i < NUM_BELTS; i++)
6138     {
6139       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6140       {
6141         int e_belt_nr = getBeltNrFromBeltElement(element);
6142         int belt_nr = i;
6143
6144         if (e_belt_nr == belt_nr)
6145         {
6146           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6147
6148           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6149         }
6150       }
6151     }
6152   }
6153 }
6154
6155 static void ToggleBeltSwitch(int x, int y)
6156 {
6157   static int belt_base_element[4] =
6158   {
6159     EL_CONVEYOR_BELT_1_LEFT,
6160     EL_CONVEYOR_BELT_2_LEFT,
6161     EL_CONVEYOR_BELT_3_LEFT,
6162     EL_CONVEYOR_BELT_4_LEFT
6163   };
6164   static int belt_base_active_element[4] =
6165   {
6166     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6167     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6168     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6169     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6170   };
6171   static int belt_base_switch_element[4] =
6172   {
6173     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6174     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6175     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6176     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6177   };
6178   static int belt_move_dir[4] =
6179   {
6180     MV_LEFT,
6181     MV_NONE,
6182     MV_RIGHT,
6183     MV_NONE,
6184   };
6185
6186   int element = Feld[x][y];
6187   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6188   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6189   int belt_dir = belt_move_dir[belt_dir_nr];
6190   int xx, yy, i;
6191
6192   if (!IS_BELT_SWITCH(element))
6193     return;
6194
6195   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6196   game.belt_dir[belt_nr] = belt_dir;
6197
6198   if (belt_dir_nr == 3)
6199     belt_dir_nr = 1;
6200
6201   /* set frame order for belt animation graphic according to belt direction */
6202   for (i = 0; i < NUM_BELT_PARTS; i++)
6203   {
6204     int element = belt_base_active_element[belt_nr] + i;
6205     int graphic_1 = el2img(element);
6206     int graphic_2 = el2panelimg(element);
6207
6208     if (belt_dir == MV_LEFT)
6209     {
6210       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6211       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6212     }
6213     else
6214     {
6215       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6216       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6217     }
6218   }
6219
6220   SCAN_PLAYFIELD(xx, yy)
6221   {
6222     int element = Feld[xx][yy];
6223
6224     if (IS_BELT_SWITCH(element))
6225     {
6226       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6227
6228       if (e_belt_nr == belt_nr)
6229       {
6230         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6231         TEST_DrawLevelField(xx, yy);
6232       }
6233     }
6234     else if (IS_BELT(element) && belt_dir != MV_NONE)
6235     {
6236       int e_belt_nr = getBeltNrFromBeltElement(element);
6237
6238       if (e_belt_nr == belt_nr)
6239       {
6240         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6241
6242         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6243         TEST_DrawLevelField(xx, yy);
6244       }
6245     }
6246     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6247     {
6248       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6249
6250       if (e_belt_nr == belt_nr)
6251       {
6252         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6253
6254         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6255         TEST_DrawLevelField(xx, yy);
6256       }
6257     }
6258   }
6259 }
6260
6261 static void ToggleSwitchgateSwitch(int x, int y)
6262 {
6263   int xx, yy;
6264
6265   game.switchgate_pos = !game.switchgate_pos;
6266
6267   SCAN_PLAYFIELD(xx, yy)
6268   {
6269     int element = Feld[xx][yy];
6270
6271 #if !USE_BOTH_SWITCHGATE_SWITCHES
6272     if (element == EL_SWITCHGATE_SWITCH_UP ||
6273         element == EL_SWITCHGATE_SWITCH_DOWN)
6274     {
6275       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6276       TEST_DrawLevelField(xx, yy);
6277     }
6278     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6279              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6280     {
6281       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6282       TEST_DrawLevelField(xx, yy);
6283     }
6284 #else
6285     if (element == EL_SWITCHGATE_SWITCH_UP)
6286     {
6287       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6288       TEST_DrawLevelField(xx, yy);
6289     }
6290     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6291     {
6292       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6293       TEST_DrawLevelField(xx, yy);
6294     }
6295     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6296     {
6297       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6298       TEST_DrawLevelField(xx, yy);
6299     }
6300     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6301     {
6302       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6303       TEST_DrawLevelField(xx, yy);
6304     }
6305 #endif
6306     else if (element == EL_SWITCHGATE_OPEN ||
6307              element == EL_SWITCHGATE_OPENING)
6308     {
6309       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6310
6311       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6312     }
6313     else if (element == EL_SWITCHGATE_CLOSED ||
6314              element == EL_SWITCHGATE_CLOSING)
6315     {
6316       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6317
6318       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6319     }
6320   }
6321 }
6322
6323 static int getInvisibleActiveFromInvisibleElement(int element)
6324 {
6325   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6326           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6327           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6328           element);
6329 }
6330
6331 static int getInvisibleFromInvisibleActiveElement(int element)
6332 {
6333   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6334           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6335           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6336           element);
6337 }
6338
6339 static void RedrawAllLightSwitchesAndInvisibleElements()
6340 {
6341   int x, y;
6342
6343   SCAN_PLAYFIELD(x, y)
6344   {
6345     int element = Feld[x][y];
6346
6347     if (element == EL_LIGHT_SWITCH &&
6348         game.light_time_left > 0)
6349     {
6350       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6351       TEST_DrawLevelField(x, y);
6352     }
6353     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6354              game.light_time_left == 0)
6355     {
6356       Feld[x][y] = EL_LIGHT_SWITCH;
6357       TEST_DrawLevelField(x, y);
6358     }
6359     else if (element == EL_EMC_DRIPPER &&
6360              game.light_time_left > 0)
6361     {
6362       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6363       TEST_DrawLevelField(x, y);
6364     }
6365     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6366              game.light_time_left == 0)
6367     {
6368       Feld[x][y] = EL_EMC_DRIPPER;
6369       TEST_DrawLevelField(x, y);
6370     }
6371     else if (element == EL_INVISIBLE_STEELWALL ||
6372              element == EL_INVISIBLE_WALL ||
6373              element == EL_INVISIBLE_SAND)
6374     {
6375       if (game.light_time_left > 0)
6376         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6377
6378       TEST_DrawLevelField(x, y);
6379
6380       /* uncrumble neighbour fields, if needed */
6381       if (element == EL_INVISIBLE_SAND)
6382         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6383     }
6384     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6385              element == EL_INVISIBLE_WALL_ACTIVE ||
6386              element == EL_INVISIBLE_SAND_ACTIVE)
6387     {
6388       if (game.light_time_left == 0)
6389         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6390
6391       TEST_DrawLevelField(x, y);
6392
6393       /* re-crumble neighbour fields, if needed */
6394       if (element == EL_INVISIBLE_SAND)
6395         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6396     }
6397   }
6398 }
6399
6400 static void RedrawAllInvisibleElementsForLenses()
6401 {
6402   int x, y;
6403
6404   SCAN_PLAYFIELD(x, y)
6405   {
6406     int element = Feld[x][y];
6407
6408     if (element == EL_EMC_DRIPPER &&
6409         game.lenses_time_left > 0)
6410     {
6411       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6412       TEST_DrawLevelField(x, y);
6413     }
6414     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6415              game.lenses_time_left == 0)
6416     {
6417       Feld[x][y] = EL_EMC_DRIPPER;
6418       TEST_DrawLevelField(x, y);
6419     }
6420     else if (element == EL_INVISIBLE_STEELWALL ||
6421              element == EL_INVISIBLE_WALL ||
6422              element == EL_INVISIBLE_SAND)
6423     {
6424       if (game.lenses_time_left > 0)
6425         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6426
6427       TEST_DrawLevelField(x, y);
6428
6429       /* uncrumble neighbour fields, if needed */
6430       if (element == EL_INVISIBLE_SAND)
6431         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6432     }
6433     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6434              element == EL_INVISIBLE_WALL_ACTIVE ||
6435              element == EL_INVISIBLE_SAND_ACTIVE)
6436     {
6437       if (game.lenses_time_left == 0)
6438         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6439
6440       TEST_DrawLevelField(x, y);
6441
6442       /* re-crumble neighbour fields, if needed */
6443       if (element == EL_INVISIBLE_SAND)
6444         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6445     }
6446   }
6447 }
6448
6449 static void RedrawAllInvisibleElementsForMagnifier()
6450 {
6451   int x, y;
6452
6453   SCAN_PLAYFIELD(x, y)
6454   {
6455     int element = Feld[x][y];
6456
6457     if (element == EL_EMC_FAKE_GRASS &&
6458         game.magnify_time_left > 0)
6459     {
6460       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6461       TEST_DrawLevelField(x, y);
6462     }
6463     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6464              game.magnify_time_left == 0)
6465     {
6466       Feld[x][y] = EL_EMC_FAKE_GRASS;
6467       TEST_DrawLevelField(x, y);
6468     }
6469     else if (IS_GATE_GRAY(element) &&
6470              game.magnify_time_left > 0)
6471     {
6472       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6473                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6474                     IS_EM_GATE_GRAY(element) ?
6475                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6476                     IS_EMC_GATE_GRAY(element) ?
6477                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6478                     IS_DC_GATE_GRAY(element) ?
6479                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6480                     element);
6481       TEST_DrawLevelField(x, y);
6482     }
6483     else if (IS_GATE_GRAY_ACTIVE(element) &&
6484              game.magnify_time_left == 0)
6485     {
6486       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6487                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6488                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6489                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6490                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6491                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6492                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6493                     EL_DC_GATE_WHITE_GRAY :
6494                     element);
6495       TEST_DrawLevelField(x, y);
6496     }
6497   }
6498 }
6499
6500 static void ToggleLightSwitch(int x, int y)
6501 {
6502   int element = Feld[x][y];
6503
6504   game.light_time_left =
6505     (element == EL_LIGHT_SWITCH ?
6506      level.time_light * FRAMES_PER_SECOND : 0);
6507
6508   RedrawAllLightSwitchesAndInvisibleElements();
6509 }
6510
6511 static void ActivateTimegateSwitch(int x, int y)
6512 {
6513   int xx, yy;
6514
6515   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6516
6517   SCAN_PLAYFIELD(xx, yy)
6518   {
6519     int element = Feld[xx][yy];
6520
6521     if (element == EL_TIMEGATE_CLOSED ||
6522         element == EL_TIMEGATE_CLOSING)
6523     {
6524       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6525       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6526     }
6527
6528     /*
6529     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6530     {
6531       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6532       TEST_DrawLevelField(xx, yy);
6533     }
6534     */
6535
6536   }
6537
6538 #if 1
6539   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6540                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6541 #else
6542   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6543 #endif
6544 }
6545
6546 void Impact(int x, int y)
6547 {
6548   boolean last_line = (y == lev_fieldy - 1);
6549   boolean object_hit = FALSE;
6550   boolean impact = (last_line || object_hit);
6551   int element = Feld[x][y];
6552   int smashed = EL_STEELWALL;
6553
6554   if (!last_line)       /* check if element below was hit */
6555   {
6556     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6557       return;
6558
6559     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6560                                          MovDir[x][y + 1] != MV_DOWN ||
6561                                          MovPos[x][y + 1] <= TILEY / 2));
6562
6563     /* do not smash moving elements that left the smashed field in time */
6564     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6565         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6566       object_hit = FALSE;
6567
6568 #if USE_QUICKSAND_IMPACT_BUGFIX
6569     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6570     {
6571       RemoveMovingField(x, y + 1);
6572       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6573       Feld[x][y + 2] = EL_ROCK;
6574       TEST_DrawLevelField(x, y + 2);
6575
6576       object_hit = TRUE;
6577     }
6578
6579     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6580     {
6581       RemoveMovingField(x, y + 1);
6582       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6583       Feld[x][y + 2] = EL_ROCK;
6584       TEST_DrawLevelField(x, y + 2);
6585
6586       object_hit = TRUE;
6587     }
6588 #endif
6589
6590     if (object_hit)
6591       smashed = MovingOrBlocked2Element(x, y + 1);
6592
6593     impact = (last_line || object_hit);
6594   }
6595
6596   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6597   {
6598     SplashAcid(x, y + 1);
6599     return;
6600   }
6601
6602   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6603   /* only reset graphic animation if graphic really changes after impact */
6604   if (impact &&
6605       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6606   {
6607     ResetGfxAnimation(x, y);
6608     TEST_DrawLevelField(x, y);
6609   }
6610
6611   if (impact && CAN_EXPLODE_IMPACT(element))
6612   {
6613     Bang(x, y);
6614     return;
6615   }
6616   else if (impact && element == EL_PEARL &&
6617            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6618   {
6619     ResetGfxAnimation(x, y);
6620
6621     Feld[x][y] = EL_PEARL_BREAKING;
6622     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6623     return;
6624   }
6625   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6626   {
6627     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6628
6629     return;
6630   }
6631
6632   if (impact && element == EL_AMOEBA_DROP)
6633   {
6634     if (object_hit && IS_PLAYER(x, y + 1))
6635       KillPlayerUnlessEnemyProtected(x, y + 1);
6636     else if (object_hit && smashed == EL_PENGUIN)
6637       Bang(x, y + 1);
6638     else
6639     {
6640       Feld[x][y] = EL_AMOEBA_GROWING;
6641       Store[x][y] = EL_AMOEBA_WET;
6642
6643       ResetRandomAnimationValue(x, y);
6644     }
6645     return;
6646   }
6647
6648   if (object_hit)               /* check which object was hit */
6649   {
6650     if ((CAN_PASS_MAGIC_WALL(element) && 
6651          (smashed == EL_MAGIC_WALL ||
6652           smashed == EL_BD_MAGIC_WALL)) ||
6653         (CAN_PASS_DC_MAGIC_WALL(element) &&
6654          smashed == EL_DC_MAGIC_WALL))
6655     {
6656       int xx, yy;
6657       int activated_magic_wall =
6658         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6659          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6660          EL_DC_MAGIC_WALL_ACTIVE);
6661
6662       /* activate magic wall / mill */
6663       SCAN_PLAYFIELD(xx, yy)
6664       {
6665         if (Feld[xx][yy] == smashed)
6666           Feld[xx][yy] = activated_magic_wall;
6667       }
6668
6669       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6670       game.magic_wall_active = TRUE;
6671
6672       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6673                             SND_MAGIC_WALL_ACTIVATING :
6674                             smashed == EL_BD_MAGIC_WALL ?
6675                             SND_BD_MAGIC_WALL_ACTIVATING :
6676                             SND_DC_MAGIC_WALL_ACTIVATING));
6677     }
6678
6679     if (IS_PLAYER(x, y + 1))
6680     {
6681       if (CAN_SMASH_PLAYER(element))
6682       {
6683         KillPlayerUnlessEnemyProtected(x, y + 1);
6684         return;
6685       }
6686     }
6687     else if (smashed == EL_PENGUIN)
6688     {
6689       if (CAN_SMASH_PLAYER(element))
6690       {
6691         Bang(x, y + 1);
6692         return;
6693       }
6694     }
6695     else if (element == EL_BD_DIAMOND)
6696     {
6697       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6698       {
6699         Bang(x, y + 1);
6700         return;
6701       }
6702     }
6703     else if (((element == EL_SP_INFOTRON ||
6704                element == EL_SP_ZONK) &&
6705               (smashed == EL_SP_SNIKSNAK ||
6706                smashed == EL_SP_ELECTRON ||
6707                smashed == EL_SP_DISK_ORANGE)) ||
6708              (element == EL_SP_INFOTRON &&
6709               smashed == EL_SP_DISK_YELLOW))
6710     {
6711       Bang(x, y + 1);
6712       return;
6713     }
6714     else if (CAN_SMASH_EVERYTHING(element))
6715     {
6716       if (IS_CLASSIC_ENEMY(smashed) ||
6717           CAN_EXPLODE_SMASHED(smashed))
6718       {
6719         Bang(x, y + 1);
6720         return;
6721       }
6722       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6723       {
6724         if (smashed == EL_LAMP ||
6725             smashed == EL_LAMP_ACTIVE)
6726         {
6727           Bang(x, y + 1);
6728           return;
6729         }
6730         else if (smashed == EL_NUT)
6731         {
6732           Feld[x][y + 1] = EL_NUT_BREAKING;
6733           PlayLevelSound(x, y, SND_NUT_BREAKING);
6734           RaiseScoreElement(EL_NUT);
6735           return;
6736         }
6737         else if (smashed == EL_PEARL)
6738         {
6739           ResetGfxAnimation(x, y);
6740
6741           Feld[x][y + 1] = EL_PEARL_BREAKING;
6742           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6743           return;
6744         }
6745         else if (smashed == EL_DIAMOND)
6746         {
6747           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6748           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6749           return;
6750         }
6751         else if (IS_BELT_SWITCH(smashed))
6752         {
6753           ToggleBeltSwitch(x, y + 1);
6754         }
6755         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6756                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6757                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6758                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6759         {
6760           ToggleSwitchgateSwitch(x, y + 1);
6761         }
6762         else if (smashed == EL_LIGHT_SWITCH ||
6763                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6764         {
6765           ToggleLightSwitch(x, y + 1);
6766         }
6767         else
6768         {
6769 #if 0
6770           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6771 #endif
6772
6773           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6774
6775           CheckElementChangeBySide(x, y + 1, smashed, element,
6776                                    CE_SWITCHED, CH_SIDE_TOP);
6777           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6778                                             CH_SIDE_TOP);
6779         }
6780       }
6781       else
6782       {
6783         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6784       }
6785     }
6786   }
6787
6788   /* play sound of magic wall / mill */
6789   if (!last_line &&
6790       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6791        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6792        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6793   {
6794     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6795       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6796     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6797       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6798     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6799       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6800
6801     return;
6802   }
6803
6804   /* play sound of object that hits the ground */
6805   if (last_line || object_hit)
6806     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6807 }
6808
6809 inline static void TurnRoundExt(int x, int y)
6810 {
6811   static struct
6812   {
6813     int dx, dy;
6814   } move_xy[] =
6815   {
6816     {  0,  0 },
6817     { -1,  0 },
6818     { +1,  0 },
6819     {  0,  0 },
6820     {  0, -1 },
6821     {  0,  0 }, { 0, 0 }, { 0, 0 },
6822     {  0, +1 }
6823   };
6824   static struct
6825   {
6826     int left, right, back;
6827   } turn[] =
6828   {
6829     { 0,        0,              0        },
6830     { MV_DOWN,  MV_UP,          MV_RIGHT },
6831     { MV_UP,    MV_DOWN,        MV_LEFT  },
6832     { 0,        0,              0        },
6833     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6834     { 0,        0,              0        },
6835     { 0,        0,              0        },
6836     { 0,        0,              0        },
6837     { MV_RIGHT, MV_LEFT,        MV_UP    }
6838   };
6839
6840   int element = Feld[x][y];
6841   int move_pattern = element_info[element].move_pattern;
6842
6843   int old_move_dir = MovDir[x][y];
6844   int left_dir  = turn[old_move_dir].left;
6845   int right_dir = turn[old_move_dir].right;
6846   int back_dir  = turn[old_move_dir].back;
6847
6848   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6849   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6850   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6851   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6852
6853   int left_x  = x + left_dx,  left_y  = y + left_dy;
6854   int right_x = x + right_dx, right_y = y + right_dy;
6855   int move_x  = x + move_dx,  move_y  = y + move_dy;
6856
6857   int xx, yy;
6858
6859   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6860   {
6861     TestIfBadThingTouchesOtherBadThing(x, y);
6862
6863     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6864       MovDir[x][y] = right_dir;
6865     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6866       MovDir[x][y] = left_dir;
6867
6868     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6869       MovDelay[x][y] = 9;
6870     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6871       MovDelay[x][y] = 1;
6872   }
6873   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6874   {
6875     TestIfBadThingTouchesOtherBadThing(x, y);
6876
6877     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6878       MovDir[x][y] = left_dir;
6879     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6880       MovDir[x][y] = right_dir;
6881
6882     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6883       MovDelay[x][y] = 9;
6884     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6885       MovDelay[x][y] = 1;
6886   }
6887   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6888   {
6889     TestIfBadThingTouchesOtherBadThing(x, y);
6890
6891     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6892       MovDir[x][y] = left_dir;
6893     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6894       MovDir[x][y] = right_dir;
6895
6896     if (MovDir[x][y] != old_move_dir)
6897       MovDelay[x][y] = 9;
6898   }
6899   else if (element == EL_YAMYAM)
6900   {
6901     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6902     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6903
6904     if (can_turn_left && can_turn_right)
6905       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6906     else if (can_turn_left)
6907       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6908     else if (can_turn_right)
6909       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6910     else
6911       MovDir[x][y] = back_dir;
6912
6913     MovDelay[x][y] = 16 + 16 * RND(3);
6914   }
6915   else if (element == EL_DARK_YAMYAM)
6916   {
6917     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6918                                                          left_x, left_y);
6919     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6920                                                          right_x, right_y);
6921
6922     if (can_turn_left && can_turn_right)
6923       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6924     else if (can_turn_left)
6925       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6926     else if (can_turn_right)
6927       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6928     else
6929       MovDir[x][y] = back_dir;
6930
6931     MovDelay[x][y] = 16 + 16 * RND(3);
6932   }
6933   else if (element == EL_PACMAN)
6934   {
6935     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6936     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6937
6938     if (can_turn_left && can_turn_right)
6939       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6940     else if (can_turn_left)
6941       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6942     else if (can_turn_right)
6943       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6944     else
6945       MovDir[x][y] = back_dir;
6946
6947     MovDelay[x][y] = 6 + RND(40);
6948   }
6949   else if (element == EL_PIG)
6950   {
6951     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6952     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6953     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6954     boolean should_turn_left, should_turn_right, should_move_on;
6955     int rnd_value = 24;
6956     int rnd = RND(rnd_value);
6957
6958     should_turn_left = (can_turn_left &&
6959                         (!can_move_on ||
6960                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6961                                                    y + back_dy + left_dy)));
6962     should_turn_right = (can_turn_right &&
6963                          (!can_move_on ||
6964                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6965                                                     y + back_dy + right_dy)));
6966     should_move_on = (can_move_on &&
6967                       (!can_turn_left ||
6968                        !can_turn_right ||
6969                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6970                                                  y + move_dy + left_dy) ||
6971                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6972                                                  y + move_dy + right_dy)));
6973
6974     if (should_turn_left || should_turn_right || should_move_on)
6975     {
6976       if (should_turn_left && should_turn_right && should_move_on)
6977         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6978                         rnd < 2 * rnd_value / 3 ? right_dir :
6979                         old_move_dir);
6980       else if (should_turn_left && should_turn_right)
6981         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6982       else if (should_turn_left && should_move_on)
6983         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6984       else if (should_turn_right && should_move_on)
6985         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6986       else if (should_turn_left)
6987         MovDir[x][y] = left_dir;
6988       else if (should_turn_right)
6989         MovDir[x][y] = right_dir;
6990       else if (should_move_on)
6991         MovDir[x][y] = old_move_dir;
6992     }
6993     else if (can_move_on && rnd > rnd_value / 8)
6994       MovDir[x][y] = old_move_dir;
6995     else if (can_turn_left && can_turn_right)
6996       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6997     else if (can_turn_left && rnd > rnd_value / 8)
6998       MovDir[x][y] = left_dir;
6999     else if (can_turn_right && rnd > rnd_value/8)
7000       MovDir[x][y] = right_dir;
7001     else
7002       MovDir[x][y] = back_dir;
7003
7004     xx = x + move_xy[MovDir[x][y]].dx;
7005     yy = y + move_xy[MovDir[x][y]].dy;
7006
7007     if (!IN_LEV_FIELD(xx, yy) ||
7008         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7009       MovDir[x][y] = old_move_dir;
7010
7011     MovDelay[x][y] = 0;
7012   }
7013   else if (element == EL_DRAGON)
7014   {
7015     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7016     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7017     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7018     int rnd_value = 24;
7019     int rnd = RND(rnd_value);
7020
7021     if (can_move_on && rnd > rnd_value / 8)
7022       MovDir[x][y] = old_move_dir;
7023     else if (can_turn_left && can_turn_right)
7024       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7025     else if (can_turn_left && rnd > rnd_value / 8)
7026       MovDir[x][y] = left_dir;
7027     else if (can_turn_right && rnd > rnd_value / 8)
7028       MovDir[x][y] = right_dir;
7029     else
7030       MovDir[x][y] = back_dir;
7031
7032     xx = x + move_xy[MovDir[x][y]].dx;
7033     yy = y + move_xy[MovDir[x][y]].dy;
7034
7035     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7036       MovDir[x][y] = old_move_dir;
7037
7038     MovDelay[x][y] = 0;
7039   }
7040   else if (element == EL_MOLE)
7041   {
7042     boolean can_move_on =
7043       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7044                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7045                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7046     if (!can_move_on)
7047     {
7048       boolean can_turn_left =
7049         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7050                               IS_AMOEBOID(Feld[left_x][left_y])));
7051
7052       boolean can_turn_right =
7053         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7054                               IS_AMOEBOID(Feld[right_x][right_y])));
7055
7056       if (can_turn_left && can_turn_right)
7057         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7058       else if (can_turn_left)
7059         MovDir[x][y] = left_dir;
7060       else
7061         MovDir[x][y] = right_dir;
7062     }
7063
7064     if (MovDir[x][y] != old_move_dir)
7065       MovDelay[x][y] = 9;
7066   }
7067   else if (element == EL_BALLOON)
7068   {
7069     MovDir[x][y] = game.wind_direction;
7070     MovDelay[x][y] = 0;
7071   }
7072   else if (element == EL_SPRING)
7073   {
7074 #if USE_NEW_SPRING_BUMPER
7075     if (MovDir[x][y] & MV_HORIZONTAL)
7076     {
7077       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7078           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7079       {
7080         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7081         ResetGfxAnimation(move_x, move_y);
7082         TEST_DrawLevelField(move_x, move_y);
7083
7084         MovDir[x][y] = back_dir;
7085       }
7086       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7087                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7088         MovDir[x][y] = MV_NONE;
7089     }
7090 #else
7091     if (MovDir[x][y] & MV_HORIZONTAL &&
7092         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7093          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7094       MovDir[x][y] = MV_NONE;
7095 #endif
7096
7097     MovDelay[x][y] = 0;
7098   }
7099   else if (element == EL_ROBOT ||
7100            element == EL_SATELLITE ||
7101            element == EL_PENGUIN ||
7102            element == EL_EMC_ANDROID)
7103   {
7104     int attr_x = -1, attr_y = -1;
7105
7106     if (AllPlayersGone)
7107     {
7108       attr_x = ExitX;
7109       attr_y = ExitY;
7110     }
7111     else
7112     {
7113       int i;
7114
7115       for (i = 0; i < MAX_PLAYERS; i++)
7116       {
7117         struct PlayerInfo *player = &stored_player[i];
7118         int jx = player->jx, jy = player->jy;
7119
7120         if (!player->active)
7121           continue;
7122
7123         if (attr_x == -1 ||
7124             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7125         {
7126           attr_x = jx;
7127           attr_y = jy;
7128         }
7129       }
7130     }
7131
7132     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7133         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7134          game.engine_version < VERSION_IDENT(3,1,0,0)))
7135     {
7136       attr_x = ZX;
7137       attr_y = ZY;
7138     }
7139
7140     if (element == EL_PENGUIN)
7141     {
7142       int i;
7143       static int xy[4][2] =
7144       {
7145         { 0, -1 },
7146         { -1, 0 },
7147         { +1, 0 },
7148         { 0, +1 }
7149       };
7150
7151       for (i = 0; i < NUM_DIRECTIONS; i++)
7152       {
7153         int ex = x + xy[i][0];
7154         int ey = y + xy[i][1];
7155
7156         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7157                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7158                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7159                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7160         {
7161           attr_x = ex;
7162           attr_y = ey;
7163           break;
7164         }
7165       }
7166     }
7167
7168     MovDir[x][y] = MV_NONE;
7169     if (attr_x < x)
7170       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7171     else if (attr_x > x)
7172       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7173     if (attr_y < y)
7174       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7175     else if (attr_y > y)
7176       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7177
7178     if (element == EL_ROBOT)
7179     {
7180       int newx, newy;
7181
7182       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7183         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7184       Moving2Blocked(x, y, &newx, &newy);
7185
7186       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7187         MovDelay[x][y] = 8 + 8 * !RND(3);
7188       else
7189         MovDelay[x][y] = 16;
7190     }
7191     else if (element == EL_PENGUIN)
7192     {
7193       int newx, newy;
7194
7195       MovDelay[x][y] = 1;
7196
7197       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7198       {
7199         boolean first_horiz = RND(2);
7200         int new_move_dir = MovDir[x][y];
7201
7202         MovDir[x][y] =
7203           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7204         Moving2Blocked(x, y, &newx, &newy);
7205
7206         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7207           return;
7208
7209         MovDir[x][y] =
7210           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7211         Moving2Blocked(x, y, &newx, &newy);
7212
7213         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7214           return;
7215
7216         MovDir[x][y] = old_move_dir;
7217         return;
7218       }
7219     }
7220     else if (element == EL_SATELLITE)
7221     {
7222       int newx, newy;
7223
7224       MovDelay[x][y] = 1;
7225
7226       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7227       {
7228         boolean first_horiz = RND(2);
7229         int new_move_dir = MovDir[x][y];
7230
7231         MovDir[x][y] =
7232           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7233         Moving2Blocked(x, y, &newx, &newy);
7234
7235         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7236           return;
7237
7238         MovDir[x][y] =
7239           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7240         Moving2Blocked(x, y, &newx, &newy);
7241
7242         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7243           return;
7244
7245         MovDir[x][y] = old_move_dir;
7246         return;
7247       }
7248     }
7249     else if (element == EL_EMC_ANDROID)
7250     {
7251       static int check_pos[16] =
7252       {
7253         -1,             /*  0 => (invalid)          */
7254         7,              /*  1 => MV_LEFT            */
7255         3,              /*  2 => MV_RIGHT           */
7256         -1,             /*  3 => (invalid)          */
7257         1,              /*  4 =>            MV_UP   */
7258         0,              /*  5 => MV_LEFT  | MV_UP   */
7259         2,              /*  6 => MV_RIGHT | MV_UP   */
7260         -1,             /*  7 => (invalid)          */
7261         5,              /*  8 =>            MV_DOWN */
7262         6,              /*  9 => MV_LEFT  | MV_DOWN */
7263         4,              /* 10 => MV_RIGHT | MV_DOWN */
7264         -1,             /* 11 => (invalid)          */
7265         -1,             /* 12 => (invalid)          */
7266         -1,             /* 13 => (invalid)          */
7267         -1,             /* 14 => (invalid)          */
7268         -1,             /* 15 => (invalid)          */
7269       };
7270       static struct
7271       {
7272         int dx, dy;
7273         int dir;
7274       } check_xy[8] =
7275       {
7276         { -1, -1,       MV_LEFT  | MV_UP   },
7277         {  0, -1,                  MV_UP   },
7278         { +1, -1,       MV_RIGHT | MV_UP   },
7279         { +1,  0,       MV_RIGHT           },
7280         { +1, +1,       MV_RIGHT | MV_DOWN },
7281         {  0, +1,                  MV_DOWN },
7282         { -1, +1,       MV_LEFT  | MV_DOWN },
7283         { -1,  0,       MV_LEFT            },
7284       };
7285       int start_pos, check_order;
7286       boolean can_clone = FALSE;
7287       int i;
7288
7289       /* check if there is any free field around current position */
7290       for (i = 0; i < 8; i++)
7291       {
7292         int newx = x + check_xy[i].dx;
7293         int newy = y + check_xy[i].dy;
7294
7295         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7296         {
7297           can_clone = TRUE;
7298
7299           break;
7300         }
7301       }
7302
7303       if (can_clone)            /* randomly find an element to clone */
7304       {
7305         can_clone = FALSE;
7306
7307         start_pos = check_pos[RND(8)];
7308         check_order = (RND(2) ? -1 : +1);
7309
7310         for (i = 0; i < 8; i++)
7311         {
7312           int pos_raw = start_pos + i * check_order;
7313           int pos = (pos_raw + 8) % 8;
7314           int newx = x + check_xy[pos].dx;
7315           int newy = y + check_xy[pos].dy;
7316
7317           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7318           {
7319             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7320             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7321
7322             Store[x][y] = Feld[newx][newy];
7323
7324             can_clone = TRUE;
7325
7326             break;
7327           }
7328         }
7329       }
7330
7331       if (can_clone)            /* randomly find a direction to move */
7332       {
7333         can_clone = FALSE;
7334
7335         start_pos = check_pos[RND(8)];
7336         check_order = (RND(2) ? -1 : +1);
7337
7338         for (i = 0; i < 8; i++)
7339         {
7340           int pos_raw = start_pos + i * check_order;
7341           int pos = (pos_raw + 8) % 8;
7342           int newx = x + check_xy[pos].dx;
7343           int newy = y + check_xy[pos].dy;
7344           int new_move_dir = check_xy[pos].dir;
7345
7346           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7347           {
7348             MovDir[x][y] = new_move_dir;
7349             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7350
7351             can_clone = TRUE;
7352
7353             break;
7354           }
7355         }
7356       }
7357
7358       if (can_clone)            /* cloning and moving successful */
7359         return;
7360
7361       /* cannot clone -- try to move towards player */
7362
7363       start_pos = check_pos[MovDir[x][y] & 0x0f];
7364       check_order = (RND(2) ? -1 : +1);
7365
7366       for (i = 0; i < 3; i++)
7367       {
7368         /* first check start_pos, then previous/next or (next/previous) pos */
7369         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7370         int pos = (pos_raw + 8) % 8;
7371         int newx = x + check_xy[pos].dx;
7372         int newy = y + check_xy[pos].dy;
7373         int new_move_dir = check_xy[pos].dir;
7374
7375         if (IS_PLAYER(newx, newy))
7376           break;
7377
7378         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7379         {
7380           MovDir[x][y] = new_move_dir;
7381           MovDelay[x][y] = level.android_move_time * 8 + 1;
7382
7383           break;
7384         }
7385       }
7386     }
7387   }
7388   else if (move_pattern == MV_TURNING_LEFT ||
7389            move_pattern == MV_TURNING_RIGHT ||
7390            move_pattern == MV_TURNING_LEFT_RIGHT ||
7391            move_pattern == MV_TURNING_RIGHT_LEFT ||
7392            move_pattern == MV_TURNING_RANDOM ||
7393            move_pattern == MV_ALL_DIRECTIONS)
7394   {
7395     boolean can_turn_left =
7396       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7397     boolean can_turn_right =
7398       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7399
7400     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7401       return;
7402
7403     if (move_pattern == MV_TURNING_LEFT)
7404       MovDir[x][y] = left_dir;
7405     else if (move_pattern == MV_TURNING_RIGHT)
7406       MovDir[x][y] = right_dir;
7407     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7408       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7409     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7410       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7411     else if (move_pattern == MV_TURNING_RANDOM)
7412       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7413                       can_turn_right && !can_turn_left ? right_dir :
7414                       RND(2) ? left_dir : right_dir);
7415     else if (can_turn_left && can_turn_right)
7416       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7417     else if (can_turn_left)
7418       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7419     else if (can_turn_right)
7420       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7421     else
7422       MovDir[x][y] = back_dir;
7423
7424     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7425   }
7426   else if (move_pattern == MV_HORIZONTAL ||
7427            move_pattern == MV_VERTICAL)
7428   {
7429     if (move_pattern & old_move_dir)
7430       MovDir[x][y] = back_dir;
7431     else if (move_pattern == MV_HORIZONTAL)
7432       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7433     else if (move_pattern == MV_VERTICAL)
7434       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7435
7436     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7437   }
7438   else if (move_pattern & MV_ANY_DIRECTION)
7439   {
7440     MovDir[x][y] = move_pattern;
7441     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7442   }
7443   else if (move_pattern & MV_WIND_DIRECTION)
7444   {
7445     MovDir[x][y] = game.wind_direction;
7446     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7447   }
7448   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7449   {
7450     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7451       MovDir[x][y] = left_dir;
7452     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7453       MovDir[x][y] = right_dir;
7454
7455     if (MovDir[x][y] != old_move_dir)
7456       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7457   }
7458   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7459   {
7460     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7461       MovDir[x][y] = right_dir;
7462     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7463       MovDir[x][y] = left_dir;
7464
7465     if (MovDir[x][y] != old_move_dir)
7466       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7467   }
7468   else if (move_pattern == MV_TOWARDS_PLAYER ||
7469            move_pattern == MV_AWAY_FROM_PLAYER)
7470   {
7471     int attr_x = -1, attr_y = -1;
7472     int newx, newy;
7473     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7474
7475     if (AllPlayersGone)
7476     {
7477       attr_x = ExitX;
7478       attr_y = ExitY;
7479     }
7480     else
7481     {
7482       int i;
7483
7484       for (i = 0; i < MAX_PLAYERS; i++)
7485       {
7486         struct PlayerInfo *player = &stored_player[i];
7487         int jx = player->jx, jy = player->jy;
7488
7489         if (!player->active)
7490           continue;
7491
7492         if (attr_x == -1 ||
7493             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7494         {
7495           attr_x = jx;
7496           attr_y = jy;
7497         }
7498       }
7499     }
7500
7501     MovDir[x][y] = MV_NONE;
7502     if (attr_x < x)
7503       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7504     else if (attr_x > x)
7505       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7506     if (attr_y < y)
7507       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7508     else if (attr_y > y)
7509       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7510
7511     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7512
7513     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7514     {
7515       boolean first_horiz = RND(2);
7516       int new_move_dir = MovDir[x][y];
7517
7518       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7519       {
7520         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7521         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7522
7523         return;
7524       }
7525
7526       MovDir[x][y] =
7527         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7528       Moving2Blocked(x, y, &newx, &newy);
7529
7530       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7531         return;
7532
7533       MovDir[x][y] =
7534         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7535       Moving2Blocked(x, y, &newx, &newy);
7536
7537       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7538         return;
7539
7540       MovDir[x][y] = old_move_dir;
7541     }
7542   }
7543   else if (move_pattern == MV_WHEN_PUSHED ||
7544            move_pattern == MV_WHEN_DROPPED)
7545   {
7546     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7547       MovDir[x][y] = MV_NONE;
7548
7549     MovDelay[x][y] = 0;
7550   }
7551   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7552   {
7553     static int test_xy[7][2] =
7554     {
7555       { 0, -1 },
7556       { -1, 0 },
7557       { +1, 0 },
7558       { 0, +1 },
7559       { 0, -1 },
7560       { -1, 0 },
7561       { +1, 0 },
7562     };
7563     static int test_dir[7] =
7564     {
7565       MV_UP,
7566       MV_LEFT,
7567       MV_RIGHT,
7568       MV_DOWN,
7569       MV_UP,
7570       MV_LEFT,
7571       MV_RIGHT,
7572     };
7573     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7574     int move_preference = -1000000;     /* start with very low preference */
7575     int new_move_dir = MV_NONE;
7576     int start_test = RND(4);
7577     int i;
7578
7579     for (i = 0; i < NUM_DIRECTIONS; i++)
7580     {
7581       int move_dir = test_dir[start_test + i];
7582       int move_dir_preference;
7583
7584       xx = x + test_xy[start_test + i][0];
7585       yy = y + test_xy[start_test + i][1];
7586
7587       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7588           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7589       {
7590         new_move_dir = move_dir;
7591
7592         break;
7593       }
7594
7595       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7596         continue;
7597
7598       move_dir_preference = -1 * RunnerVisit[xx][yy];
7599       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7600         move_dir_preference = PlayerVisit[xx][yy];
7601
7602       if (move_dir_preference > move_preference)
7603       {
7604         /* prefer field that has not been visited for the longest time */
7605         move_preference = move_dir_preference;
7606         new_move_dir = move_dir;
7607       }
7608       else if (move_dir_preference == move_preference &&
7609                move_dir == old_move_dir)
7610       {
7611         /* prefer last direction when all directions are preferred equally */
7612         move_preference = move_dir_preference;
7613         new_move_dir = move_dir;
7614       }
7615     }
7616
7617     MovDir[x][y] = new_move_dir;
7618     if (old_move_dir != new_move_dir)
7619       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7620   }
7621 }
7622
7623 static void TurnRound(int x, int y)
7624 {
7625   int direction = MovDir[x][y];
7626
7627   TurnRoundExt(x, y);
7628
7629   GfxDir[x][y] = MovDir[x][y];
7630
7631   if (direction != MovDir[x][y])
7632     GfxFrame[x][y] = 0;
7633
7634   if (MovDelay[x][y])
7635     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7636
7637   ResetGfxFrame(x, y, FALSE);
7638 }
7639
7640 static boolean JustBeingPushed(int x, int y)
7641 {
7642   int i;
7643
7644   for (i = 0; i < MAX_PLAYERS; i++)
7645   {
7646     struct PlayerInfo *player = &stored_player[i];
7647
7648     if (player->active && player->is_pushing && player->MovPos)
7649     {
7650       int next_jx = player->jx + (player->jx - player->last_jx);
7651       int next_jy = player->jy + (player->jy - player->last_jy);
7652
7653       if (x == next_jx && y == next_jy)
7654         return TRUE;
7655     }
7656   }
7657
7658   return FALSE;
7659 }
7660
7661 void StartMoving(int x, int y)
7662 {
7663   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7664   int element = Feld[x][y];
7665
7666   if (Stop[x][y])
7667     return;
7668
7669   if (MovDelay[x][y] == 0)
7670     GfxAction[x][y] = ACTION_DEFAULT;
7671
7672   if (CAN_FALL(element) && y < lev_fieldy - 1)
7673   {
7674     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7675         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7676       if (JustBeingPushed(x, y))
7677         return;
7678
7679     if (element == EL_QUICKSAND_FULL)
7680     {
7681       if (IS_FREE(x, y + 1))
7682       {
7683         InitMovingField(x, y, MV_DOWN);
7684         started_moving = TRUE;
7685
7686         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7687 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7688         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7689           Store[x][y] = EL_ROCK;
7690 #else
7691         Store[x][y] = EL_ROCK;
7692 #endif
7693
7694         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7695       }
7696       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7697       {
7698         if (!MovDelay[x][y])
7699         {
7700           MovDelay[x][y] = TILEY + 1;
7701
7702           ResetGfxAnimation(x, y);
7703           ResetGfxAnimation(x, y + 1);
7704         }
7705
7706         if (MovDelay[x][y])
7707         {
7708           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7709           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7710
7711           MovDelay[x][y]--;
7712           if (MovDelay[x][y])
7713             return;
7714         }
7715
7716         Feld[x][y] = EL_QUICKSAND_EMPTY;
7717         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7718         Store[x][y + 1] = Store[x][y];
7719         Store[x][y] = 0;
7720
7721         PlayLevelSoundAction(x, y, ACTION_FILLING);
7722       }
7723       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7724       {
7725         if (!MovDelay[x][y])
7726         {
7727           MovDelay[x][y] = TILEY + 1;
7728
7729           ResetGfxAnimation(x, y);
7730           ResetGfxAnimation(x, y + 1);
7731         }
7732
7733         if (MovDelay[x][y])
7734         {
7735           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7736           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7737
7738           MovDelay[x][y]--;
7739           if (MovDelay[x][y])
7740             return;
7741         }
7742
7743         Feld[x][y] = EL_QUICKSAND_EMPTY;
7744         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7745         Store[x][y + 1] = Store[x][y];
7746         Store[x][y] = 0;
7747
7748         PlayLevelSoundAction(x, y, ACTION_FILLING);
7749       }
7750     }
7751     else if (element == EL_QUICKSAND_FAST_FULL)
7752     {
7753       if (IS_FREE(x, y + 1))
7754       {
7755         InitMovingField(x, y, MV_DOWN);
7756         started_moving = TRUE;
7757
7758         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7759 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7760         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7761           Store[x][y] = EL_ROCK;
7762 #else
7763         Store[x][y] = EL_ROCK;
7764 #endif
7765
7766         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7767       }
7768       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7769       {
7770         if (!MovDelay[x][y])
7771         {
7772           MovDelay[x][y] = TILEY + 1;
7773
7774           ResetGfxAnimation(x, y);
7775           ResetGfxAnimation(x, y + 1);
7776         }
7777
7778         if (MovDelay[x][y])
7779         {
7780           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7781           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7782
7783           MovDelay[x][y]--;
7784           if (MovDelay[x][y])
7785             return;
7786         }
7787
7788         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7789         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7790         Store[x][y + 1] = Store[x][y];
7791         Store[x][y] = 0;
7792
7793         PlayLevelSoundAction(x, y, ACTION_FILLING);
7794       }
7795       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7796       {
7797         if (!MovDelay[x][y])
7798         {
7799           MovDelay[x][y] = TILEY + 1;
7800
7801           ResetGfxAnimation(x, y);
7802           ResetGfxAnimation(x, y + 1);
7803         }
7804
7805         if (MovDelay[x][y])
7806         {
7807           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7808           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7809
7810           MovDelay[x][y]--;
7811           if (MovDelay[x][y])
7812             return;
7813         }
7814
7815         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7816         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7817         Store[x][y + 1] = Store[x][y];
7818         Store[x][y] = 0;
7819
7820         PlayLevelSoundAction(x, y, ACTION_FILLING);
7821       }
7822     }
7823     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7824              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7825     {
7826       InitMovingField(x, y, MV_DOWN);
7827       started_moving = TRUE;
7828
7829       Feld[x][y] = EL_QUICKSAND_FILLING;
7830       Store[x][y] = element;
7831
7832       PlayLevelSoundAction(x, y, ACTION_FILLING);
7833     }
7834     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7835              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7836     {
7837       InitMovingField(x, y, MV_DOWN);
7838       started_moving = TRUE;
7839
7840       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7841       Store[x][y] = element;
7842
7843       PlayLevelSoundAction(x, y, ACTION_FILLING);
7844     }
7845     else if (element == EL_MAGIC_WALL_FULL)
7846     {
7847       if (IS_FREE(x, y + 1))
7848       {
7849         InitMovingField(x, y, MV_DOWN);
7850         started_moving = TRUE;
7851
7852         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7853         Store[x][y] = EL_CHANGED(Store[x][y]);
7854       }
7855       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7856       {
7857         if (!MovDelay[x][y])
7858           MovDelay[x][y] = TILEY/4 + 1;
7859
7860         if (MovDelay[x][y])
7861         {
7862           MovDelay[x][y]--;
7863           if (MovDelay[x][y])
7864             return;
7865         }
7866
7867         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7868         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7869         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7870         Store[x][y] = 0;
7871       }
7872     }
7873     else if (element == EL_BD_MAGIC_WALL_FULL)
7874     {
7875       if (IS_FREE(x, y + 1))
7876       {
7877         InitMovingField(x, y, MV_DOWN);
7878         started_moving = TRUE;
7879
7880         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7881         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7882       }
7883       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7884       {
7885         if (!MovDelay[x][y])
7886           MovDelay[x][y] = TILEY/4 + 1;
7887
7888         if (MovDelay[x][y])
7889         {
7890           MovDelay[x][y]--;
7891           if (MovDelay[x][y])
7892             return;
7893         }
7894
7895         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7896         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7897         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7898         Store[x][y] = 0;
7899       }
7900     }
7901     else if (element == EL_DC_MAGIC_WALL_FULL)
7902     {
7903       if (IS_FREE(x, y + 1))
7904       {
7905         InitMovingField(x, y, MV_DOWN);
7906         started_moving = TRUE;
7907
7908         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7909         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7910       }
7911       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7912       {
7913         if (!MovDelay[x][y])
7914           MovDelay[x][y] = TILEY/4 + 1;
7915
7916         if (MovDelay[x][y])
7917         {
7918           MovDelay[x][y]--;
7919           if (MovDelay[x][y])
7920             return;
7921         }
7922
7923         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7924         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7925         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7926         Store[x][y] = 0;
7927       }
7928     }
7929     else if ((CAN_PASS_MAGIC_WALL(element) &&
7930               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7931                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7932              (CAN_PASS_DC_MAGIC_WALL(element) &&
7933               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7934
7935     {
7936       InitMovingField(x, y, MV_DOWN);
7937       started_moving = TRUE;
7938
7939       Feld[x][y] =
7940         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7941          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7942          EL_DC_MAGIC_WALL_FILLING);
7943       Store[x][y] = element;
7944     }
7945     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7946     {
7947       SplashAcid(x, y + 1);
7948
7949       InitMovingField(x, y, MV_DOWN);
7950       started_moving = TRUE;
7951
7952       Store[x][y] = EL_ACID;
7953     }
7954     else if (
7955 #if USE_FIX_IMPACT_COLLISION
7956              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7957               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7958 #else
7959              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7960               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7961 #endif
7962              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7963               CAN_FALL(element) && WasJustFalling[x][y] &&
7964               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7965
7966              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7967               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7968               (Feld[x][y + 1] == EL_BLOCKED)))
7969     {
7970       /* this is needed for a special case not covered by calling "Impact()"
7971          from "ContinueMoving()": if an element moves to a tile directly below
7972          another element which was just falling on that tile (which was empty
7973          in the previous frame), the falling element above would just stop
7974          instead of smashing the element below (in previous version, the above
7975          element was just checked for "moving" instead of "falling", resulting
7976          in incorrect smashes caused by horizontal movement of the above
7977          element; also, the case of the player being the element to smash was
7978          simply not covered here... :-/ ) */
7979
7980       CheckCollision[x][y] = 0;
7981       CheckImpact[x][y] = 0;
7982
7983       Impact(x, y);
7984     }
7985     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7986     {
7987       if (MovDir[x][y] == MV_NONE)
7988       {
7989         InitMovingField(x, y, MV_DOWN);
7990         started_moving = TRUE;
7991       }
7992     }
7993     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7994     {
7995       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7996         MovDir[x][y] = MV_DOWN;
7997
7998       InitMovingField(x, y, MV_DOWN);
7999       started_moving = TRUE;
8000     }
8001     else if (element == EL_AMOEBA_DROP)
8002     {
8003       Feld[x][y] = EL_AMOEBA_GROWING;
8004       Store[x][y] = EL_AMOEBA_WET;
8005     }
8006     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8007               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8008              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8009              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8010     {
8011       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8012                                 (IS_FREE(x - 1, y + 1) ||
8013                                  Feld[x - 1][y + 1] == EL_ACID));
8014       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8015                                 (IS_FREE(x + 1, y + 1) ||
8016                                  Feld[x + 1][y + 1] == EL_ACID));
8017       boolean can_fall_any  = (can_fall_left || can_fall_right);
8018       boolean can_fall_both = (can_fall_left && can_fall_right);
8019       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8020
8021 #if USE_NEW_ALL_SLIPPERY
8022       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8023       {
8024         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8025           can_fall_right = FALSE;
8026         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8027           can_fall_left = FALSE;
8028         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8029           can_fall_right = FALSE;
8030         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8031           can_fall_left = FALSE;
8032
8033         can_fall_any  = (can_fall_left || can_fall_right);
8034         can_fall_both = FALSE;
8035       }
8036 #else
8037       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8038       {
8039         if (slippery_type == SLIPPERY_ONLY_LEFT)
8040           can_fall_right = FALSE;
8041         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8042           can_fall_left = FALSE;
8043         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8044           can_fall_right = FALSE;
8045         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8046           can_fall_left = FALSE;
8047
8048         can_fall_any  = (can_fall_left || can_fall_right);
8049         can_fall_both = (can_fall_left && can_fall_right);
8050       }
8051 #endif
8052
8053 #if USE_NEW_ALL_SLIPPERY
8054 #else
8055 #if USE_NEW_SP_SLIPPERY
8056       /* !!! better use the same properties as for custom elements here !!! */
8057       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8058                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8059       {
8060         can_fall_right = FALSE;         /* slip down on left side */
8061         can_fall_both = FALSE;
8062       }
8063 #endif
8064 #endif
8065
8066 #if USE_NEW_ALL_SLIPPERY
8067       if (can_fall_both)
8068       {
8069         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8070           can_fall_right = FALSE;       /* slip down on left side */
8071         else
8072           can_fall_left = !(can_fall_right = RND(2));
8073
8074         can_fall_both = FALSE;
8075       }
8076 #else
8077       if (can_fall_both)
8078       {
8079         if (game.emulation == EMU_BOULDERDASH ||
8080             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8081           can_fall_right = FALSE;       /* slip down on left side */
8082         else
8083           can_fall_left = !(can_fall_right = RND(2));
8084
8085         can_fall_both = FALSE;
8086       }
8087 #endif
8088
8089       if (can_fall_any)
8090       {
8091         /* if not determined otherwise, prefer left side for slipping down */
8092         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8093         started_moving = TRUE;
8094       }
8095     }
8096 #if 0
8097     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8098 #else
8099     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8100 #endif
8101     {
8102       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8103       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8104       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8105       int belt_dir = game.belt_dir[belt_nr];
8106
8107       if ((belt_dir == MV_LEFT  && left_is_free) ||
8108           (belt_dir == MV_RIGHT && right_is_free))
8109       {
8110         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8111
8112         InitMovingField(x, y, belt_dir);
8113         started_moving = TRUE;
8114
8115         Pushed[x][y] = TRUE;
8116         Pushed[nextx][y] = TRUE;
8117
8118         GfxAction[x][y] = ACTION_DEFAULT;
8119       }
8120       else
8121       {
8122         MovDir[x][y] = 0;       /* if element was moving, stop it */
8123       }
8124     }
8125   }
8126
8127   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8128 #if 0
8129   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8130 #else
8131   if (CAN_MOVE(element) && !started_moving)
8132 #endif
8133   {
8134     int move_pattern = element_info[element].move_pattern;
8135     int newx, newy;
8136
8137 #if 0
8138 #if DEBUG
8139     if (MovDir[x][y] == MV_NONE)
8140     {
8141       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8142              x, y, element, element_info[element].token_name);
8143       printf("StartMoving(): This should never happen!\n");
8144     }
8145 #endif
8146 #endif
8147
8148     Moving2Blocked(x, y, &newx, &newy);
8149
8150     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8151       return;
8152
8153     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8154         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8155     {
8156       WasJustMoving[x][y] = 0;
8157       CheckCollision[x][y] = 0;
8158
8159       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8160
8161       if (Feld[x][y] != element)        /* element has changed */
8162         return;
8163     }
8164
8165     if (!MovDelay[x][y])        /* start new movement phase */
8166     {
8167       /* all objects that can change their move direction after each step
8168          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8169
8170       if (element != EL_YAMYAM &&
8171           element != EL_DARK_YAMYAM &&
8172           element != EL_PACMAN &&
8173           !(move_pattern & MV_ANY_DIRECTION) &&
8174           move_pattern != MV_TURNING_LEFT &&
8175           move_pattern != MV_TURNING_RIGHT &&
8176           move_pattern != MV_TURNING_LEFT_RIGHT &&
8177           move_pattern != MV_TURNING_RIGHT_LEFT &&
8178           move_pattern != MV_TURNING_RANDOM)
8179       {
8180         TurnRound(x, y);
8181
8182         if (MovDelay[x][y] && (element == EL_BUG ||
8183                                element == EL_SPACESHIP ||
8184                                element == EL_SP_SNIKSNAK ||
8185                                element == EL_SP_ELECTRON ||
8186                                element == EL_MOLE))
8187           TEST_DrawLevelField(x, y);
8188       }
8189     }
8190
8191     if (MovDelay[x][y])         /* wait some time before next movement */
8192     {
8193       MovDelay[x][y]--;
8194
8195       if (element == EL_ROBOT ||
8196           element == EL_YAMYAM ||
8197           element == EL_DARK_YAMYAM)
8198       {
8199         DrawLevelElementAnimationIfNeeded(x, y, element);
8200         PlayLevelSoundAction(x, y, ACTION_WAITING);
8201       }
8202       else if (element == EL_SP_ELECTRON)
8203         DrawLevelElementAnimationIfNeeded(x, y, element);
8204       else if (element == EL_DRAGON)
8205       {
8206         int i;
8207         int dir = MovDir[x][y];
8208         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8209         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8210         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8211                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8212                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8213                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8214         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8215
8216         GfxAction[x][y] = ACTION_ATTACKING;
8217
8218         if (IS_PLAYER(x, y))
8219           DrawPlayerField(x, y);
8220         else
8221           TEST_DrawLevelField(x, y);
8222
8223         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8224
8225         for (i = 1; i <= 3; i++)
8226         {
8227           int xx = x + i * dx;
8228           int yy = y + i * dy;
8229           int sx = SCREENX(xx);
8230           int sy = SCREENY(yy);
8231           int flame_graphic = graphic + (i - 1);
8232
8233           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8234             break;
8235
8236           if (MovDelay[x][y])
8237           {
8238             int flamed = MovingOrBlocked2Element(xx, yy);
8239
8240             /* !!! */
8241 #if 0
8242             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8243               Bang(xx, yy);
8244             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8245               RemoveMovingField(xx, yy);
8246             else
8247               RemoveField(xx, yy);
8248 #else
8249             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8250               Bang(xx, yy);
8251             else
8252               RemoveMovingField(xx, yy);
8253 #endif
8254
8255             ChangeDelay[xx][yy] = 0;
8256
8257             Feld[xx][yy] = EL_FLAMES;
8258
8259             if (IN_SCR_FIELD(sx, sy))
8260             {
8261               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8262               DrawGraphic(sx, sy, flame_graphic, frame);
8263             }
8264           }
8265           else
8266           {
8267             if (Feld[xx][yy] == EL_FLAMES)
8268               Feld[xx][yy] = EL_EMPTY;
8269             TEST_DrawLevelField(xx, yy);
8270           }
8271         }
8272       }
8273
8274       if (MovDelay[x][y])       /* element still has to wait some time */
8275       {
8276         PlayLevelSoundAction(x, y, ACTION_WAITING);
8277
8278         return;
8279       }
8280     }
8281
8282     /* now make next step */
8283
8284     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8285
8286     if (DONT_COLLIDE_WITH(element) &&
8287         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8288         !PLAYER_ENEMY_PROTECTED(newx, newy))
8289     {
8290       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8291
8292       return;
8293     }
8294
8295     else if (CAN_MOVE_INTO_ACID(element) &&
8296              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8297              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8298              (MovDir[x][y] == MV_DOWN ||
8299               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8300     {
8301       SplashAcid(newx, newy);
8302       Store[x][y] = EL_ACID;
8303     }
8304     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8305     {
8306       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8307           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8308           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8309           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8310       {
8311         RemoveField(x, y);
8312         TEST_DrawLevelField(x, y);
8313
8314         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8315         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8316           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8317
8318         local_player->friends_still_needed--;
8319         if (!local_player->friends_still_needed &&
8320             !local_player->GameOver && AllPlayersGone)
8321           PlayerWins(local_player);
8322
8323         return;
8324       }
8325       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8326       {
8327         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8328           TEST_DrawLevelField(newx, newy);
8329         else
8330           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8331       }
8332       else if (!IS_FREE(newx, newy))
8333       {
8334         GfxAction[x][y] = ACTION_WAITING;
8335
8336         if (IS_PLAYER(x, y))
8337           DrawPlayerField(x, y);
8338         else
8339           TEST_DrawLevelField(x, y);
8340
8341         return;
8342       }
8343     }
8344     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8345     {
8346       if (IS_FOOD_PIG(Feld[newx][newy]))
8347       {
8348         if (IS_MOVING(newx, newy))
8349           RemoveMovingField(newx, newy);
8350         else
8351         {
8352           Feld[newx][newy] = EL_EMPTY;
8353           TEST_DrawLevelField(newx, newy);
8354         }
8355
8356         PlayLevelSound(x, y, SND_PIG_DIGGING);
8357       }
8358       else if (!IS_FREE(newx, newy))
8359       {
8360         if (IS_PLAYER(x, y))
8361           DrawPlayerField(x, y);
8362         else
8363           TEST_DrawLevelField(x, y);
8364
8365         return;
8366       }
8367     }
8368     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8369     {
8370       if (Store[x][y] != EL_EMPTY)
8371       {
8372         boolean can_clone = FALSE;
8373         int xx, yy;
8374
8375         /* check if element to clone is still there */
8376         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8377         {
8378           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8379           {
8380             can_clone = TRUE;
8381
8382             break;
8383           }
8384         }
8385
8386         /* cannot clone or target field not free anymore -- do not clone */
8387         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8388           Store[x][y] = EL_EMPTY;
8389       }
8390
8391       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8392       {
8393         if (IS_MV_DIAGONAL(MovDir[x][y]))
8394         {
8395           int diagonal_move_dir = MovDir[x][y];
8396           int stored = Store[x][y];
8397           int change_delay = 8;
8398           int graphic;
8399
8400           /* android is moving diagonally */
8401
8402           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8403
8404           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8405           GfxElement[x][y] = EL_EMC_ANDROID;
8406           GfxAction[x][y] = ACTION_SHRINKING;
8407           GfxDir[x][y] = diagonal_move_dir;
8408           ChangeDelay[x][y] = change_delay;
8409
8410           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8411                                    GfxDir[x][y]);
8412
8413           DrawLevelGraphicAnimation(x, y, graphic);
8414           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8415
8416           if (Feld[newx][newy] == EL_ACID)
8417           {
8418             SplashAcid(newx, newy);
8419
8420             return;
8421           }
8422
8423           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8424
8425           Store[newx][newy] = EL_EMC_ANDROID;
8426           GfxElement[newx][newy] = EL_EMC_ANDROID;
8427           GfxAction[newx][newy] = ACTION_GROWING;
8428           GfxDir[newx][newy] = diagonal_move_dir;
8429           ChangeDelay[newx][newy] = change_delay;
8430
8431           graphic = el_act_dir2img(GfxElement[newx][newy],
8432                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8433
8434           DrawLevelGraphicAnimation(newx, newy, graphic);
8435           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8436
8437           return;
8438         }
8439         else
8440         {
8441           Feld[newx][newy] = EL_EMPTY;
8442           TEST_DrawLevelField(newx, newy);
8443
8444           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8445         }
8446       }
8447       else if (!IS_FREE(newx, newy))
8448       {
8449 #if 0
8450         if (IS_PLAYER(x, y))
8451           DrawPlayerField(x, y);
8452         else
8453           TEST_DrawLevelField(x, y);
8454 #endif
8455
8456         return;
8457       }
8458     }
8459     else if (IS_CUSTOM_ELEMENT(element) &&
8460              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8461     {
8462 #if 1
8463       if (!DigFieldByCE(newx, newy, element))
8464         return;
8465 #else
8466       int new_element = Feld[newx][newy];
8467
8468       if (!IS_FREE(newx, newy))
8469       {
8470         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8471                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8472                       ACTION_BREAKING);
8473
8474         /* no element can dig solid indestructible elements */
8475         if (IS_INDESTRUCTIBLE(new_element) &&
8476             !IS_DIGGABLE(new_element) &&
8477             !IS_COLLECTIBLE(new_element))
8478           return;
8479
8480         if (AmoebaNr[newx][newy] &&
8481             (new_element == EL_AMOEBA_FULL ||
8482              new_element == EL_BD_AMOEBA ||
8483              new_element == EL_AMOEBA_GROWING))
8484         {
8485           AmoebaCnt[AmoebaNr[newx][newy]]--;
8486           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8487         }
8488
8489         if (IS_MOVING(newx, newy))
8490           RemoveMovingField(newx, newy);
8491         else
8492         {
8493           RemoveField(newx, newy);
8494           TEST_DrawLevelField(newx, newy);
8495         }
8496
8497         /* if digged element was about to explode, prevent the explosion */
8498         ExplodeField[newx][newy] = EX_TYPE_NONE;
8499
8500         PlayLevelSoundAction(x, y, action);
8501       }
8502
8503       Store[newx][newy] = EL_EMPTY;
8504
8505 #if 1
8506       /* this makes it possible to leave the removed element again */
8507       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8508         Store[newx][newy] = new_element;
8509 #else
8510       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8511       {
8512         int move_leave_element = element_info[element].move_leave_element;
8513
8514         /* this makes it possible to leave the removed element again */
8515         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8516                              new_element : move_leave_element);
8517       }
8518 #endif
8519
8520 #endif
8521
8522       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8523       {
8524         RunnerVisit[x][y] = FrameCounter;
8525         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8526       }
8527     }
8528     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8529     {
8530       if (!IS_FREE(newx, newy))
8531       {
8532         if (IS_PLAYER(x, y))
8533           DrawPlayerField(x, y);
8534         else
8535           TEST_DrawLevelField(x, y);
8536
8537         return;
8538       }
8539       else
8540       {
8541         boolean wanna_flame = !RND(10);
8542         int dx = newx - x, dy = newy - y;
8543         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8544         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8545         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8546                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8547         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8548                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8549
8550         if ((wanna_flame ||
8551              IS_CLASSIC_ENEMY(element1) ||
8552              IS_CLASSIC_ENEMY(element2)) &&
8553             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8554             element1 != EL_FLAMES && element2 != EL_FLAMES)
8555         {
8556           ResetGfxAnimation(x, y);
8557           GfxAction[x][y] = ACTION_ATTACKING;
8558
8559           if (IS_PLAYER(x, y))
8560             DrawPlayerField(x, y);
8561           else
8562             TEST_DrawLevelField(x, y);
8563
8564           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8565
8566           MovDelay[x][y] = 50;
8567
8568           /* !!! */
8569 #if 0
8570           RemoveField(newx, newy);
8571 #endif
8572           Feld[newx][newy] = EL_FLAMES;
8573           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8574           {
8575 #if 0
8576             RemoveField(newx1, newy1);
8577 #endif
8578             Feld[newx1][newy1] = EL_FLAMES;
8579           }
8580           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8581           {
8582 #if 0
8583             RemoveField(newx2, newy2);
8584 #endif
8585             Feld[newx2][newy2] = EL_FLAMES;
8586           }
8587
8588           return;
8589         }
8590       }
8591     }
8592     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8593              Feld[newx][newy] == EL_DIAMOND)
8594     {
8595       if (IS_MOVING(newx, newy))
8596         RemoveMovingField(newx, newy);
8597       else
8598       {
8599         Feld[newx][newy] = EL_EMPTY;
8600         TEST_DrawLevelField(newx, newy);
8601       }
8602
8603       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8604     }
8605     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8606              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8607     {
8608       if (AmoebaNr[newx][newy])
8609       {
8610         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8611         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8612             Feld[newx][newy] == EL_BD_AMOEBA)
8613           AmoebaCnt[AmoebaNr[newx][newy]]--;
8614       }
8615
8616 #if 0
8617       /* !!! test !!! */
8618       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8619       {
8620         RemoveMovingField(newx, newy);
8621       }
8622 #else
8623       if (IS_MOVING(newx, newy))
8624       {
8625         RemoveMovingField(newx, newy);
8626       }
8627 #endif
8628       else
8629       {
8630         Feld[newx][newy] = EL_EMPTY;
8631         TEST_DrawLevelField(newx, newy);
8632       }
8633
8634       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8635     }
8636     else if ((element == EL_PACMAN || element == EL_MOLE)
8637              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8638     {
8639       if (AmoebaNr[newx][newy])
8640       {
8641         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8642         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8643             Feld[newx][newy] == EL_BD_AMOEBA)
8644           AmoebaCnt[AmoebaNr[newx][newy]]--;
8645       }
8646
8647       if (element == EL_MOLE)
8648       {
8649         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8650         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8651
8652         ResetGfxAnimation(x, y);
8653         GfxAction[x][y] = ACTION_DIGGING;
8654         TEST_DrawLevelField(x, y);
8655
8656         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8657
8658         return;                         /* wait for shrinking amoeba */
8659       }
8660       else      /* element == EL_PACMAN */
8661       {
8662         Feld[newx][newy] = EL_EMPTY;
8663         TEST_DrawLevelField(newx, newy);
8664         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8665       }
8666     }
8667     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8668              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8669               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8670     {
8671       /* wait for shrinking amoeba to completely disappear */
8672       return;
8673     }
8674     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8675     {
8676       /* object was running against a wall */
8677
8678       TurnRound(x, y);
8679
8680 #if 0
8681       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8682       if (move_pattern & MV_ANY_DIRECTION &&
8683           move_pattern == MovDir[x][y])
8684       {
8685         int blocking_element =
8686           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8687
8688         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8689                                  MovDir[x][y]);
8690
8691         element = Feld[x][y];   /* element might have changed */
8692       }
8693 #endif
8694
8695       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8696         DrawLevelElementAnimation(x, y, element);
8697
8698       if (DONT_TOUCH(element))
8699         TestIfBadThingTouchesPlayer(x, y);
8700
8701       return;
8702     }
8703
8704     InitMovingField(x, y, MovDir[x][y]);
8705
8706     PlayLevelSoundAction(x, y, ACTION_MOVING);
8707   }
8708
8709   if (MovDir[x][y])
8710     ContinueMoving(x, y);
8711 }
8712
8713 void ContinueMoving(int x, int y)
8714 {
8715   int element = Feld[x][y];
8716   struct ElementInfo *ei = &element_info[element];
8717   int direction = MovDir[x][y];
8718   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8719   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8720   int newx = x + dx, newy = y + dy;
8721   int stored = Store[x][y];
8722   int stored_new = Store[newx][newy];
8723   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8724   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8725   boolean last_line = (newy == lev_fieldy - 1);
8726
8727   MovPos[x][y] += getElementMoveStepsize(x, y);
8728
8729   if (pushed_by_player) /* special case: moving object pushed by player */
8730     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8731
8732   if (ABS(MovPos[x][y]) < TILEX)
8733   {
8734 #if 0
8735     int ee = Feld[x][y];
8736     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8737     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8738
8739     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8740            x, y, ABS(MovPos[x][y]),
8741            ee, gg, ff,
8742            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8743 #endif
8744
8745     TEST_DrawLevelField(x, y);
8746
8747     return;     /* element is still moving */
8748   }
8749
8750   /* element reached destination field */
8751
8752   Feld[x][y] = EL_EMPTY;
8753   Feld[newx][newy] = element;
8754   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8755
8756   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8757   {
8758     element = Feld[newx][newy] = EL_ACID;
8759   }
8760   else if (element == EL_MOLE)
8761   {
8762     Feld[x][y] = EL_SAND;
8763
8764     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8765   }
8766   else if (element == EL_QUICKSAND_FILLING)
8767   {
8768     element = Feld[newx][newy] = get_next_element(element);
8769     Store[newx][newy] = Store[x][y];
8770   }
8771   else if (element == EL_QUICKSAND_EMPTYING)
8772   {
8773     Feld[x][y] = get_next_element(element);
8774     element = Feld[newx][newy] = Store[x][y];
8775   }
8776   else if (element == EL_QUICKSAND_FAST_FILLING)
8777   {
8778     element = Feld[newx][newy] = get_next_element(element);
8779     Store[newx][newy] = Store[x][y];
8780   }
8781   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8782   {
8783     Feld[x][y] = get_next_element(element);
8784     element = Feld[newx][newy] = Store[x][y];
8785   }
8786   else if (element == EL_MAGIC_WALL_FILLING)
8787   {
8788     element = Feld[newx][newy] = get_next_element(element);
8789     if (!game.magic_wall_active)
8790       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8791     Store[newx][newy] = Store[x][y];
8792   }
8793   else if (element == EL_MAGIC_WALL_EMPTYING)
8794   {
8795     Feld[x][y] = get_next_element(element);
8796     if (!game.magic_wall_active)
8797       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8798     element = Feld[newx][newy] = Store[x][y];
8799
8800 #if USE_NEW_CUSTOM_VALUE
8801     InitField(newx, newy, FALSE);
8802 #endif
8803   }
8804   else if (element == EL_BD_MAGIC_WALL_FILLING)
8805   {
8806     element = Feld[newx][newy] = get_next_element(element);
8807     if (!game.magic_wall_active)
8808       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8809     Store[newx][newy] = Store[x][y];
8810   }
8811   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8812   {
8813     Feld[x][y] = get_next_element(element);
8814     if (!game.magic_wall_active)
8815       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8816     element = Feld[newx][newy] = Store[x][y];
8817
8818 #if USE_NEW_CUSTOM_VALUE
8819     InitField(newx, newy, FALSE);
8820 #endif
8821   }
8822   else if (element == EL_DC_MAGIC_WALL_FILLING)
8823   {
8824     element = Feld[newx][newy] = get_next_element(element);
8825     if (!game.magic_wall_active)
8826       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8827     Store[newx][newy] = Store[x][y];
8828   }
8829   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8830   {
8831     Feld[x][y] = get_next_element(element);
8832     if (!game.magic_wall_active)
8833       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8834     element = Feld[newx][newy] = Store[x][y];
8835
8836 #if USE_NEW_CUSTOM_VALUE
8837     InitField(newx, newy, FALSE);
8838 #endif
8839   }
8840   else if (element == EL_AMOEBA_DROPPING)
8841   {
8842     Feld[x][y] = get_next_element(element);
8843     element = Feld[newx][newy] = Store[x][y];
8844   }
8845   else if (element == EL_SOKOBAN_OBJECT)
8846   {
8847     if (Back[x][y])
8848       Feld[x][y] = Back[x][y];
8849
8850     if (Back[newx][newy])
8851       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8852
8853     Back[x][y] = Back[newx][newy] = 0;
8854   }
8855
8856   Store[x][y] = EL_EMPTY;
8857   MovPos[x][y] = 0;
8858   MovDir[x][y] = 0;
8859   MovDelay[x][y] = 0;
8860
8861   MovDelay[newx][newy] = 0;
8862
8863   if (CAN_CHANGE_OR_HAS_ACTION(element))
8864   {
8865     /* copy element change control values to new field */
8866     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8867     ChangePage[newx][newy]  = ChangePage[x][y];
8868     ChangeCount[newx][newy] = ChangeCount[x][y];
8869     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8870   }
8871
8872 #if USE_NEW_CUSTOM_VALUE
8873   CustomValue[newx][newy] = CustomValue[x][y];
8874 #endif
8875
8876   ChangeDelay[x][y] = 0;
8877   ChangePage[x][y] = -1;
8878   ChangeCount[x][y] = 0;
8879   ChangeEvent[x][y] = -1;
8880
8881 #if USE_NEW_CUSTOM_VALUE
8882   CustomValue[x][y] = 0;
8883 #endif
8884
8885   /* copy animation control values to new field */
8886   GfxFrame[newx][newy]  = GfxFrame[x][y];
8887   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8888   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8889   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8890
8891   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8892
8893   /* some elements can leave other elements behind after moving */
8894 #if 1
8895   if (ei->move_leave_element != EL_EMPTY &&
8896       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8897       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8898 #else
8899   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8900       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8901       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8902 #endif
8903   {
8904     int move_leave_element = ei->move_leave_element;
8905
8906 #if 1
8907 #if 1
8908     /* this makes it possible to leave the removed element again */
8909     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8910       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8911 #else
8912     /* this makes it possible to leave the removed element again */
8913     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8914       move_leave_element = stored;
8915 #endif
8916 #else
8917     /* this makes it possible to leave the removed element again */
8918     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8919         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8920       move_leave_element = stored;
8921 #endif
8922
8923     Feld[x][y] = move_leave_element;
8924
8925     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8926       MovDir[x][y] = direction;
8927
8928     InitField(x, y, FALSE);
8929
8930     if (GFX_CRUMBLED(Feld[x][y]))
8931       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8932
8933     if (ELEM_IS_PLAYER(move_leave_element))
8934       RelocatePlayer(x, y, move_leave_element);
8935   }
8936
8937   /* do this after checking for left-behind element */
8938   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8939
8940   if (!CAN_MOVE(element) ||
8941       (CAN_FALL(element) && direction == MV_DOWN &&
8942        (element == EL_SPRING ||
8943         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8944         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8945     GfxDir[x][y] = MovDir[newx][newy] = 0;
8946
8947   TEST_DrawLevelField(x, y);
8948   TEST_DrawLevelField(newx, newy);
8949
8950   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8951
8952   /* prevent pushed element from moving on in pushed direction */
8953   if (pushed_by_player && CAN_MOVE(element) &&
8954       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8955       !(element_info[element].move_pattern & direction))
8956     TurnRound(newx, newy);
8957
8958   /* prevent elements on conveyor belt from moving on in last direction */
8959   if (pushed_by_conveyor && CAN_FALL(element) &&
8960       direction & MV_HORIZONTAL)
8961     MovDir[newx][newy] = 0;
8962
8963   if (!pushed_by_player)
8964   {
8965     int nextx = newx + dx, nexty = newy + dy;
8966     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8967
8968     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8969
8970     if (CAN_FALL(element) && direction == MV_DOWN)
8971       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8972
8973     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8974       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8975
8976 #if USE_FIX_IMPACT_COLLISION
8977     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8978       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8979 #endif
8980   }
8981
8982   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8983   {
8984     TestIfBadThingTouchesPlayer(newx, newy);
8985     TestIfBadThingTouchesFriend(newx, newy);
8986
8987     if (!IS_CUSTOM_ELEMENT(element))
8988       TestIfBadThingTouchesOtherBadThing(newx, newy);
8989   }
8990   else if (element == EL_PENGUIN)
8991     TestIfFriendTouchesBadThing(newx, newy);
8992
8993   if (DONT_GET_HIT_BY(element))
8994   {
8995     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8996   }
8997
8998   /* give the player one last chance (one more frame) to move away */
8999   if (CAN_FALL(element) && direction == MV_DOWN &&
9000       (last_line || (!IS_FREE(x, newy + 1) &&
9001                      (!IS_PLAYER(x, newy + 1) ||
9002                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9003     Impact(x, newy);
9004
9005   if (pushed_by_player && !game.use_change_when_pushing_bug)
9006   {
9007     int push_side = MV_DIR_OPPOSITE(direction);
9008     struct PlayerInfo *player = PLAYERINFO(x, y);
9009
9010     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9011                                player->index_bit, push_side);
9012     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9013                                         player->index_bit, push_side);
9014   }
9015
9016   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9017     MovDelay[newx][newy] = 1;
9018
9019   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9020
9021   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9022
9023 #if 0
9024   if (ChangePage[newx][newy] != -1)             /* delayed change */
9025   {
9026     int page = ChangePage[newx][newy];
9027     struct ElementChangeInfo *change = &ei->change_page[page];
9028
9029     ChangePage[newx][newy] = -1;
9030
9031     if (change->can_change)
9032     {
9033       if (ChangeElement(newx, newy, element, page))
9034       {
9035         if (change->post_change_function)
9036           change->post_change_function(newx, newy);
9037       }
9038     }
9039
9040     if (change->has_action)
9041       ExecuteCustomElementAction(newx, newy, element, page);
9042   }
9043 #endif
9044
9045   TestIfElementHitsCustomElement(newx, newy, direction);
9046   TestIfPlayerTouchesCustomElement(newx, newy);
9047   TestIfElementTouchesCustomElement(newx, newy);
9048
9049   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9050       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9051     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9052                              MV_DIR_OPPOSITE(direction));
9053 }
9054
9055 int AmoebeNachbarNr(int ax, int ay)
9056 {
9057   int i;
9058   int element = Feld[ax][ay];
9059   int group_nr = 0;
9060   static int xy[4][2] =
9061   {
9062     { 0, -1 },
9063     { -1, 0 },
9064     { +1, 0 },
9065     { 0, +1 }
9066   };
9067
9068   for (i = 0; i < NUM_DIRECTIONS; i++)
9069   {
9070     int x = ax + xy[i][0];
9071     int y = ay + xy[i][1];
9072
9073     if (!IN_LEV_FIELD(x, y))
9074       continue;
9075
9076     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9077       group_nr = AmoebaNr[x][y];
9078   }
9079
9080   return group_nr;
9081 }
9082
9083 void AmoebenVereinigen(int ax, int ay)
9084 {
9085   int i, x, y, xx, yy;
9086   int new_group_nr = AmoebaNr[ax][ay];
9087   static int xy[4][2] =
9088   {
9089     { 0, -1 },
9090     { -1, 0 },
9091     { +1, 0 },
9092     { 0, +1 }
9093   };
9094
9095   if (new_group_nr == 0)
9096     return;
9097
9098   for (i = 0; i < NUM_DIRECTIONS; i++)
9099   {
9100     x = ax + xy[i][0];
9101     y = ay + xy[i][1];
9102
9103     if (!IN_LEV_FIELD(x, y))
9104       continue;
9105
9106     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9107          Feld[x][y] == EL_BD_AMOEBA ||
9108          Feld[x][y] == EL_AMOEBA_DEAD) &&
9109         AmoebaNr[x][y] != new_group_nr)
9110     {
9111       int old_group_nr = AmoebaNr[x][y];
9112
9113       if (old_group_nr == 0)
9114         return;
9115
9116       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9117       AmoebaCnt[old_group_nr] = 0;
9118       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9119       AmoebaCnt2[old_group_nr] = 0;
9120
9121       SCAN_PLAYFIELD(xx, yy)
9122       {
9123         if (AmoebaNr[xx][yy] == old_group_nr)
9124           AmoebaNr[xx][yy] = new_group_nr;
9125       }
9126     }
9127   }
9128 }
9129
9130 void AmoebeUmwandeln(int ax, int ay)
9131 {
9132   int i, x, y;
9133
9134   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9135   {
9136     int group_nr = AmoebaNr[ax][ay];
9137
9138 #ifdef DEBUG
9139     if (group_nr == 0)
9140     {
9141       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9142       printf("AmoebeUmwandeln(): This should never happen!\n");
9143       return;
9144     }
9145 #endif
9146
9147     SCAN_PLAYFIELD(x, y)
9148     {
9149       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9150       {
9151         AmoebaNr[x][y] = 0;
9152         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9153       }
9154     }
9155
9156     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9157                             SND_AMOEBA_TURNING_TO_GEM :
9158                             SND_AMOEBA_TURNING_TO_ROCK));
9159     Bang(ax, ay);
9160   }
9161   else
9162   {
9163     static int xy[4][2] =
9164     {
9165       { 0, -1 },
9166       { -1, 0 },
9167       { +1, 0 },
9168       { 0, +1 }
9169     };
9170
9171     for (i = 0; i < NUM_DIRECTIONS; i++)
9172     {
9173       x = ax + xy[i][0];
9174       y = ay + xy[i][1];
9175
9176       if (!IN_LEV_FIELD(x, y))
9177         continue;
9178
9179       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9180       {
9181         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9182                               SND_AMOEBA_TURNING_TO_GEM :
9183                               SND_AMOEBA_TURNING_TO_ROCK));
9184         Bang(x, y);
9185       }
9186     }
9187   }
9188 }
9189
9190 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9191 {
9192   int x, y;
9193   int group_nr = AmoebaNr[ax][ay];
9194   boolean done = FALSE;
9195
9196 #ifdef DEBUG
9197   if (group_nr == 0)
9198   {
9199     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9200     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9201     return;
9202   }
9203 #endif
9204
9205   SCAN_PLAYFIELD(x, y)
9206   {
9207     if (AmoebaNr[x][y] == group_nr &&
9208         (Feld[x][y] == EL_AMOEBA_DEAD ||
9209          Feld[x][y] == EL_BD_AMOEBA ||
9210          Feld[x][y] == EL_AMOEBA_GROWING))
9211     {
9212       AmoebaNr[x][y] = 0;
9213       Feld[x][y] = new_element;
9214       InitField(x, y, FALSE);
9215       TEST_DrawLevelField(x, y);
9216       done = TRUE;
9217     }
9218   }
9219
9220   if (done)
9221     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9222                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9223                             SND_BD_AMOEBA_TURNING_TO_GEM));
9224 }
9225
9226 void AmoebeWaechst(int x, int y)
9227 {
9228   static unsigned long sound_delay = 0;
9229   static unsigned long sound_delay_value = 0;
9230
9231   if (!MovDelay[x][y])          /* start new growing cycle */
9232   {
9233     MovDelay[x][y] = 7;
9234
9235     if (DelayReached(&sound_delay, sound_delay_value))
9236     {
9237       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9238       sound_delay_value = 30;
9239     }
9240   }
9241
9242   if (MovDelay[x][y])           /* wait some time before growing bigger */
9243   {
9244     MovDelay[x][y]--;
9245     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9246     {
9247       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9248                                            6 - MovDelay[x][y]);
9249
9250       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9251     }
9252
9253     if (!MovDelay[x][y])
9254     {
9255       Feld[x][y] = Store[x][y];
9256       Store[x][y] = 0;
9257       TEST_DrawLevelField(x, y);
9258     }
9259   }
9260 }
9261
9262 void AmoebaDisappearing(int x, int y)
9263 {
9264   static unsigned long sound_delay = 0;
9265   static unsigned long sound_delay_value = 0;
9266
9267   if (!MovDelay[x][y])          /* start new shrinking cycle */
9268   {
9269     MovDelay[x][y] = 7;
9270
9271     if (DelayReached(&sound_delay, sound_delay_value))
9272       sound_delay_value = 30;
9273   }
9274
9275   if (MovDelay[x][y])           /* wait some time before shrinking */
9276   {
9277     MovDelay[x][y]--;
9278     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9279     {
9280       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9281                                            6 - MovDelay[x][y]);
9282
9283       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9284     }
9285
9286     if (!MovDelay[x][y])
9287     {
9288       Feld[x][y] = EL_EMPTY;
9289       TEST_DrawLevelField(x, y);
9290
9291       /* don't let mole enter this field in this cycle;
9292          (give priority to objects falling to this field from above) */
9293       Stop[x][y] = TRUE;
9294     }
9295   }
9296 }
9297
9298 void AmoebeAbleger(int ax, int ay)
9299 {
9300   int i;
9301   int element = Feld[ax][ay];
9302   int graphic = el2img(element);
9303   int newax = ax, neway = ay;
9304   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9305   static int xy[4][2] =
9306   {
9307     { 0, -1 },
9308     { -1, 0 },
9309     { +1, 0 },
9310     { 0, +1 }
9311   };
9312
9313   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9314   {
9315     Feld[ax][ay] = EL_AMOEBA_DEAD;
9316     TEST_DrawLevelField(ax, ay);
9317     return;
9318   }
9319
9320   if (IS_ANIMATED(graphic))
9321     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9322
9323   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9324     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9325
9326   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9327   {
9328     MovDelay[ax][ay]--;
9329     if (MovDelay[ax][ay])
9330       return;
9331   }
9332
9333   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9334   {
9335     int start = RND(4);
9336     int x = ax + xy[start][0];
9337     int y = ay + xy[start][1];
9338
9339     if (!IN_LEV_FIELD(x, y))
9340       return;
9341
9342     if (IS_FREE(x, y) ||
9343         CAN_GROW_INTO(Feld[x][y]) ||
9344         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9345         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9346     {
9347       newax = x;
9348       neway = y;
9349     }
9350
9351     if (newax == ax && neway == ay)
9352       return;
9353   }
9354   else                          /* normal or "filled" (BD style) amoeba */
9355   {
9356     int start = RND(4);
9357     boolean waiting_for_player = FALSE;
9358
9359     for (i = 0; i < NUM_DIRECTIONS; i++)
9360     {
9361       int j = (start + i) % 4;
9362       int x = ax + xy[j][0];
9363       int y = ay + xy[j][1];
9364
9365       if (!IN_LEV_FIELD(x, y))
9366         continue;
9367
9368       if (IS_FREE(x, y) ||
9369           CAN_GROW_INTO(Feld[x][y]) ||
9370           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9371           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9372       {
9373         newax = x;
9374         neway = y;
9375         break;
9376       }
9377       else if (IS_PLAYER(x, y))
9378         waiting_for_player = TRUE;
9379     }
9380
9381     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9382     {
9383       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9384       {
9385         Feld[ax][ay] = EL_AMOEBA_DEAD;
9386         TEST_DrawLevelField(ax, ay);
9387         AmoebaCnt[AmoebaNr[ax][ay]]--;
9388
9389         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9390         {
9391           if (element == EL_AMOEBA_FULL)
9392             AmoebeUmwandeln(ax, ay);
9393           else if (element == EL_BD_AMOEBA)
9394             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9395         }
9396       }
9397       return;
9398     }
9399     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9400     {
9401       /* amoeba gets larger by growing in some direction */
9402
9403       int new_group_nr = AmoebaNr[ax][ay];
9404
9405 #ifdef DEBUG
9406   if (new_group_nr == 0)
9407   {
9408     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9409     printf("AmoebeAbleger(): This should never happen!\n");
9410     return;
9411   }
9412 #endif
9413
9414       AmoebaNr[newax][neway] = new_group_nr;
9415       AmoebaCnt[new_group_nr]++;
9416       AmoebaCnt2[new_group_nr]++;
9417
9418       /* if amoeba touches other amoeba(s) after growing, unify them */
9419       AmoebenVereinigen(newax, neway);
9420
9421       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9422       {
9423         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9424         return;
9425       }
9426     }
9427   }
9428
9429   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9430       (neway == lev_fieldy - 1 && newax != ax))
9431   {
9432     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9433     Store[newax][neway] = element;
9434   }
9435   else if (neway == ay || element == EL_EMC_DRIPPER)
9436   {
9437     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9438
9439     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9440   }
9441   else
9442   {
9443     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9444     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9445     Store[ax][ay] = EL_AMOEBA_DROP;
9446     ContinueMoving(ax, ay);
9447     return;
9448   }
9449
9450   TEST_DrawLevelField(newax, neway);
9451 }
9452
9453 void Life(int ax, int ay)
9454 {
9455   int x1, y1, x2, y2;
9456   int life_time = 40;
9457   int element = Feld[ax][ay];
9458   int graphic = el2img(element);
9459   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9460                          level.biomaze);
9461   boolean changed = FALSE;
9462
9463   if (IS_ANIMATED(graphic))
9464     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9465
9466   if (Stop[ax][ay])
9467     return;
9468
9469   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9470     MovDelay[ax][ay] = life_time;
9471
9472   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9473   {
9474     MovDelay[ax][ay]--;
9475     if (MovDelay[ax][ay])
9476       return;
9477   }
9478
9479   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9480   {
9481     int xx = ax+x1, yy = ay+y1;
9482     int nachbarn = 0;
9483
9484     if (!IN_LEV_FIELD(xx, yy))
9485       continue;
9486
9487     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9488     {
9489       int x = xx+x2, y = yy+y2;
9490
9491       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9492         continue;
9493
9494       if (((Feld[x][y] == element ||
9495             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9496            !Stop[x][y]) ||
9497           (IS_FREE(x, y) && Stop[x][y]))
9498         nachbarn++;
9499     }
9500
9501     if (xx == ax && yy == ay)           /* field in the middle */
9502     {
9503       if (nachbarn < life_parameter[0] ||
9504           nachbarn > life_parameter[1])
9505       {
9506         Feld[xx][yy] = EL_EMPTY;
9507         if (!Stop[xx][yy])
9508           TEST_DrawLevelField(xx, yy);
9509         Stop[xx][yy] = TRUE;
9510         changed = TRUE;
9511       }
9512     }
9513     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9514     {                                   /* free border field */
9515       if (nachbarn >= life_parameter[2] &&
9516           nachbarn <= life_parameter[3])
9517       {
9518         Feld[xx][yy] = element;
9519         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9520         if (!Stop[xx][yy])
9521           TEST_DrawLevelField(xx, yy);
9522         Stop[xx][yy] = TRUE;
9523         changed = TRUE;
9524       }
9525     }
9526   }
9527
9528   if (changed)
9529     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9530                    SND_GAME_OF_LIFE_GROWING);
9531 }
9532
9533 static void InitRobotWheel(int x, int y)
9534 {
9535   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9536 }
9537
9538 static void RunRobotWheel(int x, int y)
9539 {
9540   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9541 }
9542
9543 static void StopRobotWheel(int x, int y)
9544 {
9545   if (ZX == x && ZY == y)
9546   {
9547     ZX = ZY = -1;
9548
9549     game.robot_wheel_active = FALSE;
9550   }
9551 }
9552
9553 static void InitTimegateWheel(int x, int y)
9554 {
9555   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9556 }
9557
9558 static void RunTimegateWheel(int x, int y)
9559 {
9560   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9561 }
9562
9563 static void InitMagicBallDelay(int x, int y)
9564 {
9565 #if 1
9566   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9567 #else
9568   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9569 #endif
9570 }
9571
9572 static void ActivateMagicBall(int bx, int by)
9573 {
9574   int x, y;
9575
9576   if (level.ball_random)
9577   {
9578     int pos_border = RND(8);    /* select one of the eight border elements */
9579     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9580     int xx = pos_content % 3;
9581     int yy = pos_content / 3;
9582
9583     x = bx - 1 + xx;
9584     y = by - 1 + yy;
9585
9586     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9587       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9588   }
9589   else
9590   {
9591     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9592     {
9593       int xx = x - bx + 1;
9594       int yy = y - by + 1;
9595
9596       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9597         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9598     }
9599   }
9600
9601   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9602 }
9603
9604 void CheckExit(int x, int y)
9605 {
9606   if (local_player->gems_still_needed > 0 ||
9607       local_player->sokobanfields_still_needed > 0 ||
9608       local_player->lights_still_needed > 0)
9609   {
9610     int element = Feld[x][y];
9611     int graphic = el2img(element);
9612
9613     if (IS_ANIMATED(graphic))
9614       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9615
9616     return;
9617   }
9618
9619   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9620     return;
9621
9622   Feld[x][y] = EL_EXIT_OPENING;
9623
9624   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9625 }
9626
9627 void CheckExitEM(int x, int y)
9628 {
9629   if (local_player->gems_still_needed > 0 ||
9630       local_player->sokobanfields_still_needed > 0 ||
9631       local_player->lights_still_needed > 0)
9632   {
9633     int element = Feld[x][y];
9634     int graphic = el2img(element);
9635
9636     if (IS_ANIMATED(graphic))
9637       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9638
9639     return;
9640   }
9641
9642   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9643     return;
9644
9645   Feld[x][y] = EL_EM_EXIT_OPENING;
9646
9647   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9648 }
9649
9650 void CheckExitSteel(int x, int y)
9651 {
9652   if (local_player->gems_still_needed > 0 ||
9653       local_player->sokobanfields_still_needed > 0 ||
9654       local_player->lights_still_needed > 0)
9655   {
9656     int element = Feld[x][y];
9657     int graphic = el2img(element);
9658
9659     if (IS_ANIMATED(graphic))
9660       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9661
9662     return;
9663   }
9664
9665   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9666     return;
9667
9668   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9669
9670   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9671 }
9672
9673 void CheckExitSteelEM(int x, int y)
9674 {
9675   if (local_player->gems_still_needed > 0 ||
9676       local_player->sokobanfields_still_needed > 0 ||
9677       local_player->lights_still_needed > 0)
9678   {
9679     int element = Feld[x][y];
9680     int graphic = el2img(element);
9681
9682     if (IS_ANIMATED(graphic))
9683       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9684
9685     return;
9686   }
9687
9688   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9689     return;
9690
9691   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9692
9693   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9694 }
9695
9696 void CheckExitSP(int x, int y)
9697 {
9698   if (local_player->gems_still_needed > 0)
9699   {
9700     int element = Feld[x][y];
9701     int graphic = el2img(element);
9702
9703     if (IS_ANIMATED(graphic))
9704       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9705
9706     return;
9707   }
9708
9709   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9710     return;
9711
9712   Feld[x][y] = EL_SP_EXIT_OPENING;
9713
9714   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9715 }
9716
9717 static void CloseAllOpenTimegates()
9718 {
9719   int x, y;
9720
9721   SCAN_PLAYFIELD(x, y)
9722   {
9723     int element = Feld[x][y];
9724
9725     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9726     {
9727       Feld[x][y] = EL_TIMEGATE_CLOSING;
9728
9729       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9730     }
9731   }
9732 }
9733
9734 void DrawTwinkleOnField(int x, int y)
9735 {
9736   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9737     return;
9738
9739   if (Feld[x][y] == EL_BD_DIAMOND)
9740     return;
9741
9742   if (MovDelay[x][y] == 0)      /* next animation frame */
9743     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9744
9745   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9746   {
9747     MovDelay[x][y]--;
9748
9749     DrawLevelElementAnimation(x, y, Feld[x][y]);
9750
9751     if (MovDelay[x][y] != 0)
9752     {
9753       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9754                                            10 - MovDelay[x][y]);
9755
9756       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9757     }
9758   }
9759 }
9760
9761 void MauerWaechst(int x, int y)
9762 {
9763   int delay = 6;
9764
9765   if (!MovDelay[x][y])          /* next animation frame */
9766     MovDelay[x][y] = 3 * delay;
9767
9768   if (MovDelay[x][y])           /* wait some time before next frame */
9769   {
9770     MovDelay[x][y]--;
9771
9772     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9773     {
9774       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9775       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9776
9777       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9778     }
9779
9780     if (!MovDelay[x][y])
9781     {
9782       if (MovDir[x][y] == MV_LEFT)
9783       {
9784         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9785           TEST_DrawLevelField(x - 1, y);
9786       }
9787       else if (MovDir[x][y] == MV_RIGHT)
9788       {
9789         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9790           TEST_DrawLevelField(x + 1, y);
9791       }
9792       else if (MovDir[x][y] == MV_UP)
9793       {
9794         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9795           TEST_DrawLevelField(x, y - 1);
9796       }
9797       else
9798       {
9799         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9800           TEST_DrawLevelField(x, y + 1);
9801       }
9802
9803       Feld[x][y] = Store[x][y];
9804       Store[x][y] = 0;
9805       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9806       TEST_DrawLevelField(x, y);
9807     }
9808   }
9809 }
9810
9811 void MauerAbleger(int ax, int ay)
9812 {
9813   int element = Feld[ax][ay];
9814   int graphic = el2img(element);
9815   boolean oben_frei = FALSE, unten_frei = FALSE;
9816   boolean links_frei = FALSE, rechts_frei = FALSE;
9817   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9818   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9819   boolean new_wall = FALSE;
9820
9821   if (IS_ANIMATED(graphic))
9822     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9823
9824   if (!MovDelay[ax][ay])        /* start building new wall */
9825     MovDelay[ax][ay] = 6;
9826
9827   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9828   {
9829     MovDelay[ax][ay]--;
9830     if (MovDelay[ax][ay])
9831       return;
9832   }
9833
9834   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9835     oben_frei = TRUE;
9836   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9837     unten_frei = TRUE;
9838   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9839     links_frei = TRUE;
9840   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9841     rechts_frei = TRUE;
9842
9843   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9844       element == EL_EXPANDABLE_WALL_ANY)
9845   {
9846     if (oben_frei)
9847     {
9848       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9849       Store[ax][ay-1] = element;
9850       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9851       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9852         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9853                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9854       new_wall = TRUE;
9855     }
9856     if (unten_frei)
9857     {
9858       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9859       Store[ax][ay+1] = element;
9860       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9861       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9862         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9863                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9864       new_wall = TRUE;
9865     }
9866   }
9867
9868   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9869       element == EL_EXPANDABLE_WALL_ANY ||
9870       element == EL_EXPANDABLE_WALL ||
9871       element == EL_BD_EXPANDABLE_WALL)
9872   {
9873     if (links_frei)
9874     {
9875       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9876       Store[ax-1][ay] = element;
9877       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9878       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9879         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9880                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9881       new_wall = TRUE;
9882     }
9883
9884     if (rechts_frei)
9885     {
9886       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9887       Store[ax+1][ay] = element;
9888       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9889       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9890         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9891                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9892       new_wall = TRUE;
9893     }
9894   }
9895
9896   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9897     TEST_DrawLevelField(ax, ay);
9898
9899   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9900     oben_massiv = TRUE;
9901   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9902     unten_massiv = TRUE;
9903   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9904     links_massiv = TRUE;
9905   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9906     rechts_massiv = TRUE;
9907
9908   if (((oben_massiv && unten_massiv) ||
9909        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9910        element == EL_EXPANDABLE_WALL) &&
9911       ((links_massiv && rechts_massiv) ||
9912        element == EL_EXPANDABLE_WALL_VERTICAL))
9913     Feld[ax][ay] = EL_WALL;
9914
9915   if (new_wall)
9916     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9917 }
9918
9919 void MauerAblegerStahl(int ax, int ay)
9920 {
9921   int element = Feld[ax][ay];
9922   int graphic = el2img(element);
9923   boolean oben_frei = FALSE, unten_frei = FALSE;
9924   boolean links_frei = FALSE, rechts_frei = FALSE;
9925   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9926   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9927   boolean new_wall = FALSE;
9928
9929   if (IS_ANIMATED(graphic))
9930     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9931
9932   if (!MovDelay[ax][ay])        /* start building new wall */
9933     MovDelay[ax][ay] = 6;
9934
9935   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9936   {
9937     MovDelay[ax][ay]--;
9938     if (MovDelay[ax][ay])
9939       return;
9940   }
9941
9942   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9943     oben_frei = TRUE;
9944   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9945     unten_frei = TRUE;
9946   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9947     links_frei = TRUE;
9948   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9949     rechts_frei = TRUE;
9950
9951   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9952       element == EL_EXPANDABLE_STEELWALL_ANY)
9953   {
9954     if (oben_frei)
9955     {
9956       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9957       Store[ax][ay-1] = element;
9958       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9959       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9960         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9961                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9962       new_wall = TRUE;
9963     }
9964     if (unten_frei)
9965     {
9966       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9967       Store[ax][ay+1] = element;
9968       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9969       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9970         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9971                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9972       new_wall = TRUE;
9973     }
9974   }
9975
9976   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9977       element == EL_EXPANDABLE_STEELWALL_ANY)
9978   {
9979     if (links_frei)
9980     {
9981       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9982       Store[ax-1][ay] = element;
9983       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9984       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9985         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9986                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9987       new_wall = TRUE;
9988     }
9989
9990     if (rechts_frei)
9991     {
9992       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9993       Store[ax+1][ay] = element;
9994       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9995       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9996         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9997                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9998       new_wall = TRUE;
9999     }
10000   }
10001
10002   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10003     oben_massiv = TRUE;
10004   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10005     unten_massiv = TRUE;
10006   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10007     links_massiv = TRUE;
10008   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10009     rechts_massiv = TRUE;
10010
10011   if (((oben_massiv && unten_massiv) ||
10012        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10013       ((links_massiv && rechts_massiv) ||
10014        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10015     Feld[ax][ay] = EL_STEELWALL;
10016
10017   if (new_wall)
10018     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10019 }
10020
10021 void CheckForDragon(int x, int y)
10022 {
10023   int i, j;
10024   boolean dragon_found = FALSE;
10025   static int xy[4][2] =
10026   {
10027     { 0, -1 },
10028     { -1, 0 },
10029     { +1, 0 },
10030     { 0, +1 }
10031   };
10032
10033   for (i = 0; i < NUM_DIRECTIONS; i++)
10034   {
10035     for (j = 0; j < 4; j++)
10036     {
10037       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10038
10039       if (IN_LEV_FIELD(xx, yy) &&
10040           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10041       {
10042         if (Feld[xx][yy] == EL_DRAGON)
10043           dragon_found = TRUE;
10044       }
10045       else
10046         break;
10047     }
10048   }
10049
10050   if (!dragon_found)
10051   {
10052     for (i = 0; i < NUM_DIRECTIONS; i++)
10053     {
10054       for (j = 0; j < 3; j++)
10055       {
10056         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10057   
10058         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10059         {
10060           Feld[xx][yy] = EL_EMPTY;
10061           TEST_DrawLevelField(xx, yy);
10062         }
10063         else
10064           break;
10065       }
10066     }
10067   }
10068 }
10069
10070 static void InitBuggyBase(int x, int y)
10071 {
10072   int element = Feld[x][y];
10073   int activating_delay = FRAMES_PER_SECOND / 4;
10074
10075   ChangeDelay[x][y] =
10076     (element == EL_SP_BUGGY_BASE ?
10077      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10078      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10079      activating_delay :
10080      element == EL_SP_BUGGY_BASE_ACTIVE ?
10081      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10082 }
10083
10084 static void WarnBuggyBase(int x, int y)
10085 {
10086   int i;
10087   static int xy[4][2] =
10088   {
10089     { 0, -1 },
10090     { -1, 0 },
10091     { +1, 0 },
10092     { 0, +1 }
10093   };
10094
10095   for (i = 0; i < NUM_DIRECTIONS; i++)
10096   {
10097     int xx = x + xy[i][0];
10098     int yy = y + xy[i][1];
10099
10100     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10101     {
10102       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10103
10104       break;
10105     }
10106   }
10107 }
10108
10109 static void InitTrap(int x, int y)
10110 {
10111   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10112 }
10113
10114 static void ActivateTrap(int x, int y)
10115 {
10116   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10117 }
10118
10119 static void ChangeActiveTrap(int x, int y)
10120 {
10121   int graphic = IMG_TRAP_ACTIVE;
10122
10123   /* if new animation frame was drawn, correct crumbled sand border */
10124   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10125     TEST_DrawLevelFieldCrumbledSand(x, y);
10126 }
10127
10128 static int getSpecialActionElement(int element, int number, int base_element)
10129 {
10130   return (element != EL_EMPTY ? element :
10131           number != -1 ? base_element + number - 1 :
10132           EL_EMPTY);
10133 }
10134
10135 static int getModifiedActionNumber(int value_old, int operator, int operand,
10136                                    int value_min, int value_max)
10137 {
10138   int value_new = (operator == CA_MODE_SET      ? operand :
10139                    operator == CA_MODE_ADD      ? value_old + operand :
10140                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10141                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10142                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10143                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10144                    value_old);
10145
10146   return (value_new < value_min ? value_min :
10147           value_new > value_max ? value_max :
10148           value_new);
10149 }
10150
10151 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10152 {
10153   struct ElementInfo *ei = &element_info[element];
10154   struct ElementChangeInfo *change = &ei->change_page[page];
10155   int target_element = change->target_element;
10156   int action_type = change->action_type;
10157   int action_mode = change->action_mode;
10158   int action_arg = change->action_arg;
10159   int action_element = change->action_element;
10160   int i;
10161
10162   if (!change->has_action)
10163     return;
10164
10165   /* ---------- determine action paramater values -------------------------- */
10166
10167   int level_time_value =
10168     (level.time > 0 ? TimeLeft :
10169      TimePlayed);
10170
10171   int action_arg_element =
10172     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10173      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10174      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10175      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10176      EL_EMPTY);
10177
10178   int action_arg_direction =
10179     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10180      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10181      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10182      change->actual_trigger_side :
10183      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10184      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10185      MV_NONE);
10186
10187   int action_arg_number_min =
10188     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10189      CA_ARG_MIN);
10190
10191   int action_arg_number_max =
10192     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10193      action_type == CA_SET_LEVEL_GEMS ? 999 :
10194      action_type == CA_SET_LEVEL_TIME ? 9999 :
10195      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10196      action_type == CA_SET_CE_VALUE ? 9999 :
10197      action_type == CA_SET_CE_SCORE ? 9999 :
10198      CA_ARG_MAX);
10199
10200   int action_arg_number_reset =
10201     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10202      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10203      action_type == CA_SET_LEVEL_TIME ? level.time :
10204      action_type == CA_SET_LEVEL_SCORE ? 0 :
10205 #if USE_NEW_CUSTOM_VALUE
10206      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10207 #else
10208      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10209 #endif
10210      action_type == CA_SET_CE_SCORE ? 0 :
10211      0);
10212
10213   int action_arg_number =
10214     (action_arg <= CA_ARG_MAX ? action_arg :
10215      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10216      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10217      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10218      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10219      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10220      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10221 #if USE_NEW_CUSTOM_VALUE
10222      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10223 #else
10224      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10225 #endif
10226      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10227      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10228      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10229      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10230      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10231      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10232      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10233      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10234      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10235      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10236      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10237      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10238      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10239      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10240      -1);
10241
10242   int action_arg_number_old =
10243     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10244      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10245      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10246      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10247      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10248      0);
10249
10250   int action_arg_number_new =
10251     getModifiedActionNumber(action_arg_number_old,
10252                             action_mode, action_arg_number,
10253                             action_arg_number_min, action_arg_number_max);
10254
10255 #if 1
10256   int trigger_player_bits = change->actual_trigger_player_bits;
10257 #else
10258   int trigger_player_bits =
10259     (change->actual_trigger_player >= EL_PLAYER_1 &&
10260      change->actual_trigger_player <= EL_PLAYER_4 ?
10261      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10262      PLAYER_BITS_ANY);
10263 #endif
10264
10265   int action_arg_player_bits =
10266     (action_arg >= CA_ARG_PLAYER_1 &&
10267      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10268      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10269      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10270      PLAYER_BITS_ANY);
10271
10272   /* ---------- execute action  -------------------------------------------- */
10273
10274   switch (action_type)
10275   {
10276     case CA_NO_ACTION:
10277     {
10278       return;
10279     }
10280
10281     /* ---------- level actions  ------------------------------------------- */
10282
10283     case CA_RESTART_LEVEL:
10284     {
10285       game.restart_level = TRUE;
10286
10287       break;
10288     }
10289
10290     case CA_SHOW_ENVELOPE:
10291     {
10292       int element = getSpecialActionElement(action_arg_element,
10293                                             action_arg_number, EL_ENVELOPE_1);
10294
10295       if (IS_ENVELOPE(element))
10296         local_player->show_envelope = element;
10297
10298       break;
10299     }
10300
10301     case CA_SET_LEVEL_TIME:
10302     {
10303       if (level.time > 0)       /* only modify limited time value */
10304       {
10305         TimeLeft = action_arg_number_new;
10306
10307 #if 1
10308         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10309
10310         DisplayGameControlValues();
10311 #else
10312         DrawGameValue_Time(TimeLeft);
10313 #endif
10314
10315         if (!TimeLeft && setup.time_limit)
10316           for (i = 0; i < MAX_PLAYERS; i++)
10317             KillPlayer(&stored_player[i]);
10318       }
10319
10320       break;
10321     }
10322
10323     case CA_SET_LEVEL_SCORE:
10324     {
10325       local_player->score = action_arg_number_new;
10326
10327 #if 1
10328       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10329
10330       DisplayGameControlValues();
10331 #else
10332       DrawGameValue_Score(local_player->score);
10333 #endif
10334
10335       break;
10336     }
10337
10338     case CA_SET_LEVEL_GEMS:
10339     {
10340       local_player->gems_still_needed = action_arg_number_new;
10341
10342 #if 1
10343       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10344
10345       DisplayGameControlValues();
10346 #else
10347       DrawGameValue_Emeralds(local_player->gems_still_needed);
10348 #endif
10349
10350       break;
10351     }
10352
10353 #if !USE_PLAYER_GRAVITY
10354     case CA_SET_LEVEL_GRAVITY:
10355     {
10356       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10357                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10358                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10359                       game.gravity);
10360       break;
10361     }
10362 #endif
10363
10364     case CA_SET_LEVEL_WIND:
10365     {
10366       game.wind_direction = action_arg_direction;
10367
10368       break;
10369     }
10370
10371     /* ---------- player actions  ------------------------------------------ */
10372
10373     case CA_MOVE_PLAYER:
10374     {
10375       /* automatically move to the next field in specified direction */
10376       for (i = 0; i < MAX_PLAYERS; i++)
10377         if (trigger_player_bits & (1 << i))
10378           stored_player[i].programmed_action = action_arg_direction;
10379
10380       break;
10381     }
10382
10383     case CA_EXIT_PLAYER:
10384     {
10385       for (i = 0; i < MAX_PLAYERS; i++)
10386         if (action_arg_player_bits & (1 << i))
10387           PlayerWins(&stored_player[i]);
10388
10389       break;
10390     }
10391
10392     case CA_KILL_PLAYER:
10393     {
10394       for (i = 0; i < MAX_PLAYERS; i++)
10395         if (action_arg_player_bits & (1 << i))
10396           KillPlayer(&stored_player[i]);
10397
10398       break;
10399     }
10400
10401     case CA_SET_PLAYER_KEYS:
10402     {
10403       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10404       int element = getSpecialActionElement(action_arg_element,
10405                                             action_arg_number, EL_KEY_1);
10406
10407       if (IS_KEY(element))
10408       {
10409         for (i = 0; i < MAX_PLAYERS; i++)
10410         {
10411           if (trigger_player_bits & (1 << i))
10412           {
10413             stored_player[i].key[KEY_NR(element)] = key_state;
10414
10415             DrawGameDoorValues();
10416           }
10417         }
10418       }
10419
10420       break;
10421     }
10422
10423     case CA_SET_PLAYER_SPEED:
10424     {
10425       for (i = 0; i < MAX_PLAYERS; i++)
10426       {
10427         if (trigger_player_bits & (1 << i))
10428         {
10429           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10430
10431           if (action_arg == CA_ARG_SPEED_FASTER &&
10432               stored_player[i].cannot_move)
10433           {
10434             action_arg_number = STEPSIZE_VERY_SLOW;
10435           }
10436           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10437                    action_arg == CA_ARG_SPEED_FASTER)
10438           {
10439             action_arg_number = 2;
10440             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10441                            CA_MODE_MULTIPLY);
10442           }
10443           else if (action_arg == CA_ARG_NUMBER_RESET)
10444           {
10445             action_arg_number = level.initial_player_stepsize[i];
10446           }
10447
10448           move_stepsize =
10449             getModifiedActionNumber(move_stepsize,
10450                                     action_mode,
10451                                     action_arg_number,
10452                                     action_arg_number_min,
10453                                     action_arg_number_max);
10454
10455           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10456         }
10457       }
10458
10459       break;
10460     }
10461
10462     case CA_SET_PLAYER_SHIELD:
10463     {
10464       for (i = 0; i < MAX_PLAYERS; i++)
10465       {
10466         if (trigger_player_bits & (1 << i))
10467         {
10468           if (action_arg == CA_ARG_SHIELD_OFF)
10469           {
10470             stored_player[i].shield_normal_time_left = 0;
10471             stored_player[i].shield_deadly_time_left = 0;
10472           }
10473           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10474           {
10475             stored_player[i].shield_normal_time_left = 999999;
10476           }
10477           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10478           {
10479             stored_player[i].shield_normal_time_left = 999999;
10480             stored_player[i].shield_deadly_time_left = 999999;
10481           }
10482         }
10483       }
10484
10485       break;
10486     }
10487
10488 #if USE_PLAYER_GRAVITY
10489     case CA_SET_PLAYER_GRAVITY:
10490     {
10491       for (i = 0; i < MAX_PLAYERS; i++)
10492       {
10493         if (trigger_player_bits & (1 << i))
10494         {
10495           stored_player[i].gravity =
10496             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10497              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10498              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10499              stored_player[i].gravity);
10500         }
10501       }
10502
10503       break;
10504     }
10505 #endif
10506
10507     case CA_SET_PLAYER_ARTWORK:
10508     {
10509       for (i = 0; i < MAX_PLAYERS; i++)
10510       {
10511         if (trigger_player_bits & (1 << i))
10512         {
10513           int artwork_element = action_arg_element;
10514
10515           if (action_arg == CA_ARG_ELEMENT_RESET)
10516             artwork_element =
10517               (level.use_artwork_element[i] ? level.artwork_element[i] :
10518                stored_player[i].element_nr);
10519
10520 #if USE_GFX_RESET_PLAYER_ARTWORK
10521           if (stored_player[i].artwork_element != artwork_element)
10522             stored_player[i].Frame = 0;
10523 #endif
10524
10525           stored_player[i].artwork_element = artwork_element;
10526
10527           SetPlayerWaiting(&stored_player[i], FALSE);
10528
10529           /* set number of special actions for bored and sleeping animation */
10530           stored_player[i].num_special_action_bored =
10531             get_num_special_action(artwork_element,
10532                                    ACTION_BORING_1, ACTION_BORING_LAST);
10533           stored_player[i].num_special_action_sleeping =
10534             get_num_special_action(artwork_element,
10535                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10536         }
10537       }
10538
10539       break;
10540     }
10541
10542     /* ---------- CE actions  ---------------------------------------------- */
10543
10544     case CA_SET_CE_VALUE:
10545     {
10546 #if USE_NEW_CUSTOM_VALUE
10547       int last_ce_value = CustomValue[x][y];
10548
10549       CustomValue[x][y] = action_arg_number_new;
10550
10551       if (CustomValue[x][y] != last_ce_value)
10552       {
10553         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10554         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10555
10556         if (CustomValue[x][y] == 0)
10557         {
10558           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10559           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10560         }
10561       }
10562 #endif
10563
10564       break;
10565     }
10566
10567     case CA_SET_CE_SCORE:
10568     {
10569 #if USE_NEW_CUSTOM_VALUE
10570       int last_ce_score = ei->collect_score;
10571
10572       ei->collect_score = action_arg_number_new;
10573
10574       if (ei->collect_score != last_ce_score)
10575       {
10576         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10577         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10578
10579         if (ei->collect_score == 0)
10580         {
10581           int xx, yy;
10582
10583           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10584           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10585
10586           /*
10587             This is a very special case that seems to be a mixture between
10588             CheckElementChange() and CheckTriggeredElementChange(): while
10589             the first one only affects single elements that are triggered
10590             directly, the second one affects multiple elements in the playfield
10591             that are triggered indirectly by another element. This is a third
10592             case: Changing the CE score always affects multiple identical CEs,
10593             so every affected CE must be checked, not only the single CE for
10594             which the CE score was changed in the first place (as every instance
10595             of that CE shares the same CE score, and therefore also can change)!
10596           */
10597           SCAN_PLAYFIELD(xx, yy)
10598           {
10599             if (Feld[xx][yy] == element)
10600               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10601                                  CE_SCORE_GETS_ZERO);
10602           }
10603         }
10604       }
10605 #endif
10606
10607       break;
10608     }
10609
10610     /* ---------- engine actions  ------------------------------------------ */
10611
10612     case CA_SET_ENGINE_SCAN_MODE:
10613     {
10614       InitPlayfieldScanMode(action_arg);
10615
10616       break;
10617     }
10618
10619     default:
10620       break;
10621   }
10622 }
10623
10624 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10625 {
10626   int old_element = Feld[x][y];
10627   int new_element = GetElementFromGroupElement(element);
10628   int previous_move_direction = MovDir[x][y];
10629 #if USE_NEW_CUSTOM_VALUE
10630   int last_ce_value = CustomValue[x][y];
10631 #endif
10632   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10633   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10634   boolean add_player_onto_element = (new_element_is_player &&
10635 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10636                                      /* this breaks SnakeBite when a snake is
10637                                         halfway through a door that closes */
10638                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10639                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10640 #endif
10641                                      IS_WALKABLE(old_element));
10642
10643 #if 0
10644   /* check if element under the player changes from accessible to unaccessible
10645      (needed for special case of dropping element which then changes) */
10646   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10647       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10648   {
10649     Bang(x, y);
10650
10651     return;
10652   }
10653 #endif
10654
10655   if (!add_player_onto_element)
10656   {
10657     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10658       RemoveMovingField(x, y);
10659     else
10660       RemoveField(x, y);
10661
10662     Feld[x][y] = new_element;
10663
10664 #if !USE_GFX_RESET_GFX_ANIMATION
10665     ResetGfxAnimation(x, y);
10666     ResetRandomAnimationValue(x, y);
10667 #endif
10668
10669     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10670       MovDir[x][y] = previous_move_direction;
10671
10672 #if USE_NEW_CUSTOM_VALUE
10673     if (element_info[new_element].use_last_ce_value)
10674       CustomValue[x][y] = last_ce_value;
10675 #endif
10676
10677     InitField_WithBug1(x, y, FALSE);
10678
10679     new_element = Feld[x][y];   /* element may have changed */
10680
10681 #if USE_GFX_RESET_GFX_ANIMATION
10682     ResetGfxAnimation(x, y);
10683     ResetRandomAnimationValue(x, y);
10684 #endif
10685
10686     TEST_DrawLevelField(x, y);
10687
10688     if (GFX_CRUMBLED(new_element))
10689       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10690   }
10691
10692 #if 1
10693   /* check if element under the player changes from accessible to unaccessible
10694      (needed for special case of dropping element which then changes) */
10695   /* (must be checked after creating new element for walkable group elements) */
10696 #if USE_FIX_KILLED_BY_NON_WALKABLE
10697   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10698       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10699   {
10700     Bang(x, y);
10701
10702     return;
10703   }
10704 #else
10705   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10706       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10707   {
10708     Bang(x, y);
10709
10710     return;
10711   }
10712 #endif
10713 #endif
10714
10715   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10716   if (new_element_is_player)
10717     RelocatePlayer(x, y, new_element);
10718
10719   if (is_change)
10720     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10721
10722   TestIfBadThingTouchesPlayer(x, y);
10723   TestIfPlayerTouchesCustomElement(x, y);
10724   TestIfElementTouchesCustomElement(x, y);
10725 }
10726
10727 static void CreateField(int x, int y, int element)
10728 {
10729   CreateFieldExt(x, y, element, FALSE);
10730 }
10731
10732 static void CreateElementFromChange(int x, int y, int element)
10733 {
10734   element = GET_VALID_RUNTIME_ELEMENT(element);
10735
10736 #if USE_STOP_CHANGED_ELEMENTS
10737   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10738   {
10739     int old_element = Feld[x][y];
10740
10741     /* prevent changed element from moving in same engine frame
10742        unless both old and new element can either fall or move */
10743     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10744         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10745       Stop[x][y] = TRUE;
10746   }
10747 #endif
10748
10749   CreateFieldExt(x, y, element, TRUE);
10750 }
10751
10752 static boolean ChangeElement(int x, int y, int element, int page)
10753 {
10754   struct ElementInfo *ei = &element_info[element];
10755   struct ElementChangeInfo *change = &ei->change_page[page];
10756   int ce_value = CustomValue[x][y];
10757   int ce_score = ei->collect_score;
10758   int target_element;
10759   int old_element = Feld[x][y];
10760
10761   /* always use default change event to prevent running into a loop */
10762   if (ChangeEvent[x][y] == -1)
10763     ChangeEvent[x][y] = CE_DELAY;
10764
10765   if (ChangeEvent[x][y] == CE_DELAY)
10766   {
10767     /* reset actual trigger element, trigger player and action element */
10768     change->actual_trigger_element = EL_EMPTY;
10769     change->actual_trigger_player = EL_PLAYER_1;
10770     change->actual_trigger_player_bits = CH_PLAYER_1;
10771     change->actual_trigger_side = CH_SIDE_NONE;
10772     change->actual_trigger_ce_value = 0;
10773     change->actual_trigger_ce_score = 0;
10774   }
10775
10776   /* do not change elements more than a specified maximum number of changes */
10777   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10778     return FALSE;
10779
10780   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10781
10782   if (change->explode)
10783   {
10784     Bang(x, y);
10785
10786     return TRUE;
10787   }
10788
10789   if (change->use_target_content)
10790   {
10791     boolean complete_replace = TRUE;
10792     boolean can_replace[3][3];
10793     int xx, yy;
10794
10795     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10796     {
10797       boolean is_empty;
10798       boolean is_walkable;
10799       boolean is_diggable;
10800       boolean is_collectible;
10801       boolean is_removable;
10802       boolean is_destructible;
10803       int ex = x + xx - 1;
10804       int ey = y + yy - 1;
10805       int content_element = change->target_content.e[xx][yy];
10806       int e;
10807
10808       can_replace[xx][yy] = TRUE;
10809
10810       if (ex == x && ey == y)   /* do not check changing element itself */
10811         continue;
10812
10813       if (content_element == EL_EMPTY_SPACE)
10814       {
10815         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10816
10817         continue;
10818       }
10819
10820       if (!IN_LEV_FIELD(ex, ey))
10821       {
10822         can_replace[xx][yy] = FALSE;
10823         complete_replace = FALSE;
10824
10825         continue;
10826       }
10827
10828       e = Feld[ex][ey];
10829
10830       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10831         e = MovingOrBlocked2Element(ex, ey);
10832
10833       is_empty = (IS_FREE(ex, ey) ||
10834                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10835
10836       is_walkable     = (is_empty || IS_WALKABLE(e));
10837       is_diggable     = (is_empty || IS_DIGGABLE(e));
10838       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10839       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10840       is_removable    = (is_diggable || is_collectible);
10841
10842       can_replace[xx][yy] =
10843         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10844           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10845           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10846           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10847           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10848           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10849          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10850
10851       if (!can_replace[xx][yy])
10852         complete_replace = FALSE;
10853     }
10854
10855     if (!change->only_if_complete || complete_replace)
10856     {
10857       boolean something_has_changed = FALSE;
10858
10859       if (change->only_if_complete && change->use_random_replace &&
10860           RND(100) < change->random_percentage)
10861         return FALSE;
10862
10863       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10864       {
10865         int ex = x + xx - 1;
10866         int ey = y + yy - 1;
10867         int content_element;
10868
10869         if (can_replace[xx][yy] && (!change->use_random_replace ||
10870                                     RND(100) < change->random_percentage))
10871         {
10872           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10873             RemoveMovingField(ex, ey);
10874
10875           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10876
10877           content_element = change->target_content.e[xx][yy];
10878           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10879                                               ce_value, ce_score);
10880
10881           CreateElementFromChange(ex, ey, target_element);
10882
10883           something_has_changed = TRUE;
10884
10885           /* for symmetry reasons, freeze newly created border elements */
10886           if (ex != x || ey != y)
10887             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10888         }
10889       }
10890
10891       if (something_has_changed)
10892       {
10893         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10894         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10895       }
10896     }
10897   }
10898   else
10899   {
10900     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10901                                         ce_value, ce_score);
10902
10903     if (element == EL_DIAGONAL_GROWING ||
10904         element == EL_DIAGONAL_SHRINKING)
10905     {
10906       target_element = Store[x][y];
10907
10908       Store[x][y] = EL_EMPTY;
10909     }
10910
10911     CreateElementFromChange(x, y, target_element);
10912
10913     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10914     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10915   }
10916
10917   /* this uses direct change before indirect change */
10918   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10919
10920   return TRUE;
10921 }
10922
10923 #if USE_NEW_DELAYED_ACTION
10924
10925 static void HandleElementChange(int x, int y, int page)
10926 {
10927   int element = MovingOrBlocked2Element(x, y);
10928   struct ElementInfo *ei = &element_info[element];
10929   struct ElementChangeInfo *change = &ei->change_page[page];
10930
10931 #ifdef DEBUG
10932   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10933       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10934   {
10935     printf("\n\n");
10936     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10937            x, y, element, element_info[element].token_name);
10938     printf("HandleElementChange(): This should never happen!\n");
10939     printf("\n\n");
10940   }
10941 #endif
10942
10943   /* this can happen with classic bombs on walkable, changing elements */
10944   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10945   {
10946 #if 0
10947     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10948       ChangeDelay[x][y] = 0;
10949 #endif
10950
10951     return;
10952   }
10953
10954   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10955   {
10956     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10957
10958     if (change->can_change)
10959     {
10960 #if 1
10961       /* !!! not clear why graphic animation should be reset at all here !!! */
10962       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10963 #if USE_GFX_RESET_WHEN_NOT_MOVING
10964       /* when a custom element is about to change (for example by change delay),
10965          do not reset graphic animation when the custom element is moving */
10966       if (!IS_MOVING(x, y))
10967 #endif
10968       {
10969         ResetGfxAnimation(x, y);
10970         ResetRandomAnimationValue(x, y);
10971       }
10972 #endif
10973
10974       if (change->pre_change_function)
10975         change->pre_change_function(x, y);
10976     }
10977   }
10978
10979   ChangeDelay[x][y]--;
10980
10981   if (ChangeDelay[x][y] != 0)           /* continue element change */
10982   {
10983     if (change->can_change)
10984     {
10985       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10986
10987       if (IS_ANIMATED(graphic))
10988         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10989
10990       if (change->change_function)
10991         change->change_function(x, y);
10992     }
10993   }
10994   else                                  /* finish element change */
10995   {
10996     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10997     {
10998       page = ChangePage[x][y];
10999       ChangePage[x][y] = -1;
11000
11001       change = &ei->change_page[page];
11002     }
11003
11004     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11005     {
11006       ChangeDelay[x][y] = 1;            /* try change after next move step */
11007       ChangePage[x][y] = page;          /* remember page to use for change */
11008
11009       return;
11010     }
11011
11012     if (change->can_change)
11013     {
11014       if (ChangeElement(x, y, element, page))
11015       {
11016         if (change->post_change_function)
11017           change->post_change_function(x, y);
11018       }
11019     }
11020
11021     if (change->has_action)
11022       ExecuteCustomElementAction(x, y, element, page);
11023   }
11024 }
11025
11026 #else
11027
11028 static void HandleElementChange(int x, int y, int page)
11029 {
11030   int element = MovingOrBlocked2Element(x, y);
11031   struct ElementInfo *ei = &element_info[element];
11032   struct ElementChangeInfo *change = &ei->change_page[page];
11033
11034 #ifdef DEBUG
11035   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11036   {
11037     printf("\n\n");
11038     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11039            x, y, element, element_info[element].token_name);
11040     printf("HandleElementChange(): This should never happen!\n");
11041     printf("\n\n");
11042   }
11043 #endif
11044
11045   /* this can happen with classic bombs on walkable, changing elements */
11046   if (!CAN_CHANGE(element))
11047   {
11048 #if 0
11049     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11050       ChangeDelay[x][y] = 0;
11051 #endif
11052
11053     return;
11054   }
11055
11056   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11057   {
11058     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11059
11060     ResetGfxAnimation(x, y);
11061     ResetRandomAnimationValue(x, y);
11062
11063     if (change->pre_change_function)
11064       change->pre_change_function(x, y);
11065   }
11066
11067   ChangeDelay[x][y]--;
11068
11069   if (ChangeDelay[x][y] != 0)           /* continue element change */
11070   {
11071     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11072
11073     if (IS_ANIMATED(graphic))
11074       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11075
11076     if (change->change_function)
11077       change->change_function(x, y);
11078   }
11079   else                                  /* finish element change */
11080   {
11081     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11082     {
11083       page = ChangePage[x][y];
11084       ChangePage[x][y] = -1;
11085
11086       change = &ei->change_page[page];
11087     }
11088
11089     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11090     {
11091       ChangeDelay[x][y] = 1;            /* try change after next move step */
11092       ChangePage[x][y] = page;          /* remember page to use for change */
11093
11094       return;
11095     }
11096
11097     if (ChangeElement(x, y, element, page))
11098     {
11099       if (change->post_change_function)
11100         change->post_change_function(x, y);
11101     }
11102   }
11103 }
11104
11105 #endif
11106
11107 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11108                                               int trigger_element,
11109                                               int trigger_event,
11110                                               int trigger_player,
11111                                               int trigger_side,
11112                                               int trigger_page)
11113 {
11114   boolean change_done_any = FALSE;
11115   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11116   int i;
11117
11118   if (!(trigger_events[trigger_element][trigger_event]))
11119     return FALSE;
11120
11121 #if 0
11122   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11123          trigger_event, recursion_loop_depth, recursion_loop_detected,
11124          recursion_loop_element, EL_NAME(recursion_loop_element));
11125 #endif
11126
11127   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11128
11129   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11130   {
11131     int element = EL_CUSTOM_START + i;
11132     boolean change_done = FALSE;
11133     int p;
11134
11135     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11136         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11137       continue;
11138
11139     for (p = 0; p < element_info[element].num_change_pages; p++)
11140     {
11141       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11142
11143       if (change->can_change_or_has_action &&
11144           change->has_event[trigger_event] &&
11145           change->trigger_side & trigger_side &&
11146           change->trigger_player & trigger_player &&
11147           change->trigger_page & trigger_page_bits &&
11148           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11149       {
11150         change->actual_trigger_element = trigger_element;
11151         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11152         change->actual_trigger_player_bits = trigger_player;
11153         change->actual_trigger_side = trigger_side;
11154         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11155         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11156
11157 #if 0
11158         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11159                element, EL_NAME(element), p);
11160 #endif
11161
11162         if ((change->can_change && !change_done) || change->has_action)
11163         {
11164           int x, y;
11165
11166           SCAN_PLAYFIELD(x, y)
11167           {
11168             if (Feld[x][y] == element)
11169             {
11170               if (change->can_change && !change_done)
11171               {
11172
11173 #if 0
11174                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11175                        element, EL_NAME(element), p);
11176 #endif
11177
11178                 ChangeDelay[x][y] = 1;
11179                 ChangeEvent[x][y] = trigger_event;
11180
11181                 HandleElementChange(x, y, p);
11182               }
11183 #if USE_NEW_DELAYED_ACTION
11184               else if (change->has_action)
11185               {
11186 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11187                 /* if element already changed in this frame, not only prevent
11188                    another element change (checked in ChangeElement()), but
11189                    also prevent additional element actions for this element */
11190
11191                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11192                     !level.use_action_after_change_bug)
11193                   continue;
11194 #endif
11195
11196
11197 #if 0
11198                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11199                        element, EL_NAME(element), p);
11200 #endif
11201
11202                 ExecuteCustomElementAction(x, y, element, p);
11203                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11204               }
11205 #else
11206               if (change->has_action)
11207               {
11208                 ExecuteCustomElementAction(x, y, element, p);
11209                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11210               }
11211 #endif
11212             }
11213           }
11214
11215           if (change->can_change)
11216           {
11217             change_done = TRUE;
11218             change_done_any = TRUE;
11219
11220 #if 0
11221             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11222                    element, EL_NAME(element), p);
11223 #endif
11224
11225           }
11226         }
11227       }
11228     }
11229   }
11230
11231   RECURSION_LOOP_DETECTION_END();
11232
11233   return change_done_any;
11234 }
11235
11236 static boolean CheckElementChangeExt(int x, int y,
11237                                      int element,
11238                                      int trigger_element,
11239                                      int trigger_event,
11240                                      int trigger_player,
11241                                      int trigger_side)
11242 {
11243   boolean change_done = FALSE;
11244   int p;
11245
11246   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11247       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11248     return FALSE;
11249
11250   if (Feld[x][y] == EL_BLOCKED)
11251   {
11252     Blocked2Moving(x, y, &x, &y);
11253     element = Feld[x][y];
11254   }
11255
11256 #if 0
11257   /* check if element has already changed */
11258   if (Feld[x][y] != element)
11259     return FALSE;
11260 #else
11261   /* check if element has already changed or is about to change after moving */
11262   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11263        Feld[x][y] != element) ||
11264
11265       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11266        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11267         ChangePage[x][y] != -1)))
11268     return FALSE;
11269 #endif
11270
11271 #if 0
11272   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11273          trigger_event, recursion_loop_depth, recursion_loop_detected,
11274          recursion_loop_element, EL_NAME(recursion_loop_element));
11275 #endif
11276
11277   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11278
11279   for (p = 0; p < element_info[element].num_change_pages; p++)
11280   {
11281     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11282
11283     /* check trigger element for all events where the element that is checked
11284        for changing interacts with a directly adjacent element -- this is
11285        different to element changes that affect other elements to change on the
11286        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11287     boolean check_trigger_element =
11288       (trigger_event == CE_TOUCHING_X ||
11289        trigger_event == CE_HITTING_X ||
11290        trigger_event == CE_HIT_BY_X ||
11291 #if 1
11292        /* this one was forgotten until 3.2.3 */
11293        trigger_event == CE_DIGGING_X);
11294 #endif
11295
11296     if (change->can_change_or_has_action &&
11297         change->has_event[trigger_event] &&
11298         change->trigger_side & trigger_side &&
11299         change->trigger_player & trigger_player &&
11300         (!check_trigger_element ||
11301          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11302     {
11303       change->actual_trigger_element = trigger_element;
11304       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11305       change->actual_trigger_player_bits = trigger_player;
11306       change->actual_trigger_side = trigger_side;
11307       change->actual_trigger_ce_value = CustomValue[x][y];
11308       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11309
11310       /* special case: trigger element not at (x,y) position for some events */
11311       if (check_trigger_element)
11312       {
11313         static struct
11314         {
11315           int dx, dy;
11316         } move_xy[] =
11317           {
11318             {  0,  0 },
11319             { -1,  0 },
11320             { +1,  0 },
11321             {  0,  0 },
11322             {  0, -1 },
11323             {  0,  0 }, { 0, 0 }, { 0, 0 },
11324             {  0, +1 }
11325           };
11326
11327         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11328         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11329
11330         change->actual_trigger_ce_value = CustomValue[xx][yy];
11331         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11332       }
11333
11334       if (change->can_change && !change_done)
11335       {
11336         ChangeDelay[x][y] = 1;
11337         ChangeEvent[x][y] = trigger_event;
11338
11339         HandleElementChange(x, y, p);
11340
11341         change_done = TRUE;
11342       }
11343 #if USE_NEW_DELAYED_ACTION
11344       else if (change->has_action)
11345       {
11346         ExecuteCustomElementAction(x, y, element, p);
11347         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11348       }
11349 #else
11350       if (change->has_action)
11351       {
11352         ExecuteCustomElementAction(x, y, element, p);
11353         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11354       }
11355 #endif
11356     }
11357   }
11358
11359   RECURSION_LOOP_DETECTION_END();
11360
11361   return change_done;
11362 }
11363
11364 static void PlayPlayerSound(struct PlayerInfo *player)
11365 {
11366   int jx = player->jx, jy = player->jy;
11367   int sound_element = player->artwork_element;
11368   int last_action = player->last_action_waiting;
11369   int action = player->action_waiting;
11370
11371   if (player->is_waiting)
11372   {
11373     if (action != last_action)
11374       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11375     else
11376       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11377   }
11378   else
11379   {
11380     if (action != last_action)
11381       StopSound(element_info[sound_element].sound[last_action]);
11382
11383     if (last_action == ACTION_SLEEPING)
11384       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11385   }
11386 }
11387
11388 static void PlayAllPlayersSound()
11389 {
11390   int i;
11391
11392   for (i = 0; i < MAX_PLAYERS; i++)
11393     if (stored_player[i].active)
11394       PlayPlayerSound(&stored_player[i]);
11395 }
11396
11397 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11398 {
11399   boolean last_waiting = player->is_waiting;
11400   int move_dir = player->MovDir;
11401
11402   player->dir_waiting = move_dir;
11403   player->last_action_waiting = player->action_waiting;
11404
11405   if (is_waiting)
11406   {
11407     if (!last_waiting)          /* not waiting -> waiting */
11408     {
11409       player->is_waiting = TRUE;
11410
11411       player->frame_counter_bored =
11412         FrameCounter +
11413         game.player_boring_delay_fixed +
11414         GetSimpleRandom(game.player_boring_delay_random);
11415       player->frame_counter_sleeping =
11416         FrameCounter +
11417         game.player_sleeping_delay_fixed +
11418         GetSimpleRandom(game.player_sleeping_delay_random);
11419
11420       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11421     }
11422
11423     if (game.player_sleeping_delay_fixed +
11424         game.player_sleeping_delay_random > 0 &&
11425         player->anim_delay_counter == 0 &&
11426         player->post_delay_counter == 0 &&
11427         FrameCounter >= player->frame_counter_sleeping)
11428       player->is_sleeping = TRUE;
11429     else if (game.player_boring_delay_fixed +
11430              game.player_boring_delay_random > 0 &&
11431              FrameCounter >= player->frame_counter_bored)
11432       player->is_bored = TRUE;
11433
11434     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11435                               player->is_bored ? ACTION_BORING :
11436                               ACTION_WAITING);
11437
11438     if (player->is_sleeping && player->use_murphy)
11439     {
11440       /* special case for sleeping Murphy when leaning against non-free tile */
11441
11442       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11443           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11444            !IS_MOVING(player->jx - 1, player->jy)))
11445         move_dir = MV_LEFT;
11446       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11447                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11448                 !IS_MOVING(player->jx + 1, player->jy)))
11449         move_dir = MV_RIGHT;
11450       else
11451         player->is_sleeping = FALSE;
11452
11453       player->dir_waiting = move_dir;
11454     }
11455
11456     if (player->is_sleeping)
11457     {
11458       if (player->num_special_action_sleeping > 0)
11459       {
11460         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11461         {
11462           int last_special_action = player->special_action_sleeping;
11463           int num_special_action = player->num_special_action_sleeping;
11464           int special_action =
11465             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11466              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11467              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11468              last_special_action + 1 : ACTION_SLEEPING);
11469           int special_graphic =
11470             el_act_dir2img(player->artwork_element, special_action, move_dir);
11471
11472           player->anim_delay_counter =
11473             graphic_info[special_graphic].anim_delay_fixed +
11474             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11475           player->post_delay_counter =
11476             graphic_info[special_graphic].post_delay_fixed +
11477             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11478
11479           player->special_action_sleeping = special_action;
11480         }
11481
11482         if (player->anim_delay_counter > 0)
11483         {
11484           player->action_waiting = player->special_action_sleeping;
11485           player->anim_delay_counter--;
11486         }
11487         else if (player->post_delay_counter > 0)
11488         {
11489           player->post_delay_counter--;
11490         }
11491       }
11492     }
11493     else if (player->is_bored)
11494     {
11495       if (player->num_special_action_bored > 0)
11496       {
11497         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11498         {
11499           int special_action =
11500             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11501           int special_graphic =
11502             el_act_dir2img(player->artwork_element, special_action, move_dir);
11503
11504           player->anim_delay_counter =
11505             graphic_info[special_graphic].anim_delay_fixed +
11506             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11507           player->post_delay_counter =
11508             graphic_info[special_graphic].post_delay_fixed +
11509             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11510
11511           player->special_action_bored = special_action;
11512         }
11513
11514         if (player->anim_delay_counter > 0)
11515         {
11516           player->action_waiting = player->special_action_bored;
11517           player->anim_delay_counter--;
11518         }
11519         else if (player->post_delay_counter > 0)
11520         {
11521           player->post_delay_counter--;
11522         }
11523       }
11524     }
11525   }
11526   else if (last_waiting)        /* waiting -> not waiting */
11527   {
11528     player->is_waiting = FALSE;
11529     player->is_bored = FALSE;
11530     player->is_sleeping = FALSE;
11531
11532     player->frame_counter_bored = -1;
11533     player->frame_counter_sleeping = -1;
11534
11535     player->anim_delay_counter = 0;
11536     player->post_delay_counter = 0;
11537
11538     player->dir_waiting = player->MovDir;
11539     player->action_waiting = ACTION_DEFAULT;
11540
11541     player->special_action_bored = ACTION_DEFAULT;
11542     player->special_action_sleeping = ACTION_DEFAULT;
11543   }
11544 }
11545
11546 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11547 {
11548   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11549   int left      = player_action & JOY_LEFT;
11550   int right     = player_action & JOY_RIGHT;
11551   int up        = player_action & JOY_UP;
11552   int down      = player_action & JOY_DOWN;
11553   int button1   = player_action & JOY_BUTTON_1;
11554   int button2   = player_action & JOY_BUTTON_2;
11555   int dx        = (left ? -1 : right ? 1 : 0);
11556   int dy        = (up   ? -1 : down  ? 1 : 0);
11557
11558   if (!player->active || tape.pausing)
11559     return 0;
11560
11561   if (player_action)
11562   {
11563     if (button1)
11564       snapped = SnapField(player, dx, dy);
11565     else
11566     {
11567       if (button2)
11568         dropped = DropElement(player);
11569
11570       moved = MovePlayer(player, dx, dy);
11571     }
11572
11573     if (tape.single_step && tape.recording && !tape.pausing)
11574     {
11575       if (button1 || (dropped && !moved))
11576       {
11577         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11578         SnapField(player, 0, 0);                /* stop snapping */
11579       }
11580     }
11581
11582     SetPlayerWaiting(player, FALSE);
11583
11584     return player_action;
11585   }
11586   else
11587   {
11588     /* no actions for this player (no input at player's configured device) */
11589
11590     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11591     SnapField(player, 0, 0);
11592     CheckGravityMovementWhenNotMoving(player);
11593
11594     if (player->MovPos == 0)
11595       SetPlayerWaiting(player, TRUE);
11596
11597     if (player->MovPos == 0)    /* needed for tape.playing */
11598       player->is_moving = FALSE;
11599
11600     player->is_dropping = FALSE;
11601     player->is_dropping_pressed = FALSE;
11602     player->drop_pressed_delay = 0;
11603
11604     return 0;
11605   }
11606 }
11607
11608 static void CheckLevelTime()
11609 {
11610   int i;
11611
11612   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11613   {
11614     if (level.native_em_level->lev->home == 0)  /* all players at home */
11615     {
11616       PlayerWins(local_player);
11617
11618       AllPlayersGone = TRUE;
11619
11620       level.native_em_level->lev->home = -1;
11621     }
11622
11623     if (level.native_em_level->ply[0]->alive == 0 &&
11624         level.native_em_level->ply[1]->alive == 0 &&
11625         level.native_em_level->ply[2]->alive == 0 &&
11626         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11627       AllPlayersGone = TRUE;
11628   }
11629
11630   if (TimeFrames >= FRAMES_PER_SECOND)
11631   {
11632     TimeFrames = 0;
11633     TapeTime++;
11634
11635     for (i = 0; i < MAX_PLAYERS; i++)
11636     {
11637       struct PlayerInfo *player = &stored_player[i];
11638
11639       if (SHIELD_ON(player))
11640       {
11641         player->shield_normal_time_left--;
11642
11643         if (player->shield_deadly_time_left > 0)
11644           player->shield_deadly_time_left--;
11645       }
11646     }
11647
11648     if (!local_player->LevelSolved && !level.use_step_counter)
11649     {
11650       TimePlayed++;
11651
11652       if (TimeLeft > 0)
11653       {
11654         TimeLeft--;
11655
11656         if (TimeLeft <= 10 && setup.time_limit)
11657           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11658
11659 #if 1
11660         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11661
11662         DisplayGameControlValues();
11663 #else
11664         DrawGameValue_Time(TimeLeft);
11665 #endif
11666
11667         if (!TimeLeft && setup.time_limit)
11668         {
11669           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11670             level.native_em_level->lev->killed_out_of_time = TRUE;
11671           else
11672             for (i = 0; i < MAX_PLAYERS; i++)
11673               KillPlayer(&stored_player[i]);
11674         }
11675       }
11676 #if 1
11677       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11678       {
11679         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11680
11681         DisplayGameControlValues();
11682       }
11683 #else
11684       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11685         DrawGameValue_Time(TimePlayed);
11686 #endif
11687
11688       level.native_em_level->lev->time =
11689         (level.time == 0 ? TimePlayed : TimeLeft);
11690     }
11691
11692     if (tape.recording || tape.playing)
11693       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11694   }
11695
11696 #if 1
11697   UpdateAndDisplayGameControlValues();
11698 #else
11699   UpdateGameDoorValues();
11700   DrawGameDoorValues();
11701 #endif
11702 }
11703
11704 void AdvanceFrameAndPlayerCounters(int player_nr)
11705 {
11706   int i;
11707
11708   /* advance frame counters (global frame counter and time frame counter) */
11709   FrameCounter++;
11710   TimeFrames++;
11711
11712   /* advance player counters (counters for move delay, move animation etc.) */
11713   for (i = 0; i < MAX_PLAYERS; i++)
11714   {
11715     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11716     int move_delay_value = stored_player[i].move_delay_value;
11717     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11718
11719     if (!advance_player_counters)       /* not all players may be affected */
11720       continue;
11721
11722 #if USE_NEW_PLAYER_ANIM
11723     if (move_frames == 0)       /* less than one move per game frame */
11724     {
11725       int stepsize = TILEX / move_delay_value;
11726       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11727       int count = (stored_player[i].is_moving ?
11728                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11729
11730       if (count % delay == 0)
11731         move_frames = 1;
11732     }
11733 #endif
11734
11735     stored_player[i].Frame += move_frames;
11736
11737     if (stored_player[i].MovPos != 0)
11738       stored_player[i].StepFrame += move_frames;
11739
11740     if (stored_player[i].move_delay > 0)
11741       stored_player[i].move_delay--;
11742
11743     /* due to bugs in previous versions, counter must count up, not down */
11744     if (stored_player[i].push_delay != -1)
11745       stored_player[i].push_delay++;
11746
11747     if (stored_player[i].drop_delay > 0)
11748       stored_player[i].drop_delay--;
11749
11750     if (stored_player[i].is_dropping_pressed)
11751       stored_player[i].drop_pressed_delay++;
11752   }
11753 }
11754
11755 void StartGameActions(boolean init_network_game, boolean record_tape,
11756                       long random_seed)
11757 {
11758   unsigned long new_random_seed = InitRND(random_seed);
11759
11760   if (record_tape)
11761     TapeStartRecording(new_random_seed);
11762
11763 #if defined(NETWORK_AVALIABLE)
11764   if (init_network_game)
11765   {
11766     SendToServer_StartPlaying();
11767
11768     return;
11769   }
11770 #endif
11771
11772   InitGame();
11773 }
11774
11775 void GameActions()
11776 {
11777   static unsigned long game_frame_delay = 0;
11778   unsigned long game_frame_delay_value;
11779   byte *recorded_player_action;
11780   byte summarized_player_action = 0;
11781   byte tape_action[MAX_PLAYERS];
11782   int i;
11783
11784   /* detect endless loops, caused by custom element programming */
11785   if (recursion_loop_detected && recursion_loop_depth == 0)
11786   {
11787     char *message = getStringCat3("Internal Error ! Element ",
11788                                   EL_NAME(recursion_loop_element),
11789                                   " caused endless loop ! Quit the game ?");
11790
11791     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11792           EL_NAME(recursion_loop_element));
11793
11794     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11795
11796     recursion_loop_detected = FALSE;    /* if game should be continued */
11797
11798     free(message);
11799
11800     return;
11801   }
11802
11803   if (game.restart_level)
11804     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11805
11806   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11807   {
11808     if (level.native_em_level->lev->home == 0)  /* all players at home */
11809     {
11810       PlayerWins(local_player);
11811
11812       AllPlayersGone = TRUE;
11813
11814       level.native_em_level->lev->home = -1;
11815     }
11816
11817     if (level.native_em_level->ply[0]->alive == 0 &&
11818         level.native_em_level->ply[1]->alive == 0 &&
11819         level.native_em_level->ply[2]->alive == 0 &&
11820         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11821       AllPlayersGone = TRUE;
11822   }
11823
11824   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11825     GameWon();
11826
11827   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11828     TapeStop();
11829
11830   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11831     return;
11832
11833   game_frame_delay_value =
11834     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11835
11836   if (tape.playing && tape.warp_forward && !tape.pausing)
11837     game_frame_delay_value = 0;
11838
11839   /* ---------- main game synchronization point ---------- */
11840
11841   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11842
11843   if (network_playing && !network_player_action_received)
11844   {
11845     /* try to get network player actions in time */
11846
11847 #if defined(NETWORK_AVALIABLE)
11848     /* last chance to get network player actions without main loop delay */
11849     HandleNetworking();
11850 #endif
11851
11852     /* game was quit by network peer */
11853     if (game_status != GAME_MODE_PLAYING)
11854       return;
11855
11856     if (!network_player_action_received)
11857       return;           /* failed to get network player actions in time */
11858
11859     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11860   }
11861
11862   if (tape.pausing)
11863     return;
11864
11865   /* at this point we know that we really continue executing the game */
11866
11867   network_player_action_received = FALSE;
11868
11869   /* when playing tape, read previously recorded player input from tape data */
11870   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11871
11872 #if 1
11873   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11874   if (tape.pausing)
11875     return;
11876 #endif
11877
11878   if (tape.set_centered_player)
11879   {
11880     game.centered_player_nr_next = tape.centered_player_nr_next;
11881     game.set_centered_player = TRUE;
11882   }
11883
11884   for (i = 0; i < MAX_PLAYERS; i++)
11885   {
11886     summarized_player_action |= stored_player[i].action;
11887
11888     if (!network_playing)
11889       stored_player[i].effective_action = stored_player[i].action;
11890   }
11891
11892 #if defined(NETWORK_AVALIABLE)
11893   if (network_playing)
11894     SendToServer_MovePlayer(summarized_player_action);
11895 #endif
11896
11897   if (!options.network && !setup.team_mode)
11898     local_player->effective_action = summarized_player_action;
11899
11900   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11901   {
11902     for (i = 0; i < MAX_PLAYERS; i++)
11903       stored_player[i].effective_action =
11904         (i == game.centered_player_nr ? summarized_player_action : 0);
11905   }
11906
11907   if (recorded_player_action != NULL)
11908     for (i = 0; i < MAX_PLAYERS; i++)
11909       stored_player[i].effective_action = recorded_player_action[i];
11910
11911   for (i = 0; i < MAX_PLAYERS; i++)
11912   {
11913     tape_action[i] = stored_player[i].effective_action;
11914
11915     /* (this can only happen in the R'n'D game engine) */
11916     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11917       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11918   }
11919
11920   /* only record actions from input devices, but not programmed actions */
11921   if (tape.recording)
11922     TapeRecordAction(tape_action);
11923
11924   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11925   {
11926     GameActions_EM_Main();
11927   }
11928   else
11929   {
11930     GameActions_RND();
11931   }
11932 }
11933
11934 void GameActions_EM_Main()
11935 {
11936   byte effective_action[MAX_PLAYERS];
11937   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11938   int i;
11939
11940   for (i = 0; i < MAX_PLAYERS; i++)
11941     effective_action[i] = stored_player[i].effective_action;
11942
11943   GameActions_EM(effective_action, warp_mode);
11944
11945   CheckLevelTime();
11946
11947   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11948 }
11949
11950 void GameActions_RND()
11951 {
11952   int magic_wall_x = 0, magic_wall_y = 0;
11953   int i, x, y, element, graphic;
11954
11955   InitPlayfieldScanModeVars();
11956
11957 #if USE_ONE_MORE_CHANGE_PER_FRAME
11958   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11959   {
11960     SCAN_PLAYFIELD(x, y)
11961     {
11962       ChangeCount[x][y] = 0;
11963       ChangeEvent[x][y] = -1;
11964     }
11965   }
11966 #endif
11967
11968   if (game.set_centered_player)
11969   {
11970     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11971
11972     /* switching to "all players" only possible if all players fit to screen */
11973     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11974     {
11975       game.centered_player_nr_next = game.centered_player_nr;
11976       game.set_centered_player = FALSE;
11977     }
11978
11979     /* do not switch focus to non-existing (or non-active) player */
11980     if (game.centered_player_nr_next >= 0 &&
11981         !stored_player[game.centered_player_nr_next].active)
11982     {
11983       game.centered_player_nr_next = game.centered_player_nr;
11984       game.set_centered_player = FALSE;
11985     }
11986   }
11987
11988   if (game.set_centered_player &&
11989       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11990   {
11991     int sx, sy;
11992
11993     if (game.centered_player_nr_next == -1)
11994     {
11995       setScreenCenteredToAllPlayers(&sx, &sy);
11996     }
11997     else
11998     {
11999       sx = stored_player[game.centered_player_nr_next].jx;
12000       sy = stored_player[game.centered_player_nr_next].jy;
12001     }
12002
12003     game.centered_player_nr = game.centered_player_nr_next;
12004     game.set_centered_player = FALSE;
12005
12006     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12007     DrawGameDoorValues();
12008   }
12009
12010   for (i = 0; i < MAX_PLAYERS; i++)
12011   {
12012     int actual_player_action = stored_player[i].effective_action;
12013
12014 #if 1
12015     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12016        - rnd_equinox_tetrachloride 048
12017        - rnd_equinox_tetrachloride_ii 096
12018        - rnd_emanuel_schmieg 002
12019        - doctor_sloan_ww 001, 020
12020     */
12021     if (stored_player[i].MovPos == 0)
12022       CheckGravityMovement(&stored_player[i]);
12023 #endif
12024
12025     /* overwrite programmed action with tape action */
12026     if (stored_player[i].programmed_action)
12027       actual_player_action = stored_player[i].programmed_action;
12028
12029     PlayerActions(&stored_player[i], actual_player_action);
12030
12031     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12032   }
12033
12034   ScrollScreen(NULL, SCROLL_GO_ON);
12035
12036   /* for backwards compatibility, the following code emulates a fixed bug that
12037      occured when pushing elements (causing elements that just made their last
12038      pushing step to already (if possible) make their first falling step in the
12039      same game frame, which is bad); this code is also needed to use the famous
12040      "spring push bug" which is used in older levels and might be wanted to be
12041      used also in newer levels, but in this case the buggy pushing code is only
12042      affecting the "spring" element and no other elements */
12043
12044   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12045   {
12046     for (i = 0; i < MAX_PLAYERS; i++)
12047     {
12048       struct PlayerInfo *player = &stored_player[i];
12049       int x = player->jx;
12050       int y = player->jy;
12051
12052       if (player->active && player->is_pushing && player->is_moving &&
12053           IS_MOVING(x, y) &&
12054           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12055            Feld[x][y] == EL_SPRING))
12056       {
12057         ContinueMoving(x, y);
12058
12059         /* continue moving after pushing (this is actually a bug) */
12060         if (!IS_MOVING(x, y))
12061           Stop[x][y] = FALSE;
12062       }
12063     }
12064   }
12065
12066 #if 0
12067   debug_print_timestamp(0, "start main loop profiling");
12068 #endif
12069
12070   SCAN_PLAYFIELD(x, y)
12071   {
12072     ChangeCount[x][y] = 0;
12073     ChangeEvent[x][y] = -1;
12074
12075     /* this must be handled before main playfield loop */
12076     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12077     {
12078       MovDelay[x][y]--;
12079       if (MovDelay[x][y] <= 0)
12080         RemoveField(x, y);
12081     }
12082
12083 #if USE_NEW_SNAP_DELAY
12084     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12085     {
12086       MovDelay[x][y]--;
12087       if (MovDelay[x][y] <= 0)
12088       {
12089         RemoveField(x, y);
12090         TEST_DrawLevelField(x, y);
12091
12092         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12093       }
12094     }
12095 #endif
12096
12097 #if DEBUG
12098     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12099     {
12100       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12101       printf("GameActions(): This should never happen!\n");
12102
12103       ChangePage[x][y] = -1;
12104     }
12105 #endif
12106
12107     Stop[x][y] = FALSE;
12108     if (WasJustMoving[x][y] > 0)
12109       WasJustMoving[x][y]--;
12110     if (WasJustFalling[x][y] > 0)
12111       WasJustFalling[x][y]--;
12112     if (CheckCollision[x][y] > 0)
12113       CheckCollision[x][y]--;
12114     if (CheckImpact[x][y] > 0)
12115       CheckImpact[x][y]--;
12116
12117     GfxFrame[x][y]++;
12118
12119     /* reset finished pushing action (not done in ContinueMoving() to allow
12120        continuous pushing animation for elements with zero push delay) */
12121     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12122     {
12123       ResetGfxAnimation(x, y);
12124       TEST_DrawLevelField(x, y);
12125     }
12126
12127 #if DEBUG
12128     if (IS_BLOCKED(x, y))
12129     {
12130       int oldx, oldy;
12131
12132       Blocked2Moving(x, y, &oldx, &oldy);
12133       if (!IS_MOVING(oldx, oldy))
12134       {
12135         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12136         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12137         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12138         printf("GameActions(): This should never happen!\n");
12139       }
12140     }
12141 #endif
12142   }
12143
12144 #if 0
12145   debug_print_timestamp(0, "- time for pre-main loop:");
12146 #endif
12147
12148 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12149   SCAN_PLAYFIELD(x, y)
12150   {
12151     element = Feld[x][y];
12152     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12153
12154 #if 1
12155     {
12156 #if 1
12157       int element2 = element;
12158       int graphic2 = graphic;
12159 #else
12160       int element2 = Feld[x][y];
12161       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12162 #endif
12163       int last_gfx_frame = GfxFrame[x][y];
12164
12165       if (graphic_info[graphic2].anim_global_sync)
12166         GfxFrame[x][y] = FrameCounter;
12167       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12168         GfxFrame[x][y] = CustomValue[x][y];
12169       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12170         GfxFrame[x][y] = element_info[element2].collect_score;
12171       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12172         GfxFrame[x][y] = ChangeDelay[x][y];
12173
12174       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12175         DrawLevelGraphicAnimation(x, y, graphic2);
12176     }
12177 #else
12178     ResetGfxFrame(x, y, TRUE);
12179 #endif
12180
12181 #if 1
12182     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12183         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12184       ResetRandomAnimationValue(x, y);
12185 #endif
12186
12187 #if 1
12188     SetRandomAnimationValue(x, y);
12189 #endif
12190
12191 #if 1
12192     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12193 #endif
12194   }
12195 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12196
12197 #if 0
12198   debug_print_timestamp(0, "- time for TEST loop:     -->");
12199 #endif
12200
12201   SCAN_PLAYFIELD(x, y)
12202   {
12203     element = Feld[x][y];
12204     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12205
12206     ResetGfxFrame(x, y, TRUE);
12207
12208     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12209         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12210       ResetRandomAnimationValue(x, y);
12211
12212     SetRandomAnimationValue(x, y);
12213
12214     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12215
12216     if (IS_INACTIVE(element))
12217     {
12218       if (IS_ANIMATED(graphic))
12219         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12220
12221       continue;
12222     }
12223
12224     /* this may take place after moving, so 'element' may have changed */
12225     if (IS_CHANGING(x, y) &&
12226         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12227     {
12228       int page = element_info[element].event_page_nr[CE_DELAY];
12229
12230 #if 1
12231       HandleElementChange(x, y, page);
12232 #else
12233       if (CAN_CHANGE(element))
12234         HandleElementChange(x, y, page);
12235
12236       if (HAS_ACTION(element))
12237         ExecuteCustomElementAction(x, y, element, page);
12238 #endif
12239
12240       element = Feld[x][y];
12241       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12242     }
12243
12244 #if 0   // ---------------------------------------------------------------------
12245
12246     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12247     {
12248       StartMoving(x, y);
12249
12250       element = Feld[x][y];
12251       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12252
12253       if (IS_ANIMATED(graphic) &&
12254           !IS_MOVING(x, y) &&
12255           !Stop[x][y])
12256         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12257
12258       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12259         TEST_DrawTwinkleOnField(x, y);
12260     }
12261     else if (IS_MOVING(x, y))
12262       ContinueMoving(x, y);
12263     else
12264     {
12265       switch (element)
12266       {
12267         case EL_ACID:
12268         case EL_EXIT_OPEN:
12269         case EL_EM_EXIT_OPEN:
12270         case EL_SP_EXIT_OPEN:
12271         case EL_STEEL_EXIT_OPEN:
12272         case EL_EM_STEEL_EXIT_OPEN:
12273         case EL_SP_TERMINAL:
12274         case EL_SP_TERMINAL_ACTIVE:
12275         case EL_EXTRA_TIME:
12276         case EL_SHIELD_NORMAL:
12277         case EL_SHIELD_DEADLY:
12278           if (IS_ANIMATED(graphic))
12279             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12280           break;
12281
12282         case EL_DYNAMITE_ACTIVE:
12283         case EL_EM_DYNAMITE_ACTIVE:
12284         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12285         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12286         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12287         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12288         case EL_SP_DISK_RED_ACTIVE:
12289           CheckDynamite(x, y);
12290           break;
12291
12292         case EL_AMOEBA_GROWING:
12293           AmoebeWaechst(x, y);
12294           break;
12295
12296         case EL_AMOEBA_SHRINKING:
12297           AmoebaDisappearing(x, y);
12298           break;
12299
12300 #if !USE_NEW_AMOEBA_CODE
12301         case EL_AMOEBA_WET:
12302         case EL_AMOEBA_DRY:
12303         case EL_AMOEBA_FULL:
12304         case EL_BD_AMOEBA:
12305         case EL_EMC_DRIPPER:
12306           AmoebeAbleger(x, y);
12307           break;
12308 #endif
12309
12310         case EL_GAME_OF_LIFE:
12311         case EL_BIOMAZE:
12312           Life(x, y);
12313           break;
12314
12315         case EL_EXIT_CLOSED:
12316           CheckExit(x, y);
12317           break;
12318
12319         case EL_EM_EXIT_CLOSED:
12320           CheckExitEM(x, y);
12321           break;
12322
12323         case EL_STEEL_EXIT_CLOSED:
12324           CheckExitSteel(x, y);
12325           break;
12326
12327         case EL_EM_STEEL_EXIT_CLOSED:
12328           CheckExitSteelEM(x, y);
12329           break;
12330
12331         case EL_SP_EXIT_CLOSED:
12332           CheckExitSP(x, y);
12333           break;
12334
12335         case EL_EXPANDABLE_WALL_GROWING:
12336         case EL_EXPANDABLE_STEELWALL_GROWING:
12337           MauerWaechst(x, y);
12338           break;
12339
12340         case EL_EXPANDABLE_WALL:
12341         case EL_EXPANDABLE_WALL_HORIZONTAL:
12342         case EL_EXPANDABLE_WALL_VERTICAL:
12343         case EL_EXPANDABLE_WALL_ANY:
12344         case EL_BD_EXPANDABLE_WALL:
12345           MauerAbleger(x, y);
12346           break;
12347
12348         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12349         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12350         case EL_EXPANDABLE_STEELWALL_ANY:
12351           MauerAblegerStahl(x, y);
12352           break;
12353
12354         case EL_FLAMES:
12355           CheckForDragon(x, y);
12356           break;
12357
12358         case EL_EXPLOSION:
12359           break;
12360
12361         case EL_ELEMENT_SNAPPING:
12362         case EL_DIAGONAL_SHRINKING:
12363         case EL_DIAGONAL_GROWING:
12364         {
12365           graphic =
12366             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12367
12368           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12369           break;
12370         }
12371
12372         default:
12373           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12374             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12375           break;
12376       }
12377     }
12378
12379 #else   // ---------------------------------------------------------------------
12380
12381     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12382     {
12383       StartMoving(x, y);
12384
12385       element = Feld[x][y];
12386       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12387
12388       if (IS_ANIMATED(graphic) &&
12389           !IS_MOVING(x, y) &&
12390           !Stop[x][y])
12391         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12392
12393       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12394         TEST_DrawTwinkleOnField(x, y);
12395     }
12396     else if ((element == EL_ACID ||
12397               element == EL_EXIT_OPEN ||
12398               element == EL_EM_EXIT_OPEN ||
12399               element == EL_SP_EXIT_OPEN ||
12400               element == EL_STEEL_EXIT_OPEN ||
12401               element == EL_EM_STEEL_EXIT_OPEN ||
12402               element == EL_SP_TERMINAL ||
12403               element == EL_SP_TERMINAL_ACTIVE ||
12404               element == EL_EXTRA_TIME ||
12405               element == EL_SHIELD_NORMAL ||
12406               element == EL_SHIELD_DEADLY) &&
12407              IS_ANIMATED(graphic))
12408       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12409     else if (IS_MOVING(x, y))
12410       ContinueMoving(x, y);
12411     else if (IS_ACTIVE_BOMB(element))
12412       CheckDynamite(x, y);
12413     else if (element == EL_AMOEBA_GROWING)
12414       AmoebeWaechst(x, y);
12415     else if (element == EL_AMOEBA_SHRINKING)
12416       AmoebaDisappearing(x, y);
12417
12418 #if !USE_NEW_AMOEBA_CODE
12419     else if (IS_AMOEBALIVE(element))
12420       AmoebeAbleger(x, y);
12421 #endif
12422
12423     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12424       Life(x, y);
12425     else if (element == EL_EXIT_CLOSED)
12426       CheckExit(x, y);
12427     else if (element == EL_EM_EXIT_CLOSED)
12428       CheckExitEM(x, y);
12429     else if (element == EL_STEEL_EXIT_CLOSED)
12430       CheckExitSteel(x, y);
12431     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12432       CheckExitSteelEM(x, y);
12433     else if (element == EL_SP_EXIT_CLOSED)
12434       CheckExitSP(x, y);
12435     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12436              element == EL_EXPANDABLE_STEELWALL_GROWING)
12437       MauerWaechst(x, y);
12438     else if (element == EL_EXPANDABLE_WALL ||
12439              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12440              element == EL_EXPANDABLE_WALL_VERTICAL ||
12441              element == EL_EXPANDABLE_WALL_ANY ||
12442              element == EL_BD_EXPANDABLE_WALL)
12443       MauerAbleger(x, y);
12444     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12445              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12446              element == EL_EXPANDABLE_STEELWALL_ANY)
12447       MauerAblegerStahl(x, y);
12448     else if (element == EL_FLAMES)
12449       CheckForDragon(x, y);
12450     else if (element == EL_EXPLOSION)
12451       ; /* drawing of correct explosion animation is handled separately */
12452     else if (element == EL_ELEMENT_SNAPPING ||
12453              element == EL_DIAGONAL_SHRINKING ||
12454              element == EL_DIAGONAL_GROWING)
12455     {
12456       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12457
12458       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12459     }
12460     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12461       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12462
12463 #endif  // ---------------------------------------------------------------------
12464
12465     if (IS_BELT_ACTIVE(element))
12466       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12467
12468     if (game.magic_wall_active)
12469     {
12470       int jx = local_player->jx, jy = local_player->jy;
12471
12472       /* play the element sound at the position nearest to the player */
12473       if ((element == EL_MAGIC_WALL_FULL ||
12474            element == EL_MAGIC_WALL_ACTIVE ||
12475            element == EL_MAGIC_WALL_EMPTYING ||
12476            element == EL_BD_MAGIC_WALL_FULL ||
12477            element == EL_BD_MAGIC_WALL_ACTIVE ||
12478            element == EL_BD_MAGIC_WALL_EMPTYING ||
12479            element == EL_DC_MAGIC_WALL_FULL ||
12480            element == EL_DC_MAGIC_WALL_ACTIVE ||
12481            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12482           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12483       {
12484         magic_wall_x = x;
12485         magic_wall_y = y;
12486       }
12487     }
12488   }
12489
12490 #if 0
12491   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12492 #endif
12493
12494 #if USE_NEW_AMOEBA_CODE
12495   /* new experimental amoeba growth stuff */
12496   if (!(FrameCounter % 8))
12497   {
12498     static unsigned long random = 1684108901;
12499
12500     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12501     {
12502       x = RND(lev_fieldx);
12503       y = RND(lev_fieldy);
12504       element = Feld[x][y];
12505
12506       if (!IS_PLAYER(x,y) &&
12507           (element == EL_EMPTY ||
12508            CAN_GROW_INTO(element) ||
12509            element == EL_QUICKSAND_EMPTY ||
12510            element == EL_QUICKSAND_FAST_EMPTY ||
12511            element == EL_ACID_SPLASH_LEFT ||
12512            element == EL_ACID_SPLASH_RIGHT))
12513       {
12514         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12515             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12516             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12517             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12518           Feld[x][y] = EL_AMOEBA_DROP;
12519       }
12520
12521       random = random * 129 + 1;
12522     }
12523   }
12524 #endif
12525
12526 #if 0
12527   if (game.explosions_delayed)
12528 #endif
12529   {
12530     game.explosions_delayed = FALSE;
12531
12532     SCAN_PLAYFIELD(x, y)
12533     {
12534       element = Feld[x][y];
12535
12536       if (ExplodeField[x][y])
12537         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12538       else if (element == EL_EXPLOSION)
12539         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12540
12541       ExplodeField[x][y] = EX_TYPE_NONE;
12542     }
12543
12544     game.explosions_delayed = TRUE;
12545   }
12546
12547   if (game.magic_wall_active)
12548   {
12549     if (!(game.magic_wall_time_left % 4))
12550     {
12551       int element = Feld[magic_wall_x][magic_wall_y];
12552
12553       if (element == EL_BD_MAGIC_WALL_FULL ||
12554           element == EL_BD_MAGIC_WALL_ACTIVE ||
12555           element == EL_BD_MAGIC_WALL_EMPTYING)
12556         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12557       else if (element == EL_DC_MAGIC_WALL_FULL ||
12558                element == EL_DC_MAGIC_WALL_ACTIVE ||
12559                element == EL_DC_MAGIC_WALL_EMPTYING)
12560         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12561       else
12562         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12563     }
12564
12565     if (game.magic_wall_time_left > 0)
12566     {
12567       game.magic_wall_time_left--;
12568
12569       if (!game.magic_wall_time_left)
12570       {
12571         SCAN_PLAYFIELD(x, y)
12572         {
12573           element = Feld[x][y];
12574
12575           if (element == EL_MAGIC_WALL_ACTIVE ||
12576               element == EL_MAGIC_WALL_FULL)
12577           {
12578             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12579             TEST_DrawLevelField(x, y);
12580           }
12581           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12582                    element == EL_BD_MAGIC_WALL_FULL)
12583           {
12584             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12585             TEST_DrawLevelField(x, y);
12586           }
12587           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12588                    element == EL_DC_MAGIC_WALL_FULL)
12589           {
12590             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12591             TEST_DrawLevelField(x, y);
12592           }
12593         }
12594
12595         game.magic_wall_active = FALSE;
12596       }
12597     }
12598   }
12599
12600   if (game.light_time_left > 0)
12601   {
12602     game.light_time_left--;
12603
12604     if (game.light_time_left == 0)
12605       RedrawAllLightSwitchesAndInvisibleElements();
12606   }
12607
12608   if (game.timegate_time_left > 0)
12609   {
12610     game.timegate_time_left--;
12611
12612     if (game.timegate_time_left == 0)
12613       CloseAllOpenTimegates();
12614   }
12615
12616   if (game.lenses_time_left > 0)
12617   {
12618     game.lenses_time_left--;
12619
12620     if (game.lenses_time_left == 0)
12621       RedrawAllInvisibleElementsForLenses();
12622   }
12623
12624   if (game.magnify_time_left > 0)
12625   {
12626     game.magnify_time_left--;
12627
12628     if (game.magnify_time_left == 0)
12629       RedrawAllInvisibleElementsForMagnifier();
12630   }
12631
12632   for (i = 0; i < MAX_PLAYERS; i++)
12633   {
12634     struct PlayerInfo *player = &stored_player[i];
12635
12636     if (SHIELD_ON(player))
12637     {
12638       if (player->shield_deadly_time_left)
12639         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12640       else if (player->shield_normal_time_left)
12641         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12642     }
12643   }
12644
12645 #if USE_DELAYED_GFX_REDRAW
12646   SCAN_PLAYFIELD(x, y)
12647   {
12648 #if 1
12649     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12650 #else
12651     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12652         GfxRedraw[x][y] != GFX_REDRAW_NONE)
12653 #endif
12654     {
12655       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12656          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12657
12658       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12659         DrawLevelField(x, y);
12660
12661       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12662         DrawLevelFieldCrumbledSand(x, y);
12663
12664       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12665         DrawLevelFieldCrumbledSandNeighbours(x, y);
12666
12667       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12668         DrawTwinkleOnField(x, y);
12669     }
12670
12671     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12672   }
12673 #endif
12674
12675   CheckLevelTime();
12676
12677   DrawAllPlayers();
12678   PlayAllPlayersSound();
12679
12680   if (options.debug)                    /* calculate frames per second */
12681   {
12682     static unsigned long fps_counter = 0;
12683     static int fps_frames = 0;
12684     unsigned long fps_delay_ms = Counter() - fps_counter;
12685
12686     fps_frames++;
12687
12688     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12689     {
12690       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12691
12692       fps_frames = 0;
12693       fps_counter = Counter();
12694     }
12695
12696     redraw_mask |= REDRAW_FPS;
12697   }
12698
12699   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12700
12701   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12702   {
12703     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12704
12705     local_player->show_envelope = 0;
12706   }
12707
12708 #if 0
12709   debug_print_timestamp(0, "stop main loop profiling ");
12710   printf("----------------------------------------------------------\n");
12711 #endif
12712
12713   /* use random number generator in every frame to make it less predictable */
12714   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12715     RND(1);
12716 }
12717
12718 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12719 {
12720   int min_x = x, min_y = y, max_x = x, max_y = y;
12721   int i;
12722
12723   for (i = 0; i < MAX_PLAYERS; i++)
12724   {
12725     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12726
12727     if (!stored_player[i].active || &stored_player[i] == player)
12728       continue;
12729
12730     min_x = MIN(min_x, jx);
12731     min_y = MIN(min_y, jy);
12732     max_x = MAX(max_x, jx);
12733     max_y = MAX(max_y, jy);
12734   }
12735
12736   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12737 }
12738
12739 static boolean AllPlayersInVisibleScreen()
12740 {
12741   int i;
12742
12743   for (i = 0; i < MAX_PLAYERS; i++)
12744   {
12745     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12746
12747     if (!stored_player[i].active)
12748       continue;
12749
12750     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12751       return FALSE;
12752   }
12753
12754   return TRUE;
12755 }
12756
12757 void ScrollLevel(int dx, int dy)
12758 {
12759 #if 0
12760   /* (directly solved in BlitBitmap() now) */
12761   static Bitmap *bitmap_db_field2 = NULL;
12762   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12763   int x, y;
12764 #else
12765   int x, y;
12766 #endif
12767
12768 #if 0
12769   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12770   /* only horizontal XOR vertical scroll direction allowed */
12771   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12772     return;
12773 #endif
12774
12775 #if 0
12776   /* (directly solved in BlitBitmap() now) */
12777   if (bitmap_db_field2 == NULL)
12778     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12779
12780   /* needed when blitting directly to same bitmap -- should not be needed with
12781      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12782   BlitBitmap(drawto_field, bitmap_db_field2,
12783              FX + TILEX * (dx == -1) - softscroll_offset,
12784              FY + TILEY * (dy == -1) - softscroll_offset,
12785              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12786              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12787              FX + TILEX * (dx == 1) - softscroll_offset,
12788              FY + TILEY * (dy == 1) - softscroll_offset);
12789   BlitBitmap(bitmap_db_field2, drawto_field,
12790              FX + TILEX * (dx == 1) - softscroll_offset,
12791              FY + TILEY * (dy == 1) - softscroll_offset,
12792              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12793              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12794              FX + TILEX * (dx == 1) - softscroll_offset,
12795              FY + TILEY * (dy == 1) - softscroll_offset);
12796
12797 #else
12798
12799 #if 0
12800   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12801   int xsize = (BX2 - BX1 + 1);
12802   int ysize = (BY2 - BY1 + 1);
12803   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12804   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12805   int step  = (start < end ? +1 : -1);
12806
12807   for (i = start; i != end; i += step)
12808   {
12809     BlitBitmap(drawto_field, drawto_field,
12810                FX + TILEX * (dx != 0 ? i + step : 0),
12811                FY + TILEY * (dy != 0 ? i + step : 0),
12812                TILEX * (dx != 0 ? 1 : xsize),
12813                TILEY * (dy != 0 ? 1 : ysize),
12814                FX + TILEX * (dx != 0 ? i : 0),
12815                FY + TILEY * (dy != 0 ? i : 0));
12816   }
12817
12818 #else
12819
12820   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12821
12822   BlitBitmap(drawto_field, drawto_field,
12823              FX + TILEX * (dx == -1) - softscroll_offset,
12824              FY + TILEY * (dy == -1) - softscroll_offset,
12825              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12826              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12827              FX + TILEX * (dx == 1) - softscroll_offset,
12828              FY + TILEY * (dy == 1) - softscroll_offset);
12829 #endif
12830 #endif
12831
12832   if (dx != 0)
12833   {
12834     x = (dx == 1 ? BX1 : BX2);
12835     for (y = BY1; y <= BY2; y++)
12836       DrawScreenField(x, y);
12837   }
12838
12839   if (dy != 0)
12840   {
12841     y = (dy == 1 ? BY1 : BY2);
12842     for (x = BX1; x <= BX2; x++)
12843       DrawScreenField(x, y);
12844   }
12845
12846   redraw_mask |= REDRAW_FIELD;
12847 }
12848
12849 static boolean canFallDown(struct PlayerInfo *player)
12850 {
12851   int jx = player->jx, jy = player->jy;
12852
12853   return (IN_LEV_FIELD(jx, jy + 1) &&
12854           (IS_FREE(jx, jy + 1) ||
12855            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12856           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12857           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12858 }
12859
12860 static boolean canPassField(int x, int y, int move_dir)
12861 {
12862   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12863   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12864   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12865   int nextx = x + dx;
12866   int nexty = y + dy;
12867   int element = Feld[x][y];
12868
12869   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12870           !CAN_MOVE(element) &&
12871           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12872           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12873           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12874 }
12875
12876 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12877 {
12878   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12879   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12880   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12881   int newx = x + dx;
12882   int newy = y + dy;
12883
12884   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12885           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12886           (IS_DIGGABLE(Feld[newx][newy]) ||
12887            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12888            canPassField(newx, newy, move_dir)));
12889 }
12890
12891 static void CheckGravityMovement(struct PlayerInfo *player)
12892 {
12893 #if USE_PLAYER_GRAVITY
12894   if (player->gravity && !player->programmed_action)
12895 #else
12896   if (game.gravity && !player->programmed_action)
12897 #endif
12898   {
12899     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12900     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12901     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12902     int jx = player->jx, jy = player->jy;
12903     boolean player_is_moving_to_valid_field =
12904       (!player_is_snapping &&
12905        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12906         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12907     boolean player_can_fall_down = canFallDown(player);
12908
12909     if (player_can_fall_down &&
12910         !player_is_moving_to_valid_field)
12911       player->programmed_action = MV_DOWN;
12912   }
12913 }
12914
12915 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12916 {
12917   return CheckGravityMovement(player);
12918
12919 #if USE_PLAYER_GRAVITY
12920   if (player->gravity && !player->programmed_action)
12921 #else
12922   if (game.gravity && !player->programmed_action)
12923 #endif
12924   {
12925     int jx = player->jx, jy = player->jy;
12926     boolean field_under_player_is_free =
12927       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12928     boolean player_is_standing_on_valid_field =
12929       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12930        (IS_WALKABLE(Feld[jx][jy]) &&
12931         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12932
12933     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12934       player->programmed_action = MV_DOWN;
12935   }
12936 }
12937
12938 /*
12939   MovePlayerOneStep()
12940   -----------------------------------------------------------------------------
12941   dx, dy:               direction (non-diagonal) to try to move the player to
12942   real_dx, real_dy:     direction as read from input device (can be diagonal)
12943 */
12944
12945 boolean MovePlayerOneStep(struct PlayerInfo *player,
12946                           int dx, int dy, int real_dx, int real_dy)
12947 {
12948   int jx = player->jx, jy = player->jy;
12949   int new_jx = jx + dx, new_jy = jy + dy;
12950 #if !USE_FIXED_DONT_RUN_INTO
12951   int element;
12952 #endif
12953   int can_move;
12954   boolean player_can_move = !player->cannot_move;
12955
12956   if (!player->active || (!dx && !dy))
12957     return MP_NO_ACTION;
12958
12959   player->MovDir = (dx < 0 ? MV_LEFT :
12960                     dx > 0 ? MV_RIGHT :
12961                     dy < 0 ? MV_UP :
12962                     dy > 0 ? MV_DOWN :  MV_NONE);
12963
12964   if (!IN_LEV_FIELD(new_jx, new_jy))
12965     return MP_NO_ACTION;
12966
12967   if (!player_can_move)
12968   {
12969     if (player->MovPos == 0)
12970     {
12971       player->is_moving = FALSE;
12972       player->is_digging = FALSE;
12973       player->is_collecting = FALSE;
12974       player->is_snapping = FALSE;
12975       player->is_pushing = FALSE;
12976     }
12977   }
12978
12979 #if 1
12980   if (!options.network && game.centered_player_nr == -1 &&
12981       !AllPlayersInSight(player, new_jx, new_jy))
12982     return MP_NO_ACTION;
12983 #else
12984   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12985     return MP_NO_ACTION;
12986 #endif
12987
12988 #if !USE_FIXED_DONT_RUN_INTO
12989   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12990
12991   /* (moved to DigField()) */
12992   if (player_can_move && DONT_RUN_INTO(element))
12993   {
12994     if (element == EL_ACID && dx == 0 && dy == 1)
12995     {
12996       SplashAcid(new_jx, new_jy);
12997       Feld[jx][jy] = EL_PLAYER_1;
12998       InitMovingField(jx, jy, MV_DOWN);
12999       Store[jx][jy] = EL_ACID;
13000       ContinueMoving(jx, jy);
13001       BuryPlayer(player);
13002     }
13003     else
13004       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13005
13006     return MP_MOVING;
13007   }
13008 #endif
13009
13010   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13011   if (can_move != MP_MOVING)
13012     return can_move;
13013
13014   /* check if DigField() has caused relocation of the player */
13015   if (player->jx != jx || player->jy != jy)
13016     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13017
13018   StorePlayer[jx][jy] = 0;
13019   player->last_jx = jx;
13020   player->last_jy = jy;
13021   player->jx = new_jx;
13022   player->jy = new_jy;
13023   StorePlayer[new_jx][new_jy] = player->element_nr;
13024
13025   if (player->move_delay_value_next != -1)
13026   {
13027     player->move_delay_value = player->move_delay_value_next;
13028     player->move_delay_value_next = -1;
13029   }
13030
13031   player->MovPos =
13032     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13033
13034   player->step_counter++;
13035
13036   PlayerVisit[jx][jy] = FrameCounter;
13037
13038 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13039   player->is_moving = TRUE;
13040 #endif
13041
13042 #if 1
13043   /* should better be called in MovePlayer(), but this breaks some tapes */
13044   ScrollPlayer(player, SCROLL_INIT);
13045 #endif
13046
13047   return MP_MOVING;
13048 }
13049
13050 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13051 {
13052   int jx = player->jx, jy = player->jy;
13053   int old_jx = jx, old_jy = jy;
13054   int moved = MP_NO_ACTION;
13055
13056   if (!player->active)
13057     return FALSE;
13058
13059   if (!dx && !dy)
13060   {
13061     if (player->MovPos == 0)
13062     {
13063       player->is_moving = FALSE;
13064       player->is_digging = FALSE;
13065       player->is_collecting = FALSE;
13066       player->is_snapping = FALSE;
13067       player->is_pushing = FALSE;
13068     }
13069
13070     return FALSE;
13071   }
13072
13073   if (player->move_delay > 0)
13074     return FALSE;
13075
13076   player->move_delay = -1;              /* set to "uninitialized" value */
13077
13078   /* store if player is automatically moved to next field */
13079   player->is_auto_moving = (player->programmed_action != MV_NONE);
13080
13081   /* remove the last programmed player action */
13082   player->programmed_action = 0;
13083
13084   if (player->MovPos)
13085   {
13086     /* should only happen if pre-1.2 tape recordings are played */
13087     /* this is only for backward compatibility */
13088
13089     int original_move_delay_value = player->move_delay_value;
13090
13091 #if DEBUG
13092     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13093            tape.counter);
13094 #endif
13095
13096     /* scroll remaining steps with finest movement resolution */
13097     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13098
13099     while (player->MovPos)
13100     {
13101       ScrollPlayer(player, SCROLL_GO_ON);
13102       ScrollScreen(NULL, SCROLL_GO_ON);
13103
13104       AdvanceFrameAndPlayerCounters(player->index_nr);
13105
13106       DrawAllPlayers();
13107       BackToFront();
13108     }
13109
13110     player->move_delay_value = original_move_delay_value;
13111   }
13112
13113   player->is_active = FALSE;
13114
13115   if (player->last_move_dir & MV_HORIZONTAL)
13116   {
13117     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13118       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13119   }
13120   else
13121   {
13122     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13123       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13124   }
13125
13126 #if USE_FIXED_BORDER_RUNNING_GFX
13127   if (!moved && !player->is_active)
13128   {
13129     player->is_moving = FALSE;
13130     player->is_digging = FALSE;
13131     player->is_collecting = FALSE;
13132     player->is_snapping = FALSE;
13133     player->is_pushing = FALSE;
13134   }
13135 #endif
13136
13137   jx = player->jx;
13138   jy = player->jy;
13139
13140 #if 1
13141   if (moved & MP_MOVING && !ScreenMovPos &&
13142       (player->index_nr == game.centered_player_nr ||
13143        game.centered_player_nr == -1))
13144 #else
13145   if (moved & MP_MOVING && !ScreenMovPos &&
13146       (player == local_player || !options.network))
13147 #endif
13148   {
13149     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13150     int offset = game.scroll_delay_value;
13151
13152     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13153     {
13154       /* actual player has left the screen -- scroll in that direction */
13155       if (jx != old_jx)         /* player has moved horizontally */
13156         scroll_x += (jx - old_jx);
13157       else                      /* player has moved vertically */
13158         scroll_y += (jy - old_jy);
13159     }
13160     else
13161     {
13162       if (jx != old_jx)         /* player has moved horizontally */
13163       {
13164         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13165             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13166           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13167
13168         /* don't scroll over playfield boundaries */
13169         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13170           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13171
13172         /* don't scroll more than one field at a time */
13173         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13174
13175         /* don't scroll against the player's moving direction */
13176         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13177             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13178           scroll_x = old_scroll_x;
13179       }
13180       else                      /* player has moved vertically */
13181       {
13182         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13183             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13184           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13185
13186         /* don't scroll over playfield boundaries */
13187         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13188           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13189
13190         /* don't scroll more than one field at a time */
13191         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13192
13193         /* don't scroll against the player's moving direction */
13194         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13195             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13196           scroll_y = old_scroll_y;
13197       }
13198     }
13199
13200     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13201     {
13202 #if 1
13203       if (!options.network && game.centered_player_nr == -1 &&
13204           !AllPlayersInVisibleScreen())
13205       {
13206         scroll_x = old_scroll_x;
13207         scroll_y = old_scroll_y;
13208       }
13209       else
13210 #else
13211       if (!options.network && !AllPlayersInVisibleScreen())
13212       {
13213         scroll_x = old_scroll_x;
13214         scroll_y = old_scroll_y;
13215       }
13216       else
13217 #endif
13218       {
13219         ScrollScreen(player, SCROLL_INIT);
13220         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13221       }
13222     }
13223   }
13224
13225   player->StepFrame = 0;
13226
13227   if (moved & MP_MOVING)
13228   {
13229     if (old_jx != jx && old_jy == jy)
13230       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13231     else if (old_jx == jx && old_jy != jy)
13232       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13233
13234     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13235
13236     player->last_move_dir = player->MovDir;
13237     player->is_moving = TRUE;
13238     player->is_snapping = FALSE;
13239     player->is_switching = FALSE;
13240     player->is_dropping = FALSE;
13241     player->is_dropping_pressed = FALSE;
13242     player->drop_pressed_delay = 0;
13243
13244 #if 0
13245     /* should better be called here than above, but this breaks some tapes */
13246     ScrollPlayer(player, SCROLL_INIT);
13247 #endif
13248   }
13249   else
13250   {
13251     CheckGravityMovementWhenNotMoving(player);
13252
13253     player->is_moving = FALSE;
13254
13255     /* at this point, the player is allowed to move, but cannot move right now
13256        (e.g. because of something blocking the way) -- ensure that the player
13257        is also allowed to move in the next frame (in old versions before 3.1.1,
13258        the player was forced to wait again for eight frames before next try) */
13259
13260     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13261       player->move_delay = 0;   /* allow direct movement in the next frame */
13262   }
13263
13264   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13265     player->move_delay = player->move_delay_value;
13266
13267   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13268   {
13269     TestIfPlayerTouchesBadThing(jx, jy);
13270     TestIfPlayerTouchesCustomElement(jx, jy);
13271   }
13272
13273   if (!player->active)
13274     RemovePlayer(player);
13275
13276   return moved;
13277 }
13278
13279 void ScrollPlayer(struct PlayerInfo *player, int mode)
13280 {
13281   int jx = player->jx, jy = player->jy;
13282   int last_jx = player->last_jx, last_jy = player->last_jy;
13283   int move_stepsize = TILEX / player->move_delay_value;
13284
13285 #if USE_NEW_PLAYER_SPEED
13286   if (!player->active)
13287     return;
13288
13289   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13290     return;
13291 #else
13292   if (!player->active || player->MovPos == 0)
13293     return;
13294 #endif
13295
13296   if (mode == SCROLL_INIT)
13297   {
13298     player->actual_frame_counter = FrameCounter;
13299     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13300
13301     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13302         Feld[last_jx][last_jy] == EL_EMPTY)
13303     {
13304       int last_field_block_delay = 0;   /* start with no blocking at all */
13305       int block_delay_adjustment = player->block_delay_adjustment;
13306
13307       /* if player blocks last field, add delay for exactly one move */
13308       if (player->block_last_field)
13309       {
13310         last_field_block_delay += player->move_delay_value;
13311
13312         /* when blocking enabled, prevent moving up despite gravity */
13313 #if USE_PLAYER_GRAVITY
13314         if (player->gravity && player->MovDir == MV_UP)
13315           block_delay_adjustment = -1;
13316 #else
13317         if (game.gravity && player->MovDir == MV_UP)
13318           block_delay_adjustment = -1;
13319 #endif
13320       }
13321
13322       /* add block delay adjustment (also possible when not blocking) */
13323       last_field_block_delay += block_delay_adjustment;
13324
13325       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13326       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13327     }
13328
13329 #if USE_NEW_PLAYER_SPEED
13330     if (player->MovPos != 0)    /* player has not yet reached destination */
13331       return;
13332 #else
13333     return;
13334 #endif
13335   }
13336   else if (!FrameReached(&player->actual_frame_counter, 1))
13337     return;
13338
13339 #if USE_NEW_PLAYER_SPEED
13340   if (player->MovPos != 0)
13341   {
13342     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13343     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13344
13345     /* before DrawPlayer() to draw correct player graphic for this case */
13346     if (player->MovPos == 0)
13347       CheckGravityMovement(player);
13348   }
13349 #else
13350   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13351   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13352
13353   /* before DrawPlayer() to draw correct player graphic for this case */
13354   if (player->MovPos == 0)
13355     CheckGravityMovement(player);
13356 #endif
13357
13358   if (player->MovPos == 0)      /* player reached destination field */
13359   {
13360     if (player->move_delay_reset_counter > 0)
13361     {
13362       player->move_delay_reset_counter--;
13363
13364       if (player->move_delay_reset_counter == 0)
13365       {
13366         /* continue with normal speed after quickly moving through gate */
13367         HALVE_PLAYER_SPEED(player);
13368
13369         /* be able to make the next move without delay */
13370         player->move_delay = 0;
13371       }
13372     }
13373
13374     player->last_jx = jx;
13375     player->last_jy = jy;
13376
13377     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13378         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13379         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13380         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13381         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13382         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13383     {
13384       DrawPlayer(player);       /* needed here only to cleanup last field */
13385       RemovePlayer(player);
13386
13387       if (local_player->friends_still_needed == 0 ||
13388           IS_SP_ELEMENT(Feld[jx][jy]))
13389         PlayerWins(player);
13390     }
13391
13392     /* this breaks one level: "machine", level 000 */
13393     {
13394       int move_direction = player->MovDir;
13395       int enter_side = MV_DIR_OPPOSITE(move_direction);
13396       int leave_side = move_direction;
13397       int old_jx = last_jx;
13398       int old_jy = last_jy;
13399       int old_element = Feld[old_jx][old_jy];
13400       int new_element = Feld[jx][jy];
13401
13402       if (IS_CUSTOM_ELEMENT(old_element))
13403         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13404                                    CE_LEFT_BY_PLAYER,
13405                                    player->index_bit, leave_side);
13406
13407       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13408                                           CE_PLAYER_LEAVES_X,
13409                                           player->index_bit, leave_side);
13410
13411       if (IS_CUSTOM_ELEMENT(new_element))
13412         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13413                                    player->index_bit, enter_side);
13414
13415       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13416                                           CE_PLAYER_ENTERS_X,
13417                                           player->index_bit, enter_side);
13418
13419 #if USE_FIX_CE_ACTION_WITH_PLAYER
13420       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13421                                         CE_MOVE_OF_X, move_direction);
13422 #else
13423       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13424                                         CE_MOVE_OF_X, move_direction);
13425 #endif
13426     }
13427
13428     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13429     {
13430       TestIfPlayerTouchesBadThing(jx, jy);
13431       TestIfPlayerTouchesCustomElement(jx, jy);
13432
13433       /* needed because pushed element has not yet reached its destination,
13434          so it would trigger a change event at its previous field location */
13435       if (!player->is_pushing)
13436         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13437
13438       if (!player->active)
13439         RemovePlayer(player);
13440     }
13441
13442     if (!local_player->LevelSolved && level.use_step_counter)
13443     {
13444       int i;
13445
13446       TimePlayed++;
13447
13448       if (TimeLeft > 0)
13449       {
13450         TimeLeft--;
13451
13452         if (TimeLeft <= 10 && setup.time_limit)
13453           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13454
13455 #if 1
13456         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13457
13458         DisplayGameControlValues();
13459 #else
13460         DrawGameValue_Time(TimeLeft);
13461 #endif
13462
13463         if (!TimeLeft && setup.time_limit)
13464           for (i = 0; i < MAX_PLAYERS; i++)
13465             KillPlayer(&stored_player[i]);
13466       }
13467 #if 1
13468       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13469       {
13470         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13471
13472         DisplayGameControlValues();
13473       }
13474 #else
13475       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13476         DrawGameValue_Time(TimePlayed);
13477 #endif
13478     }
13479
13480     if (tape.single_step && tape.recording && !tape.pausing &&
13481         !player->programmed_action)
13482       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13483   }
13484 }
13485
13486 void ScrollScreen(struct PlayerInfo *player, int mode)
13487 {
13488   static unsigned long screen_frame_counter = 0;
13489
13490   if (mode == SCROLL_INIT)
13491   {
13492     /* set scrolling step size according to actual player's moving speed */
13493     ScrollStepSize = TILEX / player->move_delay_value;
13494
13495     screen_frame_counter = FrameCounter;
13496     ScreenMovDir = player->MovDir;
13497     ScreenMovPos = player->MovPos;
13498     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13499     return;
13500   }
13501   else if (!FrameReached(&screen_frame_counter, 1))
13502     return;
13503
13504   if (ScreenMovPos)
13505   {
13506     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13507     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13508     redraw_mask |= REDRAW_FIELD;
13509   }
13510   else
13511     ScreenMovDir = MV_NONE;
13512 }
13513
13514 void TestIfPlayerTouchesCustomElement(int x, int y)
13515 {
13516   static int xy[4][2] =
13517   {
13518     { 0, -1 },
13519     { -1, 0 },
13520     { +1, 0 },
13521     { 0, +1 }
13522   };
13523   static int trigger_sides[4][2] =
13524   {
13525     /* center side       border side */
13526     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13527     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13528     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13529     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13530   };
13531   static int touch_dir[4] =
13532   {
13533     MV_LEFT | MV_RIGHT,
13534     MV_UP   | MV_DOWN,
13535     MV_UP   | MV_DOWN,
13536     MV_LEFT | MV_RIGHT
13537   };
13538   int center_element = Feld[x][y];      /* should always be non-moving! */
13539   int i;
13540
13541   for (i = 0; i < NUM_DIRECTIONS; i++)
13542   {
13543     int xx = x + xy[i][0];
13544     int yy = y + xy[i][1];
13545     int center_side = trigger_sides[i][0];
13546     int border_side = trigger_sides[i][1];
13547     int border_element;
13548
13549     if (!IN_LEV_FIELD(xx, yy))
13550       continue;
13551
13552     if (IS_PLAYER(x, y))                /* player found at center element */
13553     {
13554       struct PlayerInfo *player = PLAYERINFO(x, y);
13555
13556       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13557         border_element = Feld[xx][yy];          /* may be moving! */
13558       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13559         border_element = Feld[xx][yy];
13560       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13561         border_element = MovingOrBlocked2Element(xx, yy);
13562       else
13563         continue;               /* center and border element do not touch */
13564
13565       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13566                                  player->index_bit, border_side);
13567       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13568                                           CE_PLAYER_TOUCHES_X,
13569                                           player->index_bit, border_side);
13570
13571 #if USE_FIX_CE_ACTION_WITH_PLAYER
13572       {
13573         /* use player element that is initially defined in the level playfield,
13574            not the player element that corresponds to the runtime player number
13575            (example: a level that contains EL_PLAYER_3 as the only player would
13576            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13577         int player_element = PLAYERINFO(x, y)->initial_element;
13578
13579         CheckElementChangeBySide(xx, yy, border_element, player_element,
13580                                  CE_TOUCHING_X, border_side);
13581       }
13582 #endif
13583     }
13584     else if (IS_PLAYER(xx, yy))         /* player found at border element */
13585     {
13586       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13587
13588       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13589       {
13590         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13591           continue;             /* center and border element do not touch */
13592       }
13593
13594       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13595                                  player->index_bit, center_side);
13596       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13597                                           CE_PLAYER_TOUCHES_X,
13598                                           player->index_bit, center_side);
13599
13600 #if USE_FIX_CE_ACTION_WITH_PLAYER
13601       {
13602         /* use player element that is initially defined in the level playfield,
13603            not the player element that corresponds to the runtime player number
13604            (example: a level that contains EL_PLAYER_3 as the only player would
13605            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13606         int player_element = PLAYERINFO(xx, yy)->initial_element;
13607
13608         CheckElementChangeBySide(x, y, center_element, player_element,
13609                                  CE_TOUCHING_X, center_side);
13610       }
13611 #endif
13612
13613       break;
13614     }
13615   }
13616 }
13617
13618 #if USE_ELEMENT_TOUCHING_BUGFIX
13619
13620 void TestIfElementTouchesCustomElement(int x, int y)
13621 {
13622   static int xy[4][2] =
13623   {
13624     { 0, -1 },
13625     { -1, 0 },
13626     { +1, 0 },
13627     { 0, +1 }
13628   };
13629   static int trigger_sides[4][2] =
13630   {
13631     /* center side      border side */
13632     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13633     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13634     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13635     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13636   };
13637   static int touch_dir[4] =
13638   {
13639     MV_LEFT | MV_RIGHT,
13640     MV_UP   | MV_DOWN,
13641     MV_UP   | MV_DOWN,
13642     MV_LEFT | MV_RIGHT
13643   };
13644   boolean change_center_element = FALSE;
13645   int center_element = Feld[x][y];      /* should always be non-moving! */
13646   int border_element_old[NUM_DIRECTIONS];
13647   int i;
13648
13649   for (i = 0; i < NUM_DIRECTIONS; i++)
13650   {
13651     int xx = x + xy[i][0];
13652     int yy = y + xy[i][1];
13653     int border_element;
13654
13655     border_element_old[i] = -1;
13656
13657     if (!IN_LEV_FIELD(xx, yy))
13658       continue;
13659
13660     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13661       border_element = Feld[xx][yy];    /* may be moving! */
13662     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13663       border_element = Feld[xx][yy];
13664     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13665       border_element = MovingOrBlocked2Element(xx, yy);
13666     else
13667       continue;                 /* center and border element do not touch */
13668
13669     border_element_old[i] = border_element;
13670   }
13671
13672   for (i = 0; i < NUM_DIRECTIONS; i++)
13673   {
13674     int xx = x + xy[i][0];
13675     int yy = y + xy[i][1];
13676     int center_side = trigger_sides[i][0];
13677     int border_element = border_element_old[i];
13678
13679     if (border_element == -1)
13680       continue;
13681
13682     /* check for change of border element */
13683     CheckElementChangeBySide(xx, yy, border_element, center_element,
13684                              CE_TOUCHING_X, center_side);
13685
13686     /* (center element cannot be player, so we dont have to check this here) */
13687   }
13688
13689   for (i = 0; i < NUM_DIRECTIONS; i++)
13690   {
13691     int xx = x + xy[i][0];
13692     int yy = y + xy[i][1];
13693     int border_side = trigger_sides[i][1];
13694     int border_element = border_element_old[i];
13695
13696     if (border_element == -1)
13697       continue;
13698
13699     /* check for change of center element (but change it only once) */
13700     if (!change_center_element)
13701       change_center_element =
13702         CheckElementChangeBySide(x, y, center_element, border_element,
13703                                  CE_TOUCHING_X, border_side);
13704
13705 #if USE_FIX_CE_ACTION_WITH_PLAYER
13706     if (IS_PLAYER(xx, yy))
13707     {
13708       /* use player element that is initially defined in the level playfield,
13709          not the player element that corresponds to the runtime player number
13710          (example: a level that contains EL_PLAYER_3 as the only player would
13711          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13712       int player_element = PLAYERINFO(xx, yy)->initial_element;
13713
13714       CheckElementChangeBySide(x, y, center_element, player_element,
13715                                CE_TOUCHING_X, border_side);
13716     }
13717 #endif
13718   }
13719 }
13720
13721 #else
13722
13723 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13724 {
13725   static int xy[4][2] =
13726   {
13727     { 0, -1 },
13728     { -1, 0 },
13729     { +1, 0 },
13730     { 0, +1 }
13731   };
13732   static int trigger_sides[4][2] =
13733   {
13734     /* center side      border side */
13735     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13736     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13737     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13738     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13739   };
13740   static int touch_dir[4] =
13741   {
13742     MV_LEFT | MV_RIGHT,
13743     MV_UP   | MV_DOWN,
13744     MV_UP   | MV_DOWN,
13745     MV_LEFT | MV_RIGHT
13746   };
13747   boolean change_center_element = FALSE;
13748   int center_element = Feld[x][y];      /* should always be non-moving! */
13749   int i;
13750
13751   for (i = 0; i < NUM_DIRECTIONS; i++)
13752   {
13753     int xx = x + xy[i][0];
13754     int yy = y + xy[i][1];
13755     int center_side = trigger_sides[i][0];
13756     int border_side = trigger_sides[i][1];
13757     int border_element;
13758
13759     if (!IN_LEV_FIELD(xx, yy))
13760       continue;
13761
13762     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13763       border_element = Feld[xx][yy];    /* may be moving! */
13764     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13765       border_element = Feld[xx][yy];
13766     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13767       border_element = MovingOrBlocked2Element(xx, yy);
13768     else
13769       continue;                 /* center and border element do not touch */
13770
13771     /* check for change of center element (but change it only once) */
13772     if (!change_center_element)
13773       change_center_element =
13774         CheckElementChangeBySide(x, y, center_element, border_element,
13775                                  CE_TOUCHING_X, border_side);
13776
13777     /* check for change of border element */
13778     CheckElementChangeBySide(xx, yy, border_element, center_element,
13779                              CE_TOUCHING_X, center_side);
13780   }
13781 }
13782
13783 #endif
13784
13785 void TestIfElementHitsCustomElement(int x, int y, int direction)
13786 {
13787   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13788   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13789   int hitx = x + dx, hity = y + dy;
13790   int hitting_element = Feld[x][y];
13791   int touched_element;
13792
13793   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13794     return;
13795
13796   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13797                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13798
13799   if (IN_LEV_FIELD(hitx, hity))
13800   {
13801     int opposite_direction = MV_DIR_OPPOSITE(direction);
13802     int hitting_side = direction;
13803     int touched_side = opposite_direction;
13804     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13805                           MovDir[hitx][hity] != direction ||
13806                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13807
13808     object_hit = TRUE;
13809
13810     if (object_hit)
13811     {
13812       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13813                                CE_HITTING_X, touched_side);
13814
13815       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13816                                CE_HIT_BY_X, hitting_side);
13817
13818       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13819                                CE_HIT_BY_SOMETHING, opposite_direction);
13820
13821 #if USE_FIX_CE_ACTION_WITH_PLAYER
13822       if (IS_PLAYER(hitx, hity))
13823       {
13824         /* use player element that is initially defined in the level playfield,
13825            not the player element that corresponds to the runtime player number
13826            (example: a level that contains EL_PLAYER_3 as the only player would
13827            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13828         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13829
13830         CheckElementChangeBySide(x, y, hitting_element, player_element,
13831                                  CE_HITTING_X, touched_side);
13832       }
13833 #endif
13834     }
13835   }
13836
13837   /* "hitting something" is also true when hitting the playfield border */
13838   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13839                            CE_HITTING_SOMETHING, direction);
13840 }
13841
13842 #if 0
13843 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13844 {
13845   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13846   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13847   int hitx = x + dx, hity = y + dy;
13848   int hitting_element = Feld[x][y];
13849   int touched_element;
13850 #if 0
13851   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13852                         !IS_FREE(hitx, hity) &&
13853                         (!IS_MOVING(hitx, hity) ||
13854                          MovDir[hitx][hity] != direction ||
13855                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13856 #endif
13857
13858   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13859     return;
13860
13861 #if 0
13862   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13863     return;
13864 #endif
13865
13866   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13867                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13868
13869   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13870                            EP_CAN_SMASH_EVERYTHING, direction);
13871
13872   if (IN_LEV_FIELD(hitx, hity))
13873   {
13874     int opposite_direction = MV_DIR_OPPOSITE(direction);
13875     int hitting_side = direction;
13876     int touched_side = opposite_direction;
13877 #if 0
13878     int touched_element = MovingOrBlocked2Element(hitx, hity);
13879 #endif
13880 #if 1
13881     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13882                           MovDir[hitx][hity] != direction ||
13883                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13884
13885     object_hit = TRUE;
13886 #endif
13887
13888     if (object_hit)
13889     {
13890       int i;
13891
13892       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13893                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13894
13895       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13896                                CE_OTHER_IS_SMASHING, touched_side);
13897
13898       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13899                                CE_OTHER_GETS_SMASHED, hitting_side);
13900     }
13901   }
13902 }
13903 #endif
13904
13905 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13906 {
13907   int i, kill_x = -1, kill_y = -1;
13908
13909   int bad_element = -1;
13910   static int test_xy[4][2] =
13911   {
13912     { 0, -1 },
13913     { -1, 0 },
13914     { +1, 0 },
13915     { 0, +1 }
13916   };
13917   static int test_dir[4] =
13918   {
13919     MV_UP,
13920     MV_LEFT,
13921     MV_RIGHT,
13922     MV_DOWN
13923   };
13924
13925   for (i = 0; i < NUM_DIRECTIONS; i++)
13926   {
13927     int test_x, test_y, test_move_dir, test_element;
13928
13929     test_x = good_x + test_xy[i][0];
13930     test_y = good_y + test_xy[i][1];
13931
13932     if (!IN_LEV_FIELD(test_x, test_y))
13933       continue;
13934
13935     test_move_dir =
13936       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13937
13938     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13939
13940     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13941        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13942     */
13943     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13944         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13945     {
13946       kill_x = test_x;
13947       kill_y = test_y;
13948       bad_element = test_element;
13949
13950       break;
13951     }
13952   }
13953
13954   if (kill_x != -1 || kill_y != -1)
13955   {
13956     if (IS_PLAYER(good_x, good_y))
13957     {
13958       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13959
13960       if (player->shield_deadly_time_left > 0 &&
13961           !IS_INDESTRUCTIBLE(bad_element))
13962         Bang(kill_x, kill_y);
13963       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13964         KillPlayer(player);
13965     }
13966     else
13967       Bang(good_x, good_y);
13968   }
13969 }
13970
13971 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13972 {
13973   int i, kill_x = -1, kill_y = -1;
13974   int bad_element = Feld[bad_x][bad_y];
13975   static int test_xy[4][2] =
13976   {
13977     { 0, -1 },
13978     { -1, 0 },
13979     { +1, 0 },
13980     { 0, +1 }
13981   };
13982   static int touch_dir[4] =
13983   {
13984     MV_LEFT | MV_RIGHT,
13985     MV_UP   | MV_DOWN,
13986     MV_UP   | MV_DOWN,
13987     MV_LEFT | MV_RIGHT
13988   };
13989   static int test_dir[4] =
13990   {
13991     MV_UP,
13992     MV_LEFT,
13993     MV_RIGHT,
13994     MV_DOWN
13995   };
13996
13997   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13998     return;
13999
14000   for (i = 0; i < NUM_DIRECTIONS; i++)
14001   {
14002     int test_x, test_y, test_move_dir, test_element;
14003
14004     test_x = bad_x + test_xy[i][0];
14005     test_y = bad_y + test_xy[i][1];
14006
14007     if (!IN_LEV_FIELD(test_x, test_y))
14008       continue;
14009
14010     test_move_dir =
14011       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14012
14013     test_element = Feld[test_x][test_y];
14014
14015     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14016        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14017     */
14018     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14019         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14020     {
14021       /* good thing is 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         if (bad_element == EL_ROBOT && player->is_moving)
14027           continue;     /* robot does not kill player if he is moving */
14028
14029         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14030         {
14031           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14032             continue;           /* center and border element do not touch */
14033         }
14034
14035         kill_x = test_x;
14036         kill_y = test_y;
14037
14038         break;
14039       }
14040       else if (test_element == EL_PENGUIN)
14041       {
14042         kill_x = test_x;
14043         kill_y = test_y;
14044
14045         break;
14046       }
14047     }
14048   }
14049
14050   if (kill_x != -1 || kill_y != -1)
14051   {
14052     if (IS_PLAYER(kill_x, kill_y))
14053     {
14054       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14055
14056       if (player->shield_deadly_time_left > 0 &&
14057           !IS_INDESTRUCTIBLE(bad_element))
14058         Bang(bad_x, bad_y);
14059       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14060         KillPlayer(player);
14061     }
14062     else
14063       Bang(kill_x, kill_y);
14064   }
14065 }
14066
14067 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14068 {
14069   int bad_element = Feld[bad_x][bad_y];
14070   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14071   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14072   int test_x = bad_x + dx, test_y = bad_y + dy;
14073   int test_move_dir, test_element;
14074   int kill_x = -1, kill_y = -1;
14075
14076   if (!IN_LEV_FIELD(test_x, test_y))
14077     return;
14078
14079   test_move_dir =
14080     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14081
14082   test_element = Feld[test_x][test_y];
14083
14084   if (test_move_dir != bad_move_dir)
14085   {
14086     /* good thing can be player or penguin that does not move away */
14087     if (IS_PLAYER(test_x, test_y))
14088     {
14089       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14090
14091       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14092          player as being hit when he is moving towards the bad thing, because
14093          the "get hit by" condition would be lost after the player stops) */
14094       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14095         return;         /* player moves away from bad thing */
14096
14097       kill_x = test_x;
14098       kill_y = test_y;
14099     }
14100     else if (test_element == EL_PENGUIN)
14101     {
14102       kill_x = test_x;
14103       kill_y = test_y;
14104     }
14105   }
14106
14107   if (kill_x != -1 || kill_y != -1)
14108   {
14109     if (IS_PLAYER(kill_x, kill_y))
14110     {
14111       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14112
14113       if (player->shield_deadly_time_left > 0 &&
14114           !IS_INDESTRUCTIBLE(bad_element))
14115         Bang(bad_x, bad_y);
14116       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14117         KillPlayer(player);
14118     }
14119     else
14120       Bang(kill_x, kill_y);
14121   }
14122 }
14123
14124 void TestIfPlayerTouchesBadThing(int x, int y)
14125 {
14126   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14127 }
14128
14129 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14130 {
14131   TestIfGoodThingHitsBadThing(x, y, move_dir);
14132 }
14133
14134 void TestIfBadThingTouchesPlayer(int x, int y)
14135 {
14136   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14137 }
14138
14139 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14140 {
14141   TestIfBadThingHitsGoodThing(x, y, move_dir);
14142 }
14143
14144 void TestIfFriendTouchesBadThing(int x, int y)
14145 {
14146   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14147 }
14148
14149 void TestIfBadThingTouchesFriend(int x, int y)
14150 {
14151   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14152 }
14153
14154 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14155 {
14156   int i, kill_x = bad_x, kill_y = bad_y;
14157   static int xy[4][2] =
14158   {
14159     { 0, -1 },
14160     { -1, 0 },
14161     { +1, 0 },
14162     { 0, +1 }
14163   };
14164
14165   for (i = 0; i < NUM_DIRECTIONS; i++)
14166   {
14167     int x, y, element;
14168
14169     x = bad_x + xy[i][0];
14170     y = bad_y + xy[i][1];
14171     if (!IN_LEV_FIELD(x, y))
14172       continue;
14173
14174     element = Feld[x][y];
14175     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14176         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14177     {
14178       kill_x = x;
14179       kill_y = y;
14180       break;
14181     }
14182   }
14183
14184   if (kill_x != bad_x || kill_y != bad_y)
14185     Bang(bad_x, bad_y);
14186 }
14187
14188 void KillPlayer(struct PlayerInfo *player)
14189 {
14190   int jx = player->jx, jy = player->jy;
14191
14192   if (!player->active)
14193     return;
14194
14195   /* the following code was introduced to prevent an infinite loop when calling
14196      -> Bang()
14197      -> CheckTriggeredElementChangeExt()
14198      -> ExecuteCustomElementAction()
14199      -> KillPlayer()
14200      -> (infinitely repeating the above sequence of function calls)
14201      which occurs when killing the player while having a CE with the setting
14202      "kill player X when explosion of <player X>"; the solution using a new
14203      field "player->killed" was chosen for backwards compatibility, although
14204      clever use of the fields "player->active" etc. would probably also work */
14205 #if 1
14206   if (player->killed)
14207     return;
14208 #endif
14209
14210   player->killed = TRUE;
14211
14212   /* remove accessible field at the player's position */
14213   Feld[jx][jy] = EL_EMPTY;
14214
14215   /* deactivate shield (else Bang()/Explode() would not work right) */
14216   player->shield_normal_time_left = 0;
14217   player->shield_deadly_time_left = 0;
14218
14219   Bang(jx, jy);
14220
14221 #if USE_PLAYER_REANIMATION
14222   if (player->killed)           /* player may have been reanimated */
14223     BuryPlayer(player);
14224 #else
14225   BuryPlayer(player);
14226 #endif
14227 }
14228
14229 static void KillPlayerUnlessEnemyProtected(int x, int y)
14230 {
14231   if (!PLAYER_ENEMY_PROTECTED(x, y))
14232     KillPlayer(PLAYERINFO(x, y));
14233 }
14234
14235 static void KillPlayerUnlessExplosionProtected(int x, int y)
14236 {
14237   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14238     KillPlayer(PLAYERINFO(x, y));
14239 }
14240
14241 void BuryPlayer(struct PlayerInfo *player)
14242 {
14243   int jx = player->jx, jy = player->jy;
14244
14245   if (!player->active)
14246     return;
14247
14248   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14249   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14250
14251   player->GameOver = TRUE;
14252   RemovePlayer(player);
14253 }
14254
14255 void RemovePlayer(struct PlayerInfo *player)
14256 {
14257   int jx = player->jx, jy = player->jy;
14258   int i, found = FALSE;
14259
14260   player->present = FALSE;
14261   player->active = FALSE;
14262
14263   if (!ExplodeField[jx][jy])
14264     StorePlayer[jx][jy] = 0;
14265
14266   if (player->is_moving)
14267     TEST_DrawLevelField(player->last_jx, player->last_jy);
14268
14269   for (i = 0; i < MAX_PLAYERS; i++)
14270     if (stored_player[i].active)
14271       found = TRUE;
14272
14273   if (!found)
14274     AllPlayersGone = TRUE;
14275
14276   ExitX = ZX = jx;
14277   ExitY = ZY = jy;
14278 }
14279
14280 #if USE_NEW_SNAP_DELAY
14281 static void setFieldForSnapping(int x, int y, int element, int direction)
14282 {
14283   struct ElementInfo *ei = &element_info[element];
14284   int direction_bit = MV_DIR_TO_BIT(direction);
14285   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14286   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14287                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14288
14289   Feld[x][y] = EL_ELEMENT_SNAPPING;
14290   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14291
14292   ResetGfxAnimation(x, y);
14293
14294   GfxElement[x][y] = element;
14295   GfxAction[x][y] = action;
14296   GfxDir[x][y] = direction;
14297   GfxFrame[x][y] = -1;
14298 }
14299 #endif
14300
14301 /*
14302   =============================================================================
14303   checkDiagonalPushing()
14304   -----------------------------------------------------------------------------
14305   check if diagonal input device direction results in pushing of object
14306   (by checking if the alternative direction is walkable, diggable, ...)
14307   =============================================================================
14308 */
14309
14310 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14311                                     int x, int y, int real_dx, int real_dy)
14312 {
14313   int jx, jy, dx, dy, xx, yy;
14314
14315   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14316     return TRUE;
14317
14318   /* diagonal direction: check alternative direction */
14319   jx = player->jx;
14320   jy = player->jy;
14321   dx = x - jx;
14322   dy = y - jy;
14323   xx = jx + (dx == 0 ? real_dx : 0);
14324   yy = jy + (dy == 0 ? real_dy : 0);
14325
14326   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14327 }
14328
14329 /*
14330   =============================================================================
14331   DigField()
14332   -----------------------------------------------------------------------------
14333   x, y:                 field next to player (non-diagonal) to try to dig to
14334   real_dx, real_dy:     direction as read from input device (can be diagonal)
14335   =============================================================================
14336 */
14337
14338 static int DigField(struct PlayerInfo *player,
14339                     int oldx, int oldy, int x, int y,
14340                     int real_dx, int real_dy, int mode)
14341 {
14342   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14343   boolean player_was_pushing = player->is_pushing;
14344   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14345   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14346   int jx = oldx, jy = oldy;
14347   int dx = x - jx, dy = y - jy;
14348   int nextx = x + dx, nexty = y + dy;
14349   int move_direction = (dx == -1 ? MV_LEFT  :
14350                         dx == +1 ? MV_RIGHT :
14351                         dy == -1 ? MV_UP    :
14352                         dy == +1 ? MV_DOWN  : MV_NONE);
14353   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14354   int dig_side = MV_DIR_OPPOSITE(move_direction);
14355   int old_element = Feld[jx][jy];
14356 #if USE_FIXED_DONT_RUN_INTO
14357   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14358 #else
14359   int element;
14360 #endif
14361   int collect_count;
14362
14363   if (is_player)                /* function can also be called by EL_PENGUIN */
14364   {
14365     if (player->MovPos == 0)
14366     {
14367       player->is_digging = FALSE;
14368       player->is_collecting = FALSE;
14369     }
14370
14371     if (player->MovPos == 0)    /* last pushing move finished */
14372       player->is_pushing = FALSE;
14373
14374     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14375     {
14376       player->is_switching = FALSE;
14377       player->push_delay = -1;
14378
14379       return MP_NO_ACTION;
14380     }
14381   }
14382
14383 #if !USE_FIXED_DONT_RUN_INTO
14384   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14385     return MP_NO_ACTION;
14386 #endif
14387
14388   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14389     old_element = Back[jx][jy];
14390
14391   /* in case of element dropped at player position, check background */
14392   else if (Back[jx][jy] != EL_EMPTY &&
14393            game.engine_version >= VERSION_IDENT(2,2,0,0))
14394     old_element = Back[jx][jy];
14395
14396   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14397     return MP_NO_ACTION;        /* field has no opening in this direction */
14398
14399   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14400     return MP_NO_ACTION;        /* field has no opening in this direction */
14401
14402 #if USE_FIXED_DONT_RUN_INTO
14403   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14404   {
14405     SplashAcid(x, y);
14406
14407     Feld[jx][jy] = player->artwork_element;
14408     InitMovingField(jx, jy, MV_DOWN);
14409     Store[jx][jy] = EL_ACID;
14410     ContinueMoving(jx, jy);
14411     BuryPlayer(player);
14412
14413     return MP_DONT_RUN_INTO;
14414   }
14415 #endif
14416
14417 #if USE_FIXED_DONT_RUN_INTO
14418   if (player_can_move && DONT_RUN_INTO(element))
14419   {
14420     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14421
14422     return MP_DONT_RUN_INTO;
14423   }
14424 #endif
14425
14426 #if USE_FIXED_DONT_RUN_INTO
14427   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14428     return MP_NO_ACTION;
14429 #endif
14430
14431 #if !USE_FIXED_DONT_RUN_INTO
14432   element = Feld[x][y];
14433 #endif
14434
14435   collect_count = element_info[element].collect_count_initial;
14436
14437   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14438     return MP_NO_ACTION;
14439
14440   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14441     player_can_move = player_can_move_or_snap;
14442
14443   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14444       game.engine_version >= VERSION_IDENT(2,2,0,0))
14445   {
14446     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14447                                player->index_bit, dig_side);
14448     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14449                                         player->index_bit, dig_side);
14450
14451     if (element == EL_DC_LANDMINE)
14452       Bang(x, y);
14453
14454     if (Feld[x][y] != element)          /* field changed by snapping */
14455       return MP_ACTION;
14456
14457     return MP_NO_ACTION;
14458   }
14459
14460 #if USE_PLAYER_GRAVITY
14461   if (player->gravity && is_player && !player->is_auto_moving &&
14462       canFallDown(player) && move_direction != MV_DOWN &&
14463       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14464     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14465 #else
14466   if (game.gravity && is_player && !player->is_auto_moving &&
14467       canFallDown(player) && move_direction != MV_DOWN &&
14468       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14469     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14470 #endif
14471
14472   if (player_can_move &&
14473       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14474   {
14475     int sound_element = SND_ELEMENT(element);
14476     int sound_action = ACTION_WALKING;
14477
14478     if (IS_RND_GATE(element))
14479     {
14480       if (!player->key[RND_GATE_NR(element)])
14481         return MP_NO_ACTION;
14482     }
14483     else if (IS_RND_GATE_GRAY(element))
14484     {
14485       if (!player->key[RND_GATE_GRAY_NR(element)])
14486         return MP_NO_ACTION;
14487     }
14488     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14489     {
14490       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14491         return MP_NO_ACTION;
14492     }
14493     else if (element == EL_EXIT_OPEN ||
14494              element == EL_EM_EXIT_OPEN ||
14495              element == EL_STEEL_EXIT_OPEN ||
14496              element == EL_EM_STEEL_EXIT_OPEN ||
14497              element == EL_SP_EXIT_OPEN ||
14498              element == EL_SP_EXIT_OPENING)
14499     {
14500       sound_action = ACTION_PASSING;    /* player is passing exit */
14501     }
14502     else if (element == EL_EMPTY)
14503     {
14504       sound_action = ACTION_MOVING;             /* nothing to walk on */
14505     }
14506
14507     /* play sound from background or player, whatever is available */
14508     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14509       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14510     else
14511       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14512   }
14513   else if (player_can_move &&
14514            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14515   {
14516     if (!ACCESS_FROM(element, opposite_direction))
14517       return MP_NO_ACTION;      /* field not accessible from this direction */
14518
14519     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14520       return MP_NO_ACTION;
14521
14522     if (IS_EM_GATE(element))
14523     {
14524       if (!player->key[EM_GATE_NR(element)])
14525         return MP_NO_ACTION;
14526     }
14527     else if (IS_EM_GATE_GRAY(element))
14528     {
14529       if (!player->key[EM_GATE_GRAY_NR(element)])
14530         return MP_NO_ACTION;
14531     }
14532     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14533     {
14534       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14535         return MP_NO_ACTION;
14536     }
14537     else if (IS_EMC_GATE(element))
14538     {
14539       if (!player->key[EMC_GATE_NR(element)])
14540         return MP_NO_ACTION;
14541     }
14542     else if (IS_EMC_GATE_GRAY(element))
14543     {
14544       if (!player->key[EMC_GATE_GRAY_NR(element)])
14545         return MP_NO_ACTION;
14546     }
14547     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14548     {
14549       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14550         return MP_NO_ACTION;
14551     }
14552     else if (element == EL_DC_GATE_WHITE ||
14553              element == EL_DC_GATE_WHITE_GRAY ||
14554              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14555     {
14556       if (player->num_white_keys == 0)
14557         return MP_NO_ACTION;
14558
14559       player->num_white_keys--;
14560     }
14561     else if (IS_SP_PORT(element))
14562     {
14563       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14564           element == EL_SP_GRAVITY_PORT_RIGHT ||
14565           element == EL_SP_GRAVITY_PORT_UP ||
14566           element == EL_SP_GRAVITY_PORT_DOWN)
14567 #if USE_PLAYER_GRAVITY
14568         player->gravity = !player->gravity;
14569 #else
14570         game.gravity = !game.gravity;
14571 #endif
14572       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14573                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14574                element == EL_SP_GRAVITY_ON_PORT_UP ||
14575                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14576 #if USE_PLAYER_GRAVITY
14577         player->gravity = TRUE;
14578 #else
14579         game.gravity = TRUE;
14580 #endif
14581       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14582                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14583                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14584                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14585 #if USE_PLAYER_GRAVITY
14586         player->gravity = FALSE;
14587 #else
14588         game.gravity = FALSE;
14589 #endif
14590     }
14591
14592     /* automatically move to the next field with double speed */
14593     player->programmed_action = move_direction;
14594
14595     if (player->move_delay_reset_counter == 0)
14596     {
14597       player->move_delay_reset_counter = 2;     /* two double speed steps */
14598
14599       DOUBLE_PLAYER_SPEED(player);
14600     }
14601
14602     PlayLevelSoundAction(x, y, ACTION_PASSING);
14603   }
14604   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14605   {
14606     RemoveField(x, y);
14607
14608     if (mode != DF_SNAP)
14609     {
14610       GfxElement[x][y] = GFX_ELEMENT(element);
14611       player->is_digging = TRUE;
14612     }
14613
14614     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14615
14616     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14617                                         player->index_bit, dig_side);
14618
14619     if (mode == DF_SNAP)
14620     {
14621 #if USE_NEW_SNAP_DELAY
14622       if (level.block_snap_field)
14623         setFieldForSnapping(x, y, element, move_direction);
14624       else
14625         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14626 #else
14627       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14628 #endif
14629
14630       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14631                                           player->index_bit, dig_side);
14632     }
14633   }
14634   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14635   {
14636     RemoveField(x, y);
14637
14638     if (is_player && mode != DF_SNAP)
14639     {
14640       GfxElement[x][y] = element;
14641       player->is_collecting = TRUE;
14642     }
14643
14644     if (element == EL_SPEED_PILL)
14645     {
14646       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14647     }
14648     else if (element == EL_EXTRA_TIME && level.time > 0)
14649     {
14650       TimeLeft += level.extra_time;
14651
14652 #if 1
14653       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14654
14655       DisplayGameControlValues();
14656 #else
14657       DrawGameValue_Time(TimeLeft);
14658 #endif
14659     }
14660     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14661     {
14662       player->shield_normal_time_left += level.shield_normal_time;
14663       if (element == EL_SHIELD_DEADLY)
14664         player->shield_deadly_time_left += level.shield_deadly_time;
14665     }
14666     else if (element == EL_DYNAMITE ||
14667              element == EL_EM_DYNAMITE ||
14668              element == EL_SP_DISK_RED)
14669     {
14670       if (player->inventory_size < MAX_INVENTORY_SIZE)
14671         player->inventory_element[player->inventory_size++] = element;
14672
14673       DrawGameDoorValues();
14674     }
14675     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14676     {
14677       player->dynabomb_count++;
14678       player->dynabombs_left++;
14679     }
14680     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14681     {
14682       player->dynabomb_size++;
14683     }
14684     else if (element == EL_DYNABOMB_INCREASE_POWER)
14685     {
14686       player->dynabomb_xl = TRUE;
14687     }
14688     else if (IS_KEY(element))
14689     {
14690       player->key[KEY_NR(element)] = TRUE;
14691
14692       DrawGameDoorValues();
14693     }
14694     else if (element == EL_DC_KEY_WHITE)
14695     {
14696       player->num_white_keys++;
14697
14698       /* display white keys? */
14699       /* DrawGameDoorValues(); */
14700     }
14701     else if (IS_ENVELOPE(element))
14702     {
14703       player->show_envelope = element;
14704     }
14705     else if (element == EL_EMC_LENSES)
14706     {
14707       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14708
14709       RedrawAllInvisibleElementsForLenses();
14710     }
14711     else if (element == EL_EMC_MAGNIFIER)
14712     {
14713       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14714
14715       RedrawAllInvisibleElementsForMagnifier();
14716     }
14717     else if (IS_DROPPABLE(element) ||
14718              IS_THROWABLE(element))     /* can be collected and dropped */
14719     {
14720       int i;
14721
14722       if (collect_count == 0)
14723         player->inventory_infinite_element = element;
14724       else
14725         for (i = 0; i < collect_count; i++)
14726           if (player->inventory_size < MAX_INVENTORY_SIZE)
14727             player->inventory_element[player->inventory_size++] = element;
14728
14729       DrawGameDoorValues();
14730     }
14731     else if (collect_count > 0)
14732     {
14733       local_player->gems_still_needed -= collect_count;
14734       if (local_player->gems_still_needed < 0)
14735         local_player->gems_still_needed = 0;
14736
14737 #if 1
14738       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14739
14740       DisplayGameControlValues();
14741 #else
14742       DrawGameValue_Emeralds(local_player->gems_still_needed);
14743 #endif
14744     }
14745
14746     RaiseScoreElement(element);
14747     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14748
14749     if (is_player)
14750       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14751                                           player->index_bit, dig_side);
14752
14753     if (mode == DF_SNAP)
14754     {
14755 #if USE_NEW_SNAP_DELAY
14756       if (level.block_snap_field)
14757         setFieldForSnapping(x, y, element, move_direction);
14758       else
14759         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14760 #else
14761       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14762 #endif
14763
14764       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14765                                           player->index_bit, dig_side);
14766     }
14767   }
14768   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14769   {
14770     if (mode == DF_SNAP && element != EL_BD_ROCK)
14771       return MP_NO_ACTION;
14772
14773     if (CAN_FALL(element) && dy)
14774       return MP_NO_ACTION;
14775
14776     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14777         !(element == EL_SPRING && level.use_spring_bug))
14778       return MP_NO_ACTION;
14779
14780     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14781         ((move_direction & MV_VERTICAL &&
14782           ((element_info[element].move_pattern & MV_LEFT &&
14783             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14784            (element_info[element].move_pattern & MV_RIGHT &&
14785             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14786          (move_direction & MV_HORIZONTAL &&
14787           ((element_info[element].move_pattern & MV_UP &&
14788             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14789            (element_info[element].move_pattern & MV_DOWN &&
14790             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14791       return MP_NO_ACTION;
14792
14793     /* do not push elements already moving away faster than player */
14794     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14795         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14796       return MP_NO_ACTION;
14797
14798     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14799     {
14800       if (player->push_delay_value == -1 || !player_was_pushing)
14801         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14802     }
14803     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14804     {
14805       if (player->push_delay_value == -1)
14806         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14807     }
14808     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14809     {
14810       if (!player->is_pushing)
14811         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14812     }
14813
14814     player->is_pushing = TRUE;
14815     player->is_active = TRUE;
14816
14817     if (!(IN_LEV_FIELD(nextx, nexty) &&
14818           (IS_FREE(nextx, nexty) ||
14819            (IS_SB_ELEMENT(element) &&
14820             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14821            (IS_CUSTOM_ELEMENT(element) &&
14822             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14823       return MP_NO_ACTION;
14824
14825     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14826       return MP_NO_ACTION;
14827
14828     if (player->push_delay == -1)       /* new pushing; restart delay */
14829       player->push_delay = 0;
14830
14831     if (player->push_delay < player->push_delay_value &&
14832         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14833         element != EL_SPRING && element != EL_BALLOON)
14834     {
14835       /* make sure that there is no move delay before next try to push */
14836       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14837         player->move_delay = 0;
14838
14839       return MP_NO_ACTION;
14840     }
14841
14842     if (IS_CUSTOM_ELEMENT(element) &&
14843         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14844     {
14845       if (!DigFieldByCE(nextx, nexty, element))
14846         return MP_NO_ACTION;
14847     }
14848
14849     if (IS_SB_ELEMENT(element))
14850     {
14851       if (element == EL_SOKOBAN_FIELD_FULL)
14852       {
14853         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14854         local_player->sokobanfields_still_needed++;
14855       }
14856
14857       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14858       {
14859         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14860         local_player->sokobanfields_still_needed--;
14861       }
14862
14863       Feld[x][y] = EL_SOKOBAN_OBJECT;
14864
14865       if (Back[x][y] == Back[nextx][nexty])
14866         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14867       else if (Back[x][y] != 0)
14868         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14869                                     ACTION_EMPTYING);
14870       else
14871         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14872                                     ACTION_FILLING);
14873
14874       if (local_player->sokobanfields_still_needed == 0 &&
14875           game.emulation == EMU_SOKOBAN)
14876       {
14877         PlayerWins(player);
14878
14879         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14880       }
14881     }
14882     else
14883       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14884
14885     InitMovingField(x, y, move_direction);
14886     GfxAction[x][y] = ACTION_PUSHING;
14887
14888     if (mode == DF_SNAP)
14889       ContinueMoving(x, y);
14890     else
14891       MovPos[x][y] = (dx != 0 ? dx : dy);
14892
14893     Pushed[x][y] = TRUE;
14894     Pushed[nextx][nexty] = TRUE;
14895
14896     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14897       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14898     else
14899       player->push_delay_value = -1;    /* get new value later */
14900
14901     /* check for element change _after_ element has been pushed */
14902     if (game.use_change_when_pushing_bug)
14903     {
14904       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14905                                  player->index_bit, dig_side);
14906       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14907                                           player->index_bit, dig_side);
14908     }
14909   }
14910   else if (IS_SWITCHABLE(element))
14911   {
14912     if (PLAYER_SWITCHING(player, x, y))
14913     {
14914       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14915                                           player->index_bit, dig_side);
14916
14917       return MP_ACTION;
14918     }
14919
14920     player->is_switching = TRUE;
14921     player->switch_x = x;
14922     player->switch_y = y;
14923
14924     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14925
14926     if (element == EL_ROBOT_WHEEL)
14927     {
14928       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14929       ZX = x;
14930       ZY = y;
14931
14932       game.robot_wheel_active = TRUE;
14933
14934       TEST_DrawLevelField(x, y);
14935     }
14936     else if (element == EL_SP_TERMINAL)
14937     {
14938       int xx, yy;
14939
14940       SCAN_PLAYFIELD(xx, yy)
14941       {
14942         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14943           Bang(xx, yy);
14944         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14945           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14946       }
14947     }
14948     else if (IS_BELT_SWITCH(element))
14949     {
14950       ToggleBeltSwitch(x, y);
14951     }
14952     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14953              element == EL_SWITCHGATE_SWITCH_DOWN ||
14954              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14955              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14956     {
14957       ToggleSwitchgateSwitch(x, y);
14958     }
14959     else if (element == EL_LIGHT_SWITCH ||
14960              element == EL_LIGHT_SWITCH_ACTIVE)
14961     {
14962       ToggleLightSwitch(x, y);
14963     }
14964     else if (element == EL_TIMEGATE_SWITCH ||
14965              element == EL_DC_TIMEGATE_SWITCH)
14966     {
14967       ActivateTimegateSwitch(x, y);
14968     }
14969     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14970              element == EL_BALLOON_SWITCH_RIGHT ||
14971              element == EL_BALLOON_SWITCH_UP    ||
14972              element == EL_BALLOON_SWITCH_DOWN  ||
14973              element == EL_BALLOON_SWITCH_NONE  ||
14974              element == EL_BALLOON_SWITCH_ANY)
14975     {
14976       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14977                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14978                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14979                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14980                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14981                              move_direction);
14982     }
14983     else if (element == EL_LAMP)
14984     {
14985       Feld[x][y] = EL_LAMP_ACTIVE;
14986       local_player->lights_still_needed--;
14987
14988       ResetGfxAnimation(x, y);
14989       TEST_DrawLevelField(x, y);
14990     }
14991     else if (element == EL_TIME_ORB_FULL)
14992     {
14993       Feld[x][y] = EL_TIME_ORB_EMPTY;
14994
14995       if (level.time > 0 || level.use_time_orb_bug)
14996       {
14997         TimeLeft += level.time_orb_time;
14998
14999 #if 1
15000         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15001
15002         DisplayGameControlValues();
15003 #else
15004         DrawGameValue_Time(TimeLeft);
15005 #endif
15006       }
15007
15008       ResetGfxAnimation(x, y);
15009       TEST_DrawLevelField(x, y);
15010     }
15011     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15012              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15013     {
15014       int xx, yy;
15015
15016       game.ball_state = !game.ball_state;
15017
15018       SCAN_PLAYFIELD(xx, yy)
15019       {
15020         int e = Feld[xx][yy];
15021
15022         if (game.ball_state)
15023         {
15024           if (e == EL_EMC_MAGIC_BALL)
15025             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15026           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15027             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15028         }
15029         else
15030         {
15031           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15032             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15033           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15034             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15035         }
15036       }
15037     }
15038
15039     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15040                                         player->index_bit, dig_side);
15041
15042     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15043                                         player->index_bit, dig_side);
15044
15045     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15046                                         player->index_bit, dig_side);
15047
15048     return MP_ACTION;
15049   }
15050   else
15051   {
15052     if (!PLAYER_SWITCHING(player, x, y))
15053     {
15054       player->is_switching = TRUE;
15055       player->switch_x = x;
15056       player->switch_y = y;
15057
15058       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15059                                  player->index_bit, dig_side);
15060       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15061                                           player->index_bit, dig_side);
15062
15063       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15064                                  player->index_bit, dig_side);
15065       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15066                                           player->index_bit, dig_side);
15067     }
15068
15069     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15070                                player->index_bit, dig_side);
15071     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15072                                         player->index_bit, dig_side);
15073
15074     return MP_NO_ACTION;
15075   }
15076
15077   player->push_delay = -1;
15078
15079   if (is_player)                /* function can also be called by EL_PENGUIN */
15080   {
15081     if (Feld[x][y] != element)          /* really digged/collected something */
15082     {
15083       player->is_collecting = !player->is_digging;
15084       player->is_active = TRUE;
15085     }
15086   }
15087
15088   return MP_MOVING;
15089 }
15090
15091 static boolean DigFieldByCE(int x, int y, int digging_element)
15092 {
15093   int element = Feld[x][y];
15094
15095   if (!IS_FREE(x, y))
15096   {
15097     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15098                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15099                   ACTION_BREAKING);
15100
15101     /* no element can dig solid indestructible elements */
15102     if (IS_INDESTRUCTIBLE(element) &&
15103         !IS_DIGGABLE(element) &&
15104         !IS_COLLECTIBLE(element))
15105       return FALSE;
15106
15107     if (AmoebaNr[x][y] &&
15108         (element == EL_AMOEBA_FULL ||
15109          element == EL_BD_AMOEBA ||
15110          element == EL_AMOEBA_GROWING))
15111     {
15112       AmoebaCnt[AmoebaNr[x][y]]--;
15113       AmoebaCnt2[AmoebaNr[x][y]]--;
15114     }
15115
15116     if (IS_MOVING(x, y))
15117       RemoveMovingField(x, y);
15118     else
15119     {
15120       RemoveField(x, y);
15121       TEST_DrawLevelField(x, y);
15122     }
15123
15124     /* if digged element was about to explode, prevent the explosion */
15125     ExplodeField[x][y] = EX_TYPE_NONE;
15126
15127     PlayLevelSoundAction(x, y, action);
15128   }
15129
15130   Store[x][y] = EL_EMPTY;
15131
15132 #if 1
15133   /* this makes it possible to leave the removed element again */
15134   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15135     Store[x][y] = element;
15136 #else
15137   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15138   {
15139     int move_leave_element = element_info[digging_element].move_leave_element;
15140
15141     /* this makes it possible to leave the removed element again */
15142     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15143                    element : move_leave_element);
15144   }
15145 #endif
15146
15147   return TRUE;
15148 }
15149
15150 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15151 {
15152   int jx = player->jx, jy = player->jy;
15153   int x = jx + dx, y = jy + dy;
15154   int snap_direction = (dx == -1 ? MV_LEFT  :
15155                         dx == +1 ? MV_RIGHT :
15156                         dy == -1 ? MV_UP    :
15157                         dy == +1 ? MV_DOWN  : MV_NONE);
15158   boolean can_continue_snapping = (level.continuous_snapping &&
15159                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15160
15161   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15162     return FALSE;
15163
15164   if (!player->active || !IN_LEV_FIELD(x, y))
15165     return FALSE;
15166
15167   if (dx && dy)
15168     return FALSE;
15169
15170   if (!dx && !dy)
15171   {
15172     if (player->MovPos == 0)
15173       player->is_pushing = FALSE;
15174
15175     player->is_snapping = FALSE;
15176
15177     if (player->MovPos == 0)
15178     {
15179       player->is_moving = FALSE;
15180       player->is_digging = FALSE;
15181       player->is_collecting = FALSE;
15182     }
15183
15184     return FALSE;
15185   }
15186
15187 #if USE_NEW_CONTINUOUS_SNAPPING
15188   /* prevent snapping with already pressed snap key when not allowed */
15189   if (player->is_snapping && !can_continue_snapping)
15190     return FALSE;
15191 #else
15192   if (player->is_snapping)
15193     return FALSE;
15194 #endif
15195
15196   player->MovDir = snap_direction;
15197
15198   if (player->MovPos == 0)
15199   {
15200     player->is_moving = FALSE;
15201     player->is_digging = FALSE;
15202     player->is_collecting = FALSE;
15203   }
15204
15205   player->is_dropping = FALSE;
15206   player->is_dropping_pressed = FALSE;
15207   player->drop_pressed_delay = 0;
15208
15209   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15210     return FALSE;
15211
15212   player->is_snapping = TRUE;
15213   player->is_active = TRUE;
15214
15215   if (player->MovPos == 0)
15216   {
15217     player->is_moving = FALSE;
15218     player->is_digging = FALSE;
15219     player->is_collecting = FALSE;
15220   }
15221
15222   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15223     TEST_DrawLevelField(player->last_jx, player->last_jy);
15224
15225   TEST_DrawLevelField(x, y);
15226
15227   return TRUE;
15228 }
15229
15230 static boolean DropElement(struct PlayerInfo *player)
15231 {
15232   int old_element, new_element;
15233   int dropx = player->jx, dropy = player->jy;
15234   int drop_direction = player->MovDir;
15235   int drop_side = drop_direction;
15236 #if 1
15237   int drop_element = get_next_dropped_element(player);
15238 #else
15239   int drop_element = (player->inventory_size > 0 ?
15240                       player->inventory_element[player->inventory_size - 1] :
15241                       player->inventory_infinite_element != EL_UNDEFINED ?
15242                       player->inventory_infinite_element :
15243                       player->dynabombs_left > 0 ?
15244                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15245                       EL_UNDEFINED);
15246 #endif
15247
15248   player->is_dropping_pressed = TRUE;
15249
15250   /* do not drop an element on top of another element; when holding drop key
15251      pressed without moving, dropped element must move away before the next
15252      element can be dropped (this is especially important if the next element
15253      is dynamite, which can be placed on background for historical reasons) */
15254   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15255     return MP_ACTION;
15256
15257   if (IS_THROWABLE(drop_element))
15258   {
15259     dropx += GET_DX_FROM_DIR(drop_direction);
15260     dropy += GET_DY_FROM_DIR(drop_direction);
15261
15262     if (!IN_LEV_FIELD(dropx, dropy))
15263       return FALSE;
15264   }
15265
15266   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15267   new_element = drop_element;           /* default: no change when dropping */
15268
15269   /* check if player is active, not moving and ready to drop */
15270   if (!player->active || player->MovPos || player->drop_delay > 0)
15271     return FALSE;
15272
15273   /* check if player has anything that can be dropped */
15274   if (new_element == EL_UNDEFINED)
15275     return FALSE;
15276
15277   /* check if drop key was pressed long enough for EM style dynamite */
15278   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15279     return FALSE;
15280
15281   /* check if anything can be dropped at the current position */
15282   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15283     return FALSE;
15284
15285   /* collected custom elements can only be dropped on empty fields */
15286   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15287     return FALSE;
15288
15289   if (old_element != EL_EMPTY)
15290     Back[dropx][dropy] = old_element;   /* store old element on this field */
15291
15292   ResetGfxAnimation(dropx, dropy);
15293   ResetRandomAnimationValue(dropx, dropy);
15294
15295   if (player->inventory_size > 0 ||
15296       player->inventory_infinite_element != EL_UNDEFINED)
15297   {
15298     if (player->inventory_size > 0)
15299     {
15300       player->inventory_size--;
15301
15302       DrawGameDoorValues();
15303
15304       if (new_element == EL_DYNAMITE)
15305         new_element = EL_DYNAMITE_ACTIVE;
15306       else if (new_element == EL_EM_DYNAMITE)
15307         new_element = EL_EM_DYNAMITE_ACTIVE;
15308       else if (new_element == EL_SP_DISK_RED)
15309         new_element = EL_SP_DISK_RED_ACTIVE;
15310     }
15311
15312     Feld[dropx][dropy] = new_element;
15313
15314     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15315       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15316                           el2img(Feld[dropx][dropy]), 0);
15317
15318     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15319
15320     /* needed if previous element just changed to "empty" in the last frame */
15321     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15322
15323     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15324                                player->index_bit, drop_side);
15325     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15326                                         CE_PLAYER_DROPS_X,
15327                                         player->index_bit, drop_side);
15328
15329     TestIfElementTouchesCustomElement(dropx, dropy);
15330   }
15331   else          /* player is dropping a dyna bomb */
15332   {
15333     player->dynabombs_left--;
15334
15335     Feld[dropx][dropy] = new_element;
15336
15337     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15338       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15339                           el2img(Feld[dropx][dropy]), 0);
15340
15341     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15342   }
15343
15344   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15345     InitField_WithBug1(dropx, dropy, FALSE);
15346
15347   new_element = Feld[dropx][dropy];     /* element might have changed */
15348
15349   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15350       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15351   {
15352     int move_direction, nextx, nexty;
15353
15354     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15355       MovDir[dropx][dropy] = drop_direction;
15356
15357     move_direction = MovDir[dropx][dropy];
15358     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15359     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15360
15361     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15362
15363 #if USE_FIX_IMPACT_COLLISION
15364     /* do not cause impact style collision by dropping elements that can fall */
15365     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15366 #else
15367     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15368 #endif
15369   }
15370
15371   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15372   player->is_dropping = TRUE;
15373
15374   player->drop_pressed_delay = 0;
15375   player->is_dropping_pressed = FALSE;
15376
15377   player->drop_x = dropx;
15378   player->drop_y = dropy;
15379
15380   return TRUE;
15381 }
15382
15383 /* ------------------------------------------------------------------------- */
15384 /* game sound playing functions                                              */
15385 /* ------------------------------------------------------------------------- */
15386
15387 static int *loop_sound_frame = NULL;
15388 static int *loop_sound_volume = NULL;
15389
15390 void InitPlayLevelSound()
15391 {
15392   int num_sounds = getSoundListSize();
15393
15394   checked_free(loop_sound_frame);
15395   checked_free(loop_sound_volume);
15396
15397   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15398   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15399 }
15400
15401 static void PlayLevelSound(int x, int y, int nr)
15402 {
15403   int sx = SCREENX(x), sy = SCREENY(y);
15404   int volume, stereo_position;
15405   int max_distance = 8;
15406   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15407
15408   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15409       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15410     return;
15411
15412   if (!IN_LEV_FIELD(x, y) ||
15413       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15414       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15415     return;
15416
15417   volume = SOUND_MAX_VOLUME;
15418
15419   if (!IN_SCR_FIELD(sx, sy))
15420   {
15421     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15422     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15423
15424     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15425   }
15426
15427   stereo_position = (SOUND_MAX_LEFT +
15428                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15429                      (SCR_FIELDX + 2 * max_distance));
15430
15431   if (IS_LOOP_SOUND(nr))
15432   {
15433     /* This assures that quieter loop sounds do not overwrite louder ones,
15434        while restarting sound volume comparison with each new game frame. */
15435
15436     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15437       return;
15438
15439     loop_sound_volume[nr] = volume;
15440     loop_sound_frame[nr] = FrameCounter;
15441   }
15442
15443   PlaySoundExt(nr, volume, stereo_position, type);
15444 }
15445
15446 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15447 {
15448   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15449                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15450                  y < LEVELY(BY1) ? LEVELY(BY1) :
15451                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15452                  sound_action);
15453 }
15454
15455 static void PlayLevelSoundAction(int x, int y, int action)
15456 {
15457   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15458 }
15459
15460 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15461 {
15462   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15463
15464   if (sound_effect != SND_UNDEFINED)
15465     PlayLevelSound(x, y, sound_effect);
15466 }
15467
15468 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15469                                               int action)
15470 {
15471   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15472
15473   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15474     PlayLevelSound(x, y, sound_effect);
15475 }
15476
15477 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15478 {
15479   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15480
15481   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15482     PlayLevelSound(x, y, sound_effect);
15483 }
15484
15485 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15486 {
15487   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15488
15489   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15490     StopSound(sound_effect);
15491 }
15492
15493 static void PlayLevelMusic()
15494 {
15495   if (levelset.music[level_nr] != MUS_UNDEFINED)
15496     PlayMusic(levelset.music[level_nr]);        /* from config file */
15497   else
15498     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15499 }
15500
15501 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15502 {
15503   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15504   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15505   int x = xx - 1 - offset;
15506   int y = yy - 1 - offset;
15507
15508   switch (sample)
15509   {
15510     case SAMPLE_blank:
15511       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15512       break;
15513
15514     case SAMPLE_roll:
15515       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15516       break;
15517
15518     case SAMPLE_stone:
15519       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15520       break;
15521
15522     case SAMPLE_nut:
15523       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15524       break;
15525
15526     case SAMPLE_crack:
15527       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15528       break;
15529
15530     case SAMPLE_bug:
15531       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15532       break;
15533
15534     case SAMPLE_tank:
15535       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15536       break;
15537
15538     case SAMPLE_android_clone:
15539       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15540       break;
15541
15542     case SAMPLE_android_move:
15543       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15544       break;
15545
15546     case SAMPLE_spring:
15547       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15548       break;
15549
15550     case SAMPLE_slurp:
15551       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15552       break;
15553
15554     case SAMPLE_eater:
15555       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15556       break;
15557
15558     case SAMPLE_eater_eat:
15559       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15560       break;
15561
15562     case SAMPLE_alien:
15563       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15564       break;
15565
15566     case SAMPLE_collect:
15567       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15568       break;
15569
15570     case SAMPLE_diamond:
15571       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15572       break;
15573
15574     case SAMPLE_squash:
15575       /* !!! CHECK THIS !!! */
15576 #if 1
15577       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15578 #else
15579       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15580 #endif
15581       break;
15582
15583     case SAMPLE_wonderfall:
15584       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15585       break;
15586
15587     case SAMPLE_drip:
15588       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15589       break;
15590
15591     case SAMPLE_push:
15592       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15593       break;
15594
15595     case SAMPLE_dirt:
15596       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15597       break;
15598
15599     case SAMPLE_acid:
15600       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15601       break;
15602
15603     case SAMPLE_ball:
15604       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15605       break;
15606
15607     case SAMPLE_grow:
15608       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15609       break;
15610
15611     case SAMPLE_wonder:
15612       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15613       break;
15614
15615     case SAMPLE_door:
15616       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15617       break;
15618
15619     case SAMPLE_exit_open:
15620       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15621       break;
15622
15623     case SAMPLE_exit_leave:
15624       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15625       break;
15626
15627     case SAMPLE_dynamite:
15628       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15629       break;
15630
15631     case SAMPLE_tick:
15632       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15633       break;
15634
15635     case SAMPLE_press:
15636       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15637       break;
15638
15639     case SAMPLE_wheel:
15640       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15641       break;
15642
15643     case SAMPLE_boom:
15644       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15645       break;
15646
15647     case SAMPLE_die:
15648       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15649       break;
15650
15651     case SAMPLE_time:
15652       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15653       break;
15654
15655     default:
15656       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15657       break;
15658   }
15659 }
15660
15661 #if 0
15662 void ChangeTime(int value)
15663 {
15664   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15665
15666   *time += value;
15667
15668   /* EMC game engine uses value from time counter of RND game engine */
15669   level.native_em_level->lev->time = *time;
15670
15671   DrawGameValue_Time(*time);
15672 }
15673
15674 void RaiseScore(int value)
15675 {
15676   /* EMC game engine and RND game engine have separate score counters */
15677   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15678                 &level.native_em_level->lev->score : &local_player->score);
15679
15680   *score += value;
15681
15682   DrawGameValue_Score(*score);
15683 }
15684 #endif
15685
15686 void RaiseScore(int value)
15687 {
15688   local_player->score += value;
15689
15690 #if 1
15691   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15692
15693   DisplayGameControlValues();
15694 #else
15695   DrawGameValue_Score(local_player->score);
15696 #endif
15697 }
15698
15699 void RaiseScoreElement(int element)
15700 {
15701   switch (element)
15702   {
15703     case EL_EMERALD:
15704     case EL_BD_DIAMOND:
15705     case EL_EMERALD_YELLOW:
15706     case EL_EMERALD_RED:
15707     case EL_EMERALD_PURPLE:
15708     case EL_SP_INFOTRON:
15709       RaiseScore(level.score[SC_EMERALD]);
15710       break;
15711     case EL_DIAMOND:
15712       RaiseScore(level.score[SC_DIAMOND]);
15713       break;
15714     case EL_CRYSTAL:
15715       RaiseScore(level.score[SC_CRYSTAL]);
15716       break;
15717     case EL_PEARL:
15718       RaiseScore(level.score[SC_PEARL]);
15719       break;
15720     case EL_BUG:
15721     case EL_BD_BUTTERFLY:
15722     case EL_SP_ELECTRON:
15723       RaiseScore(level.score[SC_BUG]);
15724       break;
15725     case EL_SPACESHIP:
15726     case EL_BD_FIREFLY:
15727     case EL_SP_SNIKSNAK:
15728       RaiseScore(level.score[SC_SPACESHIP]);
15729       break;
15730     case EL_YAMYAM:
15731     case EL_DARK_YAMYAM:
15732       RaiseScore(level.score[SC_YAMYAM]);
15733       break;
15734     case EL_ROBOT:
15735       RaiseScore(level.score[SC_ROBOT]);
15736       break;
15737     case EL_PACMAN:
15738       RaiseScore(level.score[SC_PACMAN]);
15739       break;
15740     case EL_NUT:
15741       RaiseScore(level.score[SC_NUT]);
15742       break;
15743     case EL_DYNAMITE:
15744     case EL_EM_DYNAMITE:
15745     case EL_SP_DISK_RED:
15746     case EL_DYNABOMB_INCREASE_NUMBER:
15747     case EL_DYNABOMB_INCREASE_SIZE:
15748     case EL_DYNABOMB_INCREASE_POWER:
15749       RaiseScore(level.score[SC_DYNAMITE]);
15750       break;
15751     case EL_SHIELD_NORMAL:
15752     case EL_SHIELD_DEADLY:
15753       RaiseScore(level.score[SC_SHIELD]);
15754       break;
15755     case EL_EXTRA_TIME:
15756       RaiseScore(level.extra_time_score);
15757       break;
15758     case EL_KEY_1:
15759     case EL_KEY_2:
15760     case EL_KEY_3:
15761     case EL_KEY_4:
15762     case EL_EM_KEY_1:
15763     case EL_EM_KEY_2:
15764     case EL_EM_KEY_3:
15765     case EL_EM_KEY_4:
15766     case EL_EMC_KEY_5:
15767     case EL_EMC_KEY_6:
15768     case EL_EMC_KEY_7:
15769     case EL_EMC_KEY_8:
15770     case EL_DC_KEY_WHITE:
15771       RaiseScore(level.score[SC_KEY]);
15772       break;
15773     default:
15774       RaiseScore(element_info[element].collect_score);
15775       break;
15776   }
15777 }
15778
15779 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15780 {
15781   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15782   {
15783 #if defined(NETWORK_AVALIABLE)
15784     if (options.network)
15785       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15786     else
15787 #endif
15788     {
15789       if (quick_quit)
15790       {
15791 #if 1
15792
15793 #if 1
15794         FadeSkipNextFadeIn();
15795 #else
15796         fading = fading_none;
15797 #endif
15798
15799 #else
15800         OpenDoor(DOOR_CLOSE_1);
15801 #endif
15802
15803         game_status = GAME_MODE_MAIN;
15804
15805 #if 1
15806         DrawAndFadeInMainMenu(REDRAW_FIELD);
15807 #else
15808         DrawMainMenu();
15809 #endif
15810       }
15811       else
15812       {
15813 #if 0
15814         FadeOut(REDRAW_FIELD);
15815 #endif
15816
15817         game_status = GAME_MODE_MAIN;
15818
15819         DrawAndFadeInMainMenu(REDRAW_FIELD);
15820       }
15821     }
15822   }
15823   else          /* continue playing the game */
15824   {
15825     if (tape.playing && tape.deactivate_display)
15826       TapeDeactivateDisplayOff(TRUE);
15827
15828     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15829
15830     if (tape.playing && tape.deactivate_display)
15831       TapeDeactivateDisplayOn();
15832   }
15833 }
15834
15835 void RequestQuitGame(boolean ask_if_really_quit)
15836 {
15837   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15838   boolean skip_request = AllPlayersGone || quick_quit;
15839
15840   RequestQuitGameExt(skip_request, quick_quit,
15841                      "Do you really want to quit the game ?");
15842 }
15843
15844
15845 /* ------------------------------------------------------------------------- */
15846 /* random generator functions                                                */
15847 /* ------------------------------------------------------------------------- */
15848
15849 unsigned int InitEngineRandom_RND(long seed)
15850 {
15851   game.num_random_calls = 0;
15852
15853 #if 0
15854   unsigned int rnd_seed = InitEngineRandom(seed);
15855
15856   printf("::: START RND: %d\n", rnd_seed);
15857
15858   return rnd_seed;
15859 #else
15860
15861   return InitEngineRandom(seed);
15862
15863 #endif
15864
15865 }
15866
15867 unsigned int RND(int max)
15868 {
15869   if (max > 0)
15870   {
15871     game.num_random_calls++;
15872
15873     return GetEngineRandom(max);
15874   }
15875
15876   return 0;
15877 }
15878
15879
15880 /* ------------------------------------------------------------------------- */
15881 /* game engine snapshot handling functions                                   */
15882 /* ------------------------------------------------------------------------- */
15883
15884 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15885
15886 struct EngineSnapshotInfo
15887 {
15888   /* runtime values for custom element collect score */
15889   int collect_score[NUM_CUSTOM_ELEMENTS];
15890
15891   /* runtime values for group element choice position */
15892   int choice_pos[NUM_GROUP_ELEMENTS];
15893
15894   /* runtime values for belt position animations */
15895   int belt_graphic[4 * NUM_BELT_PARTS];
15896   int belt_anim_mode[4 * NUM_BELT_PARTS];
15897 };
15898
15899 struct EngineSnapshotNodeInfo
15900 {
15901   void *buffer_orig;
15902   void *buffer_copy;
15903   int size;
15904 };
15905
15906 static struct EngineSnapshotInfo engine_snapshot_rnd;
15907 static ListNode *engine_snapshot_list = NULL;
15908 static char *snapshot_level_identifier = NULL;
15909 static int snapshot_level_nr = -1;
15910
15911 void FreeEngineSnapshot()
15912 {
15913   while (engine_snapshot_list != NULL)
15914     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15915                        checked_free);
15916
15917   setString(&snapshot_level_identifier, NULL);
15918   snapshot_level_nr = -1;
15919 }
15920
15921 static void SaveEngineSnapshotValues_RND()
15922 {
15923   static int belt_base_active_element[4] =
15924   {
15925     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15926     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15927     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15928     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15929   };
15930   int i, j;
15931
15932   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15933   {
15934     int element = EL_CUSTOM_START + i;
15935
15936     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15937   }
15938
15939   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15940   {
15941     int element = EL_GROUP_START + i;
15942
15943     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15944   }
15945
15946   for (i = 0; i < 4; i++)
15947   {
15948     for (j = 0; j < NUM_BELT_PARTS; j++)
15949     {
15950       int element = belt_base_active_element[i] + j;
15951       int graphic = el2img(element);
15952       int anim_mode = graphic_info[graphic].anim_mode;
15953
15954       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15955       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15956     }
15957   }
15958 }
15959
15960 static void LoadEngineSnapshotValues_RND()
15961 {
15962   unsigned long num_random_calls = game.num_random_calls;
15963   int i, j;
15964
15965   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15966   {
15967     int element = EL_CUSTOM_START + i;
15968
15969     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15970   }
15971
15972   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15973   {
15974     int element = EL_GROUP_START + i;
15975
15976     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15977   }
15978
15979   for (i = 0; i < 4; i++)
15980   {
15981     for (j = 0; j < NUM_BELT_PARTS; j++)
15982     {
15983       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15984       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15985
15986       graphic_info[graphic].anim_mode = anim_mode;
15987     }
15988   }
15989
15990   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15991   {
15992     InitRND(tape.random_seed);
15993     for (i = 0; i < num_random_calls; i++)
15994       RND(1);
15995   }
15996
15997   if (game.num_random_calls != num_random_calls)
15998   {
15999     Error(ERR_INFO, "number of random calls out of sync");
16000     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16001     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16002     Error(ERR_EXIT, "this should not happen -- please debug");
16003   }
16004 }
16005
16006 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16007 {
16008   struct EngineSnapshotNodeInfo *bi =
16009     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16010
16011   bi->buffer_orig = buffer;
16012   bi->buffer_copy = checked_malloc(size);
16013   bi->size = size;
16014
16015   memcpy(bi->buffer_copy, buffer, size);
16016
16017   addNodeToList(&engine_snapshot_list, NULL, bi);
16018 }
16019
16020 void SaveEngineSnapshot()
16021 {
16022   FreeEngineSnapshot();         /* free previous snapshot, if needed */
16023
16024   if (level_editor_test_game)   /* do not save snapshots from editor */
16025     return;
16026
16027   /* copy some special values to a structure better suited for the snapshot */
16028
16029   SaveEngineSnapshotValues_RND();
16030   SaveEngineSnapshotValues_EM();
16031
16032   /* save values stored in special snapshot structure */
16033
16034   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16035   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16036
16037   /* save further RND engine values */
16038
16039   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16040   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16041   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16042
16043   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16044   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16045   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16046   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16047
16048   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16049   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16050   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16051   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16052   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16053
16054   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16055   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16056   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16057
16058   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16059
16060   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16061
16062   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16063   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16064
16065   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16066   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16067   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16068   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16069   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16070   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16071   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16072   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16073   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16074   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16075   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16076   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16077   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16078   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16079   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16080   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16081   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16082   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16083
16084   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16085   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16086
16087   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16088   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16089   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16090
16091   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16092   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16093
16094   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16095   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16096   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16097   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16098   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16099
16100   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16101   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16102
16103   /* save level identification information */
16104
16105   setString(&snapshot_level_identifier, leveldir_current->identifier);
16106   snapshot_level_nr = level_nr;
16107
16108 #if 0
16109   ListNode *node = engine_snapshot_list;
16110   int num_bytes = 0;
16111
16112   while (node != NULL)
16113   {
16114     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16115
16116     node = node->next;
16117   }
16118
16119   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16120 #endif
16121 }
16122
16123 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16124 {
16125   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16126 }
16127
16128 void LoadEngineSnapshot()
16129 {
16130   ListNode *node = engine_snapshot_list;
16131
16132   if (engine_snapshot_list == NULL)
16133     return;
16134
16135   while (node != NULL)
16136   {
16137     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16138
16139     node = node->next;
16140   }
16141
16142   /* restore special values from snapshot structure */
16143
16144   LoadEngineSnapshotValues_RND();
16145   LoadEngineSnapshotValues_EM();
16146 }
16147
16148 boolean CheckEngineSnapshot()
16149 {
16150   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16151           snapshot_level_nr == level_nr);
16152 }
16153
16154
16155 /* ---------- new game button stuff ---------------------------------------- */
16156
16157 /* graphic position values for game buttons */
16158 #define GAME_BUTTON_XSIZE       30
16159 #define GAME_BUTTON_YSIZE       30
16160 #define GAME_BUTTON_XPOS        5
16161 #define GAME_BUTTON_YPOS        215
16162 #define SOUND_BUTTON_XPOS       5
16163 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16164
16165 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16166 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16167 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16168 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16169 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16170 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16171
16172 static struct
16173 {
16174   int *x, *y;
16175   int gd_x, gd_y;
16176   int gadget_id;
16177   char *infotext;
16178 } gamebutton_info[NUM_GAME_BUTTONS] =
16179 {
16180 #if 1
16181   {
16182     &game.button.stop.x,        &game.button.stop.y,
16183     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16184     GAME_CTRL_ID_STOP,
16185     "stop game"
16186   },
16187   {
16188     &game.button.pause.x,       &game.button.pause.y,
16189     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16190     GAME_CTRL_ID_PAUSE,
16191     "pause game"
16192   },
16193   {
16194     &game.button.play.x,        &game.button.play.y,
16195     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16196     GAME_CTRL_ID_PLAY,
16197     "play game"
16198   },
16199   {
16200     &game.button.sound_music.x, &game.button.sound_music.y,
16201     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16202     SOUND_CTRL_ID_MUSIC,
16203     "background music on/off"
16204   },
16205   {
16206     &game.button.sound_loops.x, &game.button.sound_loops.y,
16207     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16208     SOUND_CTRL_ID_LOOPS,
16209     "sound loops on/off"
16210   },
16211   {
16212     &game.button.sound_simple.x,&game.button.sound_simple.y,
16213     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16214     SOUND_CTRL_ID_SIMPLE,
16215     "normal sounds on/off"
16216   }
16217 #else
16218   {
16219     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16220     GAME_CTRL_ID_STOP,
16221     "stop game"
16222   },
16223   {
16224     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16225     GAME_CTRL_ID_PAUSE,
16226     "pause game"
16227   },
16228   {
16229     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16230     GAME_CTRL_ID_PLAY,
16231     "play game"
16232   },
16233   {
16234     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16235     SOUND_CTRL_ID_MUSIC,
16236     "background music on/off"
16237   },
16238   {
16239     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16240     SOUND_CTRL_ID_LOOPS,
16241     "sound loops on/off"
16242   },
16243   {
16244     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16245     SOUND_CTRL_ID_SIMPLE,
16246     "normal sounds on/off"
16247   }
16248 #endif
16249 };
16250
16251 void CreateGameButtons()
16252 {
16253   int i;
16254
16255   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16256   {
16257     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16258     struct GadgetInfo *gi;
16259     int button_type;
16260     boolean checked;
16261     unsigned long event_mask;
16262     int x, y;
16263     int gd_xoffset, gd_yoffset;
16264     int gd_x1, gd_x2, gd_y1, gd_y2;
16265     int id = i;
16266
16267     x = DX + *gamebutton_info[i].x;
16268     y = DY + *gamebutton_info[i].y;
16269     gd_xoffset = gamebutton_info[i].gd_x;
16270     gd_yoffset = gamebutton_info[i].gd_y;
16271     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16272     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16273
16274     if (id == GAME_CTRL_ID_STOP ||
16275         id == GAME_CTRL_ID_PAUSE ||
16276         id == GAME_CTRL_ID_PLAY)
16277     {
16278       button_type = GD_TYPE_NORMAL_BUTTON;
16279       checked = FALSE;
16280       event_mask = GD_EVENT_RELEASED;
16281       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16282       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16283     }
16284     else
16285     {
16286       button_type = GD_TYPE_CHECK_BUTTON;
16287       checked =
16288         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16289          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16290          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16291       event_mask = GD_EVENT_PRESSED;
16292       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16293       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16294     }
16295
16296     gi = CreateGadget(GDI_CUSTOM_ID, id,
16297                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16298 #if 1
16299                       GDI_X, x,
16300                       GDI_Y, y,
16301 #else
16302                       GDI_X, DX + gd_xoffset,
16303                       GDI_Y, DY + gd_yoffset,
16304 #endif
16305                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16306                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16307                       GDI_TYPE, button_type,
16308                       GDI_STATE, GD_BUTTON_UNPRESSED,
16309                       GDI_CHECKED, checked,
16310                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16311                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16312                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16313                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16314                       GDI_DIRECT_DRAW, FALSE,
16315                       GDI_EVENT_MASK, event_mask,
16316                       GDI_CALLBACK_ACTION, HandleGameButtons,
16317                       GDI_END);
16318
16319     if (gi == NULL)
16320       Error(ERR_EXIT, "cannot create gadget");
16321
16322     game_gadget[id] = gi;
16323   }
16324 }
16325
16326 void FreeGameButtons()
16327 {
16328   int i;
16329
16330   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16331     FreeGadget(game_gadget[i]);
16332 }
16333
16334 static void MapGameButtons()
16335 {
16336   int i;
16337
16338   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16339     MapGadget(game_gadget[i]);
16340 }
16341
16342 void UnmapGameButtons()
16343 {
16344   int i;
16345
16346   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16347     UnmapGadget(game_gadget[i]);
16348 }
16349
16350 void RedrawGameButtons()
16351 {
16352   int i;
16353
16354   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16355     RedrawGadget(game_gadget[i]);
16356 }
16357
16358 static void HandleGameButtons(struct GadgetInfo *gi)
16359 {
16360   int id = gi->custom_id;
16361
16362   if (game_status != GAME_MODE_PLAYING)
16363     return;
16364
16365   switch (id)
16366   {
16367     case GAME_CTRL_ID_STOP:
16368       if (tape.playing)
16369         TapeStop();
16370       else
16371         RequestQuitGame(TRUE);
16372       break;
16373
16374     case GAME_CTRL_ID_PAUSE:
16375       if (options.network)
16376       {
16377 #if defined(NETWORK_AVALIABLE)
16378         if (tape.pausing)
16379           SendToServer_ContinuePlaying();
16380         else
16381           SendToServer_PausePlaying();
16382 #endif
16383       }
16384       else
16385         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16386       break;
16387
16388     case GAME_CTRL_ID_PLAY:
16389       if (tape.pausing)
16390       {
16391 #if defined(NETWORK_AVALIABLE)
16392         if (options.network)
16393           SendToServer_ContinuePlaying();
16394         else
16395 #endif
16396         {
16397           tape.pausing = FALSE;
16398           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16399         }
16400       }
16401       break;
16402
16403     case SOUND_CTRL_ID_MUSIC:
16404       if (setup.sound_music)
16405       { 
16406         setup.sound_music = FALSE;
16407         FadeMusic();
16408       }
16409       else if (audio.music_available)
16410       { 
16411         setup.sound = setup.sound_music = TRUE;
16412
16413         SetAudioMode(setup.sound);
16414
16415         PlayLevelMusic();
16416       }
16417       break;
16418
16419     case SOUND_CTRL_ID_LOOPS:
16420       if (setup.sound_loops)
16421         setup.sound_loops = FALSE;
16422       else if (audio.loops_available)
16423       {
16424         setup.sound = setup.sound_loops = TRUE;
16425         SetAudioMode(setup.sound);
16426       }
16427       break;
16428
16429     case SOUND_CTRL_ID_SIMPLE:
16430       if (setup.sound_simple)
16431         setup.sound_simple = FALSE;
16432       else if (audio.sound_available)
16433       {
16434         setup.sound = setup.sound_simple = TRUE;
16435         SetAudioMode(setup.sound);
16436       }
16437       break;
16438
16439     default:
16440       break;
16441   }
16442 }