rnd-20081007-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_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
69
70 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
71
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y)                               \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
78         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y)                           \
80         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
81 #else
82 #define TEST_DrawLevelField(x, y)                               \
83              DrawLevelField(x, y)
84 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
85              DrawLevelFieldCrumbledSand(x, y)
86 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
87              DrawLevelFieldCrumbledSandNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y)                           \
89              DrawTwinkleOnField(x, y)
90 #endif
91
92
93 /* for DigField() */
94 #define DF_NO_PUSH              0
95 #define DF_DIG                  1
96 #define DF_SNAP                 2
97
98 /* for MovePlayer() */
99 #define MP_NO_ACTION            0
100 #define MP_MOVING               1
101 #define MP_ACTION               2
102 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
103
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT             0
106 #define SCROLL_GO_ON            1
107
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START          0
110 #define EX_TYPE_NONE            0
111 #define EX_TYPE_NORMAL          (1 << 0)
112 #define EX_TYPE_CENTER          (1 << 1)
113 #define EX_TYPE_BORDER          (1 << 2)
114 #define EX_TYPE_CROSS           (1 << 3)
115 #define EX_TYPE_DYNA            (1 << 4)
116 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
117
118 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
122
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
139 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
140
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1               (DX + XX_LEVEL1)
143 #define DX_LEVEL2               (DX + XX_LEVEL2)
144 #define DX_LEVEL                (DX + XX_LEVEL)
145 #define DY_LEVEL                (DY + YY_LEVEL)
146 #define DX_EMERALDS             (DX + XX_EMERALDS)
147 #define DY_EMERALDS             (DY + YY_EMERALDS)
148 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
150 #define DX_KEYS                 (DX + XX_KEYS)
151 #define DY_KEYS                 (DY + YY_KEYS)
152 #define DX_SCORE                (DX + XX_SCORE)
153 #define DY_SCORE                (DY + YY_SCORE)
154 #define DX_TIME1                (DX + XX_TIME1)
155 #define DX_TIME2                (DX + XX_TIME2)
156 #define DX_TIME                 (DX + XX_TIME)
157 #define DY_TIME                 (DY + YY_TIME)
158
159 #if 1
160 /* game panel display and control definitions */
161
162 #define GAME_PANEL_LEVEL_NUMBER                 0
163 #define GAME_PANEL_GEMS                         1
164 #define GAME_PANEL_INVENTORY_COUNT              2
165 #define GAME_PANEL_INVENTORY_FIRST_1            3
166 #define GAME_PANEL_INVENTORY_FIRST_2            4
167 #define GAME_PANEL_INVENTORY_FIRST_3            5
168 #define GAME_PANEL_INVENTORY_FIRST_4            6
169 #define GAME_PANEL_INVENTORY_FIRST_5            7
170 #define GAME_PANEL_INVENTORY_FIRST_6            8
171 #define GAME_PANEL_INVENTORY_FIRST_7            9
172 #define GAME_PANEL_INVENTORY_FIRST_8            10
173 #define GAME_PANEL_INVENTORY_LAST_1             11
174 #define GAME_PANEL_INVENTORY_LAST_2             12
175 #define GAME_PANEL_INVENTORY_LAST_3             13
176 #define GAME_PANEL_INVENTORY_LAST_4             14
177 #define GAME_PANEL_INVENTORY_LAST_5             15
178 #define GAME_PANEL_INVENTORY_LAST_6             16
179 #define GAME_PANEL_INVENTORY_LAST_7             17
180 #define GAME_PANEL_INVENTORY_LAST_8             18
181 #define GAME_PANEL_KEY_1                        19
182 #define GAME_PANEL_KEY_2                        20
183 #define GAME_PANEL_KEY_3                        21
184 #define GAME_PANEL_KEY_4                        22
185 #define GAME_PANEL_KEY_5                        23
186 #define GAME_PANEL_KEY_6                        24
187 #define GAME_PANEL_KEY_7                        25
188 #define GAME_PANEL_KEY_8                        26
189 #define GAME_PANEL_KEY_WHITE                    27
190 #define GAME_PANEL_KEY_WHITE_COUNT              28
191 #define GAME_PANEL_SCORE                        29
192 #define GAME_PANEL_HIGHSCORE                    30
193 #define GAME_PANEL_TIME                         31
194 #define GAME_PANEL_TIME_HH                      32
195 #define GAME_PANEL_TIME_MM                      33
196 #define GAME_PANEL_TIME_SS                      34
197 #define GAME_PANEL_SHIELD_NORMAL                35
198 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
199 #define GAME_PANEL_SHIELD_DEADLY                37
200 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
201 #define GAME_PANEL_EXIT                         39
202 #define GAME_PANEL_EMC_MAGIC_BALL               40
203 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
204 #define GAME_PANEL_LIGHT_SWITCH                 42
205 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
206 #define GAME_PANEL_TIMEGATE_SWITCH              44
207 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
208 #define GAME_PANEL_SWITCHGATE_SWITCH            46
209 #define GAME_PANEL_EMC_LENSES                   47
210 #define GAME_PANEL_EMC_LENSES_TIME              48
211 #define GAME_PANEL_EMC_MAGNIFIER                49
212 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
213 #define GAME_PANEL_BALLOON_SWITCH               51
214 #define GAME_PANEL_DYNABOMB_NUMBER              52
215 #define GAME_PANEL_DYNABOMB_SIZE                53
216 #define GAME_PANEL_DYNABOMB_POWER               54
217 #define GAME_PANEL_PENGUINS                     55
218 #define GAME_PANEL_SOKOBAN_OBJECTS              56
219 #define GAME_PANEL_SOKOBAN_FIELDS               57
220 #define GAME_PANEL_ROBOT_WHEEL                  58
221 #define GAME_PANEL_CONVEYOR_BELT_1              59
222 #define GAME_PANEL_CONVEYOR_BELT_2              60
223 #define GAME_PANEL_CONVEYOR_BELT_3              61
224 #define GAME_PANEL_CONVEYOR_BELT_4              62
225 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
226 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
227 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
228 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
229 #define GAME_PANEL_MAGIC_WALL                   67
230 #define GAME_PANEL_MAGIC_WALL_TIME              68
231 #define GAME_PANEL_GRAVITY_STATE                69
232 #define GAME_PANEL_GRAPHIC_1                    70
233 #define GAME_PANEL_GRAPHIC_2                    71
234 #define GAME_PANEL_GRAPHIC_3                    72
235 #define GAME_PANEL_GRAPHIC_4                    73
236 #define GAME_PANEL_GRAPHIC_5                    74
237 #define GAME_PANEL_GRAPHIC_6                    75
238 #define GAME_PANEL_GRAPHIC_7                    76
239 #define GAME_PANEL_GRAPHIC_8                    77
240 #define GAME_PANEL_ELEMENT_1                    78
241 #define GAME_PANEL_ELEMENT_2                    79
242 #define GAME_PANEL_ELEMENT_3                    80
243 #define GAME_PANEL_ELEMENT_4                    81
244 #define GAME_PANEL_ELEMENT_5                    82
245 #define GAME_PANEL_ELEMENT_6                    83
246 #define GAME_PANEL_ELEMENT_7                    84
247 #define GAME_PANEL_ELEMENT_8                    85
248 #define GAME_PANEL_ELEMENT_COUNT_1              86
249 #define GAME_PANEL_ELEMENT_COUNT_2              87
250 #define GAME_PANEL_ELEMENT_COUNT_3              88
251 #define GAME_PANEL_ELEMENT_COUNT_4              89
252 #define GAME_PANEL_ELEMENT_COUNT_5              90
253 #define GAME_PANEL_ELEMENT_COUNT_6              91
254 #define GAME_PANEL_ELEMENT_COUNT_7              92
255 #define GAME_PANEL_ELEMENT_COUNT_8              93
256 #define GAME_PANEL_CE_SCORE_1                   94
257 #define GAME_PANEL_CE_SCORE_2                   95
258 #define GAME_PANEL_CE_SCORE_3                   96
259 #define GAME_PANEL_CE_SCORE_4                   97
260 #define GAME_PANEL_CE_SCORE_5                   98
261 #define GAME_PANEL_CE_SCORE_6                   99
262 #define GAME_PANEL_CE_SCORE_7                   100
263 #define GAME_PANEL_CE_SCORE_8                   101
264 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
265 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
266 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
267 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
268 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
269 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
270 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
271 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
272 #define GAME_PANEL_PLAYER_NAME                  110
273 #define GAME_PANEL_LEVEL_NAME                   111
274 #define GAME_PANEL_LEVEL_AUTHOR                 112
275
276 #define NUM_GAME_PANEL_CONTROLS                 113
277
278 struct GamePanelOrderInfo
279 {
280   int nr;
281   int sort_priority;
282 };
283
284 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
285
286 struct GamePanelControlInfo
287 {
288   int nr;
289
290   struct TextPosInfo *pos;
291   int type;
292
293   int value, last_value;
294   int frame, last_frame;
295   int gfx_frame;
296   int gfx_random;
297 };
298
299 static struct GamePanelControlInfo game_panel_controls[] =
300 {
301   {
302     GAME_PANEL_LEVEL_NUMBER,
303     &game.panel.level_number,
304     TYPE_INTEGER,
305   },
306   {
307     GAME_PANEL_GEMS,
308     &game.panel.gems,
309     TYPE_INTEGER,
310   },
311   {
312     GAME_PANEL_INVENTORY_COUNT,
313     &game.panel.inventory_count,
314     TYPE_INTEGER,
315   },
316   {
317     GAME_PANEL_INVENTORY_FIRST_1,
318     &game.panel.inventory_first[0],
319     TYPE_ELEMENT,
320   },
321   {
322     GAME_PANEL_INVENTORY_FIRST_2,
323     &game.panel.inventory_first[1],
324     TYPE_ELEMENT,
325   },
326   {
327     GAME_PANEL_INVENTORY_FIRST_3,
328     &game.panel.inventory_first[2],
329     TYPE_ELEMENT,
330   },
331   {
332     GAME_PANEL_INVENTORY_FIRST_4,
333     &game.panel.inventory_first[3],
334     TYPE_ELEMENT,
335   },
336   {
337     GAME_PANEL_INVENTORY_FIRST_5,
338     &game.panel.inventory_first[4],
339     TYPE_ELEMENT,
340   },
341   {
342     GAME_PANEL_INVENTORY_FIRST_6,
343     &game.panel.inventory_first[5],
344     TYPE_ELEMENT,
345   },
346   {
347     GAME_PANEL_INVENTORY_FIRST_7,
348     &game.panel.inventory_first[6],
349     TYPE_ELEMENT,
350   },
351   {
352     GAME_PANEL_INVENTORY_FIRST_8,
353     &game.panel.inventory_first[7],
354     TYPE_ELEMENT,
355   },
356   {
357     GAME_PANEL_INVENTORY_LAST_1,
358     &game.panel.inventory_last[0],
359     TYPE_ELEMENT,
360   },
361   {
362     GAME_PANEL_INVENTORY_LAST_2,
363     &game.panel.inventory_last[1],
364     TYPE_ELEMENT,
365   },
366   {
367     GAME_PANEL_INVENTORY_LAST_3,
368     &game.panel.inventory_last[2],
369     TYPE_ELEMENT,
370   },
371   {
372     GAME_PANEL_INVENTORY_LAST_4,
373     &game.panel.inventory_last[3],
374     TYPE_ELEMENT,
375   },
376   {
377     GAME_PANEL_INVENTORY_LAST_5,
378     &game.panel.inventory_last[4],
379     TYPE_ELEMENT,
380   },
381   {
382     GAME_PANEL_INVENTORY_LAST_6,
383     &game.panel.inventory_last[5],
384     TYPE_ELEMENT,
385   },
386   {
387     GAME_PANEL_INVENTORY_LAST_7,
388     &game.panel.inventory_last[6],
389     TYPE_ELEMENT,
390   },
391   {
392     GAME_PANEL_INVENTORY_LAST_8,
393     &game.panel.inventory_last[7],
394     TYPE_ELEMENT,
395   },
396   {
397     GAME_PANEL_KEY_1,
398     &game.panel.key[0],
399     TYPE_ELEMENT,
400   },
401   {
402     GAME_PANEL_KEY_2,
403     &game.panel.key[1],
404     TYPE_ELEMENT,
405   },
406   {
407     GAME_PANEL_KEY_3,
408     &game.panel.key[2],
409     TYPE_ELEMENT,
410   },
411   {
412     GAME_PANEL_KEY_4,
413     &game.panel.key[3],
414     TYPE_ELEMENT,
415   },
416   {
417     GAME_PANEL_KEY_5,
418     &game.panel.key[4],
419     TYPE_ELEMENT,
420   },
421   {
422     GAME_PANEL_KEY_6,
423     &game.panel.key[5],
424     TYPE_ELEMENT,
425   },
426   {
427     GAME_PANEL_KEY_7,
428     &game.panel.key[6],
429     TYPE_ELEMENT,
430   },
431   {
432     GAME_PANEL_KEY_8,
433     &game.panel.key[7],
434     TYPE_ELEMENT,
435   },
436   {
437     GAME_PANEL_KEY_WHITE,
438     &game.panel.key_white,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_KEY_WHITE_COUNT,
443     &game.panel.key_white_count,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SCORE,
448     &game.panel.score,
449     TYPE_INTEGER,
450   },
451   {
452     GAME_PANEL_HIGHSCORE,
453     &game.panel.highscore,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_TIME,
458     &game.panel.time,
459     TYPE_INTEGER,
460   },
461   {
462     GAME_PANEL_TIME_HH,
463     &game.panel.time_hh,
464     TYPE_INTEGER,
465   },
466   {
467     GAME_PANEL_TIME_MM,
468     &game.panel.time_mm,
469     TYPE_INTEGER,
470   },
471   {
472     GAME_PANEL_TIME_SS,
473     &game.panel.time_ss,
474     TYPE_INTEGER,
475   },
476   {
477     GAME_PANEL_SHIELD_NORMAL,
478     &game.panel.shield_normal,
479     TYPE_ELEMENT,
480   },
481   {
482     GAME_PANEL_SHIELD_NORMAL_TIME,
483     &game.panel.shield_normal_time,
484     TYPE_INTEGER,
485   },
486   {
487     GAME_PANEL_SHIELD_DEADLY,
488     &game.panel.shield_deadly,
489     TYPE_ELEMENT,
490   },
491   {
492     GAME_PANEL_SHIELD_DEADLY_TIME,
493     &game.panel.shield_deadly_time,
494     TYPE_INTEGER,
495   },
496   {
497     GAME_PANEL_EXIT,
498     &game.panel.exit,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_MAGIC_BALL,
503     &game.panel.emc_magic_ball,
504     TYPE_ELEMENT,
505   },
506   {
507     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
508     &game.panel.emc_magic_ball_switch,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_LIGHT_SWITCH,
513     &game.panel.light_switch,
514     TYPE_ELEMENT,
515   },
516   {
517     GAME_PANEL_LIGHT_SWITCH_TIME,
518     &game.panel.light_switch_time,
519     TYPE_INTEGER,
520   },
521   {
522     GAME_PANEL_TIMEGATE_SWITCH,
523     &game.panel.timegate_switch,
524     TYPE_ELEMENT,
525   },
526   {
527     GAME_PANEL_TIMEGATE_SWITCH_TIME,
528     &game.panel.timegate_switch_time,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_SWITCHGATE_SWITCH,
533     &game.panel.switchgate_switch,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_EMC_LENSES,
538     &game.panel.emc_lenses,
539     TYPE_ELEMENT,
540   },
541   {
542     GAME_PANEL_EMC_LENSES_TIME,
543     &game.panel.emc_lenses_time,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_EMC_MAGNIFIER,
548     &game.panel.emc_magnifier,
549     TYPE_ELEMENT,
550   },
551   {
552     GAME_PANEL_EMC_MAGNIFIER_TIME,
553     &game.panel.emc_magnifier_time,
554     TYPE_INTEGER,
555   },
556   {
557     GAME_PANEL_BALLOON_SWITCH,
558     &game.panel.balloon_switch,
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_DYNABOMB_NUMBER,
563     &game.panel.dynabomb_number,
564     TYPE_INTEGER,
565   },
566   {
567     GAME_PANEL_DYNABOMB_SIZE,
568     &game.panel.dynabomb_size,
569     TYPE_INTEGER,
570   },
571   {
572     GAME_PANEL_DYNABOMB_POWER,
573     &game.panel.dynabomb_power,
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_PENGUINS,
578     &game.panel.penguins,
579     TYPE_INTEGER,
580   },
581   {
582     GAME_PANEL_SOKOBAN_OBJECTS,
583     &game.panel.sokoban_objects,
584     TYPE_INTEGER,
585   },
586   {
587     GAME_PANEL_SOKOBAN_FIELDS,
588     &game.panel.sokoban_fields,
589     TYPE_INTEGER,
590   },
591   {
592     GAME_PANEL_ROBOT_WHEEL,
593     &game.panel.robot_wheel,
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_CONVEYOR_BELT_1,
598     &game.panel.conveyor_belt[0],
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_CONVEYOR_BELT_2,
603     &game.panel.conveyor_belt[1],
604     TYPE_ELEMENT,
605   },
606   {
607     GAME_PANEL_CONVEYOR_BELT_3,
608     &game.panel.conveyor_belt[2],
609     TYPE_ELEMENT,
610   },
611   {
612     GAME_PANEL_CONVEYOR_BELT_4,
613     &game.panel.conveyor_belt[3],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
618     &game.panel.conveyor_belt_switch[0],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
623     &game.panel.conveyor_belt_switch[1],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
628     &game.panel.conveyor_belt_switch[2],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
633     &game.panel.conveyor_belt_switch[3],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_MAGIC_WALL,
638     &game.panel.magic_wall,
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_MAGIC_WALL_TIME,
643     &game.panel.magic_wall_time,
644     TYPE_INTEGER,
645   },
646   {
647     GAME_PANEL_GRAVITY_STATE,
648     &game.panel.gravity_state,
649     TYPE_STRING,
650   },
651   {
652     GAME_PANEL_GRAPHIC_1,
653     &game.panel.graphic[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_GRAPHIC_2,
658     &game.panel.graphic[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_GRAPHIC_3,
663     &game.panel.graphic[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_GRAPHIC_4,
668     &game.panel.graphic[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_GRAPHIC_5,
673     &game.panel.graphic[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_GRAPHIC_6,
678     &game.panel.graphic[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_GRAPHIC_7,
683     &game.panel.graphic[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_GRAPHIC_8,
688     &game.panel.graphic[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_1,
693     &game.panel.element[0],
694     TYPE_ELEMENT,
695   },
696   {
697     GAME_PANEL_ELEMENT_2,
698     &game.panel.element[1],
699     TYPE_ELEMENT,
700   },
701   {
702     GAME_PANEL_ELEMENT_3,
703     &game.panel.element[2],
704     TYPE_ELEMENT,
705   },
706   {
707     GAME_PANEL_ELEMENT_4,
708     &game.panel.element[3],
709     TYPE_ELEMENT,
710   },
711   {
712     GAME_PANEL_ELEMENT_5,
713     &game.panel.element[4],
714     TYPE_ELEMENT,
715   },
716   {
717     GAME_PANEL_ELEMENT_6,
718     &game.panel.element[5],
719     TYPE_ELEMENT,
720   },
721   {
722     GAME_PANEL_ELEMENT_7,
723     &game.panel.element[6],
724     TYPE_ELEMENT,
725   },
726   {
727     GAME_PANEL_ELEMENT_8,
728     &game.panel.element[7],
729     TYPE_ELEMENT,
730   },
731   {
732     GAME_PANEL_ELEMENT_COUNT_1,
733     &game.panel.element_count[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_ELEMENT_COUNT_2,
738     &game.panel.element_count[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_ELEMENT_COUNT_3,
743     &game.panel.element_count[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_ELEMENT_COUNT_4,
748     &game.panel.element_count[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_ELEMENT_COUNT_5,
753     &game.panel.element_count[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_ELEMENT_COUNT_6,
758     &game.panel.element_count[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_ELEMENT_COUNT_7,
763     &game.panel.element_count[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_ELEMENT_COUNT_8,
768     &game.panel.element_count[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1,
773     &game.panel.ce_score[0],
774     TYPE_INTEGER,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2,
778     &game.panel.ce_score[1],
779     TYPE_INTEGER,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3,
783     &game.panel.ce_score[2],
784     TYPE_INTEGER,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4,
788     &game.panel.ce_score[3],
789     TYPE_INTEGER,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5,
793     &game.panel.ce_score[4],
794     TYPE_INTEGER,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6,
798     &game.panel.ce_score[5],
799     TYPE_INTEGER,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7,
803     &game.panel.ce_score[6],
804     TYPE_INTEGER,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8,
808     &game.panel.ce_score[7],
809     TYPE_INTEGER,
810   },
811   {
812     GAME_PANEL_CE_SCORE_1_ELEMENT,
813     &game.panel.ce_score_element[0],
814     TYPE_ELEMENT,
815   },
816   {
817     GAME_PANEL_CE_SCORE_2_ELEMENT,
818     &game.panel.ce_score_element[1],
819     TYPE_ELEMENT,
820   },
821   {
822     GAME_PANEL_CE_SCORE_3_ELEMENT,
823     &game.panel.ce_score_element[2],
824     TYPE_ELEMENT,
825   },
826   {
827     GAME_PANEL_CE_SCORE_4_ELEMENT,
828     &game.panel.ce_score_element[3],
829     TYPE_ELEMENT,
830   },
831   {
832     GAME_PANEL_CE_SCORE_5_ELEMENT,
833     &game.panel.ce_score_element[4],
834     TYPE_ELEMENT,
835   },
836   {
837     GAME_PANEL_CE_SCORE_6_ELEMENT,
838     &game.panel.ce_score_element[5],
839     TYPE_ELEMENT,
840   },
841   {
842     GAME_PANEL_CE_SCORE_7_ELEMENT,
843     &game.panel.ce_score_element[6],
844     TYPE_ELEMENT,
845   },
846   {
847     GAME_PANEL_CE_SCORE_8_ELEMENT,
848     &game.panel.ce_score_element[7],
849     TYPE_ELEMENT,
850   },
851   {
852     GAME_PANEL_PLAYER_NAME,
853     &game.panel.player_name,
854     TYPE_STRING,
855   },
856   {
857     GAME_PANEL_LEVEL_NAME,
858     &game.panel.level_name,
859     TYPE_STRING,
860   },
861   {
862     GAME_PANEL_LEVEL_AUTHOR,
863     &game.panel.level_author,
864     TYPE_STRING,
865   },
866
867   {
868     -1,
869     NULL,
870     -1,
871   }
872 };
873 #endif
874
875
876 /* values for delayed check of falling and moving elements and for collision */
877 #define CHECK_DELAY_MOVING      3
878 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
879 #define CHECK_DELAY_COLLISION   2
880 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
881
882 /* values for initial player move delay (initial delay counter value) */
883 #define INITIAL_MOVE_DELAY_OFF  -1
884 #define INITIAL_MOVE_DELAY_ON   0
885
886 /* values for player movement speed (which is in fact a delay value) */
887 #define MOVE_DELAY_MIN_SPEED    32
888 #define MOVE_DELAY_NORMAL_SPEED 8
889 #define MOVE_DELAY_HIGH_SPEED   4
890 #define MOVE_DELAY_MAX_SPEED    1
891
892 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
893 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
894
895 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
896 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
897
898 /* values for other actions */
899 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
900 #define MOVE_STEPSIZE_MIN       (1)
901 #define MOVE_STEPSIZE_MAX       (TILEX)
902
903 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
904 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
905
906 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
907
908 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
909                                  RND(element_info[e].push_delay_random))
910 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
911                                  RND(element_info[e].drop_delay_random))
912 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
913                                  RND(element_info[e].move_delay_random))
914 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
915                                     (element_info[e].move_delay_random))
916 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
917                                  RND(element_info[e].ce_value_random_initial))
918 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
919 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
920                                  RND((c)->delay_random * (c)->delay_frames))
921 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
922                                  RND((c)->delay_random))
923
924
925 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
926          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
927
928 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
929         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
930          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
931          (be) + (e) - EL_SELF)
932
933 #define GET_PLAYER_FROM_BITS(p)                                         \
934         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
935
936 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
937         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
938          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
939          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
940          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
941          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
942          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
943          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
944          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
945          (e))
946
947 #define CAN_GROW_INTO(e)                                                \
948         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
949
950 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
951                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (CAN_MOVE_INTO_ACID(e) &&       \
957                                          Feld[x][y] == EL_ACID) ||      \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
962                                         (CAN_MOVE_INTO_ACID(e) &&       \
963                                          Feld[x][y] == EL_ACID) ||      \
964                                         (condition)))
965
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
967                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
968                                         (condition) ||                  \
969                                         (CAN_MOVE_INTO_ACID(e) &&       \
970                                          Feld[x][y] == EL_ACID) ||      \
971                                         (DONT_COLLIDE_WITH(e) &&        \
972                                          IS_PLAYER(x, y) &&             \
973                                          !PLAYER_ENEMY_PROTECTED(x, y))))
974
975 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
976         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
977
978 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
980
981 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
983
984 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
985         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
986                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
987
988 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
990
991 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
992         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
993
994 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
995         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
996
997 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
999
1000 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1002
1003 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1005                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1006                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1007                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1008                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1009 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1011
1012 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1013         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1014
1015 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1016         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017
1018 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1019         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1020                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1021
1022 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1023
1024 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1025                 (!IS_PLAYER(x, y) &&                                    \
1026                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1027
1028 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1029         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1030
1031 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1032 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1033
1034 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1035 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1036 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1037 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1038
1039 /* game button identifiers */
1040 #define GAME_CTRL_ID_STOP               0
1041 #define GAME_CTRL_ID_PAUSE              1
1042 #define GAME_CTRL_ID_PLAY               2
1043 #define SOUND_CTRL_ID_MUSIC             3
1044 #define SOUND_CTRL_ID_LOOPS             4
1045 #define SOUND_CTRL_ID_SIMPLE            5
1046
1047 #define NUM_GAME_BUTTONS                6
1048
1049
1050 /* forward declaration for internal use */
1051
1052 static void CreateField(int, int, int);
1053
1054 static void ResetGfxAnimation(int, int);
1055
1056 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1057 static void AdvanceFrameAndPlayerCounters(int);
1058
1059 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1060 static boolean MovePlayer(struct PlayerInfo *, int, int);
1061 static void ScrollPlayer(struct PlayerInfo *, int);
1062 static void ScrollScreen(struct PlayerInfo *, int);
1063
1064 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1065 static boolean DigFieldByCE(int, int, int);
1066 static boolean SnapField(struct PlayerInfo *, int, int);
1067 static boolean DropElement(struct PlayerInfo *);
1068
1069 static void InitBeltMovement(void);
1070 static void CloseAllOpenTimegates(void);
1071 static void CheckGravityMovement(struct PlayerInfo *);
1072 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1073 static void KillPlayerUnlessEnemyProtected(int, int);
1074 static void KillPlayerUnlessExplosionProtected(int, int);
1075
1076 static void TestIfPlayerTouchesCustomElement(int, int);
1077 static void TestIfElementTouchesCustomElement(int, int);
1078 static void TestIfElementHitsCustomElement(int, int, int);
1079 #if 0
1080 static void TestIfElementSmashesCustomElement(int, int, int);
1081 #endif
1082
1083 static void HandleElementChange(int, int, int);
1084 static void ExecuteCustomElementAction(int, int, int, int);
1085 static boolean ChangeElement(int, int, int, int);
1086
1087 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1088 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1089         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1090 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1091         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1092 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1093         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1094 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1096
1097 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1098 #define CheckElementChange(x, y, e, te, ev)                             \
1099         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1100 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1101         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1102 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1103         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1104
1105 static void PlayLevelSound(int, int, int);
1106 static void PlayLevelSoundNearest(int, int, int);
1107 static void PlayLevelSoundAction(int, int, int);
1108 static void PlayLevelSoundElementAction(int, int, int, int);
1109 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1110 static void PlayLevelSoundActionIfLoop(int, int, int);
1111 static void StopLevelSoundActionIfLoop(int, int, int);
1112 static void PlayLevelMusic();
1113
1114 static void MapGameButtons();
1115 static void HandleGameButtons(struct GadgetInfo *);
1116
1117 int AmoebeNachbarNr(int, int);
1118 void AmoebeUmwandeln(int, int);
1119 void ContinueMoving(int, int);
1120 void Bang(int, int);
1121 void InitMovDir(int, int);
1122 void InitAmoebaNr(int, int);
1123 int NewHiScore(void);
1124
1125 void TestIfGoodThingHitsBadThing(int, int, int);
1126 void TestIfBadThingHitsGoodThing(int, int, int);
1127 void TestIfPlayerTouchesBadThing(int, int);
1128 void TestIfPlayerRunsIntoBadThing(int, int, int);
1129 void TestIfBadThingTouchesPlayer(int, int);
1130 void TestIfBadThingRunsIntoPlayer(int, int, int);
1131 void TestIfFriendTouchesBadThing(int, int);
1132 void TestIfBadThingTouchesFriend(int, int);
1133 void TestIfBadThingTouchesOtherBadThing(int, int);
1134 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1135
1136 void KillPlayer(struct PlayerInfo *);
1137 void BuryPlayer(struct PlayerInfo *);
1138 void RemovePlayer(struct PlayerInfo *);
1139
1140 static int getInvisibleActiveFromInvisibleElement(int);
1141 static int getInvisibleFromInvisibleActiveElement(int);
1142
1143 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1144
1145 /* for detection of endless loops, caused by custom element programming */
1146 /* (using maximal playfield width x 10 is just a rough approximation) */
1147 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1148
1149 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1150 {                                                                       \
1151   if (recursion_loop_detected)                                          \
1152     return (rc);                                                        \
1153                                                                         \
1154   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1155   {                                                                     \
1156     recursion_loop_detected = TRUE;                                     \
1157     recursion_loop_element = (e);                                       \
1158   }                                                                     \
1159                                                                         \
1160   recursion_loop_depth++;                                               \
1161 }
1162
1163 #define RECURSION_LOOP_DETECTION_END()                                  \
1164 {                                                                       \
1165   recursion_loop_depth--;                                               \
1166 }
1167
1168 static int recursion_loop_depth;
1169 static boolean recursion_loop_detected;
1170 static boolean recursion_loop_element;
1171
1172 static int map_player_action[MAX_PLAYERS];
1173
1174
1175 /* ------------------------------------------------------------------------- */
1176 /* definition of elements that automatically change to other elements after  */
1177 /* a specified time, eventually calling a function when changing             */
1178 /* ------------------------------------------------------------------------- */
1179
1180 /* forward declaration for changer functions */
1181 static void InitBuggyBase(int, int);
1182 static void WarnBuggyBase(int, int);
1183
1184 static void InitTrap(int, int);
1185 static void ActivateTrap(int, int);
1186 static void ChangeActiveTrap(int, int);
1187
1188 static void InitRobotWheel(int, int);
1189 static void RunRobotWheel(int, int);
1190 static void StopRobotWheel(int, int);
1191
1192 static void InitTimegateWheel(int, int);
1193 static void RunTimegateWheel(int, int);
1194
1195 static void InitMagicBallDelay(int, int);
1196 static void ActivateMagicBall(int, int);
1197
1198 struct ChangingElementInfo
1199 {
1200   int element;
1201   int target_element;
1202   int change_delay;
1203   void (*pre_change_function)(int x, int y);
1204   void (*change_function)(int x, int y);
1205   void (*post_change_function)(int x, int y);
1206 };
1207
1208 static struct ChangingElementInfo change_delay_list[] =
1209 {
1210   {
1211     EL_NUT_BREAKING,
1212     EL_EMERALD,
1213     6,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_PEARL_BREAKING,
1220     EL_EMPTY,
1221     8,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_OPENING,
1228     EL_EXIT_OPEN,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EXIT_CLOSING,
1236     EL_EXIT_CLOSED,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_OPENING,
1244     EL_STEEL_EXIT_OPEN,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_STEEL_EXIT_CLOSING,
1252     EL_STEEL_EXIT_CLOSED,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_OPENING,
1260     EL_EM_EXIT_OPEN,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_EXIT_CLOSING,
1268 #if 1
1269     EL_EMPTY,
1270 #else
1271     EL_EM_EXIT_CLOSED,
1272 #endif
1273     29,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_EM_STEEL_EXIT_OPENING,
1280     EL_EM_STEEL_EXIT_OPEN,
1281     29,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_EM_STEEL_EXIT_CLOSING,
1288 #if 1
1289     EL_STEELWALL,
1290 #else
1291     EL_EM_STEEL_EXIT_CLOSED,
1292 #endif
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SP_EXIT_OPENING,
1300     EL_SP_EXIT_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SP_EXIT_CLOSING,
1308     EL_SP_EXIT_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_SWITCHGATE_OPENING,
1316     EL_SWITCHGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_SWITCHGATE_CLOSING,
1324     EL_SWITCHGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330   {
1331     EL_TIMEGATE_OPENING,
1332     EL_TIMEGATE_OPEN,
1333     29,
1334     NULL,
1335     NULL,
1336     NULL
1337   },
1338   {
1339     EL_TIMEGATE_CLOSING,
1340     EL_TIMEGATE_CLOSED,
1341     29,
1342     NULL,
1343     NULL,
1344     NULL
1345   },
1346
1347   {
1348     EL_ACID_SPLASH_LEFT,
1349     EL_EMPTY,
1350     8,
1351     NULL,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_ACID_SPLASH_RIGHT,
1357     EL_EMPTY,
1358     8,
1359     NULL,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE,
1365     EL_SP_BUGGY_BASE_ACTIVATING,
1366     0,
1367     InitBuggyBase,
1368     NULL,
1369     NULL
1370   },
1371   {
1372     EL_SP_BUGGY_BASE_ACTIVATING,
1373     EL_SP_BUGGY_BASE_ACTIVE,
1374     0,
1375     InitBuggyBase,
1376     NULL,
1377     NULL
1378   },
1379   {
1380     EL_SP_BUGGY_BASE_ACTIVE,
1381     EL_SP_BUGGY_BASE,
1382     0,
1383     InitBuggyBase,
1384     WarnBuggyBase,
1385     NULL
1386   },
1387   {
1388     EL_TRAP,
1389     EL_TRAP_ACTIVE,
1390     0,
1391     InitTrap,
1392     NULL,
1393     ActivateTrap
1394   },
1395   {
1396     EL_TRAP_ACTIVE,
1397     EL_TRAP,
1398     31,
1399     NULL,
1400     ChangeActiveTrap,
1401     NULL
1402   },
1403   {
1404     EL_ROBOT_WHEEL_ACTIVE,
1405     EL_ROBOT_WHEEL,
1406     0,
1407     InitRobotWheel,
1408     RunRobotWheel,
1409     StopRobotWheel
1410   },
1411   {
1412     EL_TIMEGATE_SWITCH_ACTIVE,
1413     EL_TIMEGATE_SWITCH,
1414     0,
1415     InitTimegateWheel,
1416     RunTimegateWheel,
1417     NULL
1418   },
1419   {
1420     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1421     EL_DC_TIMEGATE_SWITCH,
1422     0,
1423     InitTimegateWheel,
1424     RunTimegateWheel,
1425     NULL
1426   },
1427   {
1428     EL_EMC_MAGIC_BALL_ACTIVE,
1429     EL_EMC_MAGIC_BALL_ACTIVE,
1430     0,
1431     InitMagicBallDelay,
1432     NULL,
1433     ActivateMagicBall
1434   },
1435   {
1436     EL_EMC_SPRING_BUMPER_ACTIVE,
1437     EL_EMC_SPRING_BUMPER,
1438     8,
1439     NULL,
1440     NULL,
1441     NULL
1442   },
1443   {
1444     EL_DIAGONAL_SHRINKING,
1445     EL_UNDEFINED,
1446     0,
1447     NULL,
1448     NULL,
1449     NULL
1450   },
1451   {
1452     EL_DIAGONAL_GROWING,
1453     EL_UNDEFINED,
1454     0,
1455     NULL,
1456     NULL,
1457     NULL,
1458   },
1459
1460   {
1461     EL_UNDEFINED,
1462     EL_UNDEFINED,
1463     -1,
1464     NULL,
1465     NULL,
1466     NULL
1467   }
1468 };
1469
1470 struct
1471 {
1472   int element;
1473   int push_delay_fixed, push_delay_random;
1474 }
1475 push_delay_list[] =
1476 {
1477   { EL_SPRING,                  0, 0 },
1478   { EL_BALLOON,                 0, 0 },
1479
1480   { EL_SOKOBAN_OBJECT,          2, 0 },
1481   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1482   { EL_SATELLITE,               2, 0 },
1483   { EL_SP_DISK_YELLOW,          2, 0 },
1484
1485   { EL_UNDEFINED,               0, 0 },
1486 };
1487
1488 struct
1489 {
1490   int element;
1491   int move_stepsize;
1492 }
1493 move_stepsize_list[] =
1494 {
1495   { EL_AMOEBA_DROP,             2 },
1496   { EL_AMOEBA_DROPPING,         2 },
1497   { EL_QUICKSAND_FILLING,       1 },
1498   { EL_QUICKSAND_EMPTYING,      1 },
1499   { EL_QUICKSAND_FAST_FILLING,  2 },
1500   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1501   { EL_MAGIC_WALL_FILLING,      2 },
1502   { EL_MAGIC_WALL_EMPTYING,     2 },
1503   { EL_BD_MAGIC_WALL_FILLING,   2 },
1504   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1505   { EL_DC_MAGIC_WALL_FILLING,   2 },
1506   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1507
1508   { EL_UNDEFINED,               0 },
1509 };
1510
1511 struct
1512 {
1513   int element;
1514   int count;
1515 }
1516 collect_count_list[] =
1517 {
1518   { EL_EMERALD,                 1 },
1519   { EL_BD_DIAMOND,              1 },
1520   { EL_EMERALD_YELLOW,          1 },
1521   { EL_EMERALD_RED,             1 },
1522   { EL_EMERALD_PURPLE,          1 },
1523   { EL_DIAMOND,                 3 },
1524   { EL_SP_INFOTRON,             1 },
1525   { EL_PEARL,                   5 },
1526   { EL_CRYSTAL,                 8 },
1527
1528   { EL_UNDEFINED,               0 },
1529 };
1530
1531 struct
1532 {
1533   int element;
1534   int direction;
1535 }
1536 access_direction_list[] =
1537 {
1538   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1539   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1540   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1541   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1542   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1543   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1544   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1545   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1546   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1547   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1548   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1549
1550   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1551   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1552   { EL_SP_PORT_UP,                                                   MV_DOWN },
1553   { EL_SP_PORT_DOWN,                                         MV_UP           },
1554   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1555   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1556   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1557   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1558   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1559   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1560   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1561   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1562   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1563   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1564   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1565   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1566   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1567   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1568   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1569
1570   { EL_UNDEFINED,                       MV_NONE                              }
1571 };
1572
1573 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1574
1575 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1576 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1577 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1578                                  IS_JUST_CHANGING(x, y))
1579
1580 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1581
1582 /* static variables for playfield scan mode (scanning forward or backward) */
1583 static int playfield_scan_start_x = 0;
1584 static int playfield_scan_start_y = 0;
1585 static int playfield_scan_delta_x = 1;
1586 static int playfield_scan_delta_y = 1;
1587
1588 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1589                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1590                                      (y) += playfield_scan_delta_y)     \
1591                                 for ((x) = playfield_scan_start_x;      \
1592                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1593                                      (x) += playfield_scan_delta_x)
1594
1595 #ifdef DEBUG
1596 void DEBUG_SetMaximumDynamite()
1597 {
1598   int i;
1599
1600   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1601     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1602       local_player->inventory_element[local_player->inventory_size++] =
1603         EL_DYNAMITE;
1604 }
1605 #endif
1606
1607 static void InitPlayfieldScanModeVars()
1608 {
1609   if (game.use_reverse_scan_direction)
1610   {
1611     playfield_scan_start_x = lev_fieldx - 1;
1612     playfield_scan_start_y = lev_fieldy - 1;
1613
1614     playfield_scan_delta_x = -1;
1615     playfield_scan_delta_y = -1;
1616   }
1617   else
1618   {
1619     playfield_scan_start_x = 0;
1620     playfield_scan_start_y = 0;
1621
1622     playfield_scan_delta_x = 1;
1623     playfield_scan_delta_y = 1;
1624   }
1625 }
1626
1627 static void InitPlayfieldScanMode(int mode)
1628 {
1629   game.use_reverse_scan_direction =
1630     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1631
1632   InitPlayfieldScanModeVars();
1633 }
1634
1635 static int get_move_delay_from_stepsize(int move_stepsize)
1636 {
1637   move_stepsize =
1638     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1639
1640   /* make sure that stepsize value is always a power of 2 */
1641   move_stepsize = (1 << log_2(move_stepsize));
1642
1643   return TILEX / move_stepsize;
1644 }
1645
1646 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1647                                boolean init_game)
1648 {
1649   int player_nr = player->index_nr;
1650   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1651   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1652
1653   /* do no immediately change move delay -- the player might just be moving */
1654   player->move_delay_value_next = move_delay;
1655
1656   /* information if player can move must be set separately */
1657   player->cannot_move = cannot_move;
1658
1659   if (init_game)
1660   {
1661     player->move_delay       = game.initial_move_delay[player_nr];
1662     player->move_delay_value = game.initial_move_delay_value[player_nr];
1663
1664     player->move_delay_value_next = -1;
1665
1666     player->move_delay_reset_counter = 0;
1667   }
1668 }
1669
1670 void GetPlayerConfig()
1671 {
1672   GameFrameDelay = setup.game_frame_delay;
1673
1674   if (!audio.sound_available)
1675     setup.sound_simple = FALSE;
1676
1677   if (!audio.loops_available)
1678     setup.sound_loops = FALSE;
1679
1680   if (!audio.music_available)
1681     setup.sound_music = FALSE;
1682
1683   if (!video.fullscreen_available)
1684     setup.fullscreen = FALSE;
1685
1686   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1687
1688   SetAudioMode(setup.sound);
1689   InitJoysticks();
1690 }
1691
1692 int GetElementFromGroupElement(int element)
1693 {
1694   if (IS_GROUP_ELEMENT(element))
1695   {
1696     struct ElementGroupInfo *group = element_info[element].group;
1697     int last_anim_random_frame = gfx.anim_random_frame;
1698     int element_pos;
1699
1700     if (group->choice_mode == ANIM_RANDOM)
1701       gfx.anim_random_frame = RND(group->num_elements_resolved);
1702
1703     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1704                                     group->choice_mode, 0,
1705                                     group->choice_pos);
1706
1707     if (group->choice_mode == ANIM_RANDOM)
1708       gfx.anim_random_frame = last_anim_random_frame;
1709
1710     group->choice_pos++;
1711
1712     element = group->element_resolved[element_pos];
1713   }
1714
1715   return element;
1716 }
1717
1718 static void InitPlayerField(int x, int y, int element, boolean init_game)
1719 {
1720   if (element == EL_SP_MURPHY)
1721   {
1722     if (init_game)
1723     {
1724       if (stored_player[0].present)
1725       {
1726         Feld[x][y] = EL_SP_MURPHY_CLONE;
1727
1728         return;
1729       }
1730       else
1731       {
1732         stored_player[0].initial_element = element;
1733         stored_player[0].use_murphy = TRUE;
1734
1735         if (!level.use_artwork_element[0])
1736           stored_player[0].artwork_element = EL_SP_MURPHY;
1737       }
1738
1739       Feld[x][y] = EL_PLAYER_1;
1740     }
1741   }
1742
1743   if (init_game)
1744   {
1745     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1746     int jx = player->jx, jy = player->jy;
1747
1748     player->present = TRUE;
1749
1750     player->block_last_field = (element == EL_SP_MURPHY ?
1751                                 level.sp_block_last_field :
1752                                 level.block_last_field);
1753
1754     /* ---------- initialize player's last field block delay --------------- */
1755
1756     /* always start with reliable default value (no adjustment needed) */
1757     player->block_delay_adjustment = 0;
1758
1759     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1760     if (player->block_last_field && element == EL_SP_MURPHY)
1761       player->block_delay_adjustment = 1;
1762
1763     /* special case 2: in game engines before 3.1.1, blocking was different */
1764     if (game.use_block_last_field_bug)
1765       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1766
1767     if (!options.network || player->connected)
1768     {
1769       player->active = TRUE;
1770
1771       /* remove potentially duplicate players */
1772       if (StorePlayer[jx][jy] == Feld[x][y])
1773         StorePlayer[jx][jy] = 0;
1774
1775       StorePlayer[x][y] = Feld[x][y];
1776
1777       if (options.debug)
1778       {
1779         printf("Player %d activated.\n", player->element_nr);
1780         printf("[Local player is %d and currently %s.]\n",
1781                local_player->element_nr,
1782                local_player->active ? "active" : "not active");
1783       }
1784     }
1785
1786     Feld[x][y] = EL_EMPTY;
1787
1788     player->jx = player->last_jx = x;
1789     player->jy = player->last_jy = y;
1790   }
1791
1792 #if USE_PLAYER_REANIMATION
1793   if (!init_game)
1794   {
1795     int player_nr = GET_PLAYER_NR(element);
1796     struct PlayerInfo *player = &stored_player[player_nr];
1797
1798     if (player->active && player->killed)
1799       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1800   }
1801 #endif
1802 }
1803
1804 static void InitField(int x, int y, boolean init_game)
1805 {
1806   int element = Feld[x][y];
1807
1808   switch (element)
1809   {
1810     case EL_SP_MURPHY:
1811     case EL_PLAYER_1:
1812     case EL_PLAYER_2:
1813     case EL_PLAYER_3:
1814     case EL_PLAYER_4:
1815       InitPlayerField(x, y, element, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_PLAYER:
1819       element = Feld[x][y] = EL_PLAYER_1;
1820       InitField(x, y, init_game);
1821
1822       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1823       InitField(x, y, init_game);
1824       break;
1825
1826     case EL_SOKOBAN_FIELD_EMPTY:
1827       local_player->sokobanfields_still_needed++;
1828       break;
1829
1830     case EL_STONEBLOCK:
1831       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1833       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1839       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1840         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1841       break;
1842
1843     case EL_BUG:
1844     case EL_BUG_RIGHT:
1845     case EL_BUG_UP:
1846     case EL_BUG_LEFT:
1847     case EL_BUG_DOWN:
1848     case EL_SPACESHIP:
1849     case EL_SPACESHIP_RIGHT:
1850     case EL_SPACESHIP_UP:
1851     case EL_SPACESHIP_LEFT:
1852     case EL_SPACESHIP_DOWN:
1853     case EL_BD_BUTTERFLY:
1854     case EL_BD_BUTTERFLY_RIGHT:
1855     case EL_BD_BUTTERFLY_UP:
1856     case EL_BD_BUTTERFLY_LEFT:
1857     case EL_BD_BUTTERFLY_DOWN:
1858     case EL_BD_FIREFLY:
1859     case EL_BD_FIREFLY_RIGHT:
1860     case EL_BD_FIREFLY_UP:
1861     case EL_BD_FIREFLY_LEFT:
1862     case EL_BD_FIREFLY_DOWN:
1863     case EL_PACMAN_RIGHT:
1864     case EL_PACMAN_UP:
1865     case EL_PACMAN_LEFT:
1866     case EL_PACMAN_DOWN:
1867     case EL_YAMYAM:
1868     case EL_YAMYAM_LEFT:
1869     case EL_YAMYAM_RIGHT:
1870     case EL_YAMYAM_UP:
1871     case EL_YAMYAM_DOWN:
1872     case EL_DARK_YAMYAM:
1873     case EL_ROBOT:
1874     case EL_PACMAN:
1875     case EL_SP_SNIKSNAK:
1876     case EL_SP_ELECTRON:
1877     case EL_MOLE:
1878     case EL_MOLE_LEFT:
1879     case EL_MOLE_RIGHT:
1880     case EL_MOLE_UP:
1881     case EL_MOLE_DOWN:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Feld[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       local_player->lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       local_player->friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    /* more than one switch -- set it like the first switch */
1948         {
1949           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954 #if !USE_BOTH_SWITCHGATE_SWITCHES
1955     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1956       if (init_game)
1957         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1958       break;
1959
1960     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1961       if (init_game)
1962         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1963       break;
1964 #endif
1965
1966     case EL_LIGHT_SWITCH_ACTIVE:
1967       if (init_game)
1968         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1969       break;
1970
1971     case EL_INVISIBLE_STEELWALL:
1972     case EL_INVISIBLE_WALL:
1973     case EL_INVISIBLE_SAND:
1974       if (game.light_time_left > 0 ||
1975           game.lenses_time_left > 0)
1976         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1977       break;
1978
1979     case EL_EMC_MAGIC_BALL:
1980       if (game.ball_state)
1981         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1982       break;
1983
1984     case EL_EMC_MAGIC_BALL_SWITCH:
1985       if (game.ball_state)
1986         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1987       break;
1988
1989     case EL_TRIGGER_PLAYER:
1990     case EL_TRIGGER_ELEMENT:
1991     case EL_TRIGGER_CE_VALUE:
1992     case EL_TRIGGER_CE_SCORE:
1993     case EL_SELF:
1994     case EL_ANY_ELEMENT:
1995     case EL_CURRENT_CE_VALUE:
1996     case EL_CURRENT_CE_SCORE:
1997     case EL_PREV_CE_1:
1998     case EL_PREV_CE_2:
1999     case EL_PREV_CE_3:
2000     case EL_PREV_CE_4:
2001     case EL_PREV_CE_5:
2002     case EL_PREV_CE_6:
2003     case EL_PREV_CE_7:
2004     case EL_PREV_CE_8:
2005     case EL_NEXT_CE_1:
2006     case EL_NEXT_CE_2:
2007     case EL_NEXT_CE_3:
2008     case EL_NEXT_CE_4:
2009     case EL_NEXT_CE_5:
2010     case EL_NEXT_CE_6:
2011     case EL_NEXT_CE_7:
2012     case EL_NEXT_CE_8:
2013       /* reference elements should not be used on the playfield */
2014       Feld[x][y] = EL_EMPTY;
2015       break;
2016
2017     default:
2018       if (IS_CUSTOM_ELEMENT(element))
2019       {
2020         if (CAN_MOVE(element))
2021           InitMovDir(x, y);
2022
2023 #if USE_NEW_CUSTOM_VALUE
2024         if (!element_info[element].use_last_ce_value || init_game)
2025           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2026 #endif
2027       }
2028       else if (IS_GROUP_ELEMENT(element))
2029       {
2030         Feld[x][y] = GetElementFromGroupElement(element);
2031
2032         InitField(x, y, init_game);
2033       }
2034
2035       break;
2036   }
2037
2038   if (!init_game)
2039     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2040 }
2041
2042 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2043 {
2044   InitField(x, y, init_game);
2045
2046   /* not needed to call InitMovDir() -- already done by InitField()! */
2047   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2048       CAN_MOVE(Feld[x][y]))
2049     InitMovDir(x, y);
2050 }
2051
2052 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2053 {
2054   int old_element = Feld[x][y];
2055
2056   InitField(x, y, init_game);
2057
2058   /* not needed to call InitMovDir() -- already done by InitField()! */
2059   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2060       CAN_MOVE(old_element) &&
2061       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2062     InitMovDir(x, y);
2063
2064   /* this case is in fact a combination of not less than three bugs:
2065      first, it calls InitMovDir() for elements that can move, although this is
2066      already done by InitField(); then, it checks the element that was at this
2067      field _before_ the call to InitField() (which can change it); lastly, it
2068      was not called for "mole with direction" elements, which were treated as
2069      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2070   */
2071 }
2072
2073 #if 1
2074
2075 static int get_key_element_from_nr(int key_nr)
2076 {
2077   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2078                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2079                           EL_EM_KEY_1 : EL_KEY_1);
2080
2081   return key_base_element + key_nr;
2082 }
2083
2084 static int get_next_dropped_element(struct PlayerInfo *player)
2085 {
2086   return (player->inventory_size > 0 ?
2087           player->inventory_element[player->inventory_size - 1] :
2088           player->inventory_infinite_element != EL_UNDEFINED ?
2089           player->inventory_infinite_element :
2090           player->dynabombs_left > 0 ?
2091           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2092           EL_UNDEFINED);
2093 }
2094
2095 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2096 {
2097   /* pos >= 0: get element from bottom of the stack;
2098      pos <  0: get element from top of the stack */
2099
2100   if (pos < 0)
2101   {
2102     int min_inventory_size = -pos;
2103     int inventory_pos = player->inventory_size - min_inventory_size;
2104     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2105
2106     return (player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             player->inventory_infinite_element != EL_UNDEFINED ?
2109             player->inventory_infinite_element :
2110             player->dynabombs_left >= min_dynabombs_left ?
2111             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2112             EL_UNDEFINED);
2113   }
2114   else
2115   {
2116     int min_dynabombs_left = pos + 1;
2117     int min_inventory_size = pos + 1 - player->dynabombs_left;
2118     int inventory_pos = pos - player->dynabombs_left;
2119
2120     return (player->inventory_infinite_element != EL_UNDEFINED ?
2121             player->inventory_infinite_element :
2122             player->dynabombs_left >= min_dynabombs_left ?
2123             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2124             player->inventory_size >= min_inventory_size ?
2125             player->inventory_element[inventory_pos] :
2126             EL_UNDEFINED);
2127   }
2128 }
2129
2130 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2131 {
2132   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2133   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2134   int compare_result;
2135
2136   if (gpo1->sort_priority != gpo2->sort_priority)
2137     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2138   else
2139     compare_result = gpo1->nr - gpo2->nr;
2140
2141   return compare_result;
2142 }
2143
2144 void InitGameControlValues()
2145 {
2146   int i;
2147
2148   for (i = 0; game_panel_controls[i].nr != -1; i++)
2149   {
2150     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2151     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2152     struct TextPosInfo *pos = gpc->pos;
2153     int nr = gpc->nr;
2154     int type = gpc->type;
2155
2156     if (nr != i)
2157     {
2158       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2159       Error(ERR_EXIT, "this should not happen -- please debug");
2160     }
2161
2162     /* force update of game controls after initialization */
2163     gpc->value = gpc->last_value = -1;
2164     gpc->frame = gpc->last_frame = -1;
2165     gpc->gfx_frame = -1;
2166
2167     /* determine panel value width for later calculation of alignment */
2168     if (type == TYPE_INTEGER || type == TYPE_STRING)
2169     {
2170       pos->width = pos->size * getFontWidth(pos->font);
2171       pos->height = getFontHeight(pos->font);
2172     }
2173     else if (type == TYPE_ELEMENT)
2174     {
2175       pos->width = pos->size;
2176       pos->height = pos->size;
2177     }
2178
2179     /* fill structure for game panel draw order */
2180     gpo->nr = gpc->nr;
2181     gpo->sort_priority = pos->sort_priority;
2182   }
2183
2184   /* sort game panel controls according to sort_priority and control number */
2185   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2186         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2187 }
2188
2189 void UpdatePlayfieldElementCount()
2190 {
2191   boolean use_element_count = FALSE;
2192   int i, j, x, y;
2193
2194   /* first check if it is needed at all to calculate playfield element count */
2195   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2196     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2197       use_element_count = TRUE;
2198
2199   if (!use_element_count)
2200     return;
2201
2202   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2203     element_info[i].element_count = 0;
2204
2205   SCAN_PLAYFIELD(x, y)
2206   {
2207     element_info[Feld[x][y]].element_count++;
2208   }
2209
2210   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2211     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2212       if (IS_IN_GROUP(j, i))
2213         element_info[EL_GROUP_START + i].element_count +=
2214           element_info[j].element_count;
2215 }
2216
2217 void UpdateGameControlValues()
2218 {
2219   int i, k;
2220   int time = (local_player->LevelSolved ?
2221               local_player->LevelSolved_CountingTime :
2222               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2223               level.native_em_level->lev->time :
2224               level.time == 0 ? TimePlayed : TimeLeft);
2225   int score = (local_player->LevelSolved ?
2226                local_player->LevelSolved_CountingScore :
2227                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2228                level.native_em_level->lev->score :
2229                local_player->score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               level.native_em_level->lev->required :
2232               local_player->gems_still_needed);
2233   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234                      level.native_em_level->lev->required > 0 :
2235                      local_player->gems_still_needed > 0 ||
2236                      local_player->sokobanfields_still_needed > 0 ||
2237                      local_player->lights_still_needed > 0);
2238
2239   UpdatePlayfieldElementCount();
2240
2241   /* update game panel control values */
2242
2243   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2244   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2245
2246   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2247   for (i = 0; i < MAX_NUM_KEYS; i++)
2248     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2249   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2250   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2251
2252   if (game.centered_player_nr == -1)
2253   {
2254     for (i = 0; i < MAX_PLAYERS; i++)
2255     {
2256       for (k = 0; k < MAX_NUM_KEYS; k++)
2257       {
2258         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2259         {
2260           if (level.native_em_level->ply[i]->keys & (1 << k))
2261             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2262               get_key_element_from_nr(k);
2263         }
2264         else if (stored_player[i].key[k])
2265           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2266             get_key_element_from_nr(k);
2267       }
2268
2269       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2271           level.native_em_level->ply[i]->dynamite;
2272       else
2273         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2274           stored_player[i].inventory_size;
2275
2276       if (stored_player[i].num_white_keys > 0)
2277         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2278           EL_DC_KEY_WHITE;
2279
2280       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2281         stored_player[i].num_white_keys;
2282     }
2283   }
2284   else
2285   {
2286     int player_nr = game.centered_player_nr;
2287
2288     for (k = 0; k < MAX_NUM_KEYS; k++)
2289     {
2290       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291       {
2292         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2293           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294             get_key_element_from_nr(k);
2295       }
2296       else if (stored_player[player_nr].key[k])
2297         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298           get_key_element_from_nr(k);
2299     }
2300
2301     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2303         level.native_em_level->ply[player_nr]->dynamite;
2304     else
2305       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2306         stored_player[player_nr].inventory_size;
2307
2308     if (stored_player[player_nr].num_white_keys > 0)
2309       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2310
2311     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312       stored_player[player_nr].num_white_keys;
2313   }
2314
2315   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2316   {
2317     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2318       get_inventory_element_from_pos(local_player, i);
2319     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2320       get_inventory_element_from_pos(local_player, -i - 1);
2321   }
2322
2323   game_panel_controls[GAME_PANEL_SCORE].value = score;
2324   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2325
2326   game_panel_controls[GAME_PANEL_TIME].value = time;
2327
2328   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2329   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2330   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2331
2332   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2333     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2334      EL_EMPTY);
2335   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2336     local_player->shield_normal_time_left;
2337   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2338     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2339      EL_EMPTY);
2340   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2341     local_player->shield_deadly_time_left;
2342
2343   game_panel_controls[GAME_PANEL_EXIT].value =
2344     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2345
2346   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2347     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2348   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2349     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2350      EL_EMC_MAGIC_BALL_SWITCH);
2351
2352   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2353     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2354   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2355     game.light_time_left;
2356
2357   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2358     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2359   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2360     game.timegate_time_left;
2361
2362   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2363     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2364
2365   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2366     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2367   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2368     game.lenses_time_left;
2369
2370   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2371     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2372   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2373     game.magnify_time_left;
2374
2375   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2376     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2377      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2378      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2379      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2380      EL_BALLOON_SWITCH_NONE);
2381
2382   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2383     local_player->dynabomb_count;
2384   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2385     local_player->dynabomb_size;
2386   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2387     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2388
2389   game_panel_controls[GAME_PANEL_PENGUINS].value =
2390     local_player->friends_still_needed;
2391
2392   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2393     local_player->sokobanfields_still_needed;
2394   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2395     local_player->sokobanfields_still_needed;
2396
2397   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2398     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2399
2400   for (i = 0; i < NUM_BELTS; i++)
2401   {
2402     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2403       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2404        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2405     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2406       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2407   }
2408
2409   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2410     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2411   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2412     game.magic_wall_time_left;
2413
2414 #if USE_PLAYER_GRAVITY
2415   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2416     local_player->gravity;
2417 #else
2418   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2419 #endif
2420
2421   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2422     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2423
2424   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2425     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2426       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2427        game.panel.element[i].id : EL_UNDEFINED);
2428
2429   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2430     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2431       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2432        element_info[game.panel.element_count[i].id].element_count : 0);
2433
2434   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2435     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2436       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2437        element_info[game.panel.ce_score[i].id].collect_score : 0);
2438
2439   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2440     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2441       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2442        element_info[game.panel.ce_score_element[i].id].collect_score :
2443        EL_UNDEFINED);
2444
2445   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2446   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2447   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2448
2449   /* update game panel control frames */
2450
2451   for (i = 0; game_panel_controls[i].nr != -1; i++)
2452   {
2453     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2454
2455     if (gpc->type == TYPE_ELEMENT)
2456     {
2457       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2458       {
2459         int last_anim_random_frame = gfx.anim_random_frame;
2460         int element = gpc->value;
2461         int graphic = el2panelimg(element);
2462
2463         if (gpc->value != gpc->last_value)
2464         {
2465           gpc->gfx_frame = 0;
2466           gpc->gfx_random = INIT_GFX_RANDOM();
2467         }
2468         else
2469         {
2470           gpc->gfx_frame++;
2471
2472           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2473               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2474             gpc->gfx_random = INIT_GFX_RANDOM();
2475         }
2476
2477         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2478           gfx.anim_random_frame = gpc->gfx_random;
2479
2480         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2481           gpc->gfx_frame = element_info[element].collect_score;
2482
2483         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2484                                               gpc->gfx_frame);
2485
2486         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2487           gfx.anim_random_frame = last_anim_random_frame;
2488       }
2489     }
2490   }
2491 }
2492
2493 void DisplayGameControlValues()
2494 {
2495   boolean redraw_panel = FALSE;
2496   int i;
2497
2498   for (i = 0; game_panel_controls[i].nr != -1; i++)
2499   {
2500     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2501
2502     if (PANEL_DEACTIVATED(gpc->pos))
2503       continue;
2504
2505     if (gpc->value == gpc->last_value &&
2506         gpc->frame == gpc->last_frame)
2507       continue;
2508
2509     redraw_panel = TRUE;
2510   }
2511
2512   if (!redraw_panel)
2513     return;
2514
2515   /* copy default game door content to main double buffer */
2516   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2517              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2518
2519   /* redraw game control buttons */
2520 #if 1
2521   RedrawGameButtons();
2522 #else
2523   UnmapGameButtons();
2524   MapGameButtons();
2525 #endif
2526
2527   game_status = GAME_MODE_PSEUDO_PANEL;
2528
2529 #if 1
2530   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2531 #else
2532   for (i = 0; game_panel_controls[i].nr != -1; i++)
2533 #endif
2534   {
2535 #if 1
2536     int nr = game_panel_order[i].nr;
2537     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2538 #else
2539     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2540     int nr = gpc->nr;
2541 #endif
2542     struct TextPosInfo *pos = gpc->pos;
2543     int type = gpc->type;
2544     int value = gpc->value;
2545     int frame = gpc->frame;
2546 #if 0
2547     int last_value = gpc->last_value;
2548     int last_frame = gpc->last_frame;
2549 #endif
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558 #if 0
2559     if (value == last_value && frame == last_frame)
2560       continue;
2561 #endif
2562
2563     gpc->last_value = value;
2564     gpc->last_frame = frame;
2565
2566 #if 0
2567     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2568 #endif
2569
2570     if (type == TYPE_INTEGER)
2571     {
2572       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2573           nr == GAME_PANEL_TIME)
2574       {
2575         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2576
2577         if (use_dynamic_size)           /* use dynamic number of digits */
2578         {
2579           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2580           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2581           int size2 = size1 + 1;
2582           int font1 = pos->font;
2583           int font2 = pos->font_alt;
2584
2585           size = (value < value_change ? size1 : size2);
2586           font = (value < value_change ? font1 : font2);
2587
2588 #if 0
2589           /* clear background if value just changed its size (dynamic digits) */
2590           if ((last_value < value_change) != (value < value_change))
2591           {
2592             int width1 = size1 * getFontWidth(font1);
2593             int width2 = size2 * getFontWidth(font2);
2594             int max_width = MAX(width1, width2);
2595             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2596
2597             pos->width = max_width;
2598
2599             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2600                                        max_width, max_height);
2601           }
2602 #endif
2603         }
2604       }
2605
2606 #if 1
2607       /* correct text size if "digits" is zero or less */
2608       if (size <= 0)
2609         size = strlen(int2str(value, size));
2610
2611       /* dynamically correct text alignment */
2612       pos->width = size * getFontWidth(font);
2613 #endif
2614
2615       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2616                   int2str(value, size), font, mask_mode);
2617     }
2618     else if (type == TYPE_ELEMENT)
2619     {
2620       int element, graphic;
2621       Bitmap *src_bitmap;
2622       int src_x, src_y;
2623       int width, height;
2624       int dst_x = PANEL_XPOS(pos);
2625       int dst_y = PANEL_YPOS(pos);
2626
2627 #if 1
2628       if (value != EL_UNDEFINED && value != EL_EMPTY)
2629       {
2630         element = value;
2631         graphic = el2panelimg(value);
2632
2633         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2634
2635 #if 1
2636         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2637           size = TILESIZE;
2638 #endif
2639
2640         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2641                               &src_x, &src_y);
2642
2643         width  = graphic_info[graphic].width  * size / TILESIZE;
2644         height = graphic_info[graphic].height * size / TILESIZE;
2645
2646         if (draw_masked)
2647         {
2648           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2649                         dst_x - src_x, dst_y - src_y);
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         }
2653         else
2654         {
2655           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2656                      dst_x, dst_y);
2657         }
2658       }
2659 #else
2660       if (value == EL_UNDEFINED || value == EL_EMPTY)
2661       {
2662         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2663         graphic = el2panelimg(element);
2664
2665         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2666         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2667         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2668       }
2669       else
2670       {
2671         element = value;
2672         graphic = el2panelimg(value);
2673
2674         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2675       }
2676
2677       width  = graphic_info[graphic].width  * size / TILESIZE;
2678       height = graphic_info[graphic].height * size / TILESIZE;
2679
2680       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2681 #endif
2682     }
2683     else if (type == TYPE_STRING)
2684     {
2685       boolean active = (value != 0);
2686       char *state_normal = "off";
2687       char *state_active = "on";
2688       char *state = (active ? state_active : state_normal);
2689       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2690                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2691                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2692                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2693
2694       if (nr == GAME_PANEL_GRAVITY_STATE)
2695       {
2696         int font1 = pos->font;          /* (used for normal state) */
2697         int font2 = pos->font_alt;      /* (used for active state) */
2698 #if 0
2699         int size1 = strlen(state_normal);
2700         int size2 = strlen(state_active);
2701         int width1 = size1 * getFontWidth(font1);
2702         int width2 = size2 * getFontWidth(font2);
2703         int max_width = MAX(width1, width2);
2704         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2705
2706         pos->width = max_width;
2707
2708         /* clear background for values that may have changed its size */
2709         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2710                                    max_width, max_height);
2711 #endif
2712
2713         font = (active ? font2 : font1);
2714       }
2715
2716       if (s != NULL)
2717       {
2718         char *s_cut;
2719
2720 #if 1
2721         if (size <= 0)
2722         {
2723           /* don't truncate output if "chars" is zero or less */
2724           size = strlen(s);
2725
2726           /* dynamically correct text alignment */
2727           pos->width = size * getFontWidth(font);
2728         }
2729 #endif
2730
2731         s_cut = getStringCopyN(s, size);
2732
2733         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2734                     s_cut, font, mask_mode);
2735
2736         free(s_cut);
2737       }
2738     }
2739
2740     redraw_mask |= REDRAW_DOOR_1;
2741   }
2742
2743   game_status = GAME_MODE_PLAYING;
2744 }
2745
2746 void UpdateAndDisplayGameControlValues()
2747 {
2748   if (tape.warp_forward)
2749     return;
2750
2751   UpdateGameControlValues();
2752   DisplayGameControlValues();
2753 }
2754
2755 void DrawGameValue_Emeralds(int value)
2756 {
2757   struct TextPosInfo *pos = &game.panel.gems;
2758 #if 1
2759   int font_nr = pos->font;
2760 #else
2761   int font_nr = FONT_TEXT_2;
2762 #endif
2763   int font_width = getFontWidth(font_nr);
2764   int chars = pos->size;
2765
2766 #if 1
2767   return;       /* !!! USE NEW STUFF !!! */
2768 #endif
2769
2770   if (PANEL_DEACTIVATED(pos))
2771     return;
2772
2773   pos->width = chars * font_width;
2774
2775   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2776 }
2777
2778 void DrawGameValue_Dynamite(int value)
2779 {
2780   struct TextPosInfo *pos = &game.panel.inventory_count;
2781 #if 1
2782   int font_nr = pos->font;
2783 #else
2784   int font_nr = FONT_TEXT_2;
2785 #endif
2786   int font_width = getFontWidth(font_nr);
2787   int chars = pos->size;
2788
2789 #if 1
2790   return;       /* !!! USE NEW STUFF !!! */
2791 #endif
2792
2793   if (PANEL_DEACTIVATED(pos))
2794     return;
2795
2796   pos->width = chars * font_width;
2797
2798   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2799 }
2800
2801 void DrawGameValue_Score(int value)
2802 {
2803   struct TextPosInfo *pos = &game.panel.score;
2804 #if 1
2805   int font_nr = pos->font;
2806 #else
2807   int font_nr = FONT_TEXT_2;
2808 #endif
2809   int font_width = getFontWidth(font_nr);
2810   int chars = pos->size;
2811
2812 #if 1
2813   return;       /* !!! USE NEW STUFF !!! */
2814 #endif
2815
2816   if (PANEL_DEACTIVATED(pos))
2817     return;
2818
2819   pos->width = chars * font_width;
2820
2821   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2822 }
2823
2824 void DrawGameValue_Time(int value)
2825 {
2826   struct TextPosInfo *pos = &game.panel.time;
2827   static int last_value = -1;
2828   int chars1 = 3;
2829   int chars2 = 4;
2830   int chars = pos->size;
2831 #if 1
2832   int font1_nr = pos->font;
2833   int font2_nr = pos->font_alt;
2834 #else
2835   int font1_nr = FONT_TEXT_2;
2836   int font2_nr = FONT_TEXT_1;
2837 #endif
2838   int font_nr = font1_nr;
2839   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2840
2841 #if 1
2842   return;       /* !!! USE NEW STUFF !!! */
2843 #endif
2844
2845   if (PANEL_DEACTIVATED(pos))
2846     return;
2847
2848   if (use_dynamic_chars)                /* use dynamic number of chars */
2849   {
2850     chars   = (value < 1000 ? chars1   : chars2);
2851     font_nr = (value < 1000 ? font1_nr : font2_nr);
2852   }
2853
2854   /* clear background if value just changed its size (dynamic chars only) */
2855   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2856   {
2857     int width1 = chars1 * getFontWidth(font1_nr);
2858     int width2 = chars2 * getFontWidth(font2_nr);
2859     int max_width = MAX(width1, width2);
2860     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2861
2862     pos->width = max_width;
2863
2864     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2865                                max_width, max_height);
2866   }
2867
2868   pos->width = chars * getFontWidth(font_nr);
2869
2870   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2871
2872   last_value = value;
2873 }
2874
2875 void DrawGameValue_Level(int value)
2876 {
2877   struct TextPosInfo *pos = &game.panel.level_number;
2878   int chars1 = 2;
2879   int chars2 = 3;
2880   int chars = pos->size;
2881 #if 1
2882   int font1_nr = pos->font;
2883   int font2_nr = pos->font_alt;
2884 #else
2885   int font1_nr = FONT_TEXT_2;
2886   int font2_nr = FONT_TEXT_1;
2887 #endif
2888   int font_nr = font1_nr;
2889   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2890
2891 #if 1
2892   return;       /* !!! USE NEW STUFF !!! */
2893 #endif
2894
2895   if (PANEL_DEACTIVATED(pos))
2896     return;
2897
2898   if (use_dynamic_chars)                /* use dynamic number of chars */
2899   {
2900     chars   = (level_nr < 100 ? chars1   : chars2);
2901     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2902   }
2903
2904   pos->width = chars * getFontWidth(font_nr);
2905
2906   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2907 }
2908
2909 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2910 {
2911 #if 0
2912   struct TextPosInfo *pos = &game.panel.keys;
2913 #endif
2914 #if 0
2915   int base_key_graphic = EL_KEY_1;
2916 #endif
2917   int i;
2918
2919 #if 1
2920   return;       /* !!! USE NEW STUFF !!! */
2921 #endif
2922
2923 #if 0
2924   if (PANEL_DEACTIVATED(pos))
2925     return;
2926 #endif
2927
2928 #if 0
2929   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2930     base_key_graphic = EL_EM_KEY_1;
2931 #endif
2932
2933 #if 0
2934   pos->width = 4 * MINI_TILEX;
2935 #endif
2936
2937 #if 1
2938   for (i = 0; i < MAX_NUM_KEYS; i++)
2939 #else
2940   /* currently only 4 of 8 possible keys are displayed */
2941   for (i = 0; i < STD_NUM_KEYS; i++)
2942 #endif
2943   {
2944 #if 1
2945     struct TextPosInfo *pos = &game.panel.key[i];
2946 #endif
2947     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2948     int src_y = DOOR_GFX_PAGEY1 + 123;
2949 #if 1
2950     int dst_x = PANEL_XPOS(pos);
2951     int dst_y = PANEL_YPOS(pos);
2952 #else
2953     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2954     int dst_y = PANEL_YPOS(pos);
2955 #endif
2956
2957 #if 1
2958     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2959                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2960                    EL_KEY_1) + i;
2961     int graphic = el2edimg(element);
2962 #endif
2963
2964 #if 1
2965     if (PANEL_DEACTIVATED(pos))
2966       continue;
2967 #endif
2968
2969 #if 0
2970     /* masked blit with tiles from half-size scaled bitmap does not work yet
2971        (no mask bitmap created for these sizes after loading and scaling) --
2972        solution: load without creating mask, scale, then create final mask */
2973
2974     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2975                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2976
2977     if (key[i])
2978     {
2979 #if 0
2980       int graphic = el2edimg(base_key_graphic + i);
2981 #endif
2982       Bitmap *src_bitmap;
2983       int src_x, src_y;
2984
2985       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2986
2987       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2988                     dst_x - src_x, dst_y - src_y);
2989       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2990                        dst_x, dst_y);
2991     }
2992 #else
2993 #if 1
2994     if (key[i])
2995       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2996     else
2997       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2998                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2999 #else
3000     if (key[i])
3001       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3002     else
3003       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3004                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3005 #endif
3006 #endif
3007   }
3008 }
3009
3010 #else
3011
3012 void DrawGameValue_Emeralds(int value)
3013 {
3014   int font_nr = FONT_TEXT_2;
3015   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3016
3017   if (PANEL_DEACTIVATED(game.panel.gems))
3018     return;
3019
3020   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3021 }
3022
3023 void DrawGameValue_Dynamite(int value)
3024 {
3025   int font_nr = FONT_TEXT_2;
3026   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3027
3028   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3029     return;
3030
3031   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3032 }
3033
3034 void DrawGameValue_Score(int value)
3035 {
3036   int font_nr = FONT_TEXT_2;
3037   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3038
3039   if (PANEL_DEACTIVATED(game.panel.score))
3040     return;
3041
3042   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3043 }
3044
3045 void DrawGameValue_Time(int value)
3046 {
3047   int font1_nr = FONT_TEXT_2;
3048 #if 1
3049   int font2_nr = FONT_TEXT_1;
3050 #else
3051   int font2_nr = FONT_LEVEL_NUMBER;
3052 #endif
3053   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3054   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3055
3056   if (PANEL_DEACTIVATED(game.panel.time))
3057     return;
3058
3059   /* clear background if value just changed its size */
3060   if (value == 999 || value == 1000)
3061     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3062
3063   if (value < 1000)
3064     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3065   else
3066     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3067 }
3068
3069 void DrawGameValue_Level(int value)
3070 {
3071   int font1_nr = FONT_TEXT_2;
3072 #if 1
3073   int font2_nr = FONT_TEXT_1;
3074 #else
3075   int font2_nr = FONT_LEVEL_NUMBER;
3076 #endif
3077
3078   if (PANEL_DEACTIVATED(game.panel.level))
3079     return;
3080
3081   if (level_nr < 100)
3082     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3083   else
3084     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3085 }
3086
3087 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3088 {
3089   int base_key_graphic = EL_KEY_1;
3090   int i;
3091
3092   if (PANEL_DEACTIVATED(game.panel.keys))
3093     return;
3094
3095   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3096     base_key_graphic = EL_EM_KEY_1;
3097
3098   /* currently only 4 of 8 possible keys are displayed */
3099   for (i = 0; i < STD_NUM_KEYS; i++)
3100   {
3101     int x = XX_KEYS + i * MINI_TILEX;
3102     int y = YY_KEYS;
3103
3104     if (key[i])
3105       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3106     else
3107       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3108                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3109   }
3110 }
3111
3112 #endif
3113
3114 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3115                        int key_bits)
3116 {
3117   int key[MAX_NUM_KEYS];
3118   int i;
3119
3120   /* prevent EM engine from updating time/score values parallel to GameWon() */
3121   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3122       local_player->LevelSolved)
3123     return;
3124
3125   for (i = 0; i < MAX_NUM_KEYS; i++)
3126     key[i] = key_bits & (1 << i);
3127
3128   DrawGameValue_Level(level_nr);
3129
3130   DrawGameValue_Emeralds(emeralds);
3131   DrawGameValue_Dynamite(dynamite);
3132   DrawGameValue_Score(score);
3133   DrawGameValue_Time(time);
3134
3135   DrawGameValue_Keys(key);
3136 }
3137
3138 void UpdateGameDoorValues()
3139 {
3140   UpdateGameControlValues();
3141 }
3142
3143 void DrawGameDoorValues()
3144 {
3145   DisplayGameControlValues();
3146 }
3147
3148 void DrawGameDoorValues_OLD()
3149 {
3150   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3151   int dynamite_value = 0;
3152   int score_value = (local_player->LevelSolved ? local_player->score_final :
3153                      local_player->score);
3154   int gems_value = local_player->gems_still_needed;
3155   int key_bits = 0;
3156   int i, j;
3157
3158   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3159   {
3160     DrawGameDoorValues_EM();
3161
3162     return;
3163   }
3164
3165   if (game.centered_player_nr == -1)
3166   {
3167     for (i = 0; i < MAX_PLAYERS; i++)
3168     {
3169       for (j = 0; j < MAX_NUM_KEYS; j++)
3170         if (stored_player[i].key[j])
3171           key_bits |= (1 << j);
3172
3173       dynamite_value += stored_player[i].inventory_size;
3174     }
3175   }
3176   else
3177   {
3178     int player_nr = game.centered_player_nr;
3179
3180     for (i = 0; i < MAX_NUM_KEYS; i++)
3181       if (stored_player[player_nr].key[i])
3182         key_bits |= (1 << i);
3183
3184     dynamite_value = stored_player[player_nr].inventory_size;
3185   }
3186
3187   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3188                     key_bits);
3189 }
3190
3191
3192 /*
3193   =============================================================================
3194   InitGameEngine()
3195   -----------------------------------------------------------------------------
3196   initialize game engine due to level / tape version number
3197   =============================================================================
3198 */
3199
3200 static void InitGameEngine()
3201 {
3202   int i, j, k, l, x, y;
3203
3204   /* set game engine from tape file when re-playing, else from level file */
3205   game.engine_version = (tape.playing ? tape.engine_version :
3206                          level.game_version);
3207
3208   /* ---------------------------------------------------------------------- */
3209   /* set flags for bugs and changes according to active game engine version */
3210   /* ---------------------------------------------------------------------- */
3211
3212   /*
3213     Summary of bugfix/change:
3214     Fixed handling for custom elements that change when pushed by the player.
3215
3216     Fixed/changed in version:
3217     3.1.0
3218
3219     Description:
3220     Before 3.1.0, custom elements that "change when pushing" changed directly
3221     after the player started pushing them (until then handled in "DigField()").
3222     Since 3.1.0, these custom elements are not changed until the "pushing"
3223     move of the element is finished (now handled in "ContinueMoving()").
3224
3225     Affected levels/tapes:
3226     The first condition is generally needed for all levels/tapes before version
3227     3.1.0, which might use the old behaviour before it was changed; known tapes
3228     that are affected are some tapes from the level set "Walpurgis Gardens" by
3229     Jamie Cullen.
3230     The second condition is an exception from the above case and is needed for
3231     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3232     above (including some development versions of 3.1.0), but before it was
3233     known that this change would break tapes like the above and was fixed in
3234     3.1.1, so that the changed behaviour was active although the engine version
3235     while recording maybe was before 3.1.0. There is at least one tape that is
3236     affected by this exception, which is the tape for the one-level set "Bug
3237     Machine" by Juergen Bonhagen.
3238   */
3239
3240   game.use_change_when_pushing_bug =
3241     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3242      !(tape.playing &&
3243        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3244        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3245
3246   /*
3247     Summary of bugfix/change:
3248     Fixed handling for blocking the field the player leaves when moving.
3249
3250     Fixed/changed in version:
3251     3.1.1
3252
3253     Description:
3254     Before 3.1.1, when "block last field when moving" was enabled, the field
3255     the player is leaving when moving was blocked for the time of the move,
3256     and was directly unblocked afterwards. This resulted in the last field
3257     being blocked for exactly one less than the number of frames of one player
3258     move. Additionally, even when blocking was disabled, the last field was
3259     blocked for exactly one frame.
3260     Since 3.1.1, due to changes in player movement handling, the last field
3261     is not blocked at all when blocking is disabled. When blocking is enabled,
3262     the last field is blocked for exactly the number of frames of one player
3263     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3264     last field is blocked for exactly one more than the number of frames of
3265     one player move.
3266
3267     Affected levels/tapes:
3268     (!!! yet to be determined -- probably many !!!)
3269   */
3270
3271   game.use_block_last_field_bug =
3272     (game.engine_version < VERSION_IDENT(3,1,1,0));
3273
3274   /*
3275     Summary of bugfix/change:
3276     Changed behaviour of CE changes with multiple changes per single frame.
3277
3278     Fixed/changed in version:
3279     3.2.0-6
3280
3281     Description:
3282     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3283     This resulted in race conditions where CEs seem to behave strange in some
3284     situations (where triggered CE changes were just skipped because there was
3285     already a CE change on that tile in the playfield in that engine frame).
3286     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3287     (The number of changes per frame must be limited in any case, because else
3288     it is easily possible to define CE changes that would result in an infinite
3289     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3290     should be set large enough so that it would only be reached in cases where
3291     the corresponding CE change conditions run into a loop. Therefore, it seems
3292     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3293     maximal number of change pages for custom elements.)
3294
3295     Affected levels/tapes:
3296     Probably many.
3297   */
3298
3299 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3300   game.max_num_changes_per_frame = 1;
3301 #else
3302   game.max_num_changes_per_frame =
3303     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3304 #endif
3305
3306   /* ---------------------------------------------------------------------- */
3307
3308   /* default scan direction: scan playfield from top/left to bottom/right */
3309   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3310
3311   /* dynamically adjust element properties according to game engine version */
3312   InitElementPropertiesEngine(game.engine_version);
3313
3314 #if 0
3315   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3316   printf("          tape version == %06d [%s] [file: %06d]\n",
3317          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3318          tape.file_version);
3319   printf("       => game.engine_version == %06d\n", game.engine_version);
3320 #endif
3321
3322   /* ---------- initialize player's initial move delay --------------------- */
3323
3324   /* dynamically adjust player properties according to level information */
3325   for (i = 0; i < MAX_PLAYERS; i++)
3326     game.initial_move_delay_value[i] =
3327       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3328
3329   /* dynamically adjust player properties according to game engine version */
3330   for (i = 0; i < MAX_PLAYERS; i++)
3331     game.initial_move_delay[i] =
3332       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3333        game.initial_move_delay_value[i] : 0);
3334
3335   /* ---------- initialize player's initial push delay --------------------- */
3336
3337   /* dynamically adjust player properties according to game engine version */
3338   game.initial_push_delay_value =
3339     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3340
3341   /* ---------- initialize changing elements ------------------------------- */
3342
3343   /* initialize changing elements information */
3344   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3345   {
3346     struct ElementInfo *ei = &element_info[i];
3347
3348     /* this pointer might have been changed in the level editor */
3349     ei->change = &ei->change_page[0];
3350
3351     if (!IS_CUSTOM_ELEMENT(i))
3352     {
3353       ei->change->target_element = EL_EMPTY_SPACE;
3354       ei->change->delay_fixed = 0;
3355       ei->change->delay_random = 0;
3356       ei->change->delay_frames = 1;
3357     }
3358
3359     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3360     {
3361       ei->has_change_event[j] = FALSE;
3362
3363       ei->event_page_nr[j] = 0;
3364       ei->event_page[j] = &ei->change_page[0];
3365     }
3366   }
3367
3368   /* add changing elements from pre-defined list */
3369   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3370   {
3371     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3372     struct ElementInfo *ei = &element_info[ch_delay->element];
3373
3374     ei->change->target_element       = ch_delay->target_element;
3375     ei->change->delay_fixed          = ch_delay->change_delay;
3376
3377     ei->change->pre_change_function  = ch_delay->pre_change_function;
3378     ei->change->change_function      = ch_delay->change_function;
3379     ei->change->post_change_function = ch_delay->post_change_function;
3380
3381     ei->change->can_change = TRUE;
3382     ei->change->can_change_or_has_action = TRUE;
3383
3384     ei->has_change_event[CE_DELAY] = TRUE;
3385
3386     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3387     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3388   }
3389
3390   /* ---------- initialize internal run-time variables --------------------- */
3391
3392   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3393   {
3394     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3395
3396     for (j = 0; j < ei->num_change_pages; j++)
3397     {
3398       ei->change_page[j].can_change_or_has_action =
3399         (ei->change_page[j].can_change |
3400          ei->change_page[j].has_action);
3401     }
3402   }
3403
3404   /* add change events from custom element configuration */
3405   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3406   {
3407     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3408
3409     for (j = 0; j < ei->num_change_pages; j++)
3410     {
3411       if (!ei->change_page[j].can_change_or_has_action)
3412         continue;
3413
3414       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3415       {
3416         /* only add event page for the first page found with this event */
3417         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3418         {
3419           ei->has_change_event[k] = TRUE;
3420
3421           ei->event_page_nr[k] = j;
3422           ei->event_page[k] = &ei->change_page[j];
3423         }
3424       }
3425     }
3426   }
3427
3428 #if 1
3429   /* ---------- initialize reference elements in change conditions --------- */
3430
3431   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3432   {
3433     int element = EL_CUSTOM_START + i;
3434     struct ElementInfo *ei = &element_info[element];
3435
3436     for (j = 0; j < ei->num_change_pages; j++)
3437     {
3438       int trigger_element = ei->change_page[j].initial_trigger_element;
3439
3440       if (trigger_element >= EL_PREV_CE_8 &&
3441           trigger_element <= EL_NEXT_CE_8)
3442         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3443
3444       ei->change_page[j].trigger_element = trigger_element;
3445     }
3446   }
3447 #endif
3448
3449   /* ---------- initialize run-time trigger player and element ------------- */
3450
3451   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3452   {
3453     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3454
3455     for (j = 0; j < ei->num_change_pages; j++)
3456     {
3457       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3458       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3459       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3460       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3461       ei->change_page[j].actual_trigger_ce_value = 0;
3462       ei->change_page[j].actual_trigger_ce_score = 0;
3463     }
3464   }
3465
3466   /* ---------- initialize trigger events ---------------------------------- */
3467
3468   /* initialize trigger events information */
3469   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3470     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3471       trigger_events[i][j] = FALSE;
3472
3473   /* add trigger events from element change event properties */
3474   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3475   {
3476     struct ElementInfo *ei = &element_info[i];
3477
3478     for (j = 0; j < ei->num_change_pages; j++)
3479     {
3480       if (!ei->change_page[j].can_change_or_has_action)
3481         continue;
3482
3483       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3484       {
3485         int trigger_element = ei->change_page[j].trigger_element;
3486
3487         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3488         {
3489           if (ei->change_page[j].has_event[k])
3490           {
3491             if (IS_GROUP_ELEMENT(trigger_element))
3492             {
3493               struct ElementGroupInfo *group =
3494                 element_info[trigger_element].group;
3495
3496               for (l = 0; l < group->num_elements_resolved; l++)
3497                 trigger_events[group->element_resolved[l]][k] = TRUE;
3498             }
3499             else if (trigger_element == EL_ANY_ELEMENT)
3500               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3501                 trigger_events[l][k] = TRUE;
3502             else
3503               trigger_events[trigger_element][k] = TRUE;
3504           }
3505         }
3506       }
3507     }
3508   }
3509
3510   /* ---------- initialize push delay -------------------------------------- */
3511
3512   /* initialize push delay values to default */
3513   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3514   {
3515     if (!IS_CUSTOM_ELEMENT(i))
3516     {
3517       /* set default push delay values (corrected since version 3.0.7-1) */
3518       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3519       {
3520         element_info[i].push_delay_fixed = 2;
3521         element_info[i].push_delay_random = 8;
3522       }
3523       else
3524       {
3525         element_info[i].push_delay_fixed = 8;
3526         element_info[i].push_delay_random = 8;
3527       }
3528     }
3529   }
3530
3531   /* set push delay value for certain elements from pre-defined list */
3532   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3533   {
3534     int e = push_delay_list[i].element;
3535
3536     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3537     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3538   }
3539
3540   /* set push delay value for Supaplex elements for newer engine versions */
3541   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3542   {
3543     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3544     {
3545       if (IS_SP_ELEMENT(i))
3546       {
3547         /* set SP push delay to just enough to push under a falling zonk */
3548         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3549
3550         element_info[i].push_delay_fixed  = delay;
3551         element_info[i].push_delay_random = 0;
3552       }
3553     }
3554   }
3555
3556   /* ---------- initialize move stepsize ----------------------------------- */
3557
3558   /* initialize move stepsize values to default */
3559   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3560     if (!IS_CUSTOM_ELEMENT(i))
3561       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3562
3563   /* set move stepsize value for certain elements from pre-defined list */
3564   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3565   {
3566     int e = move_stepsize_list[i].element;
3567
3568     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3569   }
3570
3571   /* ---------- initialize collect score ----------------------------------- */
3572
3573   /* initialize collect score values for custom elements from initial value */
3574   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3575     if (IS_CUSTOM_ELEMENT(i))
3576       element_info[i].collect_score = element_info[i].collect_score_initial;
3577
3578   /* ---------- initialize collect count ----------------------------------- */
3579
3580   /* initialize collect count values for non-custom elements */
3581   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3582     if (!IS_CUSTOM_ELEMENT(i))
3583       element_info[i].collect_count_initial = 0;
3584
3585   /* add collect count values for all elements from pre-defined list */
3586   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3587     element_info[collect_count_list[i].element].collect_count_initial =
3588       collect_count_list[i].count;
3589
3590   /* ---------- initialize access direction -------------------------------- */
3591
3592   /* initialize access direction values to default (access from every side) */
3593   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3594     if (!IS_CUSTOM_ELEMENT(i))
3595       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3596
3597   /* set access direction value for certain elements from pre-defined list */
3598   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3599     element_info[access_direction_list[i].element].access_direction =
3600       access_direction_list[i].direction;
3601
3602   /* ---------- initialize explosion content ------------------------------- */
3603   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3604   {
3605     if (IS_CUSTOM_ELEMENT(i))
3606       continue;
3607
3608     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3609     {
3610       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3611
3612       element_info[i].content.e[x][y] =
3613         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3614          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3615          i == EL_PLAYER_3 ? EL_EMERALD :
3616          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3617          i == EL_MOLE ? EL_EMERALD_RED :
3618          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3619          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3620          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3621          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3622          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3623          i == EL_WALL_EMERALD ? EL_EMERALD :
3624          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3625          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3626          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3627          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3628          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3629          i == EL_WALL_PEARL ? EL_PEARL :
3630          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3631          EL_EMPTY);
3632     }
3633   }
3634
3635   /* ---------- initialize recursion detection ------------------------------ */
3636   recursion_loop_depth = 0;
3637   recursion_loop_detected = FALSE;
3638   recursion_loop_element = EL_UNDEFINED;
3639
3640   /* ---------- initialize graphics engine ---------------------------------- */
3641   game.scroll_delay_value =
3642     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3643      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3644   game.scroll_delay_value =
3645     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3646 }
3647
3648 int get_num_special_action(int element, int action_first, int action_last)
3649 {
3650   int num_special_action = 0;
3651   int i, j;
3652
3653   for (i = action_first; i <= action_last; i++)
3654   {
3655     boolean found = FALSE;
3656
3657     for (j = 0; j < NUM_DIRECTIONS; j++)
3658       if (el_act_dir2img(element, i, j) !=
3659           el_act_dir2img(element, ACTION_DEFAULT, j))
3660         found = TRUE;
3661
3662     if (found)
3663       num_special_action++;
3664     else
3665       break;
3666   }
3667
3668   return num_special_action;
3669 }
3670
3671
3672 /*
3673   =============================================================================
3674   InitGame()
3675   -----------------------------------------------------------------------------
3676   initialize and start new game
3677   =============================================================================
3678 */
3679
3680 void InitGame()
3681 {
3682   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3683   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3684   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3685 #if 0
3686   boolean do_fading = (game_status == GAME_MODE_MAIN);
3687 #endif
3688 #if 1
3689   int initial_move_dir = MV_DOWN;
3690 #else
3691   int initial_move_dir = MV_NONE;
3692 #endif
3693   int i, j, x, y;
3694
3695   game_status = GAME_MODE_PLAYING;
3696
3697   InitGameEngine();
3698   InitGameControlValues();
3699
3700   /* don't play tapes over network */
3701   network_playing = (options.network && !tape.playing);
3702
3703   for (i = 0; i < MAX_PLAYERS; i++)
3704   {
3705     struct PlayerInfo *player = &stored_player[i];
3706
3707     player->index_nr = i;
3708     player->index_bit = (1 << i);
3709     player->element_nr = EL_PLAYER_1 + i;
3710
3711     player->present = FALSE;
3712     player->active = FALSE;
3713     player->mapped = FALSE;
3714
3715     player->killed = FALSE;
3716     player->reanimated = FALSE;
3717
3718     player->action = 0;
3719     player->effective_action = 0;
3720     player->programmed_action = 0;
3721
3722     player->score = 0;
3723     player->score_final = 0;
3724
3725     player->gems_still_needed = level.gems_needed;
3726     player->sokobanfields_still_needed = 0;
3727     player->lights_still_needed = 0;
3728     player->friends_still_needed = 0;
3729
3730     for (j = 0; j < MAX_NUM_KEYS; j++)
3731       player->key[j] = FALSE;
3732
3733     player->num_white_keys = 0;
3734
3735     player->dynabomb_count = 0;
3736     player->dynabomb_size = 1;
3737     player->dynabombs_left = 0;
3738     player->dynabomb_xl = FALSE;
3739
3740     player->MovDir = initial_move_dir;
3741     player->MovPos = 0;
3742     player->GfxPos = 0;
3743     player->GfxDir = initial_move_dir;
3744     player->GfxAction = ACTION_DEFAULT;
3745     player->Frame = 0;
3746     player->StepFrame = 0;
3747
3748     player->initial_element = player->element_nr;
3749     player->artwork_element =
3750       (level.use_artwork_element[i] ? level.artwork_element[i] :
3751        player->element_nr);
3752     player->use_murphy = FALSE;
3753
3754     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3755     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3756
3757     player->gravity = level.initial_player_gravity[i];
3758
3759     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3760
3761     player->actual_frame_counter = 0;
3762
3763     player->step_counter = 0;
3764
3765     player->last_move_dir = initial_move_dir;
3766
3767     player->is_active = FALSE;
3768
3769     player->is_waiting = FALSE;
3770     player->is_moving = FALSE;
3771     player->is_auto_moving = FALSE;
3772     player->is_digging = FALSE;
3773     player->is_snapping = FALSE;
3774     player->is_collecting = FALSE;
3775     player->is_pushing = FALSE;
3776     player->is_switching = FALSE;
3777     player->is_dropping = FALSE;
3778     player->is_dropping_pressed = FALSE;
3779
3780     player->is_bored = FALSE;
3781     player->is_sleeping = FALSE;
3782
3783     player->frame_counter_bored = -1;
3784     player->frame_counter_sleeping = -1;
3785
3786     player->anim_delay_counter = 0;
3787     player->post_delay_counter = 0;
3788
3789     player->dir_waiting = initial_move_dir;
3790     player->action_waiting = ACTION_DEFAULT;
3791     player->last_action_waiting = ACTION_DEFAULT;
3792     player->special_action_bored = ACTION_DEFAULT;
3793     player->special_action_sleeping = ACTION_DEFAULT;
3794
3795     player->switch_x = -1;
3796     player->switch_y = -1;
3797
3798     player->drop_x = -1;
3799     player->drop_y = -1;
3800
3801     player->show_envelope = 0;
3802
3803     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3804
3805     player->push_delay       = -1;      /* initialized when pushing starts */
3806     player->push_delay_value = game.initial_push_delay_value;
3807
3808     player->drop_delay = 0;
3809     player->drop_pressed_delay = 0;
3810
3811     player->last_jx = -1;
3812     player->last_jy = -1;
3813     player->jx = -1;
3814     player->jy = -1;
3815
3816     player->shield_normal_time_left = 0;
3817     player->shield_deadly_time_left = 0;
3818
3819     player->inventory_infinite_element = EL_UNDEFINED;
3820     player->inventory_size = 0;
3821
3822     if (level.use_initial_inventory[i])
3823     {
3824       for (j = 0; j < level.initial_inventory_size[i]; j++)
3825       {
3826         int element = level.initial_inventory_content[i][j];
3827         int collect_count = element_info[element].collect_count_initial;
3828         int k;
3829
3830         if (!IS_CUSTOM_ELEMENT(element))
3831           collect_count = 1;
3832
3833         if (collect_count == 0)
3834           player->inventory_infinite_element = element;
3835         else
3836           for (k = 0; k < collect_count; k++)
3837             if (player->inventory_size < MAX_INVENTORY_SIZE)
3838               player->inventory_element[player->inventory_size++] = element;
3839       }
3840     }
3841
3842     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3843     SnapField(player, 0, 0);
3844
3845     player->LevelSolved = FALSE;
3846     player->GameOver = FALSE;
3847
3848     player->LevelSolved_GameWon = FALSE;
3849     player->LevelSolved_GameEnd = FALSE;
3850     player->LevelSolved_PanelOff = FALSE;
3851     player->LevelSolved_SaveTape = FALSE;
3852     player->LevelSolved_SaveScore = FALSE;
3853     player->LevelSolved_CountingTime = 0;
3854     player->LevelSolved_CountingScore = 0;
3855
3856     map_player_action[i] = i;
3857   }
3858
3859   network_player_action_received = FALSE;
3860
3861 #if defined(NETWORK_AVALIABLE)
3862   /* initial null action */
3863   if (network_playing)
3864     SendToServer_MovePlayer(MV_NONE);
3865 #endif
3866
3867   ZX = ZY = -1;
3868   ExitX = ExitY = -1;
3869
3870   FrameCounter = 0;
3871   TimeFrames = 0;
3872   TimePlayed = 0;
3873   TimeLeft = level.time;
3874   TapeTime = 0;
3875
3876   ScreenMovDir = MV_NONE;
3877   ScreenMovPos = 0;
3878   ScreenGfxPos = 0;
3879
3880   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3881
3882   AllPlayersGone = FALSE;
3883
3884   game.yamyam_content_nr = 0;
3885   game.robot_wheel_active = FALSE;
3886   game.magic_wall_active = FALSE;
3887   game.magic_wall_time_left = 0;
3888   game.light_time_left = 0;
3889   game.timegate_time_left = 0;
3890   game.switchgate_pos = 0;
3891   game.wind_direction = level.wind_direction_initial;
3892
3893 #if !USE_PLAYER_GRAVITY
3894   game.gravity = FALSE;
3895   game.explosions_delayed = TRUE;
3896 #endif
3897
3898   game.lenses_time_left = 0;
3899   game.magnify_time_left = 0;
3900
3901   game.ball_state = level.ball_state_initial;
3902   game.ball_content_nr = 0;
3903
3904   game.envelope_active = FALSE;
3905
3906   /* set focus to local player for network games, else to all players */
3907   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3908   game.centered_player_nr_next = game.centered_player_nr;
3909   game.set_centered_player = FALSE;
3910
3911   if (network_playing && tape.recording)
3912   {
3913     /* store client dependent player focus when recording network games */
3914     tape.centered_player_nr_next = game.centered_player_nr_next;
3915     tape.set_centered_player = TRUE;
3916   }
3917
3918   for (i = 0; i < NUM_BELTS; i++)
3919   {
3920     game.belt_dir[i] = MV_NONE;
3921     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3922   }
3923
3924   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3925     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3926
3927   SCAN_PLAYFIELD(x, y)
3928   {
3929     Feld[x][y] = level.field[x][y];
3930     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3931     ChangeDelay[x][y] = 0;
3932     ChangePage[x][y] = -1;
3933 #if USE_NEW_CUSTOM_VALUE
3934     CustomValue[x][y] = 0;              /* initialized in InitField() */
3935 #endif
3936     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3937     AmoebaNr[x][y] = 0;
3938     WasJustMoving[x][y] = 0;
3939     WasJustFalling[x][y] = 0;
3940     CheckCollision[x][y] = 0;
3941     CheckImpact[x][y] = 0;
3942     Stop[x][y] = FALSE;
3943     Pushed[x][y] = FALSE;
3944
3945     ChangeCount[x][y] = 0;
3946     ChangeEvent[x][y] = -1;
3947
3948     ExplodePhase[x][y] = 0;
3949     ExplodeDelay[x][y] = 0;
3950     ExplodeField[x][y] = EX_TYPE_NONE;
3951
3952     RunnerVisit[x][y] = 0;
3953     PlayerVisit[x][y] = 0;
3954
3955     GfxFrame[x][y] = 0;
3956     GfxRandom[x][y] = INIT_GFX_RANDOM();
3957     GfxElement[x][y] = EL_UNDEFINED;
3958     GfxAction[x][y] = ACTION_DEFAULT;
3959     GfxDir[x][y] = MV_NONE;
3960     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3961   }
3962
3963   SCAN_PLAYFIELD(x, y)
3964   {
3965     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3966       emulate_bd = FALSE;
3967     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3968       emulate_sb = FALSE;
3969     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3970       emulate_sp = FALSE;
3971
3972     InitField(x, y, TRUE);
3973
3974     ResetGfxAnimation(x, y);
3975   }
3976
3977   InitBeltMovement();
3978
3979   for (i = 0; i < MAX_PLAYERS; i++)
3980   {
3981     struct PlayerInfo *player = &stored_player[i];
3982
3983     /* set number of special actions for bored and sleeping animation */
3984     player->num_special_action_bored =
3985       get_num_special_action(player->artwork_element,
3986                              ACTION_BORING_1, ACTION_BORING_LAST);
3987     player->num_special_action_sleeping =
3988       get_num_special_action(player->artwork_element,
3989                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3990   }
3991
3992   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3993                     emulate_sb ? EMU_SOKOBAN :
3994                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3995
3996 #if USE_NEW_ALL_SLIPPERY
3997   /* initialize type of slippery elements */
3998   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3999   {
4000     if (!IS_CUSTOM_ELEMENT(i))
4001     {
4002       /* default: elements slip down either to the left or right randomly */
4003       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4004
4005       /* SP style elements prefer to slip down on the left side */
4006       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4007         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4008
4009       /* BD style elements prefer to slip down on the left side */
4010       if (game.emulation == EMU_BOULDERDASH)
4011         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4012     }
4013   }
4014 #endif
4015
4016   /* initialize explosion and ignition delay */
4017   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4018   {
4019     if (!IS_CUSTOM_ELEMENT(i))
4020     {
4021       int num_phase = 8;
4022       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4023                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4024                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4025       int last_phase = (num_phase + 1) * delay;
4026       int half_phase = (num_phase / 2) * delay;
4027
4028       element_info[i].explosion_delay = last_phase - 1;
4029       element_info[i].ignition_delay = half_phase;
4030
4031       if (i == EL_BLACK_ORB)
4032         element_info[i].ignition_delay = 1;
4033     }
4034
4035 #if 0
4036     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4037       element_info[i].explosion_delay = 1;
4038
4039     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4040       element_info[i].ignition_delay = 1;
4041 #endif
4042   }
4043
4044   /* correct non-moving belts to start moving left */
4045   for (i = 0; i < NUM_BELTS; i++)
4046     if (game.belt_dir[i] == MV_NONE)
4047       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4048
4049 #if USE_NEW_PLAYER_ASSIGNMENTS
4050   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4051   /* choose default local player */
4052   local_player = &stored_player[0];
4053
4054   for (i = 0; i < MAX_PLAYERS; i++)
4055     stored_player[i].connected = FALSE;
4056
4057   local_player->connected = TRUE;
4058   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4059
4060   if (tape.playing)
4061   {
4062     /* try to guess locally connected team mode players (needed for correct
4063        assignment of player figures from level to locally playing players) */
4064
4065     for (i = 0; i < MAX_PLAYERS; i++)
4066       if (tape.player_participates[i])
4067         stored_player[i].connected = TRUE;
4068   }
4069   else if (setup.team_mode && !options.network)
4070   {
4071     /* try to guess locally connected team mode players (needed for correct
4072        assignment of player figures from level to locally playing players) */
4073
4074     for (i = 0; i < MAX_PLAYERS; i++)
4075       if (setup.input[i].use_joystick ||
4076           setup.input[i].key.left != KSYM_UNDEFINED)
4077         stored_player[i].connected = TRUE;
4078   }
4079
4080 #if 0
4081   for (i = 0; i < MAX_PLAYERS; i++)
4082     printf("::: player %d: %s\n", i,
4083            (stored_player[i].connected ? "connected" : "not connected"));
4084
4085   for (i = 0; i < MAX_PLAYERS; i++)
4086     printf("::: player %d: %s\n", i,
4087            (stored_player[i].present ? "present" : "not present"));
4088 #endif
4089
4090   /* check if any connected player was not found in playfield */
4091   for (i = 0; i < MAX_PLAYERS; i++)
4092   {
4093     struct PlayerInfo *player = &stored_player[i];
4094
4095     if (player->connected && !player->present)
4096     {
4097       struct PlayerInfo *field_player = NULL;
4098
4099 #if 0
4100       printf("::: looking for field player for player %d ...\n", i);
4101 #endif
4102
4103       /* assign first free player found that is present in the playfield */
4104
4105       /* first try: look for unmapped playfield player that is not connected */
4106       if (field_player == NULL)
4107         for (j = 0; j < MAX_PLAYERS; j++)
4108           if (stored_player[j].present &&
4109               !stored_player[j].mapped &&
4110               !stored_player[j].connected)
4111             field_player = &stored_player[j];
4112
4113       /* second try: look for *any* unmapped playfield player */
4114       if (field_player == NULL)
4115         for (j = 0; j < MAX_PLAYERS; j++)
4116           if (stored_player[j].present &&
4117               !stored_player[j].mapped)
4118             field_player = &stored_player[j];
4119
4120       if (field_player != NULL)
4121       {
4122         int jx = field_player->jx, jy = field_player->jy;
4123
4124 #if 0
4125         printf("::: found player figure %d\n", field_player->index_nr);
4126 #endif
4127
4128         player->present = FALSE;
4129         player->active = FALSE;
4130
4131         field_player->present = TRUE;
4132         field_player->active = TRUE;
4133
4134         /*
4135         player->initial_element = field_player->initial_element;
4136         player->artwork_element = field_player->artwork_element;
4137
4138         player->block_last_field       = field_player->block_last_field;
4139         player->block_delay_adjustment = field_player->block_delay_adjustment;
4140         */
4141
4142         StorePlayer[jx][jy] = field_player->element_nr;
4143
4144         field_player->jx = field_player->last_jx = jx;
4145         field_player->jy = field_player->last_jy = jy;
4146
4147         if (local_player == player)
4148           local_player = field_player;
4149
4150         map_player_action[field_player->index_nr] = i;
4151
4152         field_player->mapped = TRUE;
4153
4154 #if 0
4155         printf("::: map_player_action[%d] == %d\n",
4156                field_player->index_nr, i);
4157 #endif
4158       }
4159     }
4160
4161     if (player->connected && player->present)
4162       player->mapped = TRUE;
4163   }
4164
4165 #else
4166
4167   /* check if any connected player was not found in playfield */
4168   for (i = 0; i < MAX_PLAYERS; i++)
4169   {
4170     struct PlayerInfo *player = &stored_player[i];
4171
4172     if (player->connected && !player->present)
4173     {
4174       for (j = 0; j < MAX_PLAYERS; j++)
4175       {
4176         struct PlayerInfo *field_player = &stored_player[j];
4177         int jx = field_player->jx, jy = field_player->jy;
4178
4179         /* assign first free player found that is present in the playfield */
4180         if (field_player->present && !field_player->connected)
4181         {
4182           player->present = TRUE;
4183           player->active = TRUE;
4184
4185           field_player->present = FALSE;
4186           field_player->active = FALSE;
4187
4188           player->initial_element = field_player->initial_element;
4189           player->artwork_element = field_player->artwork_element;
4190
4191           player->block_last_field       = field_player->block_last_field;
4192           player->block_delay_adjustment = field_player->block_delay_adjustment;
4193
4194           StorePlayer[jx][jy] = player->element_nr;
4195
4196           player->jx = player->last_jx = jx;
4197           player->jy = player->last_jy = jy;
4198
4199           break;
4200         }
4201       }
4202     }
4203   }
4204 #endif
4205
4206 #if 0
4207   printf("::: local_player->present == %d\n", local_player->present);
4208 #endif
4209
4210   if (tape.playing)
4211   {
4212     /* when playing a tape, eliminate all players who do not participate */
4213
4214 #if USE_NEW_PLAYER_ASSIGNMENTS
4215     for (i = 0; i < MAX_PLAYERS; i++)
4216     {
4217       if (stored_player[i].active &&
4218           !tape.player_participates[map_player_action[i]])
4219       {
4220         struct PlayerInfo *player = &stored_player[i];
4221         int jx = player->jx, jy = player->jy;
4222
4223         player->active = FALSE;
4224         StorePlayer[jx][jy] = 0;
4225         Feld[jx][jy] = EL_EMPTY;
4226       }
4227     }
4228 #else
4229     for (i = 0; i < MAX_PLAYERS; i++)
4230     {
4231       if (stored_player[i].active && !tape.player_participates[i])
4232       {
4233         struct PlayerInfo *player = &stored_player[i];
4234         int jx = player->jx, jy = player->jy;
4235
4236         player->active = FALSE;
4237         StorePlayer[jx][jy] = 0;
4238         Feld[jx][jy] = EL_EMPTY;
4239       }
4240     }
4241 #endif
4242   }
4243   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4244   {
4245     /* when in single player mode, eliminate all but the first active player */
4246
4247     for (i = 0; i < MAX_PLAYERS; i++)
4248     {
4249       if (stored_player[i].active)
4250       {
4251         for (j = i + 1; j < MAX_PLAYERS; j++)
4252         {
4253           if (stored_player[j].active)
4254           {
4255             struct PlayerInfo *player = &stored_player[j];
4256             int jx = player->jx, jy = player->jy;
4257
4258             player->active = FALSE;
4259             player->present = FALSE;
4260
4261             StorePlayer[jx][jy] = 0;
4262             Feld[jx][jy] = EL_EMPTY;
4263           }
4264         }
4265       }
4266     }
4267   }
4268
4269   /* when recording the game, store which players take part in the game */
4270   if (tape.recording)
4271   {
4272 #if USE_NEW_PLAYER_ASSIGNMENTS
4273     for (i = 0; i < MAX_PLAYERS; i++)
4274       if (stored_player[i].connected)
4275         tape.player_participates[i] = TRUE;
4276 #else
4277     for (i = 0; i < MAX_PLAYERS; i++)
4278       if (stored_player[i].active)
4279         tape.player_participates[i] = TRUE;
4280 #endif
4281   }
4282
4283   if (options.debug)
4284   {
4285     for (i = 0; i < MAX_PLAYERS; i++)
4286     {
4287       struct PlayerInfo *player = &stored_player[i];
4288
4289       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4290              i+1,
4291              player->present,
4292              player->connected,
4293              player->active);
4294       if (local_player == player)
4295         printf("Player  %d is local player.\n", i+1);
4296     }
4297   }
4298
4299   if (BorderElement == EL_EMPTY)
4300   {
4301     SBX_Left = 0;
4302     SBX_Right = lev_fieldx - SCR_FIELDX;
4303     SBY_Upper = 0;
4304     SBY_Lower = lev_fieldy - SCR_FIELDY;
4305   }
4306   else
4307   {
4308     SBX_Left = -1;
4309     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4310     SBY_Upper = -1;
4311     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4312   }
4313
4314   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4315     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4316
4317   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4318     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4319
4320   /* if local player not found, look for custom element that might create
4321      the player (make some assumptions about the right custom element) */
4322   if (!local_player->present)
4323   {
4324     int start_x = 0, start_y = 0;
4325     int found_rating = 0;
4326     int found_element = EL_UNDEFINED;
4327     int player_nr = local_player->index_nr;
4328
4329     SCAN_PLAYFIELD(x, y)
4330     {
4331       int element = Feld[x][y];
4332       int content;
4333       int xx, yy;
4334       boolean is_player;
4335
4336       if (level.use_start_element[player_nr] &&
4337           level.start_element[player_nr] == element &&
4338           found_rating < 4)
4339       {
4340         start_x = x;
4341         start_y = y;
4342
4343         found_rating = 4;
4344         found_element = element;
4345       }
4346
4347       if (!IS_CUSTOM_ELEMENT(element))
4348         continue;
4349
4350       if (CAN_CHANGE(element))
4351       {
4352         for (i = 0; i < element_info[element].num_change_pages; i++)
4353         {
4354           /* check for player created from custom element as single target */
4355           content = element_info[element].change_page[i].target_element;
4356           is_player = ELEM_IS_PLAYER(content);
4357
4358           if (is_player && (found_rating < 3 ||
4359                             (found_rating == 3 && element < found_element)))
4360           {
4361             start_x = x;
4362             start_y = y;
4363
4364             found_rating = 3;
4365             found_element = element;
4366           }
4367         }
4368       }
4369
4370       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4371       {
4372         /* check for player created from custom element as explosion content */
4373         content = element_info[element].content.e[xx][yy];
4374         is_player = ELEM_IS_PLAYER(content);
4375
4376         if (is_player && (found_rating < 2 ||
4377                           (found_rating == 2 && element < found_element)))
4378         {
4379           start_x = x + xx - 1;
4380           start_y = y + yy - 1;
4381
4382           found_rating = 2;
4383           found_element = element;
4384         }
4385
4386         if (!CAN_CHANGE(element))
4387           continue;
4388
4389         for (i = 0; i < element_info[element].num_change_pages; i++)
4390         {
4391           /* check for player created from custom element as extended target */
4392           content =
4393             element_info[element].change_page[i].target_content.e[xx][yy];
4394
4395           is_player = ELEM_IS_PLAYER(content);
4396
4397           if (is_player && (found_rating < 1 ||
4398                             (found_rating == 1 && element < found_element)))
4399           {
4400             start_x = x + xx - 1;
4401             start_y = y + yy - 1;
4402
4403             found_rating = 1;
4404             found_element = element;
4405           }
4406         }
4407       }
4408     }
4409
4410     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4411                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4412                 start_x - MIDPOSX);
4413
4414     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4415                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4416                 start_y - MIDPOSY);
4417   }
4418   else
4419   {
4420     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4421                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4422                 local_player->jx - MIDPOSX);
4423
4424     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4425                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4426                 local_player->jy - MIDPOSY);
4427   }
4428
4429 #if 0
4430   /* do not use PLAYING mask for fading out from main screen */
4431   game_status = GAME_MODE_MAIN;
4432 #endif
4433
4434   StopAnimation();
4435
4436   if (!game.restart_level)
4437     CloseDoor(DOOR_CLOSE_1);
4438
4439 #if 1
4440   if (level_editor_test_game)
4441     FadeSkipNextFadeIn();
4442   else
4443     FadeSetEnterScreen();
4444 #else
4445   if (level_editor_test_game)
4446     fading = fading_none;
4447   else
4448     fading = menu.destination;
4449 #endif
4450
4451 #if 1
4452   FadeOut(REDRAW_FIELD);
4453 #else
4454   if (do_fading)
4455     FadeOut(REDRAW_FIELD);
4456 #endif
4457
4458 #if 0
4459   game_status = GAME_MODE_PLAYING;
4460 #endif
4461
4462   /* !!! FIX THIS (START) !!! */
4463   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4464   {
4465     InitGameEngine_EM();
4466
4467     /* blit playfield from scroll buffer to normal back buffer for fading in */
4468     BlitScreenToBitmap_EM(backbuffer);
4469   }
4470   else
4471   {
4472     DrawLevel();
4473     DrawAllPlayers();
4474
4475     /* after drawing the level, correct some elements */
4476     if (game.timegate_time_left == 0)
4477       CloseAllOpenTimegates();
4478
4479     /* blit playfield from scroll buffer to normal back buffer for fading in */
4480     if (setup.soft_scrolling)
4481       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4482
4483     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4484   }
4485   /* !!! FIX THIS (END) !!! */
4486
4487 #if 1
4488   FadeIn(REDRAW_FIELD);
4489 #else
4490   if (do_fading)
4491     FadeIn(REDRAW_FIELD);
4492
4493   BackToFront();
4494 #endif
4495
4496   if (!game.restart_level)
4497   {
4498     /* copy default game door content to main double buffer */
4499     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4500                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4501   }
4502
4503   SetPanelBackground();
4504   SetDrawBackgroundMask(REDRAW_DOOR_1);
4505
4506 #if 1
4507   UpdateAndDisplayGameControlValues();
4508 #else
4509   UpdateGameDoorValues();
4510   DrawGameDoorValues();
4511 #endif
4512
4513   if (!game.restart_level)
4514   {
4515     UnmapGameButtons();
4516     UnmapTapeButtons();
4517     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4518     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4519     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4520     MapGameButtons();
4521     MapTapeButtons();
4522
4523     /* copy actual game door content to door double buffer for OpenDoor() */
4524     BlitBitmap(drawto, bitmap_db_door,
4525                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4526
4527     OpenDoor(DOOR_OPEN_ALL);
4528
4529     PlaySound(SND_GAME_STARTING);
4530
4531     if (setup.sound_music)
4532       PlayLevelMusic();
4533
4534     KeyboardAutoRepeatOffUnlessAutoplay();
4535
4536     if (options.debug)
4537     {
4538       for (i = 0; i < MAX_PLAYERS; i++)
4539         printf("Player %d %sactive.\n",
4540                i + 1, (stored_player[i].active ? "" : "not "));
4541     }
4542   }
4543
4544 #if 1
4545   UnmapAllGadgets();
4546
4547   MapGameButtons();
4548   MapTapeButtons();
4549 #endif
4550
4551   game.restart_level = FALSE;
4552 }
4553
4554 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4555 {
4556   /* this is used for non-R'n'D game engines to update certain engine values */
4557
4558   /* needed to determine if sounds are played within the visible screen area */
4559   scroll_x = actual_scroll_x;
4560   scroll_y = actual_scroll_y;
4561 }
4562
4563 void InitMovDir(int x, int y)
4564 {
4565   int i, element = Feld[x][y];
4566   static int xy[4][2] =
4567   {
4568     {  0, +1 },
4569     { +1,  0 },
4570     {  0, -1 },
4571     { -1,  0 }
4572   };
4573   static int direction[3][4] =
4574   {
4575     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4576     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4577     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4578   };
4579
4580   switch (element)
4581   {
4582     case EL_BUG_RIGHT:
4583     case EL_BUG_UP:
4584     case EL_BUG_LEFT:
4585     case EL_BUG_DOWN:
4586       Feld[x][y] = EL_BUG;
4587       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4588       break;
4589
4590     case EL_SPACESHIP_RIGHT:
4591     case EL_SPACESHIP_UP:
4592     case EL_SPACESHIP_LEFT:
4593     case EL_SPACESHIP_DOWN:
4594       Feld[x][y] = EL_SPACESHIP;
4595       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4596       break;
4597
4598     case EL_BD_BUTTERFLY_RIGHT:
4599     case EL_BD_BUTTERFLY_UP:
4600     case EL_BD_BUTTERFLY_LEFT:
4601     case EL_BD_BUTTERFLY_DOWN:
4602       Feld[x][y] = EL_BD_BUTTERFLY;
4603       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4604       break;
4605
4606     case EL_BD_FIREFLY_RIGHT:
4607     case EL_BD_FIREFLY_UP:
4608     case EL_BD_FIREFLY_LEFT:
4609     case EL_BD_FIREFLY_DOWN:
4610       Feld[x][y] = EL_BD_FIREFLY;
4611       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4612       break;
4613
4614     case EL_PACMAN_RIGHT:
4615     case EL_PACMAN_UP:
4616     case EL_PACMAN_LEFT:
4617     case EL_PACMAN_DOWN:
4618       Feld[x][y] = EL_PACMAN;
4619       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4620       break;
4621
4622     case EL_YAMYAM_LEFT:
4623     case EL_YAMYAM_RIGHT:
4624     case EL_YAMYAM_UP:
4625     case EL_YAMYAM_DOWN:
4626       Feld[x][y] = EL_YAMYAM;
4627       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4628       break;
4629
4630     case EL_SP_SNIKSNAK:
4631       MovDir[x][y] = MV_UP;
4632       break;
4633
4634     case EL_SP_ELECTRON:
4635       MovDir[x][y] = MV_LEFT;
4636       break;
4637
4638     case EL_MOLE_LEFT:
4639     case EL_MOLE_RIGHT:
4640     case EL_MOLE_UP:
4641     case EL_MOLE_DOWN:
4642       Feld[x][y] = EL_MOLE;
4643       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4644       break;
4645
4646     default:
4647       if (IS_CUSTOM_ELEMENT(element))
4648       {
4649         struct ElementInfo *ei = &element_info[element];
4650         int move_direction_initial = ei->move_direction_initial;
4651         int move_pattern = ei->move_pattern;
4652
4653         if (move_direction_initial == MV_START_PREVIOUS)
4654         {
4655           if (MovDir[x][y] != MV_NONE)
4656             return;
4657
4658           move_direction_initial = MV_START_AUTOMATIC;
4659         }
4660
4661         if (move_direction_initial == MV_START_RANDOM)
4662           MovDir[x][y] = 1 << RND(4);
4663         else if (move_direction_initial & MV_ANY_DIRECTION)
4664           MovDir[x][y] = move_direction_initial;
4665         else if (move_pattern == MV_ALL_DIRECTIONS ||
4666                  move_pattern == MV_TURNING_LEFT ||
4667                  move_pattern == MV_TURNING_RIGHT ||
4668                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4669                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4670                  move_pattern == MV_TURNING_RANDOM)
4671           MovDir[x][y] = 1 << RND(4);
4672         else if (move_pattern == MV_HORIZONTAL)
4673           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4674         else if (move_pattern == MV_VERTICAL)
4675           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4676         else if (move_pattern & MV_ANY_DIRECTION)
4677           MovDir[x][y] = element_info[element].move_pattern;
4678         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4679                  move_pattern == MV_ALONG_RIGHT_SIDE)
4680         {
4681           /* use random direction as default start direction */
4682           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4683             MovDir[x][y] = 1 << RND(4);
4684
4685           for (i = 0; i < NUM_DIRECTIONS; i++)
4686           {
4687             int x1 = x + xy[i][0];
4688             int y1 = y + xy[i][1];
4689
4690             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4691             {
4692               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4693                 MovDir[x][y] = direction[0][i];
4694               else
4695                 MovDir[x][y] = direction[1][i];
4696
4697               break;
4698             }
4699           }
4700         }                
4701       }
4702       else
4703       {
4704         MovDir[x][y] = 1 << RND(4);
4705
4706         if (element != EL_BUG &&
4707             element != EL_SPACESHIP &&
4708             element != EL_BD_BUTTERFLY &&
4709             element != EL_BD_FIREFLY)
4710           break;
4711
4712         for (i = 0; i < NUM_DIRECTIONS; i++)
4713         {
4714           int x1 = x + xy[i][0];
4715           int y1 = y + xy[i][1];
4716
4717           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4718           {
4719             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4720             {
4721               MovDir[x][y] = direction[0][i];
4722               break;
4723             }
4724             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4725                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4726             {
4727               MovDir[x][y] = direction[1][i];
4728               break;
4729             }
4730           }
4731         }
4732       }
4733       break;
4734   }
4735
4736   GfxDir[x][y] = MovDir[x][y];
4737 }
4738
4739 void InitAmoebaNr(int x, int y)
4740 {
4741   int i;
4742   int group_nr = AmoebeNachbarNr(x, y);
4743
4744   if (group_nr == 0)
4745   {
4746     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4747     {
4748       if (AmoebaCnt[i] == 0)
4749       {
4750         group_nr = i;
4751         break;
4752       }
4753     }
4754   }
4755
4756   AmoebaNr[x][y] = group_nr;
4757   AmoebaCnt[group_nr]++;
4758   AmoebaCnt2[group_nr]++;
4759 }
4760
4761 static void PlayerWins(struct PlayerInfo *player)
4762 {
4763   player->LevelSolved = TRUE;
4764   player->GameOver = TRUE;
4765
4766   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4767                          level.native_em_level->lev->score : player->score);
4768
4769   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4770   player->LevelSolved_CountingScore = player->score_final;
4771 }
4772
4773 void GameWon()
4774 {
4775   static int time, time_final;
4776   static int score, score_final;
4777   static int game_over_delay_1 = 0;
4778   static int game_over_delay_2 = 0;
4779   int game_over_delay_value_1 = 50;
4780   int game_over_delay_value_2 = 50;
4781
4782   if (!local_player->LevelSolved_GameWon)
4783   {
4784     int i;
4785
4786     /* do not start end game actions before the player stops moving (to exit) */
4787     if (local_player->MovPos)
4788       return;
4789
4790     local_player->LevelSolved_GameWon = TRUE;
4791     local_player->LevelSolved_SaveTape = tape.recording;
4792     local_player->LevelSolved_SaveScore = !tape.playing;
4793
4794     if (tape.auto_play)         /* tape might already be stopped here */
4795       tape.auto_play_level_solved = TRUE;
4796
4797 #if 1
4798     TapeStop();
4799 #endif
4800
4801     game_over_delay_1 = game_over_delay_value_1;
4802     game_over_delay_2 = game_over_delay_value_2;
4803
4804     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4805     score = score_final = local_player->score_final;
4806
4807     if (TimeLeft > 0)
4808     {
4809       time_final = 0;
4810       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4811     }
4812     else if (level.time == 0 && TimePlayed < 999)
4813     {
4814       time_final = 999;
4815       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4816     }
4817
4818     local_player->score_final = score_final;
4819
4820     if (level_editor_test_game)
4821     {
4822       time = time_final;
4823       score = score_final;
4824
4825 #if 1
4826       local_player->LevelSolved_CountingTime = time;
4827       local_player->LevelSolved_CountingScore = score;
4828
4829       game_panel_controls[GAME_PANEL_TIME].value = time;
4830       game_panel_controls[GAME_PANEL_SCORE].value = score;
4831
4832       DisplayGameControlValues();
4833 #else
4834       DrawGameValue_Time(time);
4835       DrawGameValue_Score(score);
4836 #endif
4837     }
4838
4839     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4840     {
4841       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4842       {
4843         /* close exit door after last player */
4844         if ((AllPlayersGone &&
4845              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4846               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4847               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4848             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4849             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4850         {
4851           int element = Feld[ExitX][ExitY];
4852
4853 #if 0
4854           if (element == EL_EM_EXIT_OPEN ||
4855               element == EL_EM_STEEL_EXIT_OPEN)
4856           {
4857             Bang(ExitX, ExitY);
4858           }
4859           else
4860 #endif
4861           {
4862             Feld[ExitX][ExitY] =
4863               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4864                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4865                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4866                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4867                EL_EM_STEEL_EXIT_CLOSING);
4868
4869             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4870           }
4871         }
4872
4873         /* player disappears */
4874         DrawLevelField(ExitX, ExitY);
4875       }
4876
4877       for (i = 0; i < MAX_PLAYERS; i++)
4878       {
4879         struct PlayerInfo *player = &stored_player[i];
4880
4881         if (player->present)
4882         {
4883           RemovePlayer(player);
4884
4885           /* player disappears */
4886           DrawLevelField(player->jx, player->jy);
4887         }
4888       }
4889     }
4890
4891     PlaySound(SND_GAME_WINNING);
4892   }
4893
4894   if (game_over_delay_1 > 0)
4895   {
4896     game_over_delay_1--;
4897
4898     return;
4899   }
4900
4901   if (time != time_final)
4902   {
4903     int time_to_go = ABS(time_final - time);
4904     int time_count_dir = (time < time_final ? +1 : -1);
4905     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4906
4907     time  += time_count_steps * time_count_dir;
4908     score += time_count_steps * level.score[SC_TIME_BONUS];
4909
4910 #if 1
4911     local_player->LevelSolved_CountingTime = time;
4912     local_player->LevelSolved_CountingScore = score;
4913
4914     game_panel_controls[GAME_PANEL_TIME].value = time;
4915     game_panel_controls[GAME_PANEL_SCORE].value = score;
4916
4917     DisplayGameControlValues();
4918 #else
4919     DrawGameValue_Time(time);
4920     DrawGameValue_Score(score);
4921 #endif
4922
4923     if (time == time_final)
4924       StopSound(SND_GAME_LEVELTIME_BONUS);
4925     else if (setup.sound_loops)
4926       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4927     else
4928       PlaySound(SND_GAME_LEVELTIME_BONUS);
4929
4930     return;
4931   }
4932
4933   local_player->LevelSolved_PanelOff = TRUE;
4934
4935   if (game_over_delay_2 > 0)
4936   {
4937     game_over_delay_2--;
4938
4939     return;
4940   }
4941
4942 #if 1
4943   GameEnd();
4944 #endif
4945 }
4946
4947 void GameEnd()
4948 {
4949   int hi_pos;
4950   boolean raise_level = FALSE;
4951
4952   local_player->LevelSolved_GameEnd = TRUE;
4953
4954   CloseDoor(DOOR_CLOSE_1);
4955
4956   if (local_player->LevelSolved_SaveTape)
4957   {
4958 #if 0
4959     TapeStop();
4960 #endif
4961
4962 #if 1
4963     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4964 #else
4965     SaveTape(tape.level_nr);            /* ask to save tape */
4966 #endif
4967   }
4968
4969   if (level_editor_test_game)
4970   {
4971     game_status = GAME_MODE_MAIN;
4972
4973 #if 1
4974     DrawAndFadeInMainMenu(REDRAW_FIELD);
4975 #else
4976     DrawMainMenu();
4977 #endif
4978
4979     return;
4980   }
4981
4982   if (!local_player->LevelSolved_SaveScore)
4983   {
4984 #if 1
4985     FadeOut(REDRAW_FIELD);
4986 #endif
4987
4988     game_status = GAME_MODE_MAIN;
4989
4990     DrawAndFadeInMainMenu(REDRAW_FIELD);
4991
4992     return;
4993   }
4994
4995   if (level_nr == leveldir_current->handicap_level)
4996   {
4997     leveldir_current->handicap_level++;
4998     SaveLevelSetup_SeriesInfo();
4999   }
5000
5001   if (level_nr < leveldir_current->last_level)
5002     raise_level = TRUE;                 /* advance to next level */
5003
5004   if ((hi_pos = NewHiScore()) >= 0) 
5005   {
5006     game_status = GAME_MODE_SCORES;
5007
5008     DrawHallOfFame(hi_pos);
5009
5010     if (raise_level)
5011     {
5012       level_nr++;
5013       TapeErase();
5014     }
5015   }
5016   else
5017   {
5018 #if 1
5019     FadeOut(REDRAW_FIELD);
5020 #endif
5021
5022     game_status = GAME_MODE_MAIN;
5023
5024     if (raise_level)
5025     {
5026       level_nr++;
5027       TapeErase();
5028     }
5029
5030     DrawAndFadeInMainMenu(REDRAW_FIELD);
5031   }
5032 }
5033
5034 int NewHiScore()
5035 {
5036   int k, l;
5037   int position = -1;
5038
5039   LoadScore(level_nr);
5040
5041   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5042       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5043     return -1;
5044
5045   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5046   {
5047     if (local_player->score_final > highscore[k].Score)
5048     {
5049       /* player has made it to the hall of fame */
5050
5051       if (k < MAX_SCORE_ENTRIES - 1)
5052       {
5053         int m = MAX_SCORE_ENTRIES - 1;
5054
5055 #ifdef ONE_PER_NAME
5056         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5057           if (strEqual(setup.player_name, highscore[l].Name))
5058             m = l;
5059         if (m == k)     /* player's new highscore overwrites his old one */
5060           goto put_into_list;
5061 #endif
5062
5063         for (l = m; l > k; l--)
5064         {
5065           strcpy(highscore[l].Name, highscore[l - 1].Name);
5066           highscore[l].Score = highscore[l - 1].Score;
5067         }
5068       }
5069
5070 #ifdef ONE_PER_NAME
5071       put_into_list:
5072 #endif
5073       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5074       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5075       highscore[k].Score = local_player->score_final; 
5076       position = k;
5077       break;
5078     }
5079
5080 #ifdef ONE_PER_NAME
5081     else if (!strncmp(setup.player_name, highscore[k].Name,
5082                       MAX_PLAYER_NAME_LEN))
5083       break;    /* player already there with a higher score */
5084 #endif
5085
5086   }
5087
5088   if (position >= 0) 
5089     SaveScore(level_nr);
5090
5091   return position;
5092 }
5093
5094 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5095 {
5096   int element = Feld[x][y];
5097   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5098   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5099   int horiz_move = (dx != 0);
5100   int sign = (horiz_move ? dx : dy);
5101   int step = sign * element_info[element].move_stepsize;
5102
5103   /* special values for move stepsize for spring and things on conveyor belt */
5104   if (horiz_move)
5105   {
5106     if (CAN_FALL(element) &&
5107         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5108       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5109     else if (element == EL_SPRING)
5110       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5111   }
5112
5113   return step;
5114 }
5115
5116 inline static int getElementMoveStepsize(int x, int y)
5117 {
5118   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5119 }
5120
5121 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5122 {
5123   if (player->GfxAction != action || player->GfxDir != dir)
5124   {
5125 #if 0
5126     printf("Player frame reset! (%d => %d, %d => %d)\n",
5127            player->GfxAction, action, player->GfxDir, dir);
5128 #endif
5129
5130     player->GfxAction = action;
5131     player->GfxDir = dir;
5132     player->Frame = 0;
5133     player->StepFrame = 0;
5134   }
5135 }
5136
5137 #if USE_GFX_RESET_GFX_ANIMATION
5138 static void ResetGfxFrame(int x, int y, boolean redraw)
5139 {
5140   int element = Feld[x][y];
5141   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5142   int last_gfx_frame = GfxFrame[x][y];
5143
5144   if (graphic_info[graphic].anim_global_sync)
5145     GfxFrame[x][y] = FrameCounter;
5146   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5147     GfxFrame[x][y] = CustomValue[x][y];
5148   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5149     GfxFrame[x][y] = element_info[element].collect_score;
5150   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5151     GfxFrame[x][y] = ChangeDelay[x][y];
5152
5153   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5154     DrawLevelGraphicAnimation(x, y, graphic);
5155 }
5156 #endif
5157
5158 static void ResetGfxAnimation(int x, int y)
5159 {
5160   GfxAction[x][y] = ACTION_DEFAULT;
5161   GfxDir[x][y] = MovDir[x][y];
5162   GfxFrame[x][y] = 0;
5163
5164 #if USE_GFX_RESET_GFX_ANIMATION
5165   ResetGfxFrame(x, y, FALSE);
5166 #endif
5167 }
5168
5169 static void ResetRandomAnimationValue(int x, int y)
5170 {
5171   GfxRandom[x][y] = INIT_GFX_RANDOM();
5172 }
5173
5174 void InitMovingField(int x, int y, int direction)
5175 {
5176   int element = Feld[x][y];
5177   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5178   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5179   int newx = x + dx;
5180   int newy = y + dy;
5181   boolean is_moving_before, is_moving_after;
5182 #if 0
5183   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5184 #endif
5185
5186   /* check if element was/is moving or being moved before/after mode change */
5187 #if 1
5188 #if 1
5189   is_moving_before = (WasJustMoving[x][y] != 0);
5190 #else
5191   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5192   is_moving_before = WasJustMoving[x][y];
5193 #endif
5194 #else
5195   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5196 #endif
5197   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5198
5199   /* reset animation only for moving elements which change direction of moving
5200      or which just started or stopped moving
5201      (else CEs with property "can move" / "not moving" are reset each frame) */
5202 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5203 #if 1
5204   if (is_moving_before != is_moving_after ||
5205       direction != MovDir[x][y])
5206     ResetGfxAnimation(x, y);
5207 #else
5208   if ((is_moving_before || is_moving_after) && !continues_moving)
5209     ResetGfxAnimation(x, y);
5210 #endif
5211 #else
5212   if (!continues_moving)
5213     ResetGfxAnimation(x, y);
5214 #endif
5215
5216   MovDir[x][y] = direction;
5217   GfxDir[x][y] = direction;
5218
5219 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5220   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5221                      direction == MV_DOWN && CAN_FALL(element) ?
5222                      ACTION_FALLING : ACTION_MOVING);
5223 #else
5224   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5225                      ACTION_FALLING : ACTION_MOVING);
5226 #endif
5227
5228   /* this is needed for CEs with property "can move" / "not moving" */
5229
5230   if (is_moving_after)
5231   {
5232     if (Feld[newx][newy] == EL_EMPTY)
5233       Feld[newx][newy] = EL_BLOCKED;
5234
5235     MovDir[newx][newy] = MovDir[x][y];
5236
5237 #if USE_NEW_CUSTOM_VALUE
5238     CustomValue[newx][newy] = CustomValue[x][y];
5239 #endif
5240
5241     GfxFrame[newx][newy] = GfxFrame[x][y];
5242     GfxRandom[newx][newy] = GfxRandom[x][y];
5243     GfxAction[newx][newy] = GfxAction[x][y];
5244     GfxDir[newx][newy] = GfxDir[x][y];
5245   }
5246 }
5247
5248 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5249 {
5250   int direction = MovDir[x][y];
5251   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5252   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5253
5254   *goes_to_x = newx;
5255   *goes_to_y = newy;
5256 }
5257
5258 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5259 {
5260   int oldx = x, oldy = y;
5261   int direction = MovDir[x][y];
5262
5263   if (direction == MV_LEFT)
5264     oldx++;
5265   else if (direction == MV_RIGHT)
5266     oldx--;
5267   else if (direction == MV_UP)
5268     oldy++;
5269   else if (direction == MV_DOWN)
5270     oldy--;
5271
5272   *comes_from_x = oldx;
5273   *comes_from_y = oldy;
5274 }
5275
5276 int MovingOrBlocked2Element(int x, int y)
5277 {
5278   int element = Feld[x][y];
5279
5280   if (element == EL_BLOCKED)
5281   {
5282     int oldx, oldy;
5283
5284     Blocked2Moving(x, y, &oldx, &oldy);
5285     return Feld[oldx][oldy];
5286   }
5287   else
5288     return element;
5289 }
5290
5291 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5292 {
5293   /* like MovingOrBlocked2Element(), but if element is moving
5294      and (x,y) is the field the moving element is just leaving,
5295      return EL_BLOCKED instead of the element value */
5296   int element = Feld[x][y];
5297
5298   if (IS_MOVING(x, y))
5299   {
5300     if (element == EL_BLOCKED)
5301     {
5302       int oldx, oldy;
5303
5304       Blocked2Moving(x, y, &oldx, &oldy);
5305       return Feld[oldx][oldy];
5306     }
5307     else
5308       return EL_BLOCKED;
5309   }
5310   else
5311     return element;
5312 }
5313
5314 static void RemoveField(int x, int y)
5315 {
5316   Feld[x][y] = EL_EMPTY;
5317
5318   MovPos[x][y] = 0;
5319   MovDir[x][y] = 0;
5320   MovDelay[x][y] = 0;
5321
5322 #if USE_NEW_CUSTOM_VALUE
5323   CustomValue[x][y] = 0;
5324 #endif
5325
5326   AmoebaNr[x][y] = 0;
5327   ChangeDelay[x][y] = 0;
5328   ChangePage[x][y] = -1;
5329   Pushed[x][y] = FALSE;
5330
5331 #if 0
5332   ExplodeField[x][y] = EX_TYPE_NONE;
5333 #endif
5334
5335   GfxElement[x][y] = EL_UNDEFINED;
5336   GfxAction[x][y] = ACTION_DEFAULT;
5337   GfxDir[x][y] = MV_NONE;
5338 #if 0
5339   /* !!! this would prevent the removed tile from being redrawn !!! */
5340   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5341 #endif
5342 }
5343
5344 void RemoveMovingField(int x, int y)
5345 {
5346   int oldx = x, oldy = y, newx = x, newy = y;
5347   int element = Feld[x][y];
5348   int next_element = EL_UNDEFINED;
5349
5350   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5351     return;
5352
5353   if (IS_MOVING(x, y))
5354   {
5355     Moving2Blocked(x, y, &newx, &newy);
5356
5357     if (Feld[newx][newy] != EL_BLOCKED)
5358     {
5359       /* element is moving, but target field is not free (blocked), but
5360          already occupied by something different (example: acid pool);
5361          in this case, only remove the moving field, but not the target */
5362
5363       RemoveField(oldx, oldy);
5364
5365       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5366
5367       TEST_DrawLevelField(oldx, oldy);
5368
5369       return;
5370     }
5371   }
5372   else if (element == EL_BLOCKED)
5373   {
5374     Blocked2Moving(x, y, &oldx, &oldy);
5375     if (!IS_MOVING(oldx, oldy))
5376       return;
5377   }
5378
5379   if (element == EL_BLOCKED &&
5380       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5381        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5382        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5383        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5384        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5385        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5386     next_element = get_next_element(Feld[oldx][oldy]);
5387
5388   RemoveField(oldx, oldy);
5389   RemoveField(newx, newy);
5390
5391   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5392
5393   if (next_element != EL_UNDEFINED)
5394     Feld[oldx][oldy] = next_element;
5395
5396   TEST_DrawLevelField(oldx, oldy);
5397   TEST_DrawLevelField(newx, newy);
5398 }
5399
5400 void DrawDynamite(int x, int y)
5401 {
5402   int sx = SCREENX(x), sy = SCREENY(y);
5403   int graphic = el2img(Feld[x][y]);
5404   int frame;
5405
5406   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5407     return;
5408
5409   if (IS_WALKABLE_INSIDE(Back[x][y]))
5410     return;
5411
5412   if (Back[x][y])
5413     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5414   else if (Store[x][y])
5415     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5416
5417   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5418
5419   if (Back[x][y] || Store[x][y])
5420     DrawGraphicThruMask(sx, sy, graphic, frame);
5421   else
5422     DrawGraphic(sx, sy, graphic, frame);
5423 }
5424
5425 void CheckDynamite(int x, int y)
5426 {
5427   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5428   {
5429     MovDelay[x][y]--;
5430
5431     if (MovDelay[x][y] != 0)
5432     {
5433       DrawDynamite(x, y);
5434       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5435
5436       return;
5437     }
5438   }
5439
5440   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5441
5442   Bang(x, y);
5443 }
5444
5445 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5446 {
5447   boolean num_checked_players = 0;
5448   int i;
5449
5450   for (i = 0; i < MAX_PLAYERS; i++)
5451   {
5452     if (stored_player[i].active)
5453     {
5454       int sx = stored_player[i].jx;
5455       int sy = stored_player[i].jy;
5456
5457       if (num_checked_players == 0)
5458       {
5459         *sx1 = *sx2 = sx;
5460         *sy1 = *sy2 = sy;
5461       }
5462       else
5463       {
5464         *sx1 = MIN(*sx1, sx);
5465         *sy1 = MIN(*sy1, sy);
5466         *sx2 = MAX(*sx2, sx);
5467         *sy2 = MAX(*sy2, sy);
5468       }
5469
5470       num_checked_players++;
5471     }
5472   }
5473 }
5474
5475 static boolean checkIfAllPlayersFitToScreen_RND()
5476 {
5477   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5478
5479   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5480
5481   return (sx2 - sx1 < SCR_FIELDX &&
5482           sy2 - sy1 < SCR_FIELDY);
5483 }
5484
5485 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5486 {
5487   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5488
5489   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5490
5491   *sx = (sx1 + sx2) / 2;
5492   *sy = (sy1 + sy2) / 2;
5493 }
5494
5495 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5496                         boolean center_screen, boolean quick_relocation)
5497 {
5498   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5499   boolean no_delay = (tape.warp_forward);
5500   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5501   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5502
5503   if (quick_relocation)
5504   {
5505     int offset = game.scroll_delay_value;
5506
5507     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5508     {
5509       if (!level.shifted_relocation || center_screen)
5510       {
5511         /* quick relocation (without scrolling), with centering of screen */
5512
5513         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5514                     x > SBX_Right + MIDPOSX ? SBX_Right :
5515                     x - MIDPOSX);
5516
5517         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5518                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5519                     y - MIDPOSY);
5520       }
5521       else
5522       {
5523         /* quick relocation (without scrolling), but do not center screen */
5524
5525         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5526                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5527                                old_x - MIDPOSX);
5528
5529         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5530                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5531                                old_y - MIDPOSY);
5532
5533         int offset_x = x + (scroll_x - center_scroll_x);
5534         int offset_y = y + (scroll_y - center_scroll_y);
5535
5536         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5537                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5538                     offset_x - MIDPOSX);
5539
5540         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5541                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5542                     offset_y - MIDPOSY);
5543       }
5544     }
5545     else
5546     {
5547       /* quick relocation (without scrolling), inside visible screen area */
5548
5549       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5550           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5551         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5552
5553       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5554           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5555         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5556
5557       /* don't scroll over playfield boundaries */
5558       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5559         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5560
5561       /* don't scroll over playfield boundaries */
5562       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5563         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5564     }
5565
5566     RedrawPlayfield(TRUE, 0,0,0,0);
5567   }
5568   else
5569   {
5570 #if 1
5571     int scroll_xx, scroll_yy;
5572
5573     if (!level.shifted_relocation || center_screen)
5574     {
5575       /* visible relocation (with scrolling), with centering of screen */
5576
5577       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5578                    x > SBX_Right + MIDPOSX ? SBX_Right :
5579                    x - MIDPOSX);
5580
5581       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5582                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5583                    y - MIDPOSY);
5584     }
5585     else
5586     {
5587       /* visible relocation (with scrolling), but do not center screen */
5588
5589       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5590                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5591                              old_x - MIDPOSX);
5592
5593       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5594                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5595                              old_y - MIDPOSY);
5596
5597       int offset_x = x + (scroll_x - center_scroll_x);
5598       int offset_y = y + (scroll_y - center_scroll_y);
5599
5600       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5601                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5602                    offset_x - MIDPOSX);
5603
5604       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5605                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5606                    offset_y - MIDPOSY);
5607     }
5608
5609 #else
5610
5611     /* visible relocation (with scrolling), with centering of screen */
5612
5613     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5614                      x > SBX_Right + MIDPOSX ? SBX_Right :
5615                      x - MIDPOSX);
5616
5617     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5618                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5619                      y - MIDPOSY);
5620 #endif
5621
5622     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5623
5624     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5625     {
5626       int dx = 0, dy = 0;
5627       int fx = FX, fy = FY;
5628
5629       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5630       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5631
5632       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5633         break;
5634
5635       scroll_x -= dx;
5636       scroll_y -= dy;
5637
5638       fx += dx * TILEX / 2;
5639       fy += dy * TILEY / 2;
5640
5641       ScrollLevel(dx, dy);
5642       DrawAllPlayers();
5643
5644       /* scroll in two steps of half tile size to make things smoother */
5645       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5646       FlushDisplay();
5647       Delay(wait_delay_value);
5648
5649       /* scroll second step to align at full tile size */
5650       BackToFront();
5651       Delay(wait_delay_value);
5652     }
5653
5654     DrawAllPlayers();
5655     BackToFront();
5656     Delay(wait_delay_value);
5657   }
5658 }
5659
5660 void RelocatePlayer(int jx, int jy, int el_player_raw)
5661 {
5662   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5663   int player_nr = GET_PLAYER_NR(el_player);
5664   struct PlayerInfo *player = &stored_player[player_nr];
5665   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5666   boolean no_delay = (tape.warp_forward);
5667   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5668   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5669   int old_jx = player->jx;
5670   int old_jy = player->jy;
5671   int old_element = Feld[old_jx][old_jy];
5672   int element = Feld[jx][jy];
5673   boolean player_relocated = (old_jx != jx || old_jy != jy);
5674
5675   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5676   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5677   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5678   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5679   int leave_side_horiz = move_dir_horiz;
5680   int leave_side_vert  = move_dir_vert;
5681   int enter_side = enter_side_horiz | enter_side_vert;
5682   int leave_side = leave_side_horiz | leave_side_vert;
5683
5684   if (player->GameOver)         /* do not reanimate dead player */
5685     return;
5686
5687   if (!player_relocated)        /* no need to relocate the player */
5688     return;
5689
5690   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5691   {
5692     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5693     DrawLevelField(jx, jy);
5694   }
5695
5696   if (player->present)
5697   {
5698     while (player->MovPos)
5699     {
5700       ScrollPlayer(player, SCROLL_GO_ON);
5701       ScrollScreen(NULL, SCROLL_GO_ON);
5702
5703       AdvanceFrameAndPlayerCounters(player->index_nr);
5704
5705       DrawPlayer(player);
5706
5707       BackToFront();
5708       Delay(wait_delay_value);
5709     }
5710
5711     DrawPlayer(player);         /* needed here only to cleanup last field */
5712     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5713
5714     player->is_moving = FALSE;
5715   }
5716
5717   if (IS_CUSTOM_ELEMENT(old_element))
5718     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5719                                CE_LEFT_BY_PLAYER,
5720                                player->index_bit, leave_side);
5721
5722   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5723                                       CE_PLAYER_LEAVES_X,
5724                                       player->index_bit, leave_side);
5725
5726   Feld[jx][jy] = el_player;
5727   InitPlayerField(jx, jy, el_player, TRUE);
5728
5729   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5730   {
5731     Feld[jx][jy] = element;
5732     InitField(jx, jy, FALSE);
5733   }
5734
5735   /* only visually relocate centered player */
5736   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5737                      FALSE, level.instant_relocation);
5738
5739   TestIfPlayerTouchesBadThing(jx, jy);
5740   TestIfPlayerTouchesCustomElement(jx, jy);
5741
5742   if (IS_CUSTOM_ELEMENT(element))
5743     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5744                                player->index_bit, enter_side);
5745
5746   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5747                                       player->index_bit, enter_side);
5748
5749 #if 1
5750   if (player->is_switching)
5751   {
5752     /* ensure that relocation while still switching an element does not cause
5753        a new element to be treated as also switched directly after relocation
5754        (this is important for teleporter switches that teleport the player to
5755        a place where another teleporter switch is in the same direction, which
5756        would then incorrectly be treated as immediately switched before the
5757        direction key that caused the switch was released) */
5758
5759     player->switch_x += jx - old_jx;
5760     player->switch_y += jy - old_jy;
5761   }
5762 #endif
5763 }
5764
5765 void Explode(int ex, int ey, int phase, int mode)
5766 {
5767   int x, y;
5768   int last_phase;
5769   int border_element;
5770
5771   /* !!! eliminate this variable !!! */
5772   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5773
5774   if (game.explosions_delayed)
5775   {
5776     ExplodeField[ex][ey] = mode;
5777     return;
5778   }
5779
5780   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5781   {
5782     int center_element = Feld[ex][ey];
5783     int artwork_element, explosion_element;     /* set these values later */
5784
5785 #if 0
5786     /* --- This is only really needed (and now handled) in "Impact()". --- */
5787     /* do not explode moving elements that left the explode field in time */
5788     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5789         center_element == EL_EMPTY &&
5790         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5791       return;
5792 #endif
5793
5794 #if 0
5795     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5796     if (mode == EX_TYPE_NORMAL ||
5797         mode == EX_TYPE_CENTER ||
5798         mode == EX_TYPE_CROSS)
5799       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5800 #endif
5801
5802     /* remove things displayed in background while burning dynamite */
5803     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5804       Back[ex][ey] = 0;
5805
5806     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5807     {
5808       /* put moving element to center field (and let it explode there) */
5809       center_element = MovingOrBlocked2Element(ex, ey);
5810       RemoveMovingField(ex, ey);
5811       Feld[ex][ey] = center_element;
5812     }
5813
5814     /* now "center_element" is finally determined -- set related values now */
5815     artwork_element = center_element;           /* for custom player artwork */
5816     explosion_element = center_element;         /* for custom player artwork */
5817
5818     if (IS_PLAYER(ex, ey))
5819     {
5820       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5821
5822       artwork_element = stored_player[player_nr].artwork_element;
5823
5824       if (level.use_explosion_element[player_nr])
5825       {
5826         explosion_element = level.explosion_element[player_nr];
5827         artwork_element = explosion_element;
5828       }
5829     }
5830
5831 #if 1
5832     if (mode == EX_TYPE_NORMAL ||
5833         mode == EX_TYPE_CENTER ||
5834         mode == EX_TYPE_CROSS)
5835       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5836 #endif
5837
5838     last_phase = element_info[explosion_element].explosion_delay + 1;
5839
5840     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5841     {
5842       int xx = x - ex + 1;
5843       int yy = y - ey + 1;
5844       int element;
5845
5846       if (!IN_LEV_FIELD(x, y) ||
5847           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5848           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5849         continue;
5850
5851       element = Feld[x][y];
5852
5853       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5854       {
5855         element = MovingOrBlocked2Element(x, y);
5856
5857         if (!IS_EXPLOSION_PROOF(element))
5858           RemoveMovingField(x, y);
5859       }
5860
5861       /* indestructible elements can only explode in center (but not flames) */
5862       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5863                                            mode == EX_TYPE_BORDER)) ||
5864           element == EL_FLAMES)
5865         continue;
5866
5867       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5868          behaviour, for example when touching a yamyam that explodes to rocks
5869          with active deadly shield, a rock is created under the player !!! */
5870       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5871 #if 0
5872       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5873           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5874            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5875 #else
5876       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5877 #endif
5878       {
5879         if (IS_ACTIVE_BOMB(element))
5880         {
5881           /* re-activate things under the bomb like gate or penguin */
5882           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5883           Back[x][y] = 0;
5884         }
5885
5886         continue;
5887       }
5888
5889       /* save walkable background elements while explosion on same tile */
5890       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5891           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5892         Back[x][y] = element;
5893
5894       /* ignite explodable elements reached by other explosion */
5895       if (element == EL_EXPLOSION)
5896         element = Store2[x][y];
5897
5898       if (AmoebaNr[x][y] &&
5899           (element == EL_AMOEBA_FULL ||
5900            element == EL_BD_AMOEBA ||
5901            element == EL_AMOEBA_GROWING))
5902       {
5903         AmoebaCnt[AmoebaNr[x][y]]--;
5904         AmoebaCnt2[AmoebaNr[x][y]]--;
5905       }
5906
5907       RemoveField(x, y);
5908
5909       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5910       {
5911         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5912
5913         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5914
5915         if (PLAYERINFO(ex, ey)->use_murphy)
5916           Store[x][y] = EL_EMPTY;
5917       }
5918
5919       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5920          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5921       else if (ELEM_IS_PLAYER(center_element))
5922         Store[x][y] = EL_EMPTY;
5923       else if (center_element == EL_YAMYAM)
5924         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5925       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5926         Store[x][y] = element_info[center_element].content.e[xx][yy];
5927 #if 1
5928       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5929          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5930          otherwise) -- FIX THIS !!! */
5931       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5932         Store[x][y] = element_info[element].content.e[1][1];
5933 #else
5934       else if (!CAN_EXPLODE(element))
5935         Store[x][y] = element_info[element].content.e[1][1];
5936 #endif
5937       else
5938         Store[x][y] = EL_EMPTY;
5939
5940       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5941           center_element == EL_AMOEBA_TO_DIAMOND)
5942         Store2[x][y] = element;
5943
5944       Feld[x][y] = EL_EXPLOSION;
5945       GfxElement[x][y] = artwork_element;
5946
5947       ExplodePhase[x][y] = 1;
5948       ExplodeDelay[x][y] = last_phase;
5949
5950       Stop[x][y] = TRUE;
5951     }
5952
5953     if (center_element == EL_YAMYAM)
5954       game.yamyam_content_nr =
5955         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5956
5957     return;
5958   }
5959
5960   if (Stop[ex][ey])
5961     return;
5962
5963   x = ex;
5964   y = ey;
5965
5966   if (phase == 1)
5967     GfxFrame[x][y] = 0;         /* restart explosion animation */
5968
5969   last_phase = ExplodeDelay[x][y];
5970
5971   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5972
5973 #ifdef DEBUG
5974
5975   /* activate this even in non-DEBUG version until cause for crash in
5976      getGraphicAnimationFrame() (see below) is found and eliminated */
5977
5978 #endif
5979 #if 1
5980
5981 #if 1
5982   /* this can happen if the player leaves an explosion just in time */
5983   if (GfxElement[x][y] == EL_UNDEFINED)
5984     GfxElement[x][y] = EL_EMPTY;
5985 #else
5986   if (GfxElement[x][y] == EL_UNDEFINED)
5987   {
5988     printf("\n\n");
5989     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5990     printf("Explode(): This should never happen!\n");
5991     printf("\n\n");
5992
5993     GfxElement[x][y] = EL_EMPTY;
5994   }
5995 #endif
5996
5997 #endif
5998
5999   border_element = Store2[x][y];
6000   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6001     border_element = StorePlayer[x][y];
6002
6003   if (phase == element_info[border_element].ignition_delay ||
6004       phase == last_phase)
6005   {
6006     boolean border_explosion = FALSE;
6007
6008     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6009         !PLAYER_EXPLOSION_PROTECTED(x, y))
6010     {
6011       KillPlayerUnlessExplosionProtected(x, y);
6012       border_explosion = TRUE;
6013     }
6014     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6015     {
6016       Feld[x][y] = Store2[x][y];
6017       Store2[x][y] = 0;
6018       Bang(x, y);
6019       border_explosion = TRUE;
6020     }
6021     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6022     {
6023       AmoebeUmwandeln(x, y);
6024       Store2[x][y] = 0;
6025       border_explosion = TRUE;
6026     }
6027
6028     /* if an element just explodes due to another explosion (chain-reaction),
6029        do not immediately end the new explosion when it was the last frame of
6030        the explosion (as it would be done in the following "if"-statement!) */
6031     if (border_explosion && phase == last_phase)
6032       return;
6033   }
6034
6035   if (phase == last_phase)
6036   {
6037     int element;
6038
6039     element = Feld[x][y] = Store[x][y];
6040     Store[x][y] = Store2[x][y] = 0;
6041     GfxElement[x][y] = EL_UNDEFINED;
6042
6043     /* player can escape from explosions and might therefore be still alive */
6044     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6045         element <= EL_PLAYER_IS_EXPLODING_4)
6046     {
6047       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6048       int explosion_element = EL_PLAYER_1 + player_nr;
6049       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6050       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6051
6052       if (level.use_explosion_element[player_nr])
6053         explosion_element = level.explosion_element[player_nr];
6054
6055       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6056                     element_info[explosion_element].content.e[xx][yy]);
6057     }
6058
6059     /* restore probably existing indestructible background element */
6060     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6061       element = Feld[x][y] = Back[x][y];
6062     Back[x][y] = 0;
6063
6064     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6065     GfxDir[x][y] = MV_NONE;
6066     ChangeDelay[x][y] = 0;
6067     ChangePage[x][y] = -1;
6068
6069 #if USE_NEW_CUSTOM_VALUE
6070     CustomValue[x][y] = 0;
6071 #endif
6072
6073     InitField_WithBug2(x, y, FALSE);
6074
6075     TEST_DrawLevelField(x, y);
6076
6077     TestIfElementTouchesCustomElement(x, y);
6078
6079     if (GFX_CRUMBLED(element))
6080       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6081
6082     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6083       StorePlayer[x][y] = 0;
6084
6085     if (ELEM_IS_PLAYER(element))
6086       RelocatePlayer(x, y, element);
6087   }
6088   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6089   {
6090     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6091     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6092
6093     if (phase == delay)
6094       TEST_DrawLevelFieldCrumbledSand(x, y);
6095
6096     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6097     {
6098       DrawLevelElement(x, y, Back[x][y]);
6099       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6100     }
6101     else if (IS_WALKABLE_UNDER(Back[x][y]))
6102     {
6103       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6104       DrawLevelElementThruMask(x, y, Back[x][y]);
6105     }
6106     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6107       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6108   }
6109 }
6110
6111 void DynaExplode(int ex, int ey)
6112 {
6113   int i, j;
6114   int dynabomb_element = Feld[ex][ey];
6115   int dynabomb_size = 1;
6116   boolean dynabomb_xl = FALSE;
6117   struct PlayerInfo *player;
6118   static int xy[4][2] =
6119   {
6120     { 0, -1 },
6121     { -1, 0 },
6122     { +1, 0 },
6123     { 0, +1 }
6124   };
6125
6126   if (IS_ACTIVE_BOMB(dynabomb_element))
6127   {
6128     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6129     dynabomb_size = player->dynabomb_size;
6130     dynabomb_xl = player->dynabomb_xl;
6131     player->dynabombs_left++;
6132   }
6133
6134   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6135
6136   for (i = 0; i < NUM_DIRECTIONS; i++)
6137   {
6138     for (j = 1; j <= dynabomb_size; j++)
6139     {
6140       int x = ex + j * xy[i][0];
6141       int y = ey + j * xy[i][1];
6142       int element;
6143
6144       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6145         break;
6146
6147       element = Feld[x][y];
6148
6149       /* do not restart explosions of fields with active bombs */
6150       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6151         continue;
6152
6153       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6154
6155       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6156           !IS_DIGGABLE(element) && !dynabomb_xl)
6157         break;
6158     }
6159   }
6160 }
6161
6162 void Bang(int x, int y)
6163 {
6164   int element = MovingOrBlocked2Element(x, y);
6165   int explosion_type = EX_TYPE_NORMAL;
6166
6167   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6168   {
6169     struct PlayerInfo *player = PLAYERINFO(x, y);
6170
6171 #if USE_FIX_CE_ACTION_WITH_PLAYER
6172     element = Feld[x][y] = player->initial_element;
6173 #else
6174     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6175                             player->element_nr);
6176 #endif
6177
6178     if (level.use_explosion_element[player->index_nr])
6179     {
6180       int explosion_element = level.explosion_element[player->index_nr];
6181
6182       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6183         explosion_type = EX_TYPE_CROSS;
6184       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6185         explosion_type = EX_TYPE_CENTER;
6186     }
6187   }
6188
6189   switch (element)
6190   {
6191     case EL_BUG:
6192     case EL_SPACESHIP:
6193     case EL_BD_BUTTERFLY:
6194     case EL_BD_FIREFLY:
6195     case EL_YAMYAM:
6196     case EL_DARK_YAMYAM:
6197     case EL_ROBOT:
6198     case EL_PACMAN:
6199     case EL_MOLE:
6200       RaiseScoreElement(element);
6201       break;
6202
6203     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6204     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6205     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6206     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6207     case EL_DYNABOMB_INCREASE_NUMBER:
6208     case EL_DYNABOMB_INCREASE_SIZE:
6209     case EL_DYNABOMB_INCREASE_POWER:
6210       explosion_type = EX_TYPE_DYNA;
6211       break;
6212
6213     case EL_DC_LANDMINE:
6214 #if 0
6215     case EL_EM_EXIT_OPEN:
6216     case EL_EM_STEEL_EXIT_OPEN:
6217 #endif
6218       explosion_type = EX_TYPE_CENTER;
6219       break;
6220
6221     case EL_PENGUIN:
6222     case EL_LAMP:
6223     case EL_LAMP_ACTIVE:
6224     case EL_AMOEBA_TO_DIAMOND:
6225       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6226         explosion_type = EX_TYPE_CENTER;
6227       break;
6228
6229     default:
6230       if (element_info[element].explosion_type == EXPLODES_CROSS)
6231         explosion_type = EX_TYPE_CROSS;
6232       else if (element_info[element].explosion_type == EXPLODES_1X1)
6233         explosion_type = EX_TYPE_CENTER;
6234       break;
6235   }
6236
6237   if (explosion_type == EX_TYPE_DYNA)
6238     DynaExplode(x, y);
6239   else
6240     Explode(x, y, EX_PHASE_START, explosion_type);
6241
6242   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6243 }
6244
6245 void SplashAcid(int x, int y)
6246 {
6247   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6248       (!IN_LEV_FIELD(x - 1, y - 2) ||
6249        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6250     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6251
6252   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6253       (!IN_LEV_FIELD(x + 1, y - 2) ||
6254        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6255     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6256
6257   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6258 }
6259
6260 static void InitBeltMovement()
6261 {
6262   static int belt_base_element[4] =
6263   {
6264     EL_CONVEYOR_BELT_1_LEFT,
6265     EL_CONVEYOR_BELT_2_LEFT,
6266     EL_CONVEYOR_BELT_3_LEFT,
6267     EL_CONVEYOR_BELT_4_LEFT
6268   };
6269   static int belt_base_active_element[4] =
6270   {
6271     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6272     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6273     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6274     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6275   };
6276
6277   int x, y, i, j;
6278
6279   /* set frame order for belt animation graphic according to belt direction */
6280   for (i = 0; i < NUM_BELTS; i++)
6281   {
6282     int belt_nr = i;
6283
6284     for (j = 0; j < NUM_BELT_PARTS; j++)
6285     {
6286       int element = belt_base_active_element[belt_nr] + j;
6287       int graphic_1 = el2img(element);
6288       int graphic_2 = el2panelimg(element);
6289
6290       if (game.belt_dir[i] == MV_LEFT)
6291       {
6292         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6293         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6294       }
6295       else
6296       {
6297         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6298         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6299       }
6300     }
6301   }
6302
6303   SCAN_PLAYFIELD(x, y)
6304   {
6305     int element = Feld[x][y];
6306
6307     for (i = 0; i < NUM_BELTS; i++)
6308     {
6309       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6310       {
6311         int e_belt_nr = getBeltNrFromBeltElement(element);
6312         int belt_nr = i;
6313
6314         if (e_belt_nr == belt_nr)
6315         {
6316           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6317
6318           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6319         }
6320       }
6321     }
6322   }
6323 }
6324
6325 static void ToggleBeltSwitch(int x, int y)
6326 {
6327   static int belt_base_element[4] =
6328   {
6329     EL_CONVEYOR_BELT_1_LEFT,
6330     EL_CONVEYOR_BELT_2_LEFT,
6331     EL_CONVEYOR_BELT_3_LEFT,
6332     EL_CONVEYOR_BELT_4_LEFT
6333   };
6334   static int belt_base_active_element[4] =
6335   {
6336     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6337     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6338     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6339     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6340   };
6341   static int belt_base_switch_element[4] =
6342   {
6343     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6344     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6345     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6346     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6347   };
6348   static int belt_move_dir[4] =
6349   {
6350     MV_LEFT,
6351     MV_NONE,
6352     MV_RIGHT,
6353     MV_NONE,
6354   };
6355
6356   int element = Feld[x][y];
6357   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6358   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6359   int belt_dir = belt_move_dir[belt_dir_nr];
6360   int xx, yy, i;
6361
6362   if (!IS_BELT_SWITCH(element))
6363     return;
6364
6365   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6366   game.belt_dir[belt_nr] = belt_dir;
6367
6368   if (belt_dir_nr == 3)
6369     belt_dir_nr = 1;
6370
6371   /* set frame order for belt animation graphic according to belt direction */
6372   for (i = 0; i < NUM_BELT_PARTS; i++)
6373   {
6374     int element = belt_base_active_element[belt_nr] + i;
6375     int graphic_1 = el2img(element);
6376     int graphic_2 = el2panelimg(element);
6377
6378     if (belt_dir == MV_LEFT)
6379     {
6380       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6381       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6382     }
6383     else
6384     {
6385       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6386       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6387     }
6388   }
6389
6390   SCAN_PLAYFIELD(xx, yy)
6391   {
6392     int element = Feld[xx][yy];
6393
6394     if (IS_BELT_SWITCH(element))
6395     {
6396       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6397
6398       if (e_belt_nr == belt_nr)
6399       {
6400         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6401         TEST_DrawLevelField(xx, yy);
6402       }
6403     }
6404     else if (IS_BELT(element) && belt_dir != MV_NONE)
6405     {
6406       int e_belt_nr = getBeltNrFromBeltElement(element);
6407
6408       if (e_belt_nr == belt_nr)
6409       {
6410         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6411
6412         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6413         TEST_DrawLevelField(xx, yy);
6414       }
6415     }
6416     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6417     {
6418       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6419
6420       if (e_belt_nr == belt_nr)
6421       {
6422         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6423
6424         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6425         TEST_DrawLevelField(xx, yy);
6426       }
6427     }
6428   }
6429 }
6430
6431 static void ToggleSwitchgateSwitch(int x, int y)
6432 {
6433   int xx, yy;
6434
6435   game.switchgate_pos = !game.switchgate_pos;
6436
6437   SCAN_PLAYFIELD(xx, yy)
6438   {
6439     int element = Feld[xx][yy];
6440
6441 #if !USE_BOTH_SWITCHGATE_SWITCHES
6442     if (element == EL_SWITCHGATE_SWITCH_UP ||
6443         element == EL_SWITCHGATE_SWITCH_DOWN)
6444     {
6445       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6446       TEST_DrawLevelField(xx, yy);
6447     }
6448     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6449              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6450     {
6451       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6452       TEST_DrawLevelField(xx, yy);
6453     }
6454 #else
6455     if (element == EL_SWITCHGATE_SWITCH_UP)
6456     {
6457       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6458       TEST_DrawLevelField(xx, yy);
6459     }
6460     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6461     {
6462       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6463       TEST_DrawLevelField(xx, yy);
6464     }
6465     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6466     {
6467       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6468       TEST_DrawLevelField(xx, yy);
6469     }
6470     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6471     {
6472       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6473       TEST_DrawLevelField(xx, yy);
6474     }
6475 #endif
6476     else if (element == EL_SWITCHGATE_OPEN ||
6477              element == EL_SWITCHGATE_OPENING)
6478     {
6479       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6480
6481       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6482     }
6483     else if (element == EL_SWITCHGATE_CLOSED ||
6484              element == EL_SWITCHGATE_CLOSING)
6485     {
6486       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6487
6488       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6489     }
6490   }
6491 }
6492
6493 static int getInvisibleActiveFromInvisibleElement(int element)
6494 {
6495   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6496           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6497           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6498           element);
6499 }
6500
6501 static int getInvisibleFromInvisibleActiveElement(int element)
6502 {
6503   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6504           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6505           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6506           element);
6507 }
6508
6509 static void RedrawAllLightSwitchesAndInvisibleElements()
6510 {
6511   int x, y;
6512
6513   SCAN_PLAYFIELD(x, y)
6514   {
6515     int element = Feld[x][y];
6516
6517     if (element == EL_LIGHT_SWITCH &&
6518         game.light_time_left > 0)
6519     {
6520       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6521       TEST_DrawLevelField(x, y);
6522     }
6523     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6524              game.light_time_left == 0)
6525     {
6526       Feld[x][y] = EL_LIGHT_SWITCH;
6527       TEST_DrawLevelField(x, y);
6528     }
6529     else if (element == EL_EMC_DRIPPER &&
6530              game.light_time_left > 0)
6531     {
6532       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6533       TEST_DrawLevelField(x, y);
6534     }
6535     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6536              game.light_time_left == 0)
6537     {
6538       Feld[x][y] = EL_EMC_DRIPPER;
6539       TEST_DrawLevelField(x, y);
6540     }
6541     else if (element == EL_INVISIBLE_STEELWALL ||
6542              element == EL_INVISIBLE_WALL ||
6543              element == EL_INVISIBLE_SAND)
6544     {
6545       if (game.light_time_left > 0)
6546         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6547
6548       TEST_DrawLevelField(x, y);
6549
6550       /* uncrumble neighbour fields, if needed */
6551       if (element == EL_INVISIBLE_SAND)
6552         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6553     }
6554     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6555              element == EL_INVISIBLE_WALL_ACTIVE ||
6556              element == EL_INVISIBLE_SAND_ACTIVE)
6557     {
6558       if (game.light_time_left == 0)
6559         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6560
6561       TEST_DrawLevelField(x, y);
6562
6563       /* re-crumble neighbour fields, if needed */
6564       if (element == EL_INVISIBLE_SAND)
6565         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6566     }
6567   }
6568 }
6569
6570 static void RedrawAllInvisibleElementsForLenses()
6571 {
6572   int x, y;
6573
6574   SCAN_PLAYFIELD(x, y)
6575   {
6576     int element = Feld[x][y];
6577
6578     if (element == EL_EMC_DRIPPER &&
6579         game.lenses_time_left > 0)
6580     {
6581       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6582       TEST_DrawLevelField(x, y);
6583     }
6584     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6585              game.lenses_time_left == 0)
6586     {
6587       Feld[x][y] = EL_EMC_DRIPPER;
6588       TEST_DrawLevelField(x, y);
6589     }
6590     else if (element == EL_INVISIBLE_STEELWALL ||
6591              element == EL_INVISIBLE_WALL ||
6592              element == EL_INVISIBLE_SAND)
6593     {
6594       if (game.lenses_time_left > 0)
6595         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6596
6597       TEST_DrawLevelField(x, y);
6598
6599       /* uncrumble neighbour fields, if needed */
6600       if (element == EL_INVISIBLE_SAND)
6601         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6602     }
6603     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6604              element == EL_INVISIBLE_WALL_ACTIVE ||
6605              element == EL_INVISIBLE_SAND_ACTIVE)
6606     {
6607       if (game.lenses_time_left == 0)
6608         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6609
6610       TEST_DrawLevelField(x, y);
6611
6612       /* re-crumble neighbour fields, if needed */
6613       if (element == EL_INVISIBLE_SAND)
6614         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6615     }
6616   }
6617 }
6618
6619 static void RedrawAllInvisibleElementsForMagnifier()
6620 {
6621   int x, y;
6622
6623   SCAN_PLAYFIELD(x, y)
6624   {
6625     int element = Feld[x][y];
6626
6627     if (element == EL_EMC_FAKE_GRASS &&
6628         game.magnify_time_left > 0)
6629     {
6630       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6631       TEST_DrawLevelField(x, y);
6632     }
6633     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6634              game.magnify_time_left == 0)
6635     {
6636       Feld[x][y] = EL_EMC_FAKE_GRASS;
6637       TEST_DrawLevelField(x, y);
6638     }
6639     else if (IS_GATE_GRAY(element) &&
6640              game.magnify_time_left > 0)
6641     {
6642       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6643                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6644                     IS_EM_GATE_GRAY(element) ?
6645                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6646                     IS_EMC_GATE_GRAY(element) ?
6647                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6648                     IS_DC_GATE_GRAY(element) ?
6649                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6650                     element);
6651       TEST_DrawLevelField(x, y);
6652     }
6653     else if (IS_GATE_GRAY_ACTIVE(element) &&
6654              game.magnify_time_left == 0)
6655     {
6656       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6657                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6658                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6659                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6660                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6661                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6662                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6663                     EL_DC_GATE_WHITE_GRAY :
6664                     element);
6665       TEST_DrawLevelField(x, y);
6666     }
6667   }
6668 }
6669
6670 static void ToggleLightSwitch(int x, int y)
6671 {
6672   int element = Feld[x][y];
6673
6674   game.light_time_left =
6675     (element == EL_LIGHT_SWITCH ?
6676      level.time_light * FRAMES_PER_SECOND : 0);
6677
6678   RedrawAllLightSwitchesAndInvisibleElements();
6679 }
6680
6681 static void ActivateTimegateSwitch(int x, int y)
6682 {
6683   int xx, yy;
6684
6685   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6686
6687   SCAN_PLAYFIELD(xx, yy)
6688   {
6689     int element = Feld[xx][yy];
6690
6691     if (element == EL_TIMEGATE_CLOSED ||
6692         element == EL_TIMEGATE_CLOSING)
6693     {
6694       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6695       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6696     }
6697
6698     /*
6699     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6700     {
6701       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6702       TEST_DrawLevelField(xx, yy);
6703     }
6704     */
6705
6706   }
6707
6708 #if 1
6709   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6710                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6711 #else
6712   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6713 #endif
6714 }
6715
6716 void Impact(int x, int y)
6717 {
6718   boolean last_line = (y == lev_fieldy - 1);
6719   boolean object_hit = FALSE;
6720   boolean impact = (last_line || object_hit);
6721   int element = Feld[x][y];
6722   int smashed = EL_STEELWALL;
6723
6724   if (!last_line)       /* check if element below was hit */
6725   {
6726     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6727       return;
6728
6729     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6730                                          MovDir[x][y + 1] != MV_DOWN ||
6731                                          MovPos[x][y + 1] <= TILEY / 2));
6732
6733     /* do not smash moving elements that left the smashed field in time */
6734     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6735         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6736       object_hit = FALSE;
6737
6738 #if USE_QUICKSAND_IMPACT_BUGFIX
6739     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6740     {
6741       RemoveMovingField(x, y + 1);
6742       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6743       Feld[x][y + 2] = EL_ROCK;
6744       TEST_DrawLevelField(x, y + 2);
6745
6746       object_hit = TRUE;
6747     }
6748
6749     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6750     {
6751       RemoveMovingField(x, y + 1);
6752       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6753       Feld[x][y + 2] = EL_ROCK;
6754       TEST_DrawLevelField(x, y + 2);
6755
6756       object_hit = TRUE;
6757     }
6758 #endif
6759
6760     if (object_hit)
6761       smashed = MovingOrBlocked2Element(x, y + 1);
6762
6763     impact = (last_line || object_hit);
6764   }
6765
6766   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6767   {
6768     SplashAcid(x, y + 1);
6769     return;
6770   }
6771
6772   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6773   /* only reset graphic animation if graphic really changes after impact */
6774   if (impact &&
6775       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6776   {
6777     ResetGfxAnimation(x, y);
6778     TEST_DrawLevelField(x, y);
6779   }
6780
6781   if (impact && CAN_EXPLODE_IMPACT(element))
6782   {
6783     Bang(x, y);
6784     return;
6785   }
6786   else if (impact && element == EL_PEARL &&
6787            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6788   {
6789     ResetGfxAnimation(x, y);
6790
6791     Feld[x][y] = EL_PEARL_BREAKING;
6792     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6793     return;
6794   }
6795   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6796   {
6797     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6798
6799     return;
6800   }
6801
6802   if (impact && element == EL_AMOEBA_DROP)
6803   {
6804     if (object_hit && IS_PLAYER(x, y + 1))
6805       KillPlayerUnlessEnemyProtected(x, y + 1);
6806     else if (object_hit && smashed == EL_PENGUIN)
6807       Bang(x, y + 1);
6808     else
6809     {
6810       Feld[x][y] = EL_AMOEBA_GROWING;
6811       Store[x][y] = EL_AMOEBA_WET;
6812
6813       ResetRandomAnimationValue(x, y);
6814     }
6815     return;
6816   }
6817
6818   if (object_hit)               /* check which object was hit */
6819   {
6820     if ((CAN_PASS_MAGIC_WALL(element) && 
6821          (smashed == EL_MAGIC_WALL ||
6822           smashed == EL_BD_MAGIC_WALL)) ||
6823         (CAN_PASS_DC_MAGIC_WALL(element) &&
6824          smashed == EL_DC_MAGIC_WALL))
6825     {
6826       int xx, yy;
6827       int activated_magic_wall =
6828         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6829          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6830          EL_DC_MAGIC_WALL_ACTIVE);
6831
6832       /* activate magic wall / mill */
6833       SCAN_PLAYFIELD(xx, yy)
6834       {
6835         if (Feld[xx][yy] == smashed)
6836           Feld[xx][yy] = activated_magic_wall;
6837       }
6838
6839       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6840       game.magic_wall_active = TRUE;
6841
6842       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6843                             SND_MAGIC_WALL_ACTIVATING :
6844                             smashed == EL_BD_MAGIC_WALL ?
6845                             SND_BD_MAGIC_WALL_ACTIVATING :
6846                             SND_DC_MAGIC_WALL_ACTIVATING));
6847     }
6848
6849     if (IS_PLAYER(x, y + 1))
6850     {
6851       if (CAN_SMASH_PLAYER(element))
6852       {
6853         KillPlayerUnlessEnemyProtected(x, y + 1);
6854         return;
6855       }
6856     }
6857     else if (smashed == EL_PENGUIN)
6858     {
6859       if (CAN_SMASH_PLAYER(element))
6860       {
6861         Bang(x, y + 1);
6862         return;
6863       }
6864     }
6865     else if (element == EL_BD_DIAMOND)
6866     {
6867       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6868       {
6869         Bang(x, y + 1);
6870         return;
6871       }
6872     }
6873     else if (((element == EL_SP_INFOTRON ||
6874                element == EL_SP_ZONK) &&
6875               (smashed == EL_SP_SNIKSNAK ||
6876                smashed == EL_SP_ELECTRON ||
6877                smashed == EL_SP_DISK_ORANGE)) ||
6878              (element == EL_SP_INFOTRON &&
6879               smashed == EL_SP_DISK_YELLOW))
6880     {
6881       Bang(x, y + 1);
6882       return;
6883     }
6884     else if (CAN_SMASH_EVERYTHING(element))
6885     {
6886       if (IS_CLASSIC_ENEMY(smashed) ||
6887           CAN_EXPLODE_SMASHED(smashed))
6888       {
6889         Bang(x, y + 1);
6890         return;
6891       }
6892       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6893       {
6894         if (smashed == EL_LAMP ||
6895             smashed == EL_LAMP_ACTIVE)
6896         {
6897           Bang(x, y + 1);
6898           return;
6899         }
6900         else if (smashed == EL_NUT)
6901         {
6902           Feld[x][y + 1] = EL_NUT_BREAKING;
6903           PlayLevelSound(x, y, SND_NUT_BREAKING);
6904           RaiseScoreElement(EL_NUT);
6905           return;
6906         }
6907         else if (smashed == EL_PEARL)
6908         {
6909           ResetGfxAnimation(x, y);
6910
6911           Feld[x][y + 1] = EL_PEARL_BREAKING;
6912           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6913           return;
6914         }
6915         else if (smashed == EL_DIAMOND)
6916         {
6917           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6918           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6919           return;
6920         }
6921         else if (IS_BELT_SWITCH(smashed))
6922         {
6923           ToggleBeltSwitch(x, y + 1);
6924         }
6925         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6926                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6927                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6928                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6929         {
6930           ToggleSwitchgateSwitch(x, y + 1);
6931         }
6932         else if (smashed == EL_LIGHT_SWITCH ||
6933                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6934         {
6935           ToggleLightSwitch(x, y + 1);
6936         }
6937         else
6938         {
6939 #if 0
6940           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6941 #endif
6942
6943           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6944
6945           CheckElementChangeBySide(x, y + 1, smashed, element,
6946                                    CE_SWITCHED, CH_SIDE_TOP);
6947           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6948                                             CH_SIDE_TOP);
6949         }
6950       }
6951       else
6952       {
6953         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6954       }
6955     }
6956   }
6957
6958   /* play sound of magic wall / mill */
6959   if (!last_line &&
6960       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6961        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6962        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6963   {
6964     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6965       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6966     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6967       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6968     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6969       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6970
6971     return;
6972   }
6973
6974   /* play sound of object that hits the ground */
6975   if (last_line || object_hit)
6976     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6977 }
6978
6979 inline static void TurnRoundExt(int x, int y)
6980 {
6981   static struct
6982   {
6983     int dx, dy;
6984   } move_xy[] =
6985   {
6986     {  0,  0 },
6987     { -1,  0 },
6988     { +1,  0 },
6989     {  0,  0 },
6990     {  0, -1 },
6991     {  0,  0 }, { 0, 0 }, { 0, 0 },
6992     {  0, +1 }
6993   };
6994   static struct
6995   {
6996     int left, right, back;
6997   } turn[] =
6998   {
6999     { 0,        0,              0        },
7000     { MV_DOWN,  MV_UP,          MV_RIGHT },
7001     { MV_UP,    MV_DOWN,        MV_LEFT  },
7002     { 0,        0,              0        },
7003     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7004     { 0,        0,              0        },
7005     { 0,        0,              0        },
7006     { 0,        0,              0        },
7007     { MV_RIGHT, MV_LEFT,        MV_UP    }
7008   };
7009
7010   int element = Feld[x][y];
7011   int move_pattern = element_info[element].move_pattern;
7012
7013   int old_move_dir = MovDir[x][y];
7014   int left_dir  = turn[old_move_dir].left;
7015   int right_dir = turn[old_move_dir].right;
7016   int back_dir  = turn[old_move_dir].back;
7017
7018   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7019   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7020   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7021   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7022
7023   int left_x  = x + left_dx,  left_y  = y + left_dy;
7024   int right_x = x + right_dx, right_y = y + right_dy;
7025   int move_x  = x + move_dx,  move_y  = y + move_dy;
7026
7027   int xx, yy;
7028
7029   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7030   {
7031     TestIfBadThingTouchesOtherBadThing(x, y);
7032
7033     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7034       MovDir[x][y] = right_dir;
7035     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7036       MovDir[x][y] = left_dir;
7037
7038     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7039       MovDelay[x][y] = 9;
7040     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7041       MovDelay[x][y] = 1;
7042   }
7043   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7044   {
7045     TestIfBadThingTouchesOtherBadThing(x, y);
7046
7047     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7048       MovDir[x][y] = left_dir;
7049     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7050       MovDir[x][y] = right_dir;
7051
7052     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7053       MovDelay[x][y] = 9;
7054     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7055       MovDelay[x][y] = 1;
7056   }
7057   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7058   {
7059     TestIfBadThingTouchesOtherBadThing(x, y);
7060
7061     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7062       MovDir[x][y] = left_dir;
7063     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7064       MovDir[x][y] = right_dir;
7065
7066     if (MovDir[x][y] != old_move_dir)
7067       MovDelay[x][y] = 9;
7068   }
7069   else if (element == EL_YAMYAM)
7070   {
7071     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7072     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7073
7074     if (can_turn_left && can_turn_right)
7075       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7076     else if (can_turn_left)
7077       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7078     else if (can_turn_right)
7079       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7080     else
7081       MovDir[x][y] = back_dir;
7082
7083     MovDelay[x][y] = 16 + 16 * RND(3);
7084   }
7085   else if (element == EL_DARK_YAMYAM)
7086   {
7087     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7088                                                          left_x, left_y);
7089     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7090                                                          right_x, right_y);
7091
7092     if (can_turn_left && can_turn_right)
7093       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7094     else if (can_turn_left)
7095       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7096     else if (can_turn_right)
7097       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7098     else
7099       MovDir[x][y] = back_dir;
7100
7101     MovDelay[x][y] = 16 + 16 * RND(3);
7102   }
7103   else if (element == EL_PACMAN)
7104   {
7105     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7106     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7107
7108     if (can_turn_left && can_turn_right)
7109       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7110     else if (can_turn_left)
7111       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7112     else if (can_turn_right)
7113       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7114     else
7115       MovDir[x][y] = back_dir;
7116
7117     MovDelay[x][y] = 6 + RND(40);
7118   }
7119   else if (element == EL_PIG)
7120   {
7121     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7122     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7123     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7124     boolean should_turn_left, should_turn_right, should_move_on;
7125     int rnd_value = 24;
7126     int rnd = RND(rnd_value);
7127
7128     should_turn_left = (can_turn_left &&
7129                         (!can_move_on ||
7130                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7131                                                    y + back_dy + left_dy)));
7132     should_turn_right = (can_turn_right &&
7133                          (!can_move_on ||
7134                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7135                                                     y + back_dy + right_dy)));
7136     should_move_on = (can_move_on &&
7137                       (!can_turn_left ||
7138                        !can_turn_right ||
7139                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7140                                                  y + move_dy + left_dy) ||
7141                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7142                                                  y + move_dy + right_dy)));
7143
7144     if (should_turn_left || should_turn_right || should_move_on)
7145     {
7146       if (should_turn_left && should_turn_right && should_move_on)
7147         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7148                         rnd < 2 * rnd_value / 3 ? right_dir :
7149                         old_move_dir);
7150       else if (should_turn_left && should_turn_right)
7151         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7152       else if (should_turn_left && should_move_on)
7153         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7154       else if (should_turn_right && should_move_on)
7155         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7156       else if (should_turn_left)
7157         MovDir[x][y] = left_dir;
7158       else if (should_turn_right)
7159         MovDir[x][y] = right_dir;
7160       else if (should_move_on)
7161         MovDir[x][y] = old_move_dir;
7162     }
7163     else if (can_move_on && rnd > rnd_value / 8)
7164       MovDir[x][y] = old_move_dir;
7165     else if (can_turn_left && can_turn_right)
7166       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7167     else if (can_turn_left && rnd > rnd_value / 8)
7168       MovDir[x][y] = left_dir;
7169     else if (can_turn_right && rnd > rnd_value/8)
7170       MovDir[x][y] = right_dir;
7171     else
7172       MovDir[x][y] = back_dir;
7173
7174     xx = x + move_xy[MovDir[x][y]].dx;
7175     yy = y + move_xy[MovDir[x][y]].dy;
7176
7177     if (!IN_LEV_FIELD(xx, yy) ||
7178         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7179       MovDir[x][y] = old_move_dir;
7180
7181     MovDelay[x][y] = 0;
7182   }
7183   else if (element == EL_DRAGON)
7184   {
7185     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7186     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7187     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7188     int rnd_value = 24;
7189     int rnd = RND(rnd_value);
7190
7191     if (can_move_on && rnd > rnd_value / 8)
7192       MovDir[x][y] = old_move_dir;
7193     else if (can_turn_left && can_turn_right)
7194       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7195     else if (can_turn_left && rnd > rnd_value / 8)
7196       MovDir[x][y] = left_dir;
7197     else if (can_turn_right && rnd > rnd_value / 8)
7198       MovDir[x][y] = right_dir;
7199     else
7200       MovDir[x][y] = back_dir;
7201
7202     xx = x + move_xy[MovDir[x][y]].dx;
7203     yy = y + move_xy[MovDir[x][y]].dy;
7204
7205     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7206       MovDir[x][y] = old_move_dir;
7207
7208     MovDelay[x][y] = 0;
7209   }
7210   else if (element == EL_MOLE)
7211   {
7212     boolean can_move_on =
7213       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7214                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7215                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7216     if (!can_move_on)
7217     {
7218       boolean can_turn_left =
7219         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7220                               IS_AMOEBOID(Feld[left_x][left_y])));
7221
7222       boolean can_turn_right =
7223         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7224                               IS_AMOEBOID(Feld[right_x][right_y])));
7225
7226       if (can_turn_left && can_turn_right)
7227         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7228       else if (can_turn_left)
7229         MovDir[x][y] = left_dir;
7230       else
7231         MovDir[x][y] = right_dir;
7232     }
7233
7234     if (MovDir[x][y] != old_move_dir)
7235       MovDelay[x][y] = 9;
7236   }
7237   else if (element == EL_BALLOON)
7238   {
7239     MovDir[x][y] = game.wind_direction;
7240     MovDelay[x][y] = 0;
7241   }
7242   else if (element == EL_SPRING)
7243   {
7244 #if USE_NEW_SPRING_BUMPER
7245     if (MovDir[x][y] & MV_HORIZONTAL)
7246     {
7247       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7248           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7249       {
7250         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7251         ResetGfxAnimation(move_x, move_y);
7252         TEST_DrawLevelField(move_x, move_y);
7253
7254         MovDir[x][y] = back_dir;
7255       }
7256       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7257                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7258         MovDir[x][y] = MV_NONE;
7259     }
7260 #else
7261     if (MovDir[x][y] & MV_HORIZONTAL &&
7262         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7263          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7264       MovDir[x][y] = MV_NONE;
7265 #endif
7266
7267     MovDelay[x][y] = 0;
7268   }
7269   else if (element == EL_ROBOT ||
7270            element == EL_SATELLITE ||
7271            element == EL_PENGUIN ||
7272            element == EL_EMC_ANDROID)
7273   {
7274     int attr_x = -1, attr_y = -1;
7275
7276     if (AllPlayersGone)
7277     {
7278       attr_x = ExitX;
7279       attr_y = ExitY;
7280     }
7281     else
7282     {
7283       int i;
7284
7285       for (i = 0; i < MAX_PLAYERS; i++)
7286       {
7287         struct PlayerInfo *player = &stored_player[i];
7288         int jx = player->jx, jy = player->jy;
7289
7290         if (!player->active)
7291           continue;
7292
7293         if (attr_x == -1 ||
7294             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7295         {
7296           attr_x = jx;
7297           attr_y = jy;
7298         }
7299       }
7300     }
7301
7302     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7303         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7304          game.engine_version < VERSION_IDENT(3,1,0,0)))
7305     {
7306       attr_x = ZX;
7307       attr_y = ZY;
7308     }
7309
7310     if (element == EL_PENGUIN)
7311     {
7312       int i;
7313       static int xy[4][2] =
7314       {
7315         { 0, -1 },
7316         { -1, 0 },
7317         { +1, 0 },
7318         { 0, +1 }
7319       };
7320
7321       for (i = 0; i < NUM_DIRECTIONS; i++)
7322       {
7323         int ex = x + xy[i][0];
7324         int ey = y + xy[i][1];
7325
7326         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7327                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7328                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7329                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7330         {
7331           attr_x = ex;
7332           attr_y = ey;
7333           break;
7334         }
7335       }
7336     }
7337
7338     MovDir[x][y] = MV_NONE;
7339     if (attr_x < x)
7340       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7341     else if (attr_x > x)
7342       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7343     if (attr_y < y)
7344       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7345     else if (attr_y > y)
7346       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7347
7348     if (element == EL_ROBOT)
7349     {
7350       int newx, newy;
7351
7352       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7353         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7354       Moving2Blocked(x, y, &newx, &newy);
7355
7356       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7357         MovDelay[x][y] = 8 + 8 * !RND(3);
7358       else
7359         MovDelay[x][y] = 16;
7360     }
7361     else if (element == EL_PENGUIN)
7362     {
7363       int newx, newy;
7364
7365       MovDelay[x][y] = 1;
7366
7367       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7368       {
7369         boolean first_horiz = RND(2);
7370         int new_move_dir = MovDir[x][y];
7371
7372         MovDir[x][y] =
7373           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7374         Moving2Blocked(x, y, &newx, &newy);
7375
7376         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7377           return;
7378
7379         MovDir[x][y] =
7380           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7381         Moving2Blocked(x, y, &newx, &newy);
7382
7383         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7384           return;
7385
7386         MovDir[x][y] = old_move_dir;
7387         return;
7388       }
7389     }
7390     else if (element == EL_SATELLITE)
7391     {
7392       int newx, newy;
7393
7394       MovDelay[x][y] = 1;
7395
7396       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7397       {
7398         boolean first_horiz = RND(2);
7399         int new_move_dir = MovDir[x][y];
7400
7401         MovDir[x][y] =
7402           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7403         Moving2Blocked(x, y, &newx, &newy);
7404
7405         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7406           return;
7407
7408         MovDir[x][y] =
7409           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7410         Moving2Blocked(x, y, &newx, &newy);
7411
7412         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7413           return;
7414
7415         MovDir[x][y] = old_move_dir;
7416         return;
7417       }
7418     }
7419     else if (element == EL_EMC_ANDROID)
7420     {
7421       static int check_pos[16] =
7422       {
7423         -1,             /*  0 => (invalid)          */
7424         7,              /*  1 => MV_LEFT            */
7425         3,              /*  2 => MV_RIGHT           */
7426         -1,             /*  3 => (invalid)          */
7427         1,              /*  4 =>            MV_UP   */
7428         0,              /*  5 => MV_LEFT  | MV_UP   */
7429         2,              /*  6 => MV_RIGHT | MV_UP   */
7430         -1,             /*  7 => (invalid)          */
7431         5,              /*  8 =>            MV_DOWN */
7432         6,              /*  9 => MV_LEFT  | MV_DOWN */
7433         4,              /* 10 => MV_RIGHT | MV_DOWN */
7434         -1,             /* 11 => (invalid)          */
7435         -1,             /* 12 => (invalid)          */
7436         -1,             /* 13 => (invalid)          */
7437         -1,             /* 14 => (invalid)          */
7438         -1,             /* 15 => (invalid)          */
7439       };
7440       static struct
7441       {
7442         int dx, dy;
7443         int dir;
7444       } check_xy[8] =
7445       {
7446         { -1, -1,       MV_LEFT  | MV_UP   },
7447         {  0, -1,                  MV_UP   },
7448         { +1, -1,       MV_RIGHT | MV_UP   },
7449         { +1,  0,       MV_RIGHT           },
7450         { +1, +1,       MV_RIGHT | MV_DOWN },
7451         {  0, +1,                  MV_DOWN },
7452         { -1, +1,       MV_LEFT  | MV_DOWN },
7453         { -1,  0,       MV_LEFT            },
7454       };
7455       int start_pos, check_order;
7456       boolean can_clone = FALSE;
7457       int i;
7458
7459       /* check if there is any free field around current position */
7460       for (i = 0; i < 8; i++)
7461       {
7462         int newx = x + check_xy[i].dx;
7463         int newy = y + check_xy[i].dy;
7464
7465         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7466         {
7467           can_clone = TRUE;
7468
7469           break;
7470         }
7471       }
7472
7473       if (can_clone)            /* randomly find an element to clone */
7474       {
7475         can_clone = FALSE;
7476
7477         start_pos = check_pos[RND(8)];
7478         check_order = (RND(2) ? -1 : +1);
7479
7480         for (i = 0; i < 8; i++)
7481         {
7482           int pos_raw = start_pos + i * check_order;
7483           int pos = (pos_raw + 8) % 8;
7484           int newx = x + check_xy[pos].dx;
7485           int newy = y + check_xy[pos].dy;
7486
7487           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7488           {
7489             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7490             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7491
7492             Store[x][y] = Feld[newx][newy];
7493
7494             can_clone = TRUE;
7495
7496             break;
7497           }
7498         }
7499       }
7500
7501       if (can_clone)            /* randomly find a direction to move */
7502       {
7503         can_clone = FALSE;
7504
7505         start_pos = check_pos[RND(8)];
7506         check_order = (RND(2) ? -1 : +1);
7507
7508         for (i = 0; i < 8; i++)
7509         {
7510           int pos_raw = start_pos + i * check_order;
7511           int pos = (pos_raw + 8) % 8;
7512           int newx = x + check_xy[pos].dx;
7513           int newy = y + check_xy[pos].dy;
7514           int new_move_dir = check_xy[pos].dir;
7515
7516           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7517           {
7518             MovDir[x][y] = new_move_dir;
7519             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7520
7521             can_clone = TRUE;
7522
7523             break;
7524           }
7525         }
7526       }
7527
7528       if (can_clone)            /* cloning and moving successful */
7529         return;
7530
7531       /* cannot clone -- try to move towards player */
7532
7533       start_pos = check_pos[MovDir[x][y] & 0x0f];
7534       check_order = (RND(2) ? -1 : +1);
7535
7536       for (i = 0; i < 3; i++)
7537       {
7538         /* first check start_pos, then previous/next or (next/previous) pos */
7539         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7540         int pos = (pos_raw + 8) % 8;
7541         int newx = x + check_xy[pos].dx;
7542         int newy = y + check_xy[pos].dy;
7543         int new_move_dir = check_xy[pos].dir;
7544
7545         if (IS_PLAYER(newx, newy))
7546           break;
7547
7548         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7549         {
7550           MovDir[x][y] = new_move_dir;
7551           MovDelay[x][y] = level.android_move_time * 8 + 1;
7552
7553           break;
7554         }
7555       }
7556     }
7557   }
7558   else if (move_pattern == MV_TURNING_LEFT ||
7559            move_pattern == MV_TURNING_RIGHT ||
7560            move_pattern == MV_TURNING_LEFT_RIGHT ||
7561            move_pattern == MV_TURNING_RIGHT_LEFT ||
7562            move_pattern == MV_TURNING_RANDOM ||
7563            move_pattern == MV_ALL_DIRECTIONS)
7564   {
7565     boolean can_turn_left =
7566       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7567     boolean can_turn_right =
7568       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7569
7570     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7571       return;
7572
7573     if (move_pattern == MV_TURNING_LEFT)
7574       MovDir[x][y] = left_dir;
7575     else if (move_pattern == MV_TURNING_RIGHT)
7576       MovDir[x][y] = right_dir;
7577     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7578       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7579     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7580       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7581     else if (move_pattern == MV_TURNING_RANDOM)
7582       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7583                       can_turn_right && !can_turn_left ? right_dir :
7584                       RND(2) ? left_dir : right_dir);
7585     else if (can_turn_left && can_turn_right)
7586       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7587     else if (can_turn_left)
7588       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7589     else if (can_turn_right)
7590       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7591     else
7592       MovDir[x][y] = back_dir;
7593
7594     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7595   }
7596   else if (move_pattern == MV_HORIZONTAL ||
7597            move_pattern == MV_VERTICAL)
7598   {
7599     if (move_pattern & old_move_dir)
7600       MovDir[x][y] = back_dir;
7601     else if (move_pattern == MV_HORIZONTAL)
7602       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7603     else if (move_pattern == MV_VERTICAL)
7604       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7605
7606     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7607   }
7608   else if (move_pattern & MV_ANY_DIRECTION)
7609   {
7610     MovDir[x][y] = move_pattern;
7611     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7612   }
7613   else if (move_pattern & MV_WIND_DIRECTION)
7614   {
7615     MovDir[x][y] = game.wind_direction;
7616     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7617   }
7618   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7619   {
7620     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7621       MovDir[x][y] = left_dir;
7622     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7623       MovDir[x][y] = right_dir;
7624
7625     if (MovDir[x][y] != old_move_dir)
7626       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7627   }
7628   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7629   {
7630     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7631       MovDir[x][y] = right_dir;
7632     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7633       MovDir[x][y] = left_dir;
7634
7635     if (MovDir[x][y] != old_move_dir)
7636       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7637   }
7638   else if (move_pattern == MV_TOWARDS_PLAYER ||
7639            move_pattern == MV_AWAY_FROM_PLAYER)
7640   {
7641     int attr_x = -1, attr_y = -1;
7642     int newx, newy;
7643     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7644
7645     if (AllPlayersGone)
7646     {
7647       attr_x = ExitX;
7648       attr_y = ExitY;
7649     }
7650     else
7651     {
7652       int i;
7653
7654       for (i = 0; i < MAX_PLAYERS; i++)
7655       {
7656         struct PlayerInfo *player = &stored_player[i];
7657         int jx = player->jx, jy = player->jy;
7658
7659         if (!player->active)
7660           continue;
7661
7662         if (attr_x == -1 ||
7663             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7664         {
7665           attr_x = jx;
7666           attr_y = jy;
7667         }
7668       }
7669     }
7670
7671     MovDir[x][y] = MV_NONE;
7672     if (attr_x < x)
7673       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7674     else if (attr_x > x)
7675       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7676     if (attr_y < y)
7677       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7678     else if (attr_y > y)
7679       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7680
7681     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7682
7683     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7684     {
7685       boolean first_horiz = RND(2);
7686       int new_move_dir = MovDir[x][y];
7687
7688       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7689       {
7690         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7691         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7692
7693         return;
7694       }
7695
7696       MovDir[x][y] =
7697         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7698       Moving2Blocked(x, y, &newx, &newy);
7699
7700       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7701         return;
7702
7703       MovDir[x][y] =
7704         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7705       Moving2Blocked(x, y, &newx, &newy);
7706
7707       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7708         return;
7709
7710       MovDir[x][y] = old_move_dir;
7711     }
7712   }
7713   else if (move_pattern == MV_WHEN_PUSHED ||
7714            move_pattern == MV_WHEN_DROPPED)
7715   {
7716     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7717       MovDir[x][y] = MV_NONE;
7718
7719     MovDelay[x][y] = 0;
7720   }
7721   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7722   {
7723     static int test_xy[7][2] =
7724     {
7725       { 0, -1 },
7726       { -1, 0 },
7727       { +1, 0 },
7728       { 0, +1 },
7729       { 0, -1 },
7730       { -1, 0 },
7731       { +1, 0 },
7732     };
7733     static int test_dir[7] =
7734     {
7735       MV_UP,
7736       MV_LEFT,
7737       MV_RIGHT,
7738       MV_DOWN,
7739       MV_UP,
7740       MV_LEFT,
7741       MV_RIGHT,
7742     };
7743     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7744     int move_preference = -1000000;     /* start with very low preference */
7745     int new_move_dir = MV_NONE;
7746     int start_test = RND(4);
7747     int i;
7748
7749     for (i = 0; i < NUM_DIRECTIONS; i++)
7750     {
7751       int move_dir = test_dir[start_test + i];
7752       int move_dir_preference;
7753
7754       xx = x + test_xy[start_test + i][0];
7755       yy = y + test_xy[start_test + i][1];
7756
7757       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7758           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7759       {
7760         new_move_dir = move_dir;
7761
7762         break;
7763       }
7764
7765       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7766         continue;
7767
7768       move_dir_preference = -1 * RunnerVisit[xx][yy];
7769       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7770         move_dir_preference = PlayerVisit[xx][yy];
7771
7772       if (move_dir_preference > move_preference)
7773       {
7774         /* prefer field that has not been visited for the longest time */
7775         move_preference = move_dir_preference;
7776         new_move_dir = move_dir;
7777       }
7778       else if (move_dir_preference == move_preference &&
7779                move_dir == old_move_dir)
7780       {
7781         /* prefer last direction when all directions are preferred equally */
7782         move_preference = move_dir_preference;
7783         new_move_dir = move_dir;
7784       }
7785     }
7786
7787     MovDir[x][y] = new_move_dir;
7788     if (old_move_dir != new_move_dir)
7789       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7790   }
7791 }
7792
7793 static void TurnRound(int x, int y)
7794 {
7795   int direction = MovDir[x][y];
7796
7797   TurnRoundExt(x, y);
7798
7799   GfxDir[x][y] = MovDir[x][y];
7800
7801   if (direction != MovDir[x][y])
7802     GfxFrame[x][y] = 0;
7803
7804   if (MovDelay[x][y])
7805     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7806
7807   ResetGfxFrame(x, y, FALSE);
7808 }
7809
7810 static boolean JustBeingPushed(int x, int y)
7811 {
7812   int i;
7813
7814   for (i = 0; i < MAX_PLAYERS; i++)
7815   {
7816     struct PlayerInfo *player = &stored_player[i];
7817
7818     if (player->active && player->is_pushing && player->MovPos)
7819     {
7820       int next_jx = player->jx + (player->jx - player->last_jx);
7821       int next_jy = player->jy + (player->jy - player->last_jy);
7822
7823       if (x == next_jx && y == next_jy)
7824         return TRUE;
7825     }
7826   }
7827
7828   return FALSE;
7829 }
7830
7831 void StartMoving(int x, int y)
7832 {
7833   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7834   int element = Feld[x][y];
7835
7836   if (Stop[x][y])
7837     return;
7838
7839   if (MovDelay[x][y] == 0)
7840     GfxAction[x][y] = ACTION_DEFAULT;
7841
7842   if (CAN_FALL(element) && y < lev_fieldy - 1)
7843   {
7844     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7845         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7846       if (JustBeingPushed(x, y))
7847         return;
7848
7849     if (element == EL_QUICKSAND_FULL)
7850     {
7851       if (IS_FREE(x, y + 1))
7852       {
7853         InitMovingField(x, y, MV_DOWN);
7854         started_moving = TRUE;
7855
7856         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7857 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7858         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7859           Store[x][y] = EL_ROCK;
7860 #else
7861         Store[x][y] = EL_ROCK;
7862 #endif
7863
7864         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7865       }
7866       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7867       {
7868         if (!MovDelay[x][y])
7869         {
7870           MovDelay[x][y] = TILEY + 1;
7871
7872           ResetGfxAnimation(x, y);
7873           ResetGfxAnimation(x, y + 1);
7874         }
7875
7876         if (MovDelay[x][y])
7877         {
7878           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7879           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7880
7881           MovDelay[x][y]--;
7882           if (MovDelay[x][y])
7883             return;
7884         }
7885
7886         Feld[x][y] = EL_QUICKSAND_EMPTY;
7887         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7888         Store[x][y + 1] = Store[x][y];
7889         Store[x][y] = 0;
7890
7891         PlayLevelSoundAction(x, y, ACTION_FILLING);
7892       }
7893       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7894       {
7895         if (!MovDelay[x][y])
7896         {
7897           MovDelay[x][y] = TILEY + 1;
7898
7899           ResetGfxAnimation(x, y);
7900           ResetGfxAnimation(x, y + 1);
7901         }
7902
7903         if (MovDelay[x][y])
7904         {
7905           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7906           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7907
7908           MovDelay[x][y]--;
7909           if (MovDelay[x][y])
7910             return;
7911         }
7912
7913         Feld[x][y] = EL_QUICKSAND_EMPTY;
7914         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7915         Store[x][y + 1] = Store[x][y];
7916         Store[x][y] = 0;
7917
7918         PlayLevelSoundAction(x, y, ACTION_FILLING);
7919       }
7920     }
7921     else if (element == EL_QUICKSAND_FAST_FULL)
7922     {
7923       if (IS_FREE(x, y + 1))
7924       {
7925         InitMovingField(x, y, MV_DOWN);
7926         started_moving = TRUE;
7927
7928         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7929 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7930         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7931           Store[x][y] = EL_ROCK;
7932 #else
7933         Store[x][y] = EL_ROCK;
7934 #endif
7935
7936         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7937       }
7938       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7939       {
7940         if (!MovDelay[x][y])
7941         {
7942           MovDelay[x][y] = TILEY + 1;
7943
7944           ResetGfxAnimation(x, y);
7945           ResetGfxAnimation(x, y + 1);
7946         }
7947
7948         if (MovDelay[x][y])
7949         {
7950           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7951           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7952
7953           MovDelay[x][y]--;
7954           if (MovDelay[x][y])
7955             return;
7956         }
7957
7958         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7959         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7960         Store[x][y + 1] = Store[x][y];
7961         Store[x][y] = 0;
7962
7963         PlayLevelSoundAction(x, y, ACTION_FILLING);
7964       }
7965       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7966       {
7967         if (!MovDelay[x][y])
7968         {
7969           MovDelay[x][y] = TILEY + 1;
7970
7971           ResetGfxAnimation(x, y);
7972           ResetGfxAnimation(x, y + 1);
7973         }
7974
7975         if (MovDelay[x][y])
7976         {
7977           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7978           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7979
7980           MovDelay[x][y]--;
7981           if (MovDelay[x][y])
7982             return;
7983         }
7984
7985         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7986         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7987         Store[x][y + 1] = Store[x][y];
7988         Store[x][y] = 0;
7989
7990         PlayLevelSoundAction(x, y, ACTION_FILLING);
7991       }
7992     }
7993     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7994              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7995     {
7996       InitMovingField(x, y, MV_DOWN);
7997       started_moving = TRUE;
7998
7999       Feld[x][y] = EL_QUICKSAND_FILLING;
8000       Store[x][y] = element;
8001
8002       PlayLevelSoundAction(x, y, ACTION_FILLING);
8003     }
8004     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8005              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8006     {
8007       InitMovingField(x, y, MV_DOWN);
8008       started_moving = TRUE;
8009
8010       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8011       Store[x][y] = element;
8012
8013       PlayLevelSoundAction(x, y, ACTION_FILLING);
8014     }
8015     else if (element == EL_MAGIC_WALL_FULL)
8016     {
8017       if (IS_FREE(x, y + 1))
8018       {
8019         InitMovingField(x, y, MV_DOWN);
8020         started_moving = TRUE;
8021
8022         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8023         Store[x][y] = EL_CHANGED(Store[x][y]);
8024       }
8025       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8026       {
8027         if (!MovDelay[x][y])
8028           MovDelay[x][y] = TILEY/4 + 1;
8029
8030         if (MovDelay[x][y])
8031         {
8032           MovDelay[x][y]--;
8033           if (MovDelay[x][y])
8034             return;
8035         }
8036
8037         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8038         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8039         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8040         Store[x][y] = 0;
8041       }
8042     }
8043     else if (element == EL_BD_MAGIC_WALL_FULL)
8044     {
8045       if (IS_FREE(x, y + 1))
8046       {
8047         InitMovingField(x, y, MV_DOWN);
8048         started_moving = TRUE;
8049
8050         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8051         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8052       }
8053       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8054       {
8055         if (!MovDelay[x][y])
8056           MovDelay[x][y] = TILEY/4 + 1;
8057
8058         if (MovDelay[x][y])
8059         {
8060           MovDelay[x][y]--;
8061           if (MovDelay[x][y])
8062             return;
8063         }
8064
8065         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8066         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8067         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8068         Store[x][y] = 0;
8069       }
8070     }
8071     else if (element == EL_DC_MAGIC_WALL_FULL)
8072     {
8073       if (IS_FREE(x, y + 1))
8074       {
8075         InitMovingField(x, y, MV_DOWN);
8076         started_moving = TRUE;
8077
8078         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8079         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8080       }
8081       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8082       {
8083         if (!MovDelay[x][y])
8084           MovDelay[x][y] = TILEY/4 + 1;
8085
8086         if (MovDelay[x][y])
8087         {
8088           MovDelay[x][y]--;
8089           if (MovDelay[x][y])
8090             return;
8091         }
8092
8093         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8094         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8095         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8096         Store[x][y] = 0;
8097       }
8098     }
8099     else if ((CAN_PASS_MAGIC_WALL(element) &&
8100               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8101                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8102              (CAN_PASS_DC_MAGIC_WALL(element) &&
8103               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8104
8105     {
8106       InitMovingField(x, y, MV_DOWN);
8107       started_moving = TRUE;
8108
8109       Feld[x][y] =
8110         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8111          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8112          EL_DC_MAGIC_WALL_FILLING);
8113       Store[x][y] = element;
8114     }
8115     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8116     {
8117       SplashAcid(x, y + 1);
8118
8119       InitMovingField(x, y, MV_DOWN);
8120       started_moving = TRUE;
8121
8122       Store[x][y] = EL_ACID;
8123     }
8124     else if (
8125 #if USE_FIX_IMPACT_COLLISION
8126              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8127               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8128 #else
8129              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8130               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8131 #endif
8132              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8133               CAN_FALL(element) && WasJustFalling[x][y] &&
8134               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8135
8136              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8137               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8138               (Feld[x][y + 1] == EL_BLOCKED)))
8139     {
8140       /* this is needed for a special case not covered by calling "Impact()"
8141          from "ContinueMoving()": if an element moves to a tile directly below
8142          another element which was just falling on that tile (which was empty
8143          in the previous frame), the falling element above would just stop
8144          instead of smashing the element below (in previous version, the above
8145          element was just checked for "moving" instead of "falling", resulting
8146          in incorrect smashes caused by horizontal movement of the above
8147          element; also, the case of the player being the element to smash was
8148          simply not covered here... :-/ ) */
8149
8150       CheckCollision[x][y] = 0;
8151       CheckImpact[x][y] = 0;
8152
8153       Impact(x, y);
8154     }
8155     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8156     {
8157       if (MovDir[x][y] == MV_NONE)
8158       {
8159         InitMovingField(x, y, MV_DOWN);
8160         started_moving = TRUE;
8161       }
8162     }
8163     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8164     {
8165       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8166         MovDir[x][y] = MV_DOWN;
8167
8168       InitMovingField(x, y, MV_DOWN);
8169       started_moving = TRUE;
8170     }
8171     else if (element == EL_AMOEBA_DROP)
8172     {
8173       Feld[x][y] = EL_AMOEBA_GROWING;
8174       Store[x][y] = EL_AMOEBA_WET;
8175     }
8176     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8177               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8178              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8179              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8180     {
8181       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8182                                 (IS_FREE(x - 1, y + 1) ||
8183                                  Feld[x - 1][y + 1] == EL_ACID));
8184       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8185                                 (IS_FREE(x + 1, y + 1) ||
8186                                  Feld[x + 1][y + 1] == EL_ACID));
8187       boolean can_fall_any  = (can_fall_left || can_fall_right);
8188       boolean can_fall_both = (can_fall_left && can_fall_right);
8189       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8190
8191 #if USE_NEW_ALL_SLIPPERY
8192       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8193       {
8194         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8195           can_fall_right = FALSE;
8196         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8197           can_fall_left = FALSE;
8198         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8199           can_fall_right = FALSE;
8200         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8201           can_fall_left = FALSE;
8202
8203         can_fall_any  = (can_fall_left || can_fall_right);
8204         can_fall_both = FALSE;
8205       }
8206 #else
8207       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8208       {
8209         if (slippery_type == SLIPPERY_ONLY_LEFT)
8210           can_fall_right = FALSE;
8211         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8212           can_fall_left = FALSE;
8213         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8214           can_fall_right = FALSE;
8215         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8216           can_fall_left = FALSE;
8217
8218         can_fall_any  = (can_fall_left || can_fall_right);
8219         can_fall_both = (can_fall_left && can_fall_right);
8220       }
8221 #endif
8222
8223 #if USE_NEW_ALL_SLIPPERY
8224 #else
8225 #if USE_NEW_SP_SLIPPERY
8226       /* !!! better use the same properties as for custom elements here !!! */
8227       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8228                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8229       {
8230         can_fall_right = FALSE;         /* slip down on left side */
8231         can_fall_both = FALSE;
8232       }
8233 #endif
8234 #endif
8235
8236 #if USE_NEW_ALL_SLIPPERY
8237       if (can_fall_both)
8238       {
8239         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8240           can_fall_right = FALSE;       /* slip down on left side */
8241         else
8242           can_fall_left = !(can_fall_right = RND(2));
8243
8244         can_fall_both = FALSE;
8245       }
8246 #else
8247       if (can_fall_both)
8248       {
8249         if (game.emulation == EMU_BOULDERDASH ||
8250             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8251           can_fall_right = FALSE;       /* slip down on left side */
8252         else
8253           can_fall_left = !(can_fall_right = RND(2));
8254
8255         can_fall_both = FALSE;
8256       }
8257 #endif
8258
8259       if (can_fall_any)
8260       {
8261         /* if not determined otherwise, prefer left side for slipping down */
8262         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8263         started_moving = TRUE;
8264       }
8265     }
8266 #if 0
8267     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8268 #else
8269     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8270 #endif
8271     {
8272       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8273       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8274       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8275       int belt_dir = game.belt_dir[belt_nr];
8276
8277       if ((belt_dir == MV_LEFT  && left_is_free) ||
8278           (belt_dir == MV_RIGHT && right_is_free))
8279       {
8280         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8281
8282         InitMovingField(x, y, belt_dir);
8283         started_moving = TRUE;
8284
8285         Pushed[x][y] = TRUE;
8286         Pushed[nextx][y] = TRUE;
8287
8288         GfxAction[x][y] = ACTION_DEFAULT;
8289       }
8290       else
8291       {
8292         MovDir[x][y] = 0;       /* if element was moving, stop it */
8293       }
8294     }
8295   }
8296
8297   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8298 #if 0
8299   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8300 #else
8301   if (CAN_MOVE(element) && !started_moving)
8302 #endif
8303   {
8304     int move_pattern = element_info[element].move_pattern;
8305     int newx, newy;
8306
8307 #if 0
8308 #if DEBUG
8309     if (MovDir[x][y] == MV_NONE)
8310     {
8311       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8312              x, y, element, element_info[element].token_name);
8313       printf("StartMoving(): This should never happen!\n");
8314     }
8315 #endif
8316 #endif
8317
8318     Moving2Blocked(x, y, &newx, &newy);
8319
8320     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8321       return;
8322
8323     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8324         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8325     {
8326       WasJustMoving[x][y] = 0;
8327       CheckCollision[x][y] = 0;
8328
8329       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8330
8331       if (Feld[x][y] != element)        /* element has changed */
8332         return;
8333     }
8334
8335     if (!MovDelay[x][y])        /* start new movement phase */
8336     {
8337       /* all objects that can change their move direction after each step
8338          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8339
8340       if (element != EL_YAMYAM &&
8341           element != EL_DARK_YAMYAM &&
8342           element != EL_PACMAN &&
8343           !(move_pattern & MV_ANY_DIRECTION) &&
8344           move_pattern != MV_TURNING_LEFT &&
8345           move_pattern != MV_TURNING_RIGHT &&
8346           move_pattern != MV_TURNING_LEFT_RIGHT &&
8347           move_pattern != MV_TURNING_RIGHT_LEFT &&
8348           move_pattern != MV_TURNING_RANDOM)
8349       {
8350         TurnRound(x, y);
8351
8352         if (MovDelay[x][y] && (element == EL_BUG ||
8353                                element == EL_SPACESHIP ||
8354                                element == EL_SP_SNIKSNAK ||
8355                                element == EL_SP_ELECTRON ||
8356                                element == EL_MOLE))
8357           TEST_DrawLevelField(x, y);
8358       }
8359     }
8360
8361     if (MovDelay[x][y])         /* wait some time before next movement */
8362     {
8363       MovDelay[x][y]--;
8364
8365       if (element == EL_ROBOT ||
8366           element == EL_YAMYAM ||
8367           element == EL_DARK_YAMYAM)
8368       {
8369         DrawLevelElementAnimationIfNeeded(x, y, element);
8370         PlayLevelSoundAction(x, y, ACTION_WAITING);
8371       }
8372       else if (element == EL_SP_ELECTRON)
8373         DrawLevelElementAnimationIfNeeded(x, y, element);
8374       else if (element == EL_DRAGON)
8375       {
8376         int i;
8377         int dir = MovDir[x][y];
8378         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8379         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8380         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8381                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8382                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8383                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8384         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8385
8386         GfxAction[x][y] = ACTION_ATTACKING;
8387
8388         if (IS_PLAYER(x, y))
8389           DrawPlayerField(x, y);
8390         else
8391           TEST_DrawLevelField(x, y);
8392
8393         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8394
8395         for (i = 1; i <= 3; i++)
8396         {
8397           int xx = x + i * dx;
8398           int yy = y + i * dy;
8399           int sx = SCREENX(xx);
8400           int sy = SCREENY(yy);
8401           int flame_graphic = graphic + (i - 1);
8402
8403           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8404             break;
8405
8406           if (MovDelay[x][y])
8407           {
8408             int flamed = MovingOrBlocked2Element(xx, yy);
8409
8410             /* !!! */
8411 #if 0
8412             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8413               Bang(xx, yy);
8414             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8415               RemoveMovingField(xx, yy);
8416             else
8417               RemoveField(xx, yy);
8418 #else
8419             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8420               Bang(xx, yy);
8421             else
8422               RemoveMovingField(xx, yy);
8423 #endif
8424
8425             ChangeDelay[xx][yy] = 0;
8426
8427             Feld[xx][yy] = EL_FLAMES;
8428
8429             if (IN_SCR_FIELD(sx, sy))
8430             {
8431               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8432               DrawGraphic(sx, sy, flame_graphic, frame);
8433             }
8434           }
8435           else
8436           {
8437             if (Feld[xx][yy] == EL_FLAMES)
8438               Feld[xx][yy] = EL_EMPTY;
8439             TEST_DrawLevelField(xx, yy);
8440           }
8441         }
8442       }
8443
8444       if (MovDelay[x][y])       /* element still has to wait some time */
8445       {
8446         PlayLevelSoundAction(x, y, ACTION_WAITING);
8447
8448         return;
8449       }
8450     }
8451
8452     /* now make next step */
8453
8454     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8455
8456     if (DONT_COLLIDE_WITH(element) &&
8457         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8458         !PLAYER_ENEMY_PROTECTED(newx, newy))
8459     {
8460       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8461
8462       return;
8463     }
8464
8465     else if (CAN_MOVE_INTO_ACID(element) &&
8466              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8467              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8468              (MovDir[x][y] == MV_DOWN ||
8469               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8470     {
8471       SplashAcid(newx, newy);
8472       Store[x][y] = EL_ACID;
8473     }
8474     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8475     {
8476       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8477           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8478           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8479           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8480       {
8481         RemoveField(x, y);
8482         TEST_DrawLevelField(x, y);
8483
8484         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8485         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8486           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8487
8488         local_player->friends_still_needed--;
8489         if (!local_player->friends_still_needed &&
8490             !local_player->GameOver && AllPlayersGone)
8491           PlayerWins(local_player);
8492
8493         return;
8494       }
8495       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8496       {
8497         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8498           TEST_DrawLevelField(newx, newy);
8499         else
8500           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8501       }
8502       else if (!IS_FREE(newx, newy))
8503       {
8504         GfxAction[x][y] = ACTION_WAITING;
8505
8506         if (IS_PLAYER(x, y))
8507           DrawPlayerField(x, y);
8508         else
8509           TEST_DrawLevelField(x, y);
8510
8511         return;
8512       }
8513     }
8514     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8515     {
8516       if (IS_FOOD_PIG(Feld[newx][newy]))
8517       {
8518         if (IS_MOVING(newx, newy))
8519           RemoveMovingField(newx, newy);
8520         else
8521         {
8522           Feld[newx][newy] = EL_EMPTY;
8523           TEST_DrawLevelField(newx, newy);
8524         }
8525
8526         PlayLevelSound(x, y, SND_PIG_DIGGING);
8527       }
8528       else if (!IS_FREE(newx, newy))
8529       {
8530         if (IS_PLAYER(x, y))
8531           DrawPlayerField(x, y);
8532         else
8533           TEST_DrawLevelField(x, y);
8534
8535         return;
8536       }
8537     }
8538     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8539     {
8540       if (Store[x][y] != EL_EMPTY)
8541       {
8542         boolean can_clone = FALSE;
8543         int xx, yy;
8544
8545         /* check if element to clone is still there */
8546         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8547         {
8548           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8549           {
8550             can_clone = TRUE;
8551
8552             break;
8553           }
8554         }
8555
8556         /* cannot clone or target field not free anymore -- do not clone */
8557         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8558           Store[x][y] = EL_EMPTY;
8559       }
8560
8561       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8562       {
8563         if (IS_MV_DIAGONAL(MovDir[x][y]))
8564         {
8565           int diagonal_move_dir = MovDir[x][y];
8566           int stored = Store[x][y];
8567           int change_delay = 8;
8568           int graphic;
8569
8570           /* android is moving diagonally */
8571
8572           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8573
8574           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8575           GfxElement[x][y] = EL_EMC_ANDROID;
8576           GfxAction[x][y] = ACTION_SHRINKING;
8577           GfxDir[x][y] = diagonal_move_dir;
8578           ChangeDelay[x][y] = change_delay;
8579
8580           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8581                                    GfxDir[x][y]);
8582
8583           DrawLevelGraphicAnimation(x, y, graphic);
8584           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8585
8586           if (Feld[newx][newy] == EL_ACID)
8587           {
8588             SplashAcid(newx, newy);
8589
8590             return;
8591           }
8592
8593           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8594
8595           Store[newx][newy] = EL_EMC_ANDROID;
8596           GfxElement[newx][newy] = EL_EMC_ANDROID;
8597           GfxAction[newx][newy] = ACTION_GROWING;
8598           GfxDir[newx][newy] = diagonal_move_dir;
8599           ChangeDelay[newx][newy] = change_delay;
8600
8601           graphic = el_act_dir2img(GfxElement[newx][newy],
8602                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8603
8604           DrawLevelGraphicAnimation(newx, newy, graphic);
8605           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8606
8607           return;
8608         }
8609         else
8610         {
8611           Feld[newx][newy] = EL_EMPTY;
8612           TEST_DrawLevelField(newx, newy);
8613
8614           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8615         }
8616       }
8617       else if (!IS_FREE(newx, newy))
8618       {
8619 #if 0
8620         if (IS_PLAYER(x, y))
8621           DrawPlayerField(x, y);
8622         else
8623           TEST_DrawLevelField(x, y);
8624 #endif
8625
8626         return;
8627       }
8628     }
8629     else if (IS_CUSTOM_ELEMENT(element) &&
8630              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8631     {
8632 #if 1
8633       if (!DigFieldByCE(newx, newy, element))
8634         return;
8635 #else
8636       int new_element = Feld[newx][newy];
8637
8638       if (!IS_FREE(newx, newy))
8639       {
8640         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8641                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8642                       ACTION_BREAKING);
8643
8644         /* no element can dig solid indestructible elements */
8645         if (IS_INDESTRUCTIBLE(new_element) &&
8646             !IS_DIGGABLE(new_element) &&
8647             !IS_COLLECTIBLE(new_element))
8648           return;
8649
8650         if (AmoebaNr[newx][newy] &&
8651             (new_element == EL_AMOEBA_FULL ||
8652              new_element == EL_BD_AMOEBA ||
8653              new_element == EL_AMOEBA_GROWING))
8654         {
8655           AmoebaCnt[AmoebaNr[newx][newy]]--;
8656           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8657         }
8658
8659         if (IS_MOVING(newx, newy))
8660           RemoveMovingField(newx, newy);
8661         else
8662         {
8663           RemoveField(newx, newy);
8664           TEST_DrawLevelField(newx, newy);
8665         }
8666
8667         /* if digged element was about to explode, prevent the explosion */
8668         ExplodeField[newx][newy] = EX_TYPE_NONE;
8669
8670         PlayLevelSoundAction(x, y, action);
8671       }
8672
8673       Store[newx][newy] = EL_EMPTY;
8674
8675 #if 1
8676       /* this makes it possible to leave the removed element again */
8677       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8678         Store[newx][newy] = new_element;
8679 #else
8680       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8681       {
8682         int move_leave_element = element_info[element].move_leave_element;
8683
8684         /* this makes it possible to leave the removed element again */
8685         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8686                              new_element : move_leave_element);
8687       }
8688 #endif
8689
8690 #endif
8691
8692       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8693       {
8694         RunnerVisit[x][y] = FrameCounter;
8695         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8696       }
8697     }
8698     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8699     {
8700       if (!IS_FREE(newx, newy))
8701       {
8702         if (IS_PLAYER(x, y))
8703           DrawPlayerField(x, y);
8704         else
8705           TEST_DrawLevelField(x, y);
8706
8707         return;
8708       }
8709       else
8710       {
8711         boolean wanna_flame = !RND(10);
8712         int dx = newx - x, dy = newy - y;
8713         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8714         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8715         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8716                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8717         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8718                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8719
8720         if ((wanna_flame ||
8721              IS_CLASSIC_ENEMY(element1) ||
8722              IS_CLASSIC_ENEMY(element2)) &&
8723             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8724             element1 != EL_FLAMES && element2 != EL_FLAMES)
8725         {
8726           ResetGfxAnimation(x, y);
8727           GfxAction[x][y] = ACTION_ATTACKING;
8728
8729           if (IS_PLAYER(x, y))
8730             DrawPlayerField(x, y);
8731           else
8732             TEST_DrawLevelField(x, y);
8733
8734           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8735
8736           MovDelay[x][y] = 50;
8737
8738           /* !!! */
8739 #if 0
8740           RemoveField(newx, newy);
8741 #endif
8742           Feld[newx][newy] = EL_FLAMES;
8743           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8744           {
8745 #if 0
8746             RemoveField(newx1, newy1);
8747 #endif
8748             Feld[newx1][newy1] = EL_FLAMES;
8749           }
8750           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8751           {
8752 #if 0
8753             RemoveField(newx2, newy2);
8754 #endif
8755             Feld[newx2][newy2] = EL_FLAMES;
8756           }
8757
8758           return;
8759         }
8760       }
8761     }
8762     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8763              Feld[newx][newy] == EL_DIAMOND)
8764     {
8765       if (IS_MOVING(newx, newy))
8766         RemoveMovingField(newx, newy);
8767       else
8768       {
8769         Feld[newx][newy] = EL_EMPTY;
8770         TEST_DrawLevelField(newx, newy);
8771       }
8772
8773       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8774     }
8775     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8776              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8777     {
8778       if (AmoebaNr[newx][newy])
8779       {
8780         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8781         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8782             Feld[newx][newy] == EL_BD_AMOEBA)
8783           AmoebaCnt[AmoebaNr[newx][newy]]--;
8784       }
8785
8786 #if 0
8787       /* !!! test !!! */
8788       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8789       {
8790         RemoveMovingField(newx, newy);
8791       }
8792 #else
8793       if (IS_MOVING(newx, newy))
8794       {
8795         RemoveMovingField(newx, newy);
8796       }
8797 #endif
8798       else
8799       {
8800         Feld[newx][newy] = EL_EMPTY;
8801         TEST_DrawLevelField(newx, newy);
8802       }
8803
8804       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8805     }
8806     else if ((element == EL_PACMAN || element == EL_MOLE)
8807              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8808     {
8809       if (AmoebaNr[newx][newy])
8810       {
8811         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8812         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8813             Feld[newx][newy] == EL_BD_AMOEBA)
8814           AmoebaCnt[AmoebaNr[newx][newy]]--;
8815       }
8816
8817       if (element == EL_MOLE)
8818       {
8819         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8820         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8821
8822         ResetGfxAnimation(x, y);
8823         GfxAction[x][y] = ACTION_DIGGING;
8824         TEST_DrawLevelField(x, y);
8825
8826         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8827
8828         return;                         /* wait for shrinking amoeba */
8829       }
8830       else      /* element == EL_PACMAN */
8831       {
8832         Feld[newx][newy] = EL_EMPTY;
8833         TEST_DrawLevelField(newx, newy);
8834         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8835       }
8836     }
8837     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8838              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8839               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8840     {
8841       /* wait for shrinking amoeba to completely disappear */
8842       return;
8843     }
8844     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8845     {
8846       /* object was running against a wall */
8847
8848       TurnRound(x, y);
8849
8850 #if 0
8851       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8852       if (move_pattern & MV_ANY_DIRECTION &&
8853           move_pattern == MovDir[x][y])
8854       {
8855         int blocking_element =
8856           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8857
8858         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8859                                  MovDir[x][y]);
8860
8861         element = Feld[x][y];   /* element might have changed */
8862       }
8863 #endif
8864
8865       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8866         DrawLevelElementAnimation(x, y, element);
8867
8868       if (DONT_TOUCH(element))
8869         TestIfBadThingTouchesPlayer(x, y);
8870
8871       return;
8872     }
8873
8874     InitMovingField(x, y, MovDir[x][y]);
8875
8876     PlayLevelSoundAction(x, y, ACTION_MOVING);
8877   }
8878
8879   if (MovDir[x][y])
8880     ContinueMoving(x, y);
8881 }
8882
8883 void ContinueMoving(int x, int y)
8884 {
8885   int element = Feld[x][y];
8886   struct ElementInfo *ei = &element_info[element];
8887   int direction = MovDir[x][y];
8888   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8889   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8890   int newx = x + dx, newy = y + dy;
8891   int stored = Store[x][y];
8892   int stored_new = Store[newx][newy];
8893   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8894   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8895   boolean last_line = (newy == lev_fieldy - 1);
8896
8897   MovPos[x][y] += getElementMoveStepsize(x, y);
8898
8899   if (pushed_by_player) /* special case: moving object pushed by player */
8900     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8901
8902   if (ABS(MovPos[x][y]) < TILEX)
8903   {
8904 #if 0
8905     int ee = Feld[x][y];
8906     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8907     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8908
8909     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8910            x, y, ABS(MovPos[x][y]),
8911            ee, gg, ff,
8912            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8913 #endif
8914
8915     TEST_DrawLevelField(x, y);
8916
8917     return;     /* element is still moving */
8918   }
8919
8920   /* element reached destination field */
8921
8922   Feld[x][y] = EL_EMPTY;
8923   Feld[newx][newy] = element;
8924   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8925
8926   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8927   {
8928     element = Feld[newx][newy] = EL_ACID;
8929   }
8930   else if (element == EL_MOLE)
8931   {
8932     Feld[x][y] = EL_SAND;
8933
8934     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8935   }
8936   else if (element == EL_QUICKSAND_FILLING)
8937   {
8938     element = Feld[newx][newy] = get_next_element(element);
8939     Store[newx][newy] = Store[x][y];
8940   }
8941   else if (element == EL_QUICKSAND_EMPTYING)
8942   {
8943     Feld[x][y] = get_next_element(element);
8944     element = Feld[newx][newy] = Store[x][y];
8945   }
8946   else if (element == EL_QUICKSAND_FAST_FILLING)
8947   {
8948     element = Feld[newx][newy] = get_next_element(element);
8949     Store[newx][newy] = Store[x][y];
8950   }
8951   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8952   {
8953     Feld[x][y] = get_next_element(element);
8954     element = Feld[newx][newy] = Store[x][y];
8955   }
8956   else if (element == EL_MAGIC_WALL_FILLING)
8957   {
8958     element = Feld[newx][newy] = get_next_element(element);
8959     if (!game.magic_wall_active)
8960       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8961     Store[newx][newy] = Store[x][y];
8962   }
8963   else if (element == EL_MAGIC_WALL_EMPTYING)
8964   {
8965     Feld[x][y] = get_next_element(element);
8966     if (!game.magic_wall_active)
8967       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8968     element = Feld[newx][newy] = Store[x][y];
8969
8970 #if USE_NEW_CUSTOM_VALUE
8971     InitField(newx, newy, FALSE);
8972 #endif
8973   }
8974   else if (element == EL_BD_MAGIC_WALL_FILLING)
8975   {
8976     element = Feld[newx][newy] = get_next_element(element);
8977     if (!game.magic_wall_active)
8978       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8979     Store[newx][newy] = Store[x][y];
8980   }
8981   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8982   {
8983     Feld[x][y] = get_next_element(element);
8984     if (!game.magic_wall_active)
8985       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8986     element = Feld[newx][newy] = Store[x][y];
8987
8988 #if USE_NEW_CUSTOM_VALUE
8989     InitField(newx, newy, FALSE);
8990 #endif
8991   }
8992   else if (element == EL_DC_MAGIC_WALL_FILLING)
8993   {
8994     element = Feld[newx][newy] = get_next_element(element);
8995     if (!game.magic_wall_active)
8996       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8997     Store[newx][newy] = Store[x][y];
8998   }
8999   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9000   {
9001     Feld[x][y] = get_next_element(element);
9002     if (!game.magic_wall_active)
9003       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9004     element = Feld[newx][newy] = Store[x][y];
9005
9006 #if USE_NEW_CUSTOM_VALUE
9007     InitField(newx, newy, FALSE);
9008 #endif
9009   }
9010   else if (element == EL_AMOEBA_DROPPING)
9011   {
9012     Feld[x][y] = get_next_element(element);
9013     element = Feld[newx][newy] = Store[x][y];
9014   }
9015   else if (element == EL_SOKOBAN_OBJECT)
9016   {
9017     if (Back[x][y])
9018       Feld[x][y] = Back[x][y];
9019
9020     if (Back[newx][newy])
9021       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9022
9023     Back[x][y] = Back[newx][newy] = 0;
9024   }
9025
9026   Store[x][y] = EL_EMPTY;
9027   MovPos[x][y] = 0;
9028   MovDir[x][y] = 0;
9029   MovDelay[x][y] = 0;
9030
9031   MovDelay[newx][newy] = 0;
9032
9033   if (CAN_CHANGE_OR_HAS_ACTION(element))
9034   {
9035     /* copy element change control values to new field */
9036     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9037     ChangePage[newx][newy]  = ChangePage[x][y];
9038     ChangeCount[newx][newy] = ChangeCount[x][y];
9039     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9040   }
9041
9042 #if USE_NEW_CUSTOM_VALUE
9043   CustomValue[newx][newy] = CustomValue[x][y];
9044 #endif
9045
9046   ChangeDelay[x][y] = 0;
9047   ChangePage[x][y] = -1;
9048   ChangeCount[x][y] = 0;
9049   ChangeEvent[x][y] = -1;
9050
9051 #if USE_NEW_CUSTOM_VALUE
9052   CustomValue[x][y] = 0;
9053 #endif
9054
9055   /* copy animation control values to new field */
9056   GfxFrame[newx][newy]  = GfxFrame[x][y];
9057   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9058   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9059   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9060
9061   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9062
9063   /* some elements can leave other elements behind after moving */
9064 #if 1
9065   if (ei->move_leave_element != EL_EMPTY &&
9066       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9067       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9068 #else
9069   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9070       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9071       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9072 #endif
9073   {
9074     int move_leave_element = ei->move_leave_element;
9075
9076 #if 1
9077 #if 1
9078     /* this makes it possible to leave the removed element again */
9079     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9080       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9081 #else
9082     /* this makes it possible to leave the removed element again */
9083     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9084       move_leave_element = stored;
9085 #endif
9086 #else
9087     /* this makes it possible to leave the removed element again */
9088     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9089         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9090       move_leave_element = stored;
9091 #endif
9092
9093     Feld[x][y] = move_leave_element;
9094
9095     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9096       MovDir[x][y] = direction;
9097
9098     InitField(x, y, FALSE);
9099
9100     if (GFX_CRUMBLED(Feld[x][y]))
9101       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9102
9103     if (ELEM_IS_PLAYER(move_leave_element))
9104       RelocatePlayer(x, y, move_leave_element);
9105   }
9106
9107   /* do this after checking for left-behind element */
9108   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9109
9110   if (!CAN_MOVE(element) ||
9111       (CAN_FALL(element) && direction == MV_DOWN &&
9112        (element == EL_SPRING ||
9113         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9114         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9115     GfxDir[x][y] = MovDir[newx][newy] = 0;
9116
9117   TEST_DrawLevelField(x, y);
9118   TEST_DrawLevelField(newx, newy);
9119
9120   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9121
9122   /* prevent pushed element from moving on in pushed direction */
9123   if (pushed_by_player && CAN_MOVE(element) &&
9124       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9125       !(element_info[element].move_pattern & direction))
9126     TurnRound(newx, newy);
9127
9128   /* prevent elements on conveyor belt from moving on in last direction */
9129   if (pushed_by_conveyor && CAN_FALL(element) &&
9130       direction & MV_HORIZONTAL)
9131     MovDir[newx][newy] = 0;
9132
9133   if (!pushed_by_player)
9134   {
9135     int nextx = newx + dx, nexty = newy + dy;
9136     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9137
9138     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9139
9140     if (CAN_FALL(element) && direction == MV_DOWN)
9141       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9142
9143     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9144       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9145
9146 #if USE_FIX_IMPACT_COLLISION
9147     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9148       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9149 #endif
9150   }
9151
9152   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9153   {
9154     TestIfBadThingTouchesPlayer(newx, newy);
9155     TestIfBadThingTouchesFriend(newx, newy);
9156
9157     if (!IS_CUSTOM_ELEMENT(element))
9158       TestIfBadThingTouchesOtherBadThing(newx, newy);
9159   }
9160   else if (element == EL_PENGUIN)
9161     TestIfFriendTouchesBadThing(newx, newy);
9162
9163   if (DONT_GET_HIT_BY(element))
9164   {
9165     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9166   }
9167
9168   /* give the player one last chance (one more frame) to move away */
9169   if (CAN_FALL(element) && direction == MV_DOWN &&
9170       (last_line || (!IS_FREE(x, newy + 1) &&
9171                      (!IS_PLAYER(x, newy + 1) ||
9172                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9173     Impact(x, newy);
9174
9175   if (pushed_by_player && !game.use_change_when_pushing_bug)
9176   {
9177     int push_side = MV_DIR_OPPOSITE(direction);
9178     struct PlayerInfo *player = PLAYERINFO(x, y);
9179
9180     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9181                                player->index_bit, push_side);
9182     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9183                                         player->index_bit, push_side);
9184   }
9185
9186   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9187     MovDelay[newx][newy] = 1;
9188
9189   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9190
9191   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9192
9193 #if 0
9194   if (ChangePage[newx][newy] != -1)             /* delayed change */
9195   {
9196     int page = ChangePage[newx][newy];
9197     struct ElementChangeInfo *change = &ei->change_page[page];
9198
9199     ChangePage[newx][newy] = -1;
9200
9201     if (change->can_change)
9202     {
9203       if (ChangeElement(newx, newy, element, page))
9204       {
9205         if (change->post_change_function)
9206           change->post_change_function(newx, newy);
9207       }
9208     }
9209
9210     if (change->has_action)
9211       ExecuteCustomElementAction(newx, newy, element, page);
9212   }
9213 #endif
9214
9215   TestIfElementHitsCustomElement(newx, newy, direction);
9216   TestIfPlayerTouchesCustomElement(newx, newy);
9217   TestIfElementTouchesCustomElement(newx, newy);
9218
9219   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9220       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9221     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9222                              MV_DIR_OPPOSITE(direction));
9223 }
9224
9225 int AmoebeNachbarNr(int ax, int ay)
9226 {
9227   int i;
9228   int element = Feld[ax][ay];
9229   int group_nr = 0;
9230   static int xy[4][2] =
9231   {
9232     { 0, -1 },
9233     { -1, 0 },
9234     { +1, 0 },
9235     { 0, +1 }
9236   };
9237
9238   for (i = 0; i < NUM_DIRECTIONS; i++)
9239   {
9240     int x = ax + xy[i][0];
9241     int y = ay + xy[i][1];
9242
9243     if (!IN_LEV_FIELD(x, y))
9244       continue;
9245
9246     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9247       group_nr = AmoebaNr[x][y];
9248   }
9249
9250   return group_nr;
9251 }
9252
9253 void AmoebenVereinigen(int ax, int ay)
9254 {
9255   int i, x, y, xx, yy;
9256   int new_group_nr = AmoebaNr[ax][ay];
9257   static int xy[4][2] =
9258   {
9259     { 0, -1 },
9260     { -1, 0 },
9261     { +1, 0 },
9262     { 0, +1 }
9263   };
9264
9265   if (new_group_nr == 0)
9266     return;
9267
9268   for (i = 0; i < NUM_DIRECTIONS; i++)
9269   {
9270     x = ax + xy[i][0];
9271     y = ay + xy[i][1];
9272
9273     if (!IN_LEV_FIELD(x, y))
9274       continue;
9275
9276     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9277          Feld[x][y] == EL_BD_AMOEBA ||
9278          Feld[x][y] == EL_AMOEBA_DEAD) &&
9279         AmoebaNr[x][y] != new_group_nr)
9280     {
9281       int old_group_nr = AmoebaNr[x][y];
9282
9283       if (old_group_nr == 0)
9284         return;
9285
9286       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9287       AmoebaCnt[old_group_nr] = 0;
9288       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9289       AmoebaCnt2[old_group_nr] = 0;
9290
9291       SCAN_PLAYFIELD(xx, yy)
9292       {
9293         if (AmoebaNr[xx][yy] == old_group_nr)
9294           AmoebaNr[xx][yy] = new_group_nr;
9295       }
9296     }
9297   }
9298 }
9299
9300 void AmoebeUmwandeln(int ax, int ay)
9301 {
9302   int i, x, y;
9303
9304   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9305   {
9306     int group_nr = AmoebaNr[ax][ay];
9307
9308 #ifdef DEBUG
9309     if (group_nr == 0)
9310     {
9311       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9312       printf("AmoebeUmwandeln(): This should never happen!\n");
9313       return;
9314     }
9315 #endif
9316
9317     SCAN_PLAYFIELD(x, y)
9318     {
9319       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9320       {
9321         AmoebaNr[x][y] = 0;
9322         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9323       }
9324     }
9325
9326     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9327                             SND_AMOEBA_TURNING_TO_GEM :
9328                             SND_AMOEBA_TURNING_TO_ROCK));
9329     Bang(ax, ay);
9330   }
9331   else
9332   {
9333     static int xy[4][2] =
9334     {
9335       { 0, -1 },
9336       { -1, 0 },
9337       { +1, 0 },
9338       { 0, +1 }
9339     };
9340
9341     for (i = 0; i < NUM_DIRECTIONS; i++)
9342     {
9343       x = ax + xy[i][0];
9344       y = ay + xy[i][1];
9345
9346       if (!IN_LEV_FIELD(x, y))
9347         continue;
9348
9349       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9350       {
9351         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9352                               SND_AMOEBA_TURNING_TO_GEM :
9353                               SND_AMOEBA_TURNING_TO_ROCK));
9354         Bang(x, y);
9355       }
9356     }
9357   }
9358 }
9359
9360 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9361 {
9362   int x, y;
9363   int group_nr = AmoebaNr[ax][ay];
9364   boolean done = FALSE;
9365
9366 #ifdef DEBUG
9367   if (group_nr == 0)
9368   {
9369     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9370     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9371     return;
9372   }
9373 #endif
9374
9375   SCAN_PLAYFIELD(x, y)
9376   {
9377     if (AmoebaNr[x][y] == group_nr &&
9378         (Feld[x][y] == EL_AMOEBA_DEAD ||
9379          Feld[x][y] == EL_BD_AMOEBA ||
9380          Feld[x][y] == EL_AMOEBA_GROWING))
9381     {
9382       AmoebaNr[x][y] = 0;
9383       Feld[x][y] = new_element;
9384       InitField(x, y, FALSE);
9385       TEST_DrawLevelField(x, y);
9386       done = TRUE;
9387     }
9388   }
9389
9390   if (done)
9391     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9392                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9393                             SND_BD_AMOEBA_TURNING_TO_GEM));
9394 }
9395
9396 void AmoebeWaechst(int x, int y)
9397 {
9398   static unsigned long sound_delay = 0;
9399   static unsigned long sound_delay_value = 0;
9400
9401   if (!MovDelay[x][y])          /* start new growing cycle */
9402   {
9403     MovDelay[x][y] = 7;
9404
9405     if (DelayReached(&sound_delay, sound_delay_value))
9406     {
9407       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9408       sound_delay_value = 30;
9409     }
9410   }
9411
9412   if (MovDelay[x][y])           /* wait some time before growing bigger */
9413   {
9414     MovDelay[x][y]--;
9415     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9416     {
9417       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9418                                            6 - MovDelay[x][y]);
9419
9420       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9421     }
9422
9423     if (!MovDelay[x][y])
9424     {
9425       Feld[x][y] = Store[x][y];
9426       Store[x][y] = 0;
9427       TEST_DrawLevelField(x, y);
9428     }
9429   }
9430 }
9431
9432 void AmoebaDisappearing(int x, int y)
9433 {
9434   static unsigned long sound_delay = 0;
9435   static unsigned long sound_delay_value = 0;
9436
9437   if (!MovDelay[x][y])          /* start new shrinking cycle */
9438   {
9439     MovDelay[x][y] = 7;
9440
9441     if (DelayReached(&sound_delay, sound_delay_value))
9442       sound_delay_value = 30;
9443   }
9444
9445   if (MovDelay[x][y])           /* wait some time before shrinking */
9446   {
9447     MovDelay[x][y]--;
9448     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9449     {
9450       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9451                                            6 - MovDelay[x][y]);
9452
9453       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9454     }
9455
9456     if (!MovDelay[x][y])
9457     {
9458       Feld[x][y] = EL_EMPTY;
9459       TEST_DrawLevelField(x, y);
9460
9461       /* don't let mole enter this field in this cycle;
9462          (give priority to objects falling to this field from above) */
9463       Stop[x][y] = TRUE;
9464     }
9465   }
9466 }
9467
9468 void AmoebeAbleger(int ax, int ay)
9469 {
9470   int i;
9471   int element = Feld[ax][ay];
9472   int graphic = el2img(element);
9473   int newax = ax, neway = ay;
9474   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9475   static int xy[4][2] =
9476   {
9477     { 0, -1 },
9478     { -1, 0 },
9479     { +1, 0 },
9480     { 0, +1 }
9481   };
9482
9483   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9484   {
9485     Feld[ax][ay] = EL_AMOEBA_DEAD;
9486     TEST_DrawLevelField(ax, ay);
9487     return;
9488   }
9489
9490   if (IS_ANIMATED(graphic))
9491     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9492
9493   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9494     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9495
9496   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9497   {
9498     MovDelay[ax][ay]--;
9499     if (MovDelay[ax][ay])
9500       return;
9501   }
9502
9503   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9504   {
9505     int start = RND(4);
9506     int x = ax + xy[start][0];
9507     int y = ay + xy[start][1];
9508
9509     if (!IN_LEV_FIELD(x, y))
9510       return;
9511
9512     if (IS_FREE(x, y) ||
9513         CAN_GROW_INTO(Feld[x][y]) ||
9514         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9515         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9516     {
9517       newax = x;
9518       neway = y;
9519     }
9520
9521     if (newax == ax && neway == ay)
9522       return;
9523   }
9524   else                          /* normal or "filled" (BD style) amoeba */
9525   {
9526     int start = RND(4);
9527     boolean waiting_for_player = FALSE;
9528
9529     for (i = 0; i < NUM_DIRECTIONS; i++)
9530     {
9531       int j = (start + i) % 4;
9532       int x = ax + xy[j][0];
9533       int y = ay + xy[j][1];
9534
9535       if (!IN_LEV_FIELD(x, y))
9536         continue;
9537
9538       if (IS_FREE(x, y) ||
9539           CAN_GROW_INTO(Feld[x][y]) ||
9540           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9541           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9542       {
9543         newax = x;
9544         neway = y;
9545         break;
9546       }
9547       else if (IS_PLAYER(x, y))
9548         waiting_for_player = TRUE;
9549     }
9550
9551     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9552     {
9553       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9554       {
9555         Feld[ax][ay] = EL_AMOEBA_DEAD;
9556         TEST_DrawLevelField(ax, ay);
9557         AmoebaCnt[AmoebaNr[ax][ay]]--;
9558
9559         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9560         {
9561           if (element == EL_AMOEBA_FULL)
9562             AmoebeUmwandeln(ax, ay);
9563           else if (element == EL_BD_AMOEBA)
9564             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9565         }
9566       }
9567       return;
9568     }
9569     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9570     {
9571       /* amoeba gets larger by growing in some direction */
9572
9573       int new_group_nr = AmoebaNr[ax][ay];
9574
9575 #ifdef DEBUG
9576   if (new_group_nr == 0)
9577   {
9578     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9579     printf("AmoebeAbleger(): This should never happen!\n");
9580     return;
9581   }
9582 #endif
9583
9584       AmoebaNr[newax][neway] = new_group_nr;
9585       AmoebaCnt[new_group_nr]++;
9586       AmoebaCnt2[new_group_nr]++;
9587
9588       /* if amoeba touches other amoeba(s) after growing, unify them */
9589       AmoebenVereinigen(newax, neway);
9590
9591       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9592       {
9593         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9594         return;
9595       }
9596     }
9597   }
9598
9599   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9600       (neway == lev_fieldy - 1 && newax != ax))
9601   {
9602     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9603     Store[newax][neway] = element;
9604   }
9605   else if (neway == ay || element == EL_EMC_DRIPPER)
9606   {
9607     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9608
9609     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9610   }
9611   else
9612   {
9613     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9614     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9615     Store[ax][ay] = EL_AMOEBA_DROP;
9616     ContinueMoving(ax, ay);
9617     return;
9618   }
9619
9620   TEST_DrawLevelField(newax, neway);
9621 }
9622
9623 void Life(int ax, int ay)
9624 {
9625   int x1, y1, x2, y2;
9626   int life_time = 40;
9627   int element = Feld[ax][ay];
9628   int graphic = el2img(element);
9629   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9630                          level.biomaze);
9631   boolean changed = FALSE;
9632
9633   if (IS_ANIMATED(graphic))
9634     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9635
9636   if (Stop[ax][ay])
9637     return;
9638
9639   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9640     MovDelay[ax][ay] = life_time;
9641
9642   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9643   {
9644     MovDelay[ax][ay]--;
9645     if (MovDelay[ax][ay])
9646       return;
9647   }
9648
9649   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9650   {
9651     int xx = ax+x1, yy = ay+y1;
9652     int nachbarn = 0;
9653
9654     if (!IN_LEV_FIELD(xx, yy))
9655       continue;
9656
9657     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9658     {
9659       int x = xx+x2, y = yy+y2;
9660
9661       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9662         continue;
9663
9664       if (((Feld[x][y] == element ||
9665             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9666            !Stop[x][y]) ||
9667           (IS_FREE(x, y) && Stop[x][y]))
9668         nachbarn++;
9669     }
9670
9671     if (xx == ax && yy == ay)           /* field in the middle */
9672     {
9673       if (nachbarn < life_parameter[0] ||
9674           nachbarn > life_parameter[1])
9675       {
9676         Feld[xx][yy] = EL_EMPTY;
9677         if (!Stop[xx][yy])
9678           TEST_DrawLevelField(xx, yy);
9679         Stop[xx][yy] = TRUE;
9680         changed = TRUE;
9681       }
9682     }
9683     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9684     {                                   /* free border field */
9685       if (nachbarn >= life_parameter[2] &&
9686           nachbarn <= life_parameter[3])
9687       {
9688         Feld[xx][yy] = element;
9689         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9690         if (!Stop[xx][yy])
9691           TEST_DrawLevelField(xx, yy);
9692         Stop[xx][yy] = TRUE;
9693         changed = TRUE;
9694       }
9695     }
9696   }
9697
9698   if (changed)
9699     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9700                    SND_GAME_OF_LIFE_GROWING);
9701 }
9702
9703 static void InitRobotWheel(int x, int y)
9704 {
9705   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9706 }
9707
9708 static void RunRobotWheel(int x, int y)
9709 {
9710   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9711 }
9712
9713 static void StopRobotWheel(int x, int y)
9714 {
9715   if (ZX == x && ZY == y)
9716   {
9717     ZX = ZY = -1;
9718
9719     game.robot_wheel_active = FALSE;
9720   }
9721 }
9722
9723 static void InitTimegateWheel(int x, int y)
9724 {
9725   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9726 }
9727
9728 static void RunTimegateWheel(int x, int y)
9729 {
9730   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9731 }
9732
9733 static void InitMagicBallDelay(int x, int y)
9734 {
9735 #if 1
9736   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9737 #else
9738   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9739 #endif
9740 }
9741
9742 static void ActivateMagicBall(int bx, int by)
9743 {
9744   int x, y;
9745
9746   if (level.ball_random)
9747   {
9748     int pos_border = RND(8);    /* select one of the eight border elements */
9749     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9750     int xx = pos_content % 3;
9751     int yy = pos_content / 3;
9752
9753     x = bx - 1 + xx;
9754     y = by - 1 + yy;
9755
9756     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9757       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9758   }
9759   else
9760   {
9761     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9762     {
9763       int xx = x - bx + 1;
9764       int yy = y - by + 1;
9765
9766       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9767         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9768     }
9769   }
9770
9771   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9772 }
9773
9774 void CheckExit(int x, int y)
9775 {
9776   if (local_player->gems_still_needed > 0 ||
9777       local_player->sokobanfields_still_needed > 0 ||
9778       local_player->lights_still_needed > 0)
9779   {
9780     int element = Feld[x][y];
9781     int graphic = el2img(element);
9782
9783     if (IS_ANIMATED(graphic))
9784       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9785
9786     return;
9787   }
9788
9789   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9790     return;
9791
9792   Feld[x][y] = EL_EXIT_OPENING;
9793
9794   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9795 }
9796
9797 void CheckExitEM(int x, int y)
9798 {
9799   if (local_player->gems_still_needed > 0 ||
9800       local_player->sokobanfields_still_needed > 0 ||
9801       local_player->lights_still_needed > 0)
9802   {
9803     int element = Feld[x][y];
9804     int graphic = el2img(element);
9805
9806     if (IS_ANIMATED(graphic))
9807       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9808
9809     return;
9810   }
9811
9812   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9813     return;
9814
9815   Feld[x][y] = EL_EM_EXIT_OPENING;
9816
9817   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9818 }
9819
9820 void CheckExitSteel(int x, int y)
9821 {
9822   if (local_player->gems_still_needed > 0 ||
9823       local_player->sokobanfields_still_needed > 0 ||
9824       local_player->lights_still_needed > 0)
9825   {
9826     int element = Feld[x][y];
9827     int graphic = el2img(element);
9828
9829     if (IS_ANIMATED(graphic))
9830       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9831
9832     return;
9833   }
9834
9835   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9836     return;
9837
9838   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9839
9840   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9841 }
9842
9843 void CheckExitSteelEM(int x, int y)
9844 {
9845   if (local_player->gems_still_needed > 0 ||
9846       local_player->sokobanfields_still_needed > 0 ||
9847       local_player->lights_still_needed > 0)
9848   {
9849     int element = Feld[x][y];
9850     int graphic = el2img(element);
9851
9852     if (IS_ANIMATED(graphic))
9853       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9854
9855     return;
9856   }
9857
9858   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9859     return;
9860
9861   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9862
9863   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9864 }
9865
9866 void CheckExitSP(int x, int y)
9867 {
9868   if (local_player->gems_still_needed > 0)
9869   {
9870     int element = Feld[x][y];
9871     int graphic = el2img(element);
9872
9873     if (IS_ANIMATED(graphic))
9874       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9875
9876     return;
9877   }
9878
9879   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9880     return;
9881
9882   Feld[x][y] = EL_SP_EXIT_OPENING;
9883
9884   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9885 }
9886
9887 static void CloseAllOpenTimegates()
9888 {
9889   int x, y;
9890
9891   SCAN_PLAYFIELD(x, y)
9892   {
9893     int element = Feld[x][y];
9894
9895     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9896     {
9897       Feld[x][y] = EL_TIMEGATE_CLOSING;
9898
9899       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9900     }
9901   }
9902 }
9903
9904 void DrawTwinkleOnField(int x, int y)
9905 {
9906   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9907     return;
9908
9909   if (Feld[x][y] == EL_BD_DIAMOND)
9910     return;
9911
9912   if (MovDelay[x][y] == 0)      /* next animation frame */
9913     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9914
9915   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9916   {
9917     MovDelay[x][y]--;
9918
9919     DrawLevelElementAnimation(x, y, Feld[x][y]);
9920
9921     if (MovDelay[x][y] != 0)
9922     {
9923       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9924                                            10 - MovDelay[x][y]);
9925
9926       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9927     }
9928   }
9929 }
9930
9931 void MauerWaechst(int x, int y)
9932 {
9933   int delay = 6;
9934
9935   if (!MovDelay[x][y])          /* next animation frame */
9936     MovDelay[x][y] = 3 * delay;
9937
9938   if (MovDelay[x][y])           /* wait some time before next frame */
9939   {
9940     MovDelay[x][y]--;
9941
9942     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9943     {
9944       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9945       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9946
9947       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9948     }
9949
9950     if (!MovDelay[x][y])
9951     {
9952       if (MovDir[x][y] == MV_LEFT)
9953       {
9954         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9955           TEST_DrawLevelField(x - 1, y);
9956       }
9957       else if (MovDir[x][y] == MV_RIGHT)
9958       {
9959         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9960           TEST_DrawLevelField(x + 1, y);
9961       }
9962       else if (MovDir[x][y] == MV_UP)
9963       {
9964         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9965           TEST_DrawLevelField(x, y - 1);
9966       }
9967       else
9968       {
9969         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9970           TEST_DrawLevelField(x, y + 1);
9971       }
9972
9973       Feld[x][y] = Store[x][y];
9974       Store[x][y] = 0;
9975       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9976       TEST_DrawLevelField(x, y);
9977     }
9978   }
9979 }
9980
9981 void MauerAbleger(int ax, int ay)
9982 {
9983   int element = Feld[ax][ay];
9984   int graphic = el2img(element);
9985   boolean oben_frei = FALSE, unten_frei = FALSE;
9986   boolean links_frei = FALSE, rechts_frei = FALSE;
9987   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9988   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9989   boolean new_wall = FALSE;
9990
9991   if (IS_ANIMATED(graphic))
9992     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9993
9994   if (!MovDelay[ax][ay])        /* start building new wall */
9995     MovDelay[ax][ay] = 6;
9996
9997   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9998   {
9999     MovDelay[ax][ay]--;
10000     if (MovDelay[ax][ay])
10001       return;
10002   }
10003
10004   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10005     oben_frei = TRUE;
10006   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10007     unten_frei = TRUE;
10008   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10009     links_frei = TRUE;
10010   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10011     rechts_frei = TRUE;
10012
10013   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10014       element == EL_EXPANDABLE_WALL_ANY)
10015   {
10016     if (oben_frei)
10017     {
10018       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10019       Store[ax][ay-1] = element;
10020       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10021       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10022         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10023                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10024       new_wall = TRUE;
10025     }
10026     if (unten_frei)
10027     {
10028       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10029       Store[ax][ay+1] = element;
10030       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10031       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10032         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10033                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10034       new_wall = TRUE;
10035     }
10036   }
10037
10038   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10039       element == EL_EXPANDABLE_WALL_ANY ||
10040       element == EL_EXPANDABLE_WALL ||
10041       element == EL_BD_EXPANDABLE_WALL)
10042   {
10043     if (links_frei)
10044     {
10045       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10046       Store[ax-1][ay] = element;
10047       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10048       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10049         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10050                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10051       new_wall = TRUE;
10052     }
10053
10054     if (rechts_frei)
10055     {
10056       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10057       Store[ax+1][ay] = element;
10058       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10059       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10060         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10061                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10062       new_wall = TRUE;
10063     }
10064   }
10065
10066   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10067     TEST_DrawLevelField(ax, ay);
10068
10069   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10070     oben_massiv = TRUE;
10071   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10072     unten_massiv = TRUE;
10073   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10074     links_massiv = TRUE;
10075   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10076     rechts_massiv = TRUE;
10077
10078   if (((oben_massiv && unten_massiv) ||
10079        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10080        element == EL_EXPANDABLE_WALL) &&
10081       ((links_massiv && rechts_massiv) ||
10082        element == EL_EXPANDABLE_WALL_VERTICAL))
10083     Feld[ax][ay] = EL_WALL;
10084
10085   if (new_wall)
10086     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10087 }
10088
10089 void MauerAblegerStahl(int ax, int ay)
10090 {
10091   int element = Feld[ax][ay];
10092   int graphic = el2img(element);
10093   boolean oben_frei = FALSE, unten_frei = FALSE;
10094   boolean links_frei = FALSE, rechts_frei = FALSE;
10095   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10096   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10097   boolean new_wall = FALSE;
10098
10099   if (IS_ANIMATED(graphic))
10100     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10101
10102   if (!MovDelay[ax][ay])        /* start building new wall */
10103     MovDelay[ax][ay] = 6;
10104
10105   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10106   {
10107     MovDelay[ax][ay]--;
10108     if (MovDelay[ax][ay])
10109       return;
10110   }
10111
10112   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10113     oben_frei = TRUE;
10114   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10115     unten_frei = TRUE;
10116   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10117     links_frei = TRUE;
10118   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10119     rechts_frei = TRUE;
10120
10121   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10122       element == EL_EXPANDABLE_STEELWALL_ANY)
10123   {
10124     if (oben_frei)
10125     {
10126       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10127       Store[ax][ay-1] = element;
10128       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10129       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10130         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10131                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10132       new_wall = TRUE;
10133     }
10134     if (unten_frei)
10135     {
10136       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10137       Store[ax][ay+1] = element;
10138       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10139       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10140         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10141                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10142       new_wall = TRUE;
10143     }
10144   }
10145
10146   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10147       element == EL_EXPANDABLE_STEELWALL_ANY)
10148   {
10149     if (links_frei)
10150     {
10151       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10152       Store[ax-1][ay] = element;
10153       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10154       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10155         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10156                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10157       new_wall = TRUE;
10158     }
10159
10160     if (rechts_frei)
10161     {
10162       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10163       Store[ax+1][ay] = element;
10164       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10165       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10166         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10167                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10168       new_wall = TRUE;
10169     }
10170   }
10171
10172   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10173     oben_massiv = TRUE;
10174   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10175     unten_massiv = TRUE;
10176   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10177     links_massiv = TRUE;
10178   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10179     rechts_massiv = TRUE;
10180
10181   if (((oben_massiv && unten_massiv) ||
10182        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10183       ((links_massiv && rechts_massiv) ||
10184        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10185     Feld[ax][ay] = EL_STEELWALL;
10186
10187   if (new_wall)
10188     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10189 }
10190
10191 void CheckForDragon(int x, int y)
10192 {
10193   int i, j;
10194   boolean dragon_found = FALSE;
10195   static int xy[4][2] =
10196   {
10197     { 0, -1 },
10198     { -1, 0 },
10199     { +1, 0 },
10200     { 0, +1 }
10201   };
10202
10203   for (i = 0; i < NUM_DIRECTIONS; i++)
10204   {
10205     for (j = 0; j < 4; j++)
10206     {
10207       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10208
10209       if (IN_LEV_FIELD(xx, yy) &&
10210           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10211       {
10212         if (Feld[xx][yy] == EL_DRAGON)
10213           dragon_found = TRUE;
10214       }
10215       else
10216         break;
10217     }
10218   }
10219
10220   if (!dragon_found)
10221   {
10222     for (i = 0; i < NUM_DIRECTIONS; i++)
10223     {
10224       for (j = 0; j < 3; j++)
10225       {
10226         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10227   
10228         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10229         {
10230           Feld[xx][yy] = EL_EMPTY;
10231           TEST_DrawLevelField(xx, yy);
10232         }
10233         else
10234           break;
10235       }
10236     }
10237   }
10238 }
10239
10240 static void InitBuggyBase(int x, int y)
10241 {
10242   int element = Feld[x][y];
10243   int activating_delay = FRAMES_PER_SECOND / 4;
10244
10245   ChangeDelay[x][y] =
10246     (element == EL_SP_BUGGY_BASE ?
10247      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10248      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10249      activating_delay :
10250      element == EL_SP_BUGGY_BASE_ACTIVE ?
10251      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10252 }
10253
10254 static void WarnBuggyBase(int x, int y)
10255 {
10256   int i;
10257   static int xy[4][2] =
10258   {
10259     { 0, -1 },
10260     { -1, 0 },
10261     { +1, 0 },
10262     { 0, +1 }
10263   };
10264
10265   for (i = 0; i < NUM_DIRECTIONS; i++)
10266   {
10267     int xx = x + xy[i][0];
10268     int yy = y + xy[i][1];
10269
10270     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10271     {
10272       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10273
10274       break;
10275     }
10276   }
10277 }
10278
10279 static void InitTrap(int x, int y)
10280 {
10281   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10282 }
10283
10284 static void ActivateTrap(int x, int y)
10285 {
10286   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10287 }
10288
10289 static void ChangeActiveTrap(int x, int y)
10290 {
10291   int graphic = IMG_TRAP_ACTIVE;
10292
10293   /* if new animation frame was drawn, correct crumbled sand border */
10294   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10295     TEST_DrawLevelFieldCrumbledSand(x, y);
10296 }
10297
10298 static int getSpecialActionElement(int element, int number, int base_element)
10299 {
10300   return (element != EL_EMPTY ? element :
10301           number != -1 ? base_element + number - 1 :
10302           EL_EMPTY);
10303 }
10304
10305 static int getModifiedActionNumber(int value_old, int operator, int operand,
10306                                    int value_min, int value_max)
10307 {
10308   int value_new = (operator == CA_MODE_SET      ? operand :
10309                    operator == CA_MODE_ADD      ? value_old + operand :
10310                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10311                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10312                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10313                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10314                    value_old);
10315
10316   return (value_new < value_min ? value_min :
10317           value_new > value_max ? value_max :
10318           value_new);
10319 }
10320
10321 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10322 {
10323   struct ElementInfo *ei = &element_info[element];
10324   struct ElementChangeInfo *change = &ei->change_page[page];
10325   int target_element = change->target_element;
10326   int action_type = change->action_type;
10327   int action_mode = change->action_mode;
10328   int action_arg = change->action_arg;
10329   int action_element = change->action_element;
10330   int i;
10331
10332   if (!change->has_action)
10333     return;
10334
10335   /* ---------- determine action paramater values -------------------------- */
10336
10337   int level_time_value =
10338     (level.time > 0 ? TimeLeft :
10339      TimePlayed);
10340
10341   int action_arg_element_raw =
10342     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10343      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10344      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10345      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10346      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10347      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10348      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10349      EL_EMPTY);
10350   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10351
10352 #if 0
10353   if (action_arg_element_raw == EL_GROUP_START)
10354     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10355 #endif
10356
10357   int action_arg_direction =
10358     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10359      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10360      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10361      change->actual_trigger_side :
10362      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10363      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10364      MV_NONE);
10365
10366   int action_arg_number_min =
10367     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10368      CA_ARG_MIN);
10369
10370   int action_arg_number_max =
10371     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10372      action_type == CA_SET_LEVEL_GEMS ? 999 :
10373      action_type == CA_SET_LEVEL_TIME ? 9999 :
10374      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10375      action_type == CA_SET_CE_VALUE ? 9999 :
10376      action_type == CA_SET_CE_SCORE ? 9999 :
10377      CA_ARG_MAX);
10378
10379   int action_arg_number_reset =
10380     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10381      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10382      action_type == CA_SET_LEVEL_TIME ? level.time :
10383      action_type == CA_SET_LEVEL_SCORE ? 0 :
10384 #if USE_NEW_CUSTOM_VALUE
10385      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10386 #else
10387      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10388 #endif
10389      action_type == CA_SET_CE_SCORE ? 0 :
10390      0);
10391
10392   int action_arg_number =
10393     (action_arg <= CA_ARG_MAX ? action_arg :
10394      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10395      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10396      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10397      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10398      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10399      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10400 #if USE_NEW_CUSTOM_VALUE
10401      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10402 #else
10403      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10404 #endif
10405      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10406      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10407      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10408      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10409      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10410      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10411      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10412      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10413      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10414      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10415      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10416      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10417      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10418      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10419      -1);
10420
10421   int action_arg_number_old =
10422     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10423      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10424      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10425      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10426      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10427      0);
10428
10429   int action_arg_number_new =
10430     getModifiedActionNumber(action_arg_number_old,
10431                             action_mode, action_arg_number,
10432                             action_arg_number_min, action_arg_number_max);
10433
10434 #if 1
10435   int trigger_player_bits =
10436     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10437      change->actual_trigger_player_bits : change->trigger_player);
10438 #else
10439   int trigger_player_bits =
10440     (change->actual_trigger_player >= EL_PLAYER_1 &&
10441      change->actual_trigger_player <= EL_PLAYER_4 ?
10442      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10443      PLAYER_BITS_ANY);
10444 #endif
10445
10446   int action_arg_player_bits =
10447     (action_arg >= CA_ARG_PLAYER_1 &&
10448      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10449      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10450      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10451      PLAYER_BITS_ANY);
10452
10453   /* ---------- execute action  -------------------------------------------- */
10454
10455   switch (action_type)
10456   {
10457     case CA_NO_ACTION:
10458     {
10459       return;
10460     }
10461
10462     /* ---------- level actions  ------------------------------------------- */
10463
10464     case CA_RESTART_LEVEL:
10465     {
10466       game.restart_level = TRUE;
10467
10468       break;
10469     }
10470
10471     case CA_SHOW_ENVELOPE:
10472     {
10473       int element = getSpecialActionElement(action_arg_element,
10474                                             action_arg_number, EL_ENVELOPE_1);
10475
10476       if (IS_ENVELOPE(element))
10477         local_player->show_envelope = element;
10478
10479       break;
10480     }
10481
10482     case CA_SET_LEVEL_TIME:
10483     {
10484       if (level.time > 0)       /* only modify limited time value */
10485       {
10486         TimeLeft = action_arg_number_new;
10487
10488 #if 1
10489         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10490
10491         DisplayGameControlValues();
10492 #else
10493         DrawGameValue_Time(TimeLeft);
10494 #endif
10495
10496         if (!TimeLeft && setup.time_limit)
10497           for (i = 0; i < MAX_PLAYERS; i++)
10498             KillPlayer(&stored_player[i]);
10499       }
10500
10501       break;
10502     }
10503
10504     case CA_SET_LEVEL_SCORE:
10505     {
10506       local_player->score = action_arg_number_new;
10507
10508 #if 1
10509       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10510
10511       DisplayGameControlValues();
10512 #else
10513       DrawGameValue_Score(local_player->score);
10514 #endif
10515
10516       break;
10517     }
10518
10519     case CA_SET_LEVEL_GEMS:
10520     {
10521       local_player->gems_still_needed = action_arg_number_new;
10522
10523 #if 1
10524       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10525
10526       DisplayGameControlValues();
10527 #else
10528       DrawGameValue_Emeralds(local_player->gems_still_needed);
10529 #endif
10530
10531       break;
10532     }
10533
10534 #if !USE_PLAYER_GRAVITY
10535     case CA_SET_LEVEL_GRAVITY:
10536     {
10537       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10538                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10539                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10540                       game.gravity);
10541       break;
10542     }
10543 #endif
10544
10545     case CA_SET_LEVEL_WIND:
10546     {
10547       game.wind_direction = action_arg_direction;
10548
10549       break;
10550     }
10551
10552     case CA_SET_LEVEL_RANDOM_SEED:
10553     {
10554 #if 1
10555       /* ensure that setting a new random seed while playing is predictable */
10556       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10557 #else
10558       InitRND(action_arg_number_new);
10559 #endif
10560
10561 #if 0
10562       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10563 #endif
10564
10565 #if 0
10566       {
10567         int i;
10568
10569         printf("::: ");
10570         for (i = 0; i < 9; i++)
10571           printf("%d, ", RND(2));
10572         printf("\n");
10573       }
10574 #endif
10575
10576       break;
10577     }
10578
10579     /* ---------- player actions  ------------------------------------------ */
10580
10581     case CA_MOVE_PLAYER:
10582     {
10583       /* automatically move to the next field in specified direction */
10584       for (i = 0; i < MAX_PLAYERS; i++)
10585         if (trigger_player_bits & (1 << i))
10586           stored_player[i].programmed_action = action_arg_direction;
10587
10588       break;
10589     }
10590
10591     case CA_EXIT_PLAYER:
10592     {
10593       for (i = 0; i < MAX_PLAYERS; i++)
10594         if (action_arg_player_bits & (1 << i))
10595           PlayerWins(&stored_player[i]);
10596
10597       break;
10598     }
10599
10600     case CA_KILL_PLAYER:
10601     {
10602       for (i = 0; i < MAX_PLAYERS; i++)
10603         if (action_arg_player_bits & (1 << i))
10604           KillPlayer(&stored_player[i]);
10605
10606       break;
10607     }
10608
10609     case CA_SET_PLAYER_KEYS:
10610     {
10611       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10612       int element = getSpecialActionElement(action_arg_element,
10613                                             action_arg_number, EL_KEY_1);
10614
10615       if (IS_KEY(element))
10616       {
10617         for (i = 0; i < MAX_PLAYERS; i++)
10618         {
10619           if (trigger_player_bits & (1 << i))
10620           {
10621             stored_player[i].key[KEY_NR(element)] = key_state;
10622
10623             DrawGameDoorValues();
10624           }
10625         }
10626       }
10627
10628       break;
10629     }
10630
10631     case CA_SET_PLAYER_SPEED:
10632     {
10633 #if 0
10634       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10635 #endif
10636
10637       for (i = 0; i < MAX_PLAYERS; i++)
10638       {
10639         if (trigger_player_bits & (1 << i))
10640         {
10641           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10642
10643           if (action_arg == CA_ARG_SPEED_FASTER &&
10644               stored_player[i].cannot_move)
10645           {
10646             action_arg_number = STEPSIZE_VERY_SLOW;
10647           }
10648           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10649                    action_arg == CA_ARG_SPEED_FASTER)
10650           {
10651             action_arg_number = 2;
10652             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10653                            CA_MODE_MULTIPLY);
10654           }
10655           else if (action_arg == CA_ARG_NUMBER_RESET)
10656           {
10657             action_arg_number = level.initial_player_stepsize[i];
10658           }
10659
10660           move_stepsize =
10661             getModifiedActionNumber(move_stepsize,
10662                                     action_mode,
10663                                     action_arg_number,
10664                                     action_arg_number_min,
10665                                     action_arg_number_max);
10666
10667           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10668         }
10669       }
10670
10671       break;
10672     }
10673
10674     case CA_SET_PLAYER_SHIELD:
10675     {
10676       for (i = 0; i < MAX_PLAYERS; i++)
10677       {
10678         if (trigger_player_bits & (1 << i))
10679         {
10680           if (action_arg == CA_ARG_SHIELD_OFF)
10681           {
10682             stored_player[i].shield_normal_time_left = 0;
10683             stored_player[i].shield_deadly_time_left = 0;
10684           }
10685           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10686           {
10687             stored_player[i].shield_normal_time_left = 999999;
10688           }
10689           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10690           {
10691             stored_player[i].shield_normal_time_left = 999999;
10692             stored_player[i].shield_deadly_time_left = 999999;
10693           }
10694         }
10695       }
10696
10697       break;
10698     }
10699
10700 #if USE_PLAYER_GRAVITY
10701     case CA_SET_PLAYER_GRAVITY:
10702     {
10703       for (i = 0; i < MAX_PLAYERS; i++)
10704       {
10705         if (trigger_player_bits & (1 << i))
10706         {
10707           stored_player[i].gravity =
10708             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10709              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10710              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10711              stored_player[i].gravity);
10712         }
10713       }
10714
10715       break;
10716     }
10717 #endif
10718
10719     case CA_SET_PLAYER_ARTWORK:
10720     {
10721       for (i = 0; i < MAX_PLAYERS; i++)
10722       {
10723         if (trigger_player_bits & (1 << i))
10724         {
10725           int artwork_element = action_arg_element;
10726
10727           if (action_arg == CA_ARG_ELEMENT_RESET)
10728             artwork_element =
10729               (level.use_artwork_element[i] ? level.artwork_element[i] :
10730                stored_player[i].element_nr);
10731
10732 #if USE_GFX_RESET_PLAYER_ARTWORK
10733           if (stored_player[i].artwork_element != artwork_element)
10734             stored_player[i].Frame = 0;
10735 #endif
10736
10737           stored_player[i].artwork_element = artwork_element;
10738
10739           SetPlayerWaiting(&stored_player[i], FALSE);
10740
10741           /* set number of special actions for bored and sleeping animation */
10742           stored_player[i].num_special_action_bored =
10743             get_num_special_action(artwork_element,
10744                                    ACTION_BORING_1, ACTION_BORING_LAST);
10745           stored_player[i].num_special_action_sleeping =
10746             get_num_special_action(artwork_element,
10747                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10748         }
10749       }
10750
10751       break;
10752     }
10753
10754     case CA_SET_PLAYER_INVENTORY:
10755     {
10756       for (i = 0; i < MAX_PLAYERS; i++)
10757       {
10758         struct PlayerInfo *player = &stored_player[i];
10759         int j, k;
10760
10761         if (trigger_player_bits & (1 << i))
10762         {
10763           int inventory_element = action_arg_element;
10764
10765           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10766               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10767               action_arg == CA_ARG_ELEMENT_ACTION)
10768           {
10769             int element = inventory_element;
10770             int collect_count = element_info[element].collect_count_initial;
10771
10772             if (!IS_CUSTOM_ELEMENT(element))
10773               collect_count = 1;
10774
10775             if (collect_count == 0)
10776               player->inventory_infinite_element = element;
10777             else
10778               for (k = 0; k < collect_count; k++)
10779                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10780                   player->inventory_element[player->inventory_size++] =
10781                     element;
10782           }
10783           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10784                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10785                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10786           {
10787             if (player->inventory_infinite_element != EL_UNDEFINED &&
10788                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10789                                      action_arg_element_raw))
10790               player->inventory_infinite_element = EL_UNDEFINED;
10791
10792             for (k = 0, j = 0; j < player->inventory_size; j++)
10793             {
10794               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10795                                         action_arg_element_raw))
10796                 player->inventory_element[k++] = player->inventory_element[j];
10797             }
10798
10799             player->inventory_size = k;
10800           }
10801           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10802           {
10803             if (player->inventory_size > 0)
10804             {
10805               for (j = 0; j < player->inventory_size - 1; j++)
10806                 player->inventory_element[j] = player->inventory_element[j + 1];
10807
10808               player->inventory_size--;
10809             }
10810           }
10811           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10812           {
10813             if (player->inventory_size > 0)
10814               player->inventory_size--;
10815           }
10816           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10817           {
10818             player->inventory_infinite_element = EL_UNDEFINED;
10819             player->inventory_size = 0;
10820           }
10821           else if (action_arg == CA_ARG_INVENTORY_RESET)
10822           {
10823             player->inventory_infinite_element = EL_UNDEFINED;
10824             player->inventory_size = 0;
10825
10826             if (level.use_initial_inventory[i])
10827             {
10828               for (j = 0; j < level.initial_inventory_size[i]; j++)
10829               {
10830                 int element = level.initial_inventory_content[i][j];
10831                 int collect_count = element_info[element].collect_count_initial;
10832
10833                 if (!IS_CUSTOM_ELEMENT(element))
10834                   collect_count = 1;
10835
10836                 if (collect_count == 0)
10837                   player->inventory_infinite_element = element;
10838                 else
10839                   for (k = 0; k < collect_count; k++)
10840                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10841                       player->inventory_element[player->inventory_size++] =
10842                         element;
10843               }
10844             }
10845           }
10846         }
10847       }
10848
10849       break;
10850     }
10851
10852     /* ---------- CE actions  ---------------------------------------------- */
10853
10854     case CA_SET_CE_VALUE:
10855     {
10856 #if USE_NEW_CUSTOM_VALUE
10857       int last_ce_value = CustomValue[x][y];
10858
10859       CustomValue[x][y] = action_arg_number_new;
10860
10861       if (CustomValue[x][y] != last_ce_value)
10862       {
10863         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10864         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10865
10866         if (CustomValue[x][y] == 0)
10867         {
10868           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10869           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10870         }
10871       }
10872 #endif
10873
10874       break;
10875     }
10876
10877     case CA_SET_CE_SCORE:
10878     {
10879 #if USE_NEW_CUSTOM_VALUE
10880       int last_ce_score = ei->collect_score;
10881
10882       ei->collect_score = action_arg_number_new;
10883
10884       if (ei->collect_score != last_ce_score)
10885       {
10886         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10887         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10888
10889         if (ei->collect_score == 0)
10890         {
10891           int xx, yy;
10892
10893           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10894           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10895
10896           /*
10897             This is a very special case that seems to be a mixture between
10898             CheckElementChange() and CheckTriggeredElementChange(): while
10899             the first one only affects single elements that are triggered
10900             directly, the second one affects multiple elements in the playfield
10901             that are triggered indirectly by another element. This is a third
10902             case: Changing the CE score always affects multiple identical CEs,
10903             so every affected CE must be checked, not only the single CE for
10904             which the CE score was changed in the first place (as every instance
10905             of that CE shares the same CE score, and therefore also can change)!
10906           */
10907           SCAN_PLAYFIELD(xx, yy)
10908           {
10909             if (Feld[xx][yy] == element)
10910               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10911                                  CE_SCORE_GETS_ZERO);
10912           }
10913         }
10914       }
10915 #endif
10916
10917       break;
10918     }
10919
10920     case CA_SET_CE_ARTWORK:
10921     {
10922       int artwork_element = action_arg_element;
10923       boolean reset_frame = FALSE;
10924       int xx, yy;
10925
10926       if (action_arg == CA_ARG_ELEMENT_RESET)
10927         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10928                            element);
10929
10930       if (ei->gfx_element != artwork_element)
10931         reset_frame = TRUE;
10932
10933       ei->gfx_element = artwork_element;
10934
10935       SCAN_PLAYFIELD(xx, yy)
10936       {
10937         if (Feld[xx][yy] == element)
10938         {
10939           if (reset_frame)
10940           {
10941             ResetGfxAnimation(xx, yy);
10942             ResetRandomAnimationValue(xx, yy);
10943           }
10944
10945           TEST_DrawLevelField(xx, yy);
10946         }
10947       }
10948
10949       break;
10950     }
10951
10952     /* ---------- engine actions  ------------------------------------------ */
10953
10954     case CA_SET_ENGINE_SCAN_MODE:
10955     {
10956       InitPlayfieldScanMode(action_arg);
10957
10958       break;
10959     }
10960
10961     default:
10962       break;
10963   }
10964 }
10965
10966 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10967 {
10968   int old_element = Feld[x][y];
10969   int new_element = GetElementFromGroupElement(element);
10970   int previous_move_direction = MovDir[x][y];
10971 #if USE_NEW_CUSTOM_VALUE
10972   int last_ce_value = CustomValue[x][y];
10973 #endif
10974   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10975   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10976   boolean add_player_onto_element = (new_element_is_player &&
10977 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10978                                      /* this breaks SnakeBite when a snake is
10979                                         halfway through a door that closes */
10980                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10981                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10982 #endif
10983                                      IS_WALKABLE(old_element));
10984
10985 #if 0
10986   /* check if element under the player changes from accessible to unaccessible
10987      (needed for special case of dropping element which then changes) */
10988   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10989       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10990   {
10991     Bang(x, y);
10992
10993     return;
10994   }
10995 #endif
10996
10997   if (!add_player_onto_element)
10998   {
10999     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11000       RemoveMovingField(x, y);
11001     else
11002       RemoveField(x, y);
11003
11004     Feld[x][y] = new_element;
11005
11006 #if !USE_GFX_RESET_GFX_ANIMATION
11007     ResetGfxAnimation(x, y);
11008     ResetRandomAnimationValue(x, y);
11009 #endif
11010
11011     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11012       MovDir[x][y] = previous_move_direction;
11013
11014 #if USE_NEW_CUSTOM_VALUE
11015     if (element_info[new_element].use_last_ce_value)
11016       CustomValue[x][y] = last_ce_value;
11017 #endif
11018
11019     InitField_WithBug1(x, y, FALSE);
11020
11021     new_element = Feld[x][y];   /* element may have changed */
11022
11023 #if USE_GFX_RESET_GFX_ANIMATION
11024     ResetGfxAnimation(x, y);
11025     ResetRandomAnimationValue(x, y);
11026 #endif
11027
11028     TEST_DrawLevelField(x, y);
11029
11030     if (GFX_CRUMBLED(new_element))
11031       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
11032   }
11033
11034 #if 1
11035   /* check if element under the player changes from accessible to unaccessible
11036      (needed for special case of dropping element which then changes) */
11037   /* (must be checked after creating new element for walkable group elements) */
11038 #if USE_FIX_KILLED_BY_NON_WALKABLE
11039   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11040       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11041   {
11042     Bang(x, y);
11043
11044     return;
11045   }
11046 #else
11047   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11048       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11049   {
11050     Bang(x, y);
11051
11052     return;
11053   }
11054 #endif
11055 #endif
11056
11057   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11058   if (new_element_is_player)
11059     RelocatePlayer(x, y, new_element);
11060
11061   if (is_change)
11062     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11063
11064   TestIfBadThingTouchesPlayer(x, y);
11065   TestIfPlayerTouchesCustomElement(x, y);
11066   TestIfElementTouchesCustomElement(x, y);
11067 }
11068
11069 static void CreateField(int x, int y, int element)
11070 {
11071   CreateFieldExt(x, y, element, FALSE);
11072 }
11073
11074 static void CreateElementFromChange(int x, int y, int element)
11075 {
11076   element = GET_VALID_RUNTIME_ELEMENT(element);
11077
11078 #if USE_STOP_CHANGED_ELEMENTS
11079   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11080   {
11081     int old_element = Feld[x][y];
11082
11083     /* prevent changed element from moving in same engine frame
11084        unless both old and new element can either fall or move */
11085     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11086         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11087       Stop[x][y] = TRUE;
11088   }
11089 #endif
11090
11091   CreateFieldExt(x, y, element, TRUE);
11092 }
11093
11094 static boolean ChangeElement(int x, int y, int element, int page)
11095 {
11096   struct ElementInfo *ei = &element_info[element];
11097   struct ElementChangeInfo *change = &ei->change_page[page];
11098   int ce_value = CustomValue[x][y];
11099   int ce_score = ei->collect_score;
11100   int target_element;
11101   int old_element = Feld[x][y];
11102
11103   /* always use default change event to prevent running into a loop */
11104   if (ChangeEvent[x][y] == -1)
11105     ChangeEvent[x][y] = CE_DELAY;
11106
11107   if (ChangeEvent[x][y] == CE_DELAY)
11108   {
11109     /* reset actual trigger element, trigger player and action element */
11110     change->actual_trigger_element = EL_EMPTY;
11111     change->actual_trigger_player = EL_EMPTY;
11112     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11113     change->actual_trigger_side = CH_SIDE_NONE;
11114     change->actual_trigger_ce_value = 0;
11115     change->actual_trigger_ce_score = 0;
11116   }
11117
11118   /* do not change elements more than a specified maximum number of changes */
11119   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11120     return FALSE;
11121
11122   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11123
11124   if (change->explode)
11125   {
11126     Bang(x, y);
11127
11128     return TRUE;
11129   }
11130
11131   if (change->use_target_content)
11132   {
11133     boolean complete_replace = TRUE;
11134     boolean can_replace[3][3];
11135     int xx, yy;
11136
11137     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11138     {
11139       boolean is_empty;
11140       boolean is_walkable;
11141       boolean is_diggable;
11142       boolean is_collectible;
11143       boolean is_removable;
11144       boolean is_destructible;
11145       int ex = x + xx - 1;
11146       int ey = y + yy - 1;
11147       int content_element = change->target_content.e[xx][yy];
11148       int e;
11149
11150       can_replace[xx][yy] = TRUE;
11151
11152       if (ex == x && ey == y)   /* do not check changing element itself */
11153         continue;
11154
11155       if (content_element == EL_EMPTY_SPACE)
11156       {
11157         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11158
11159         continue;
11160       }
11161
11162       if (!IN_LEV_FIELD(ex, ey))
11163       {
11164         can_replace[xx][yy] = FALSE;
11165         complete_replace = FALSE;
11166
11167         continue;
11168       }
11169
11170       e = Feld[ex][ey];
11171
11172       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11173         e = MovingOrBlocked2Element(ex, ey);
11174
11175       is_empty = (IS_FREE(ex, ey) ||
11176                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11177
11178       is_walkable     = (is_empty || IS_WALKABLE(e));
11179       is_diggable     = (is_empty || IS_DIGGABLE(e));
11180       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11181       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11182       is_removable    = (is_diggable || is_collectible);
11183
11184       can_replace[xx][yy] =
11185         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11186           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11187           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11188           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11189           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11190           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11191          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11192
11193       if (!can_replace[xx][yy])
11194         complete_replace = FALSE;
11195     }
11196
11197     if (!change->only_if_complete || complete_replace)
11198     {
11199       boolean something_has_changed = FALSE;
11200
11201       if (change->only_if_complete && change->use_random_replace &&
11202           RND(100) < change->random_percentage)
11203         return FALSE;
11204
11205       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11206       {
11207         int ex = x + xx - 1;
11208         int ey = y + yy - 1;
11209         int content_element;
11210
11211         if (can_replace[xx][yy] && (!change->use_random_replace ||
11212                                     RND(100) < change->random_percentage))
11213         {
11214           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11215             RemoveMovingField(ex, ey);
11216
11217           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11218
11219           content_element = change->target_content.e[xx][yy];
11220           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11221                                               ce_value, ce_score);
11222
11223           CreateElementFromChange(ex, ey, target_element);
11224
11225           something_has_changed = TRUE;
11226
11227           /* for symmetry reasons, freeze newly created border elements */
11228           if (ex != x || ey != y)
11229             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11230         }
11231       }
11232
11233       if (something_has_changed)
11234       {
11235         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11236         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11237       }
11238     }
11239   }
11240   else
11241   {
11242     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11243                                         ce_value, ce_score);
11244
11245     if (element == EL_DIAGONAL_GROWING ||
11246         element == EL_DIAGONAL_SHRINKING)
11247     {
11248       target_element = Store[x][y];
11249
11250       Store[x][y] = EL_EMPTY;
11251     }
11252
11253     CreateElementFromChange(x, y, target_element);
11254
11255     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11256     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11257   }
11258
11259   /* this uses direct change before indirect change */
11260   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11261
11262   return TRUE;
11263 }
11264
11265 #if USE_NEW_DELAYED_ACTION
11266
11267 static void HandleElementChange(int x, int y, int page)
11268 {
11269   int element = MovingOrBlocked2Element(x, y);
11270   struct ElementInfo *ei = &element_info[element];
11271   struct ElementChangeInfo *change = &ei->change_page[page];
11272   boolean handle_action_before_change = FALSE;
11273
11274 #ifdef DEBUG
11275   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11276       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11277   {
11278     printf("\n\n");
11279     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11280            x, y, element, element_info[element].token_name);
11281     printf("HandleElementChange(): This should never happen!\n");
11282     printf("\n\n");
11283   }
11284 #endif
11285
11286   /* this can happen with classic bombs on walkable, changing elements */
11287   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11288   {
11289 #if 0
11290     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11291       ChangeDelay[x][y] = 0;
11292 #endif
11293
11294     return;
11295   }
11296
11297   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11298   {
11299     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11300
11301     if (change->can_change)
11302     {
11303 #if 1
11304       /* !!! not clear why graphic animation should be reset at all here !!! */
11305       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11306 #if USE_GFX_RESET_WHEN_NOT_MOVING
11307       /* when a custom element is about to change (for example by change delay),
11308          do not reset graphic animation when the custom element is moving */
11309       if (!IS_MOVING(x, y))
11310 #endif
11311       {
11312         ResetGfxAnimation(x, y);
11313         ResetRandomAnimationValue(x, y);
11314       }
11315 #endif
11316
11317       if (change->pre_change_function)
11318         change->pre_change_function(x, y);
11319     }
11320   }
11321
11322   ChangeDelay[x][y]--;
11323
11324   if (ChangeDelay[x][y] != 0)           /* continue element change */
11325   {
11326     if (change->can_change)
11327     {
11328       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11329
11330       if (IS_ANIMATED(graphic))
11331         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11332
11333       if (change->change_function)
11334         change->change_function(x, y);
11335     }
11336   }
11337   else                                  /* finish element change */
11338   {
11339     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11340     {
11341       page = ChangePage[x][y];
11342       ChangePage[x][y] = -1;
11343
11344       change = &ei->change_page[page];
11345     }
11346
11347     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11348     {
11349       ChangeDelay[x][y] = 1;            /* try change after next move step */
11350       ChangePage[x][y] = page;          /* remember page to use for change */
11351
11352       return;
11353     }
11354
11355 #if 1
11356     /* special case: set new level random seed before changing element */
11357     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11358       handle_action_before_change = TRUE;
11359
11360     if (change->has_action && handle_action_before_change)
11361       ExecuteCustomElementAction(x, y, element, page);
11362 #endif
11363
11364     if (change->can_change)
11365     {
11366       if (ChangeElement(x, y, element, page))
11367       {
11368         if (change->post_change_function)
11369           change->post_change_function(x, y);
11370       }
11371     }
11372
11373     if (change->has_action && !handle_action_before_change)
11374       ExecuteCustomElementAction(x, y, element, page);
11375   }
11376 }
11377
11378 #else
11379
11380 static void HandleElementChange(int x, int y, int page)
11381 {
11382   int element = MovingOrBlocked2Element(x, y);
11383   struct ElementInfo *ei = &element_info[element];
11384   struct ElementChangeInfo *change = &ei->change_page[page];
11385
11386 #ifdef DEBUG
11387   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11388   {
11389     printf("\n\n");
11390     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11391            x, y, element, element_info[element].token_name);
11392     printf("HandleElementChange(): This should never happen!\n");
11393     printf("\n\n");
11394   }
11395 #endif
11396
11397   /* this can happen with classic bombs on walkable, changing elements */
11398   if (!CAN_CHANGE(element))
11399   {
11400 #if 0
11401     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11402       ChangeDelay[x][y] = 0;
11403 #endif
11404
11405     return;
11406   }
11407
11408   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11409   {
11410     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11411
11412     ResetGfxAnimation(x, y);
11413     ResetRandomAnimationValue(x, y);
11414
11415     if (change->pre_change_function)
11416       change->pre_change_function(x, y);
11417   }
11418
11419   ChangeDelay[x][y]--;
11420
11421   if (ChangeDelay[x][y] != 0)           /* continue element change */
11422   {
11423     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11424
11425     if (IS_ANIMATED(graphic))
11426       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11427
11428     if (change->change_function)
11429       change->change_function(x, y);
11430   }
11431   else                                  /* finish element change */
11432   {
11433     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11434     {
11435       page = ChangePage[x][y];
11436       ChangePage[x][y] = -1;
11437
11438       change = &ei->change_page[page];
11439     }
11440
11441     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11442     {
11443       ChangeDelay[x][y] = 1;            /* try change after next move step */
11444       ChangePage[x][y] = page;          /* remember page to use for change */
11445
11446       return;
11447     }
11448
11449     if (ChangeElement(x, y, element, page))
11450     {
11451       if (change->post_change_function)
11452         change->post_change_function(x, y);
11453     }
11454   }
11455 }
11456
11457 #endif
11458
11459 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11460                                               int trigger_element,
11461                                               int trigger_event,
11462                                               int trigger_player,
11463                                               int trigger_side,
11464                                               int trigger_page)
11465 {
11466   boolean change_done_any = FALSE;
11467   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11468   int i;
11469
11470   if (!(trigger_events[trigger_element][trigger_event]))
11471     return FALSE;
11472
11473 #if 0
11474   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11475          trigger_event, recursion_loop_depth, recursion_loop_detected,
11476          recursion_loop_element, EL_NAME(recursion_loop_element));
11477 #endif
11478
11479   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11480
11481   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11482   {
11483     int element = EL_CUSTOM_START + i;
11484     boolean change_done = FALSE;
11485     int p;
11486
11487     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11488         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11489       continue;
11490
11491     for (p = 0; p < element_info[element].num_change_pages; p++)
11492     {
11493       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11494
11495       if (change->can_change_or_has_action &&
11496           change->has_event[trigger_event] &&
11497           change->trigger_side & trigger_side &&
11498           change->trigger_player & trigger_player &&
11499           change->trigger_page & trigger_page_bits &&
11500           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11501       {
11502         change->actual_trigger_element = trigger_element;
11503         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11504         change->actual_trigger_player_bits = trigger_player;
11505         change->actual_trigger_side = trigger_side;
11506         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11507         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11508
11509 #if 0
11510         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11511                element, EL_NAME(element), p);
11512 #endif
11513
11514         if ((change->can_change && !change_done) || change->has_action)
11515         {
11516           int x, y;
11517
11518           SCAN_PLAYFIELD(x, y)
11519           {
11520             if (Feld[x][y] == element)
11521             {
11522               if (change->can_change && !change_done)
11523               {
11524 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11525                 /* if element already changed in this frame, not only prevent
11526                    another element change (checked in ChangeElement()), but
11527                    also prevent additional element actions for this element */
11528
11529                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11530                     !level.use_action_after_change_bug)
11531                   continue;
11532 #endif
11533
11534 #if 0
11535                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11536                        element, EL_NAME(element), p);
11537 #endif
11538
11539                 ChangeDelay[x][y] = 1;
11540                 ChangeEvent[x][y] = trigger_event;
11541
11542                 HandleElementChange(x, y, p);
11543               }
11544 #if USE_NEW_DELAYED_ACTION
11545               else if (change->has_action)
11546               {
11547 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11548                 /* if element already changed in this frame, not only prevent
11549                    another element change (checked in ChangeElement()), but
11550                    also prevent additional element actions for this element */
11551
11552                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11553                     !level.use_action_after_change_bug)
11554                   continue;
11555 #endif
11556
11557
11558 #if 0
11559                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11560                        element, EL_NAME(element), p);
11561 #endif
11562
11563                 ExecuteCustomElementAction(x, y, element, p);
11564                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11565               }
11566 #else
11567               if (change->has_action)
11568               {
11569                 ExecuteCustomElementAction(x, y, element, p);
11570                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11571               }
11572 #endif
11573             }
11574           }
11575
11576           if (change->can_change)
11577           {
11578             change_done = TRUE;
11579             change_done_any = TRUE;
11580
11581 #if 0
11582             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11583                    element, EL_NAME(element), p);
11584 #endif
11585
11586           }
11587         }
11588       }
11589     }
11590   }
11591
11592   RECURSION_LOOP_DETECTION_END();
11593
11594   return change_done_any;
11595 }
11596
11597 static boolean CheckElementChangeExt(int x, int y,
11598                                      int element,
11599                                      int trigger_element,
11600                                      int trigger_event,
11601                                      int trigger_player,
11602                                      int trigger_side)
11603 {
11604   boolean change_done = FALSE;
11605   int p;
11606
11607   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11608       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11609     return FALSE;
11610
11611   if (Feld[x][y] == EL_BLOCKED)
11612   {
11613     Blocked2Moving(x, y, &x, &y);
11614     element = Feld[x][y];
11615   }
11616
11617 #if 0
11618   /* check if element has already changed */
11619   if (Feld[x][y] != element)
11620     return FALSE;
11621 #else
11622   /* check if element has already changed or is about to change after moving */
11623   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11624        Feld[x][y] != element) ||
11625
11626       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11627        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11628         ChangePage[x][y] != -1)))
11629     return FALSE;
11630 #endif
11631
11632 #if 0
11633   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11634          trigger_event, recursion_loop_depth, recursion_loop_detected,
11635          recursion_loop_element, EL_NAME(recursion_loop_element));
11636 #endif
11637
11638   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11639
11640 #if 0
11641   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11642 #endif
11643
11644   for (p = 0; p < element_info[element].num_change_pages; p++)
11645   {
11646     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11647
11648     /* check trigger element for all events where the element that is checked
11649        for changing interacts with a directly adjacent element -- this is
11650        different to element changes that affect other elements to change on the
11651        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11652     boolean check_trigger_element =
11653       (trigger_event == CE_TOUCHING_X ||
11654        trigger_event == CE_HITTING_X ||
11655        trigger_event == CE_HIT_BY_X ||
11656 #if 1
11657        /* this one was forgotten until 3.2.3 */
11658        trigger_event == CE_DIGGING_X);
11659 #endif
11660
11661     if (change->can_change_or_has_action &&
11662         change->has_event[trigger_event] &&
11663         change->trigger_side & trigger_side &&
11664         change->trigger_player & trigger_player &&
11665         (!check_trigger_element ||
11666          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11667     {
11668       change->actual_trigger_element = trigger_element;
11669       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11670       change->actual_trigger_player_bits = trigger_player;
11671       change->actual_trigger_side = trigger_side;
11672       change->actual_trigger_ce_value = CustomValue[x][y];
11673       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11674
11675       /* special case: trigger element not at (x,y) position for some events */
11676       if (check_trigger_element)
11677       {
11678         static struct
11679         {
11680           int dx, dy;
11681         } move_xy[] =
11682           {
11683             {  0,  0 },
11684             { -1,  0 },
11685             { +1,  0 },
11686             {  0,  0 },
11687             {  0, -1 },
11688             {  0,  0 }, { 0, 0 }, { 0, 0 },
11689             {  0, +1 }
11690           };
11691
11692         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11693         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11694
11695         change->actual_trigger_ce_value = CustomValue[xx][yy];
11696         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11697       }
11698
11699       if (change->can_change && !change_done)
11700       {
11701         ChangeDelay[x][y] = 1;
11702         ChangeEvent[x][y] = trigger_event;
11703
11704         HandleElementChange(x, y, p);
11705
11706         change_done = TRUE;
11707       }
11708 #if USE_NEW_DELAYED_ACTION
11709       else if (change->has_action)
11710       {
11711         ExecuteCustomElementAction(x, y, element, p);
11712         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11713       }
11714 #else
11715       if (change->has_action)
11716       {
11717         ExecuteCustomElementAction(x, y, element, p);
11718         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11719       }
11720 #endif
11721     }
11722   }
11723
11724   RECURSION_LOOP_DETECTION_END();
11725
11726   return change_done;
11727 }
11728
11729 static void PlayPlayerSound(struct PlayerInfo *player)
11730 {
11731   int jx = player->jx, jy = player->jy;
11732   int sound_element = player->artwork_element;
11733   int last_action = player->last_action_waiting;
11734   int action = player->action_waiting;
11735
11736   if (player->is_waiting)
11737   {
11738     if (action != last_action)
11739       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11740     else
11741       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11742   }
11743   else
11744   {
11745     if (action != last_action)
11746       StopSound(element_info[sound_element].sound[last_action]);
11747
11748     if (last_action == ACTION_SLEEPING)
11749       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11750   }
11751 }
11752
11753 static void PlayAllPlayersSound()
11754 {
11755   int i;
11756
11757   for (i = 0; i < MAX_PLAYERS; i++)
11758     if (stored_player[i].active)
11759       PlayPlayerSound(&stored_player[i]);
11760 }
11761
11762 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11763 {
11764   boolean last_waiting = player->is_waiting;
11765   int move_dir = player->MovDir;
11766
11767   player->dir_waiting = move_dir;
11768   player->last_action_waiting = player->action_waiting;
11769
11770   if (is_waiting)
11771   {
11772     if (!last_waiting)          /* not waiting -> waiting */
11773     {
11774       player->is_waiting = TRUE;
11775
11776       player->frame_counter_bored =
11777         FrameCounter +
11778         game.player_boring_delay_fixed +
11779         GetSimpleRandom(game.player_boring_delay_random);
11780       player->frame_counter_sleeping =
11781         FrameCounter +
11782         game.player_sleeping_delay_fixed +
11783         GetSimpleRandom(game.player_sleeping_delay_random);
11784
11785       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11786     }
11787
11788     if (game.player_sleeping_delay_fixed +
11789         game.player_sleeping_delay_random > 0 &&
11790         player->anim_delay_counter == 0 &&
11791         player->post_delay_counter == 0 &&
11792         FrameCounter >= player->frame_counter_sleeping)
11793       player->is_sleeping = TRUE;
11794     else if (game.player_boring_delay_fixed +
11795              game.player_boring_delay_random > 0 &&
11796              FrameCounter >= player->frame_counter_bored)
11797       player->is_bored = TRUE;
11798
11799     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11800                               player->is_bored ? ACTION_BORING :
11801                               ACTION_WAITING);
11802
11803     if (player->is_sleeping && player->use_murphy)
11804     {
11805       /* special case for sleeping Murphy when leaning against non-free tile */
11806
11807       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11808           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11809            !IS_MOVING(player->jx - 1, player->jy)))
11810         move_dir = MV_LEFT;
11811       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11812                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11813                 !IS_MOVING(player->jx + 1, player->jy)))
11814         move_dir = MV_RIGHT;
11815       else
11816         player->is_sleeping = FALSE;
11817
11818       player->dir_waiting = move_dir;
11819     }
11820
11821     if (player->is_sleeping)
11822     {
11823       if (player->num_special_action_sleeping > 0)
11824       {
11825         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11826         {
11827           int last_special_action = player->special_action_sleeping;
11828           int num_special_action = player->num_special_action_sleeping;
11829           int special_action =
11830             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11831              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11832              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11833              last_special_action + 1 : ACTION_SLEEPING);
11834           int special_graphic =
11835             el_act_dir2img(player->artwork_element, special_action, move_dir);
11836
11837           player->anim_delay_counter =
11838             graphic_info[special_graphic].anim_delay_fixed +
11839             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11840           player->post_delay_counter =
11841             graphic_info[special_graphic].post_delay_fixed +
11842             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11843
11844           player->special_action_sleeping = special_action;
11845         }
11846
11847         if (player->anim_delay_counter > 0)
11848         {
11849           player->action_waiting = player->special_action_sleeping;
11850           player->anim_delay_counter--;
11851         }
11852         else if (player->post_delay_counter > 0)
11853         {
11854           player->post_delay_counter--;
11855         }
11856       }
11857     }
11858     else if (player->is_bored)
11859     {
11860       if (player->num_special_action_bored > 0)
11861       {
11862         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11863         {
11864           int special_action =
11865             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11866           int special_graphic =
11867             el_act_dir2img(player->artwork_element, special_action, move_dir);
11868
11869           player->anim_delay_counter =
11870             graphic_info[special_graphic].anim_delay_fixed +
11871             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11872           player->post_delay_counter =
11873             graphic_info[special_graphic].post_delay_fixed +
11874             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11875
11876           player->special_action_bored = special_action;
11877         }
11878
11879         if (player->anim_delay_counter > 0)
11880         {
11881           player->action_waiting = player->special_action_bored;
11882           player->anim_delay_counter--;
11883         }
11884         else if (player->post_delay_counter > 0)
11885         {
11886           player->post_delay_counter--;
11887         }
11888       }
11889     }
11890   }
11891   else if (last_waiting)        /* waiting -> not waiting */
11892   {
11893     player->is_waiting = FALSE;
11894     player->is_bored = FALSE;
11895     player->is_sleeping = FALSE;
11896
11897     player->frame_counter_bored = -1;
11898     player->frame_counter_sleeping = -1;
11899
11900     player->anim_delay_counter = 0;
11901     player->post_delay_counter = 0;
11902
11903     player->dir_waiting = player->MovDir;
11904     player->action_waiting = ACTION_DEFAULT;
11905
11906     player->special_action_bored = ACTION_DEFAULT;
11907     player->special_action_sleeping = ACTION_DEFAULT;
11908   }
11909 }
11910
11911 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11912 {
11913   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11914   int left      = player_action & JOY_LEFT;
11915   int right     = player_action & JOY_RIGHT;
11916   int up        = player_action & JOY_UP;
11917   int down      = player_action & JOY_DOWN;
11918   int button1   = player_action & JOY_BUTTON_1;
11919   int button2   = player_action & JOY_BUTTON_2;
11920   int dx        = (left ? -1 : right ? 1 : 0);
11921   int dy        = (up   ? -1 : down  ? 1 : 0);
11922
11923   if (!player->active || tape.pausing)
11924     return 0;
11925
11926   if (player_action)
11927   {
11928     if (button1)
11929       snapped = SnapField(player, dx, dy);
11930     else
11931     {
11932       if (button2)
11933         dropped = DropElement(player);
11934
11935       moved = MovePlayer(player, dx, dy);
11936     }
11937
11938     if (tape.single_step && tape.recording && !tape.pausing)
11939     {
11940       if (button1 || (dropped && !moved))
11941       {
11942         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11943         SnapField(player, 0, 0);                /* stop snapping */
11944       }
11945     }
11946
11947     SetPlayerWaiting(player, FALSE);
11948
11949     return player_action;
11950   }
11951   else
11952   {
11953     /* no actions for this player (no input at player's configured device) */
11954
11955     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11956     SnapField(player, 0, 0);
11957     CheckGravityMovementWhenNotMoving(player);
11958
11959     if (player->MovPos == 0)
11960       SetPlayerWaiting(player, TRUE);
11961
11962     if (player->MovPos == 0)    /* needed for tape.playing */
11963       player->is_moving = FALSE;
11964
11965     player->is_dropping = FALSE;
11966     player->is_dropping_pressed = FALSE;
11967     player->drop_pressed_delay = 0;
11968
11969     return 0;
11970   }
11971 }
11972
11973 static void CheckLevelTime()
11974 {
11975   int i;
11976
11977   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11978   {
11979     if (level.native_em_level->lev->home == 0)  /* all players at home */
11980     {
11981       PlayerWins(local_player);
11982
11983       AllPlayersGone = TRUE;
11984
11985       level.native_em_level->lev->home = -1;
11986     }
11987
11988     if (level.native_em_level->ply[0]->alive == 0 &&
11989         level.native_em_level->ply[1]->alive == 0 &&
11990         level.native_em_level->ply[2]->alive == 0 &&
11991         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11992       AllPlayersGone = TRUE;
11993   }
11994
11995   if (TimeFrames >= FRAMES_PER_SECOND)
11996   {
11997     TimeFrames = 0;
11998     TapeTime++;
11999
12000     for (i = 0; i < MAX_PLAYERS; i++)
12001     {
12002       struct PlayerInfo *player = &stored_player[i];
12003
12004       if (SHIELD_ON(player))
12005       {
12006         player->shield_normal_time_left--;
12007
12008         if (player->shield_deadly_time_left > 0)
12009           player->shield_deadly_time_left--;
12010       }
12011     }
12012
12013     if (!local_player->LevelSolved && !level.use_step_counter)
12014     {
12015       TimePlayed++;
12016
12017       if (TimeLeft > 0)
12018       {
12019         TimeLeft--;
12020
12021         if (TimeLeft <= 10 && setup.time_limit)
12022           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12023
12024 #if 1
12025         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12026
12027         DisplayGameControlValues();
12028 #else
12029         DrawGameValue_Time(TimeLeft);
12030 #endif
12031
12032         if (!TimeLeft && setup.time_limit)
12033         {
12034           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12035             level.native_em_level->lev->killed_out_of_time = TRUE;
12036           else
12037             for (i = 0; i < MAX_PLAYERS; i++)
12038               KillPlayer(&stored_player[i]);
12039         }
12040       }
12041 #if 1
12042       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12043       {
12044         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12045
12046         DisplayGameControlValues();
12047       }
12048 #else
12049       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12050         DrawGameValue_Time(TimePlayed);
12051 #endif
12052
12053       level.native_em_level->lev->time =
12054         (level.time == 0 ? TimePlayed : TimeLeft);
12055     }
12056
12057     if (tape.recording || tape.playing)
12058       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12059   }
12060
12061 #if 1
12062   UpdateAndDisplayGameControlValues();
12063 #else
12064   UpdateGameDoorValues();
12065   DrawGameDoorValues();
12066 #endif
12067 }
12068
12069 void AdvanceFrameAndPlayerCounters(int player_nr)
12070 {
12071   int i;
12072
12073   /* advance frame counters (global frame counter and time frame counter) */
12074   FrameCounter++;
12075   TimeFrames++;
12076
12077   /* advance player counters (counters for move delay, move animation etc.) */
12078   for (i = 0; i < MAX_PLAYERS; i++)
12079   {
12080     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12081     int move_delay_value = stored_player[i].move_delay_value;
12082     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12083
12084     if (!advance_player_counters)       /* not all players may be affected */
12085       continue;
12086
12087 #if USE_NEW_PLAYER_ANIM
12088     if (move_frames == 0)       /* less than one move per game frame */
12089     {
12090       int stepsize = TILEX / move_delay_value;
12091       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12092       int count = (stored_player[i].is_moving ?
12093                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12094
12095       if (count % delay == 0)
12096         move_frames = 1;
12097     }
12098 #endif
12099
12100     stored_player[i].Frame += move_frames;
12101
12102     if (stored_player[i].MovPos != 0)
12103       stored_player[i].StepFrame += move_frames;
12104
12105     if (stored_player[i].move_delay > 0)
12106       stored_player[i].move_delay--;
12107
12108     /* due to bugs in previous versions, counter must count up, not down */
12109     if (stored_player[i].push_delay != -1)
12110       stored_player[i].push_delay++;
12111
12112     if (stored_player[i].drop_delay > 0)
12113       stored_player[i].drop_delay--;
12114
12115     if (stored_player[i].is_dropping_pressed)
12116       stored_player[i].drop_pressed_delay++;
12117   }
12118 }
12119
12120 void StartGameActions(boolean init_network_game, boolean record_tape,
12121                       long random_seed)
12122 {
12123   unsigned long new_random_seed = InitRND(random_seed);
12124
12125   if (record_tape)
12126     TapeStartRecording(new_random_seed);
12127
12128 #if defined(NETWORK_AVALIABLE)
12129   if (init_network_game)
12130   {
12131     SendToServer_StartPlaying();
12132
12133     return;
12134   }
12135 #endif
12136
12137   InitGame();
12138 }
12139
12140 void GameActions()
12141 {
12142   static unsigned long game_frame_delay = 0;
12143   unsigned long game_frame_delay_value;
12144   byte *recorded_player_action;
12145   byte summarized_player_action = 0;
12146   byte tape_action[MAX_PLAYERS];
12147   int i;
12148
12149   /* detect endless loops, caused by custom element programming */
12150   if (recursion_loop_detected && recursion_loop_depth == 0)
12151   {
12152     char *message = getStringCat3("Internal Error ! Element ",
12153                                   EL_NAME(recursion_loop_element),
12154                                   " caused endless loop ! Quit the game ?");
12155
12156     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12157           EL_NAME(recursion_loop_element));
12158
12159     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12160
12161     recursion_loop_detected = FALSE;    /* if game should be continued */
12162
12163     free(message);
12164
12165     return;
12166   }
12167
12168   if (game.restart_level)
12169     StartGameActions(options.network, setup.autorecord, level.random_seed);
12170
12171   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12172   {
12173     if (level.native_em_level->lev->home == 0)  /* all players at home */
12174     {
12175       PlayerWins(local_player);
12176
12177       AllPlayersGone = TRUE;
12178
12179       level.native_em_level->lev->home = -1;
12180     }
12181
12182     if (level.native_em_level->ply[0]->alive == 0 &&
12183         level.native_em_level->ply[1]->alive == 0 &&
12184         level.native_em_level->ply[2]->alive == 0 &&
12185         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12186       AllPlayersGone = TRUE;
12187   }
12188
12189   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12190     GameWon();
12191
12192   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12193     TapeStop();
12194
12195   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12196     return;
12197
12198   game_frame_delay_value =
12199     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12200
12201   if (tape.playing && tape.warp_forward && !tape.pausing)
12202     game_frame_delay_value = 0;
12203
12204   /* ---------- main game synchronization point ---------- */
12205
12206   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12207
12208   if (network_playing && !network_player_action_received)
12209   {
12210     /* try to get network player actions in time */
12211
12212 #if defined(NETWORK_AVALIABLE)
12213     /* last chance to get network player actions without main loop delay */
12214     HandleNetworking();
12215 #endif
12216
12217     /* game was quit by network peer */
12218     if (game_status != GAME_MODE_PLAYING)
12219       return;
12220
12221     if (!network_player_action_received)
12222       return;           /* failed to get network player actions in time */
12223
12224     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12225   }
12226
12227   if (tape.pausing)
12228     return;
12229
12230   /* at this point we know that we really continue executing the game */
12231
12232   network_player_action_received = FALSE;
12233
12234   /* when playing tape, read previously recorded player input from tape data */
12235   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12236
12237 #if 1
12238   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12239   if (tape.pausing)
12240     return;
12241 #endif
12242
12243   if (tape.set_centered_player)
12244   {
12245     game.centered_player_nr_next = tape.centered_player_nr_next;
12246     game.set_centered_player = TRUE;
12247   }
12248
12249 #if 0 /* USE_NEW_PLAYER_ASSIGNMENTS */
12250   for (i = 0; i < MAX_PLAYERS; i++)
12251   {
12252     summarized_player_action |= stored_player[i].mapped_action;
12253
12254     if (!network_playing)
12255       stored_player[i].effective_action = stored_player[i].mapped_action;
12256   }
12257 #else
12258   for (i = 0; i < MAX_PLAYERS; i++)
12259   {
12260     summarized_player_action |= stored_player[i].action;
12261
12262     if (!network_playing)
12263       stored_player[i].effective_action = stored_player[i].action;
12264   }
12265 #endif
12266
12267 #if defined(NETWORK_AVALIABLE)
12268   if (network_playing)
12269     SendToServer_MovePlayer(summarized_player_action);
12270 #endif
12271
12272   if (!options.network && !setup.team_mode)
12273     local_player->effective_action = summarized_player_action;
12274
12275   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12276   {
12277     for (i = 0; i < MAX_PLAYERS; i++)
12278       stored_player[i].effective_action =
12279         (i == game.centered_player_nr ? summarized_player_action : 0);
12280   }
12281
12282 #if 0 /* USE_NEW_PLAYER_ASSIGNMENTS */
12283   if (recorded_player_action != NULL)
12284     for (i = 0; i < MAX_PLAYERS; i++)
12285       stored_player[i].effective_action =
12286         recorded_player_action[map_player_action[i]];
12287 #else
12288   if (recorded_player_action != NULL)
12289     for (i = 0; i < MAX_PLAYERS; i++)
12290       stored_player[i].effective_action =
12291         recorded_player_action[i];
12292 #endif
12293
12294   for (i = 0; i < MAX_PLAYERS; i++)
12295   {
12296 #if 0 /* USE_NEW_PLAYER_ASSIGNMENTS */
12297     tape_action[i] = stored_player[i].action;
12298 #else
12299     tape_action[i] = stored_player[i].effective_action;
12300 #endif
12301
12302     /* (this can only happen in the R'n'D game engine) */
12303     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12304       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12305   }
12306
12307   /* only record actions from input devices, but not programmed actions */
12308   if (tape.recording)
12309     TapeRecordAction(tape_action);
12310
12311 #if USE_NEW_PLAYER_ASSIGNMENTS
12312   {
12313 #if 0
12314
12315   for (i = 0; i < MAX_PLAYERS; i++)
12316     stored_player[i].mapped_action = stored_player[map_player_action[i]].action;
12317
12318 #else
12319
12320 #if 1
12321     byte unmapped_action[MAX_PLAYERS];
12322
12323     for (i = 0; i < MAX_PLAYERS; i++)
12324       unmapped_action[i] = stored_player[i].effective_action;
12325
12326     for (i = 0; i < MAX_PLAYERS; i++)
12327       stored_player[i].effective_action = unmapped_action[map_player_action[i]];
12328 #endif
12329
12330 #if 0
12331     for (i = 0; i < MAX_PLAYERS; i++)
12332       printf("::: %d: %d [%d]\n", i, stored_player[i].effective_action,
12333              map_player_action[i]);
12334 #endif
12335 #endif
12336   }
12337 #else
12338   for (i = 0; i < MAX_PLAYERS; i++)
12339     stored_player[i].mapped_action = stored_player[i].action;
12340 #endif
12341
12342   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12343   {
12344     GameActions_EM_Main();
12345   }
12346   else
12347   {
12348     GameActions_RND();
12349   }
12350 }
12351
12352 void GameActions_EM_Main()
12353 {
12354   byte effective_action[MAX_PLAYERS];
12355   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12356   int i;
12357
12358   for (i = 0; i < MAX_PLAYERS; i++)
12359     effective_action[i] = stored_player[i].effective_action;
12360
12361   GameActions_EM(effective_action, warp_mode);
12362
12363   CheckLevelTime();
12364
12365   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12366 }
12367
12368 void GameActions_RND()
12369 {
12370   int magic_wall_x = 0, magic_wall_y = 0;
12371   int i, x, y, element, graphic;
12372
12373   InitPlayfieldScanModeVars();
12374
12375 #if USE_ONE_MORE_CHANGE_PER_FRAME
12376   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12377   {
12378     SCAN_PLAYFIELD(x, y)
12379     {
12380       ChangeCount[x][y] = 0;
12381       ChangeEvent[x][y] = -1;
12382     }
12383   }
12384 #endif
12385
12386   if (game.set_centered_player)
12387   {
12388     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12389
12390     /* switching to "all players" only possible if all players fit to screen */
12391     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12392     {
12393       game.centered_player_nr_next = game.centered_player_nr;
12394       game.set_centered_player = FALSE;
12395     }
12396
12397     /* do not switch focus to non-existing (or non-active) player */
12398     if (game.centered_player_nr_next >= 0 &&
12399         !stored_player[game.centered_player_nr_next].active)
12400     {
12401       game.centered_player_nr_next = game.centered_player_nr;
12402       game.set_centered_player = FALSE;
12403     }
12404   }
12405
12406   if (game.set_centered_player &&
12407       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12408   {
12409     int sx, sy;
12410
12411     if (game.centered_player_nr_next == -1)
12412     {
12413       setScreenCenteredToAllPlayers(&sx, &sy);
12414     }
12415     else
12416     {
12417       sx = stored_player[game.centered_player_nr_next].jx;
12418       sy = stored_player[game.centered_player_nr_next].jy;
12419     }
12420
12421     game.centered_player_nr = game.centered_player_nr_next;
12422     game.set_centered_player = FALSE;
12423
12424     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12425     DrawGameDoorValues();
12426   }
12427
12428   for (i = 0; i < MAX_PLAYERS; i++)
12429   {
12430     int actual_player_action = stored_player[i].effective_action;
12431
12432 #if 1
12433     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12434        - rnd_equinox_tetrachloride 048
12435        - rnd_equinox_tetrachloride_ii 096
12436        - rnd_emanuel_schmieg 002
12437        - doctor_sloan_ww 001, 020
12438     */
12439     if (stored_player[i].MovPos == 0)
12440       CheckGravityMovement(&stored_player[i]);
12441 #endif
12442
12443     /* overwrite programmed action with tape action */
12444     if (stored_player[i].programmed_action)
12445       actual_player_action = stored_player[i].programmed_action;
12446
12447     PlayerActions(&stored_player[i], actual_player_action);
12448
12449     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12450   }
12451
12452   ScrollScreen(NULL, SCROLL_GO_ON);
12453
12454   /* for backwards compatibility, the following code emulates a fixed bug that
12455      occured when pushing elements (causing elements that just made their last
12456      pushing step to already (if possible) make their first falling step in the
12457      same game frame, which is bad); this code is also needed to use the famous
12458      "spring push bug" which is used in older levels and might be wanted to be
12459      used also in newer levels, but in this case the buggy pushing code is only
12460      affecting the "spring" element and no other elements */
12461
12462   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12463   {
12464     for (i = 0; i < MAX_PLAYERS; i++)
12465     {
12466       struct PlayerInfo *player = &stored_player[i];
12467       int x = player->jx;
12468       int y = player->jy;
12469
12470       if (player->active && player->is_pushing && player->is_moving &&
12471           IS_MOVING(x, y) &&
12472           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12473            Feld[x][y] == EL_SPRING))
12474       {
12475         ContinueMoving(x, y);
12476
12477         /* continue moving after pushing (this is actually a bug) */
12478         if (!IS_MOVING(x, y))
12479           Stop[x][y] = FALSE;
12480       }
12481     }
12482   }
12483
12484 #if 0
12485   debug_print_timestamp(0, "start main loop profiling");
12486 #endif
12487
12488   SCAN_PLAYFIELD(x, y)
12489   {
12490     ChangeCount[x][y] = 0;
12491     ChangeEvent[x][y] = -1;
12492
12493     /* this must be handled before main playfield loop */
12494     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12495     {
12496       MovDelay[x][y]--;
12497       if (MovDelay[x][y] <= 0)
12498         RemoveField(x, y);
12499     }
12500
12501 #if USE_NEW_SNAP_DELAY
12502     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12503     {
12504       MovDelay[x][y]--;
12505       if (MovDelay[x][y] <= 0)
12506       {
12507         RemoveField(x, y);
12508         TEST_DrawLevelField(x, y);
12509
12510         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12511       }
12512     }
12513 #endif
12514
12515 #if DEBUG
12516     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12517     {
12518       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12519       printf("GameActions(): This should never happen!\n");
12520
12521       ChangePage[x][y] = -1;
12522     }
12523 #endif
12524
12525     Stop[x][y] = FALSE;
12526     if (WasJustMoving[x][y] > 0)
12527       WasJustMoving[x][y]--;
12528     if (WasJustFalling[x][y] > 0)
12529       WasJustFalling[x][y]--;
12530     if (CheckCollision[x][y] > 0)
12531       CheckCollision[x][y]--;
12532     if (CheckImpact[x][y] > 0)
12533       CheckImpact[x][y]--;
12534
12535     GfxFrame[x][y]++;
12536
12537     /* reset finished pushing action (not done in ContinueMoving() to allow
12538        continuous pushing animation for elements with zero push delay) */
12539     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12540     {
12541       ResetGfxAnimation(x, y);
12542       TEST_DrawLevelField(x, y);
12543     }
12544
12545 #if DEBUG
12546     if (IS_BLOCKED(x, y))
12547     {
12548       int oldx, oldy;
12549
12550       Blocked2Moving(x, y, &oldx, &oldy);
12551       if (!IS_MOVING(oldx, oldy))
12552       {
12553         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12554         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12555         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12556         printf("GameActions(): This should never happen!\n");
12557       }
12558     }
12559 #endif
12560   }
12561
12562 #if 0
12563   debug_print_timestamp(0, "- time for pre-main loop:");
12564 #endif
12565
12566 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12567   SCAN_PLAYFIELD(x, y)
12568   {
12569     element = Feld[x][y];
12570     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12571
12572 #if 1
12573     {
12574 #if 1
12575       int element2 = element;
12576       int graphic2 = graphic;
12577 #else
12578       int element2 = Feld[x][y];
12579       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12580 #endif
12581       int last_gfx_frame = GfxFrame[x][y];
12582
12583       if (graphic_info[graphic2].anim_global_sync)
12584         GfxFrame[x][y] = FrameCounter;
12585       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12586         GfxFrame[x][y] = CustomValue[x][y];
12587       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12588         GfxFrame[x][y] = element_info[element2].collect_score;
12589       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12590         GfxFrame[x][y] = ChangeDelay[x][y];
12591
12592       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12593         DrawLevelGraphicAnimation(x, y, graphic2);
12594     }
12595 #else
12596     ResetGfxFrame(x, y, TRUE);
12597 #endif
12598
12599 #if 1
12600     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12601         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12602       ResetRandomAnimationValue(x, y);
12603 #endif
12604
12605 #if 1
12606     SetRandomAnimationValue(x, y);
12607 #endif
12608
12609 #if 1
12610     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12611 #endif
12612   }
12613 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12614
12615 #if 0
12616   debug_print_timestamp(0, "- time for TEST loop:     -->");
12617 #endif
12618
12619   SCAN_PLAYFIELD(x, y)
12620   {
12621     element = Feld[x][y];
12622     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12623
12624     ResetGfxFrame(x, y, TRUE);
12625
12626     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12627         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12628       ResetRandomAnimationValue(x, y);
12629
12630     SetRandomAnimationValue(x, y);
12631
12632     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12633
12634     if (IS_INACTIVE(element))
12635     {
12636       if (IS_ANIMATED(graphic))
12637         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12638
12639       continue;
12640     }
12641
12642     /* this may take place after moving, so 'element' may have changed */
12643     if (IS_CHANGING(x, y) &&
12644         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12645     {
12646       int page = element_info[element].event_page_nr[CE_DELAY];
12647
12648 #if 1
12649       HandleElementChange(x, y, page);
12650 #else
12651       if (CAN_CHANGE(element))
12652         HandleElementChange(x, y, page);
12653
12654       if (HAS_ACTION(element))
12655         ExecuteCustomElementAction(x, y, element, page);
12656 #endif
12657
12658       element = Feld[x][y];
12659       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12660     }
12661
12662 #if 0   // ---------------------------------------------------------------------
12663
12664     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12665     {
12666       StartMoving(x, y);
12667
12668       element = Feld[x][y];
12669       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12670
12671       if (IS_ANIMATED(graphic) &&
12672           !IS_MOVING(x, y) &&
12673           !Stop[x][y])
12674         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12675
12676       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12677         TEST_DrawTwinkleOnField(x, y);
12678     }
12679     else if (IS_MOVING(x, y))
12680       ContinueMoving(x, y);
12681     else
12682     {
12683       switch (element)
12684       {
12685         case EL_ACID:
12686         case EL_EXIT_OPEN:
12687         case EL_EM_EXIT_OPEN:
12688         case EL_SP_EXIT_OPEN:
12689         case EL_STEEL_EXIT_OPEN:
12690         case EL_EM_STEEL_EXIT_OPEN:
12691         case EL_SP_TERMINAL:
12692         case EL_SP_TERMINAL_ACTIVE:
12693         case EL_EXTRA_TIME:
12694         case EL_SHIELD_NORMAL:
12695         case EL_SHIELD_DEADLY:
12696           if (IS_ANIMATED(graphic))
12697             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12698           break;
12699
12700         case EL_DYNAMITE_ACTIVE:
12701         case EL_EM_DYNAMITE_ACTIVE:
12702         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12703         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12704         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12705         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12706         case EL_SP_DISK_RED_ACTIVE:
12707           CheckDynamite(x, y);
12708           break;
12709
12710         case EL_AMOEBA_GROWING:
12711           AmoebeWaechst(x, y);
12712           break;
12713
12714         case EL_AMOEBA_SHRINKING:
12715           AmoebaDisappearing(x, y);
12716           break;
12717
12718 #if !USE_NEW_AMOEBA_CODE
12719         case EL_AMOEBA_WET:
12720         case EL_AMOEBA_DRY:
12721         case EL_AMOEBA_FULL:
12722         case EL_BD_AMOEBA:
12723         case EL_EMC_DRIPPER:
12724           AmoebeAbleger(x, y);
12725           break;
12726 #endif
12727
12728         case EL_GAME_OF_LIFE:
12729         case EL_BIOMAZE:
12730           Life(x, y);
12731           break;
12732
12733         case EL_EXIT_CLOSED:
12734           CheckExit(x, y);
12735           break;
12736
12737         case EL_EM_EXIT_CLOSED:
12738           CheckExitEM(x, y);
12739           break;
12740
12741         case EL_STEEL_EXIT_CLOSED:
12742           CheckExitSteel(x, y);
12743           break;
12744
12745         case EL_EM_STEEL_EXIT_CLOSED:
12746           CheckExitSteelEM(x, y);
12747           break;
12748
12749         case EL_SP_EXIT_CLOSED:
12750           CheckExitSP(x, y);
12751           break;
12752
12753         case EL_EXPANDABLE_WALL_GROWING:
12754         case EL_EXPANDABLE_STEELWALL_GROWING:
12755           MauerWaechst(x, y);
12756           break;
12757
12758         case EL_EXPANDABLE_WALL:
12759         case EL_EXPANDABLE_WALL_HORIZONTAL:
12760         case EL_EXPANDABLE_WALL_VERTICAL:
12761         case EL_EXPANDABLE_WALL_ANY:
12762         case EL_BD_EXPANDABLE_WALL:
12763           MauerAbleger(x, y);
12764           break;
12765
12766         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12767         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12768         case EL_EXPANDABLE_STEELWALL_ANY:
12769           MauerAblegerStahl(x, y);
12770           break;
12771
12772         case EL_FLAMES:
12773           CheckForDragon(x, y);
12774           break;
12775
12776         case EL_EXPLOSION:
12777           break;
12778
12779         case EL_ELEMENT_SNAPPING:
12780         case EL_DIAGONAL_SHRINKING:
12781         case EL_DIAGONAL_GROWING:
12782         {
12783           graphic =
12784             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12785
12786           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12787           break;
12788         }
12789
12790         default:
12791           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12792             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12793           break;
12794       }
12795     }
12796
12797 #else   // ---------------------------------------------------------------------
12798
12799     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12800     {
12801       StartMoving(x, y);
12802
12803       element = Feld[x][y];
12804       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12805
12806       if (IS_ANIMATED(graphic) &&
12807           !IS_MOVING(x, y) &&
12808           !Stop[x][y])
12809         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12810
12811       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12812         TEST_DrawTwinkleOnField(x, y);
12813     }
12814     else if ((element == EL_ACID ||
12815               element == EL_EXIT_OPEN ||
12816               element == EL_EM_EXIT_OPEN ||
12817               element == EL_SP_EXIT_OPEN ||
12818               element == EL_STEEL_EXIT_OPEN ||
12819               element == EL_EM_STEEL_EXIT_OPEN ||
12820               element == EL_SP_TERMINAL ||
12821               element == EL_SP_TERMINAL_ACTIVE ||
12822               element == EL_EXTRA_TIME ||
12823               element == EL_SHIELD_NORMAL ||
12824               element == EL_SHIELD_DEADLY) &&
12825              IS_ANIMATED(graphic))
12826       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12827     else if (IS_MOVING(x, y))
12828       ContinueMoving(x, y);
12829     else if (IS_ACTIVE_BOMB(element))
12830       CheckDynamite(x, y);
12831     else if (element == EL_AMOEBA_GROWING)
12832       AmoebeWaechst(x, y);
12833     else if (element == EL_AMOEBA_SHRINKING)
12834       AmoebaDisappearing(x, y);
12835
12836 #if !USE_NEW_AMOEBA_CODE
12837     else if (IS_AMOEBALIVE(element))
12838       AmoebeAbleger(x, y);
12839 #endif
12840
12841     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12842       Life(x, y);
12843     else if (element == EL_EXIT_CLOSED)
12844       CheckExit(x, y);
12845     else if (element == EL_EM_EXIT_CLOSED)
12846       CheckExitEM(x, y);
12847     else if (element == EL_STEEL_EXIT_CLOSED)
12848       CheckExitSteel(x, y);
12849     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12850       CheckExitSteelEM(x, y);
12851     else if (element == EL_SP_EXIT_CLOSED)
12852       CheckExitSP(x, y);
12853     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12854              element == EL_EXPANDABLE_STEELWALL_GROWING)
12855       MauerWaechst(x, y);
12856     else if (element == EL_EXPANDABLE_WALL ||
12857              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12858              element == EL_EXPANDABLE_WALL_VERTICAL ||
12859              element == EL_EXPANDABLE_WALL_ANY ||
12860              element == EL_BD_EXPANDABLE_WALL)
12861       MauerAbleger(x, y);
12862     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12863              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12864              element == EL_EXPANDABLE_STEELWALL_ANY)
12865       MauerAblegerStahl(x, y);
12866     else if (element == EL_FLAMES)
12867       CheckForDragon(x, y);
12868     else if (element == EL_EXPLOSION)
12869       ; /* drawing of correct explosion animation is handled separately */
12870     else if (element == EL_ELEMENT_SNAPPING ||
12871              element == EL_DIAGONAL_SHRINKING ||
12872              element == EL_DIAGONAL_GROWING)
12873     {
12874       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12875
12876       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12877     }
12878     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12879       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12880
12881 #endif  // ---------------------------------------------------------------------
12882
12883     if (IS_BELT_ACTIVE(element))
12884       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12885
12886     if (game.magic_wall_active)
12887     {
12888       int jx = local_player->jx, jy = local_player->jy;
12889
12890       /* play the element sound at the position nearest to the player */
12891       if ((element == EL_MAGIC_WALL_FULL ||
12892            element == EL_MAGIC_WALL_ACTIVE ||
12893            element == EL_MAGIC_WALL_EMPTYING ||
12894            element == EL_BD_MAGIC_WALL_FULL ||
12895            element == EL_BD_MAGIC_WALL_ACTIVE ||
12896            element == EL_BD_MAGIC_WALL_EMPTYING ||
12897            element == EL_DC_MAGIC_WALL_FULL ||
12898            element == EL_DC_MAGIC_WALL_ACTIVE ||
12899            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12900           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12901       {
12902         magic_wall_x = x;
12903         magic_wall_y = y;
12904       }
12905     }
12906   }
12907
12908 #if 0
12909   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12910 #endif
12911
12912 #if USE_NEW_AMOEBA_CODE
12913   /* new experimental amoeba growth stuff */
12914   if (!(FrameCounter % 8))
12915   {
12916     static unsigned long random = 1684108901;
12917
12918     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12919     {
12920       x = RND(lev_fieldx);
12921       y = RND(lev_fieldy);
12922       element = Feld[x][y];
12923
12924       if (!IS_PLAYER(x,y) &&
12925           (element == EL_EMPTY ||
12926            CAN_GROW_INTO(element) ||
12927            element == EL_QUICKSAND_EMPTY ||
12928            element == EL_QUICKSAND_FAST_EMPTY ||
12929            element == EL_ACID_SPLASH_LEFT ||
12930            element == EL_ACID_SPLASH_RIGHT))
12931       {
12932         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12933             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12934             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12935             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12936           Feld[x][y] = EL_AMOEBA_DROP;
12937       }
12938
12939       random = random * 129 + 1;
12940     }
12941   }
12942 #endif
12943
12944 #if 0
12945   if (game.explosions_delayed)
12946 #endif
12947   {
12948     game.explosions_delayed = FALSE;
12949
12950     SCAN_PLAYFIELD(x, y)
12951     {
12952       element = Feld[x][y];
12953
12954       if (ExplodeField[x][y])
12955         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12956       else if (element == EL_EXPLOSION)
12957         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12958
12959       ExplodeField[x][y] = EX_TYPE_NONE;
12960     }
12961
12962     game.explosions_delayed = TRUE;
12963   }
12964
12965   if (game.magic_wall_active)
12966   {
12967     if (!(game.magic_wall_time_left % 4))
12968     {
12969       int element = Feld[magic_wall_x][magic_wall_y];
12970
12971       if (element == EL_BD_MAGIC_WALL_FULL ||
12972           element == EL_BD_MAGIC_WALL_ACTIVE ||
12973           element == EL_BD_MAGIC_WALL_EMPTYING)
12974         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12975       else if (element == EL_DC_MAGIC_WALL_FULL ||
12976                element == EL_DC_MAGIC_WALL_ACTIVE ||
12977                element == EL_DC_MAGIC_WALL_EMPTYING)
12978         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12979       else
12980         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12981     }
12982
12983     if (game.magic_wall_time_left > 0)
12984     {
12985       game.magic_wall_time_left--;
12986
12987       if (!game.magic_wall_time_left)
12988       {
12989         SCAN_PLAYFIELD(x, y)
12990         {
12991           element = Feld[x][y];
12992
12993           if (element == EL_MAGIC_WALL_ACTIVE ||
12994               element == EL_MAGIC_WALL_FULL)
12995           {
12996             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12997             TEST_DrawLevelField(x, y);
12998           }
12999           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13000                    element == EL_BD_MAGIC_WALL_FULL)
13001           {
13002             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13003             TEST_DrawLevelField(x, y);
13004           }
13005           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13006                    element == EL_DC_MAGIC_WALL_FULL)
13007           {
13008             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13009             TEST_DrawLevelField(x, y);
13010           }
13011         }
13012
13013         game.magic_wall_active = FALSE;
13014       }
13015     }
13016   }
13017
13018   if (game.light_time_left > 0)
13019   {
13020     game.light_time_left--;
13021
13022     if (game.light_time_left == 0)
13023       RedrawAllLightSwitchesAndInvisibleElements();
13024   }
13025
13026   if (game.timegate_time_left > 0)
13027   {
13028     game.timegate_time_left--;
13029
13030     if (game.timegate_time_left == 0)
13031       CloseAllOpenTimegates();
13032   }
13033
13034   if (game.lenses_time_left > 0)
13035   {
13036     game.lenses_time_left--;
13037
13038     if (game.lenses_time_left == 0)
13039       RedrawAllInvisibleElementsForLenses();
13040   }
13041
13042   if (game.magnify_time_left > 0)
13043   {
13044     game.magnify_time_left--;
13045
13046     if (game.magnify_time_left == 0)
13047       RedrawAllInvisibleElementsForMagnifier();
13048   }
13049
13050   for (i = 0; i < MAX_PLAYERS; i++)
13051   {
13052     struct PlayerInfo *player = &stored_player[i];
13053
13054     if (SHIELD_ON(player))
13055     {
13056       if (player->shield_deadly_time_left)
13057         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13058       else if (player->shield_normal_time_left)
13059         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13060     }
13061   }
13062
13063 #if USE_DELAYED_GFX_REDRAW
13064   SCAN_PLAYFIELD(x, y)
13065   {
13066 #if 1
13067     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13068 #else
13069     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13070         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13071 #endif
13072     {
13073       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13074          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13075
13076       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13077         DrawLevelField(x, y);
13078
13079       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13080         DrawLevelFieldCrumbledSand(x, y);
13081
13082       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13083         DrawLevelFieldCrumbledSandNeighbours(x, y);
13084
13085       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13086         DrawTwinkleOnField(x, y);
13087     }
13088
13089     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13090   }
13091 #endif
13092
13093   CheckLevelTime();
13094
13095   DrawAllPlayers();
13096   PlayAllPlayersSound();
13097
13098   if (options.debug)                    /* calculate frames per second */
13099   {
13100     static unsigned long fps_counter = 0;
13101     static int fps_frames = 0;
13102     unsigned long fps_delay_ms = Counter() - fps_counter;
13103
13104     fps_frames++;
13105
13106     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13107     {
13108       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13109
13110       fps_frames = 0;
13111       fps_counter = Counter();
13112     }
13113
13114     redraw_mask |= REDRAW_FPS;
13115   }
13116
13117   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13118
13119   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13120   {
13121     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13122
13123     local_player->show_envelope = 0;
13124   }
13125
13126 #if 0
13127   debug_print_timestamp(0, "stop main loop profiling ");
13128   printf("----------------------------------------------------------\n");
13129 #endif
13130
13131   /* use random number generator in every frame to make it less predictable */
13132   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13133     RND(1);
13134 }
13135
13136 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13137 {
13138   int min_x = x, min_y = y, max_x = x, max_y = y;
13139   int i;
13140
13141   for (i = 0; i < MAX_PLAYERS; i++)
13142   {
13143     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13144
13145     if (!stored_player[i].active || &stored_player[i] == player)
13146       continue;
13147
13148     min_x = MIN(min_x, jx);
13149     min_y = MIN(min_y, jy);
13150     max_x = MAX(max_x, jx);
13151     max_y = MAX(max_y, jy);
13152   }
13153
13154   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13155 }
13156
13157 static boolean AllPlayersInVisibleScreen()
13158 {
13159   int i;
13160
13161   for (i = 0; i < MAX_PLAYERS; i++)
13162   {
13163     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13164
13165     if (!stored_player[i].active)
13166       continue;
13167
13168     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13169       return FALSE;
13170   }
13171
13172   return TRUE;
13173 }
13174
13175 void ScrollLevel(int dx, int dy)
13176 {
13177 #if 0
13178   /* (directly solved in BlitBitmap() now) */
13179   static Bitmap *bitmap_db_field2 = NULL;
13180   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13181   int x, y;
13182 #else
13183   int x, y;
13184 #endif
13185
13186 #if 0
13187   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13188   /* only horizontal XOR vertical scroll direction allowed */
13189   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13190     return;
13191 #endif
13192
13193 #if 0
13194   /* (directly solved in BlitBitmap() now) */
13195   if (bitmap_db_field2 == NULL)
13196     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13197
13198   /* needed when blitting directly to same bitmap -- should not be needed with
13199      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13200   BlitBitmap(drawto_field, bitmap_db_field2,
13201              FX + TILEX * (dx == -1) - softscroll_offset,
13202              FY + TILEY * (dy == -1) - softscroll_offset,
13203              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13204              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13205              FX + TILEX * (dx == 1) - softscroll_offset,
13206              FY + TILEY * (dy == 1) - softscroll_offset);
13207   BlitBitmap(bitmap_db_field2, drawto_field,
13208              FX + TILEX * (dx == 1) - softscroll_offset,
13209              FY + TILEY * (dy == 1) - softscroll_offset,
13210              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13211              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13212              FX + TILEX * (dx == 1) - softscroll_offset,
13213              FY + TILEY * (dy == 1) - softscroll_offset);
13214
13215 #else
13216
13217 #if 0
13218   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13219   int xsize = (BX2 - BX1 + 1);
13220   int ysize = (BY2 - BY1 + 1);
13221   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13222   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13223   int step  = (start < end ? +1 : -1);
13224
13225   for (i = start; i != end; i += step)
13226   {
13227     BlitBitmap(drawto_field, drawto_field,
13228                FX + TILEX * (dx != 0 ? i + step : 0),
13229                FY + TILEY * (dy != 0 ? i + step : 0),
13230                TILEX * (dx != 0 ? 1 : xsize),
13231                TILEY * (dy != 0 ? 1 : ysize),
13232                FX + TILEX * (dx != 0 ? i : 0),
13233                FY + TILEY * (dy != 0 ? i : 0));
13234   }
13235
13236 #else
13237
13238   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13239
13240   BlitBitmap(drawto_field, drawto_field,
13241              FX + TILEX * (dx == -1) - softscroll_offset,
13242              FY + TILEY * (dy == -1) - softscroll_offset,
13243              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13244              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13245              FX + TILEX * (dx == 1) - softscroll_offset,
13246              FY + TILEY * (dy == 1) - softscroll_offset);
13247 #endif
13248 #endif
13249
13250   if (dx != 0)
13251   {
13252     x = (dx == 1 ? BX1 : BX2);
13253     for (y = BY1; y <= BY2; y++)
13254       DrawScreenField(x, y);
13255   }
13256
13257   if (dy != 0)
13258   {
13259     y = (dy == 1 ? BY1 : BY2);
13260     for (x = BX1; x <= BX2; x++)
13261       DrawScreenField(x, y);
13262   }
13263
13264   redraw_mask |= REDRAW_FIELD;
13265 }
13266
13267 static boolean canFallDown(struct PlayerInfo *player)
13268 {
13269   int jx = player->jx, jy = player->jy;
13270
13271   return (IN_LEV_FIELD(jx, jy + 1) &&
13272           (IS_FREE(jx, jy + 1) ||
13273            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13274           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13275           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13276 }
13277
13278 static boolean canPassField(int x, int y, int move_dir)
13279 {
13280   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13281   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13282   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13283   int nextx = x + dx;
13284   int nexty = y + dy;
13285   int element = Feld[x][y];
13286
13287   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13288           !CAN_MOVE(element) &&
13289           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13290           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13291           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13292 }
13293
13294 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13295 {
13296   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13297   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13298   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13299   int newx = x + dx;
13300   int newy = y + dy;
13301
13302   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13303           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13304           (IS_DIGGABLE(Feld[newx][newy]) ||
13305            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13306            canPassField(newx, newy, move_dir)));
13307 }
13308
13309 static void CheckGravityMovement(struct PlayerInfo *player)
13310 {
13311 #if USE_PLAYER_GRAVITY
13312   if (player->gravity && !player->programmed_action)
13313 #else
13314   if (game.gravity && !player->programmed_action)
13315 #endif
13316   {
13317     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13318     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13319     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13320     int jx = player->jx, jy = player->jy;
13321     boolean player_is_moving_to_valid_field =
13322       (!player_is_snapping &&
13323        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13324         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13325     boolean player_can_fall_down = canFallDown(player);
13326
13327     if (player_can_fall_down &&
13328         !player_is_moving_to_valid_field)
13329       player->programmed_action = MV_DOWN;
13330   }
13331 }
13332
13333 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13334 {
13335   return CheckGravityMovement(player);
13336
13337 #if USE_PLAYER_GRAVITY
13338   if (player->gravity && !player->programmed_action)
13339 #else
13340   if (game.gravity && !player->programmed_action)
13341 #endif
13342   {
13343     int jx = player->jx, jy = player->jy;
13344     boolean field_under_player_is_free =
13345       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13346     boolean player_is_standing_on_valid_field =
13347       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13348        (IS_WALKABLE(Feld[jx][jy]) &&
13349         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13350
13351     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13352       player->programmed_action = MV_DOWN;
13353   }
13354 }
13355
13356 /*
13357   MovePlayerOneStep()
13358   -----------------------------------------------------------------------------
13359   dx, dy:               direction (non-diagonal) to try to move the player to
13360   real_dx, real_dy:     direction as read from input device (can be diagonal)
13361 */
13362
13363 boolean MovePlayerOneStep(struct PlayerInfo *player,
13364                           int dx, int dy, int real_dx, int real_dy)
13365 {
13366   int jx = player->jx, jy = player->jy;
13367   int new_jx = jx + dx, new_jy = jy + dy;
13368 #if !USE_FIXED_DONT_RUN_INTO
13369   int element;
13370 #endif
13371   int can_move;
13372   boolean player_can_move = !player->cannot_move;
13373
13374   if (!player->active || (!dx && !dy))
13375     return MP_NO_ACTION;
13376
13377   player->MovDir = (dx < 0 ? MV_LEFT :
13378                     dx > 0 ? MV_RIGHT :
13379                     dy < 0 ? MV_UP :
13380                     dy > 0 ? MV_DOWN :  MV_NONE);
13381
13382   if (!IN_LEV_FIELD(new_jx, new_jy))
13383     return MP_NO_ACTION;
13384
13385   if (!player_can_move)
13386   {
13387     if (player->MovPos == 0)
13388     {
13389       player->is_moving = FALSE;
13390       player->is_digging = FALSE;
13391       player->is_collecting = FALSE;
13392       player->is_snapping = FALSE;
13393       player->is_pushing = FALSE;
13394     }
13395   }
13396
13397 #if 1
13398   if (!options.network && game.centered_player_nr == -1 &&
13399       !AllPlayersInSight(player, new_jx, new_jy))
13400     return MP_NO_ACTION;
13401 #else
13402   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13403     return MP_NO_ACTION;
13404 #endif
13405
13406 #if !USE_FIXED_DONT_RUN_INTO
13407   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13408
13409   /* (moved to DigField()) */
13410   if (player_can_move && DONT_RUN_INTO(element))
13411   {
13412     if (element == EL_ACID && dx == 0 && dy == 1)
13413     {
13414       SplashAcid(new_jx, new_jy);
13415       Feld[jx][jy] = EL_PLAYER_1;
13416       InitMovingField(jx, jy, MV_DOWN);
13417       Store[jx][jy] = EL_ACID;
13418       ContinueMoving(jx, jy);
13419       BuryPlayer(player);
13420     }
13421     else
13422       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13423
13424     return MP_MOVING;
13425   }
13426 #endif
13427
13428   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13429   if (can_move != MP_MOVING)
13430     return can_move;
13431
13432   /* check if DigField() has caused relocation of the player */
13433   if (player->jx != jx || player->jy != jy)
13434     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13435
13436   StorePlayer[jx][jy] = 0;
13437   player->last_jx = jx;
13438   player->last_jy = jy;
13439   player->jx = new_jx;
13440   player->jy = new_jy;
13441   StorePlayer[new_jx][new_jy] = player->element_nr;
13442
13443   if (player->move_delay_value_next != -1)
13444   {
13445     player->move_delay_value = player->move_delay_value_next;
13446     player->move_delay_value_next = -1;
13447   }
13448
13449   player->MovPos =
13450     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13451
13452   player->step_counter++;
13453
13454   PlayerVisit[jx][jy] = FrameCounter;
13455
13456 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13457   player->is_moving = TRUE;
13458 #endif
13459
13460 #if 1
13461   /* should better be called in MovePlayer(), but this breaks some tapes */
13462   ScrollPlayer(player, SCROLL_INIT);
13463 #endif
13464
13465   return MP_MOVING;
13466 }
13467
13468 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13469 {
13470   int jx = player->jx, jy = player->jy;
13471   int old_jx = jx, old_jy = jy;
13472   int moved = MP_NO_ACTION;
13473
13474   if (!player->active)
13475     return FALSE;
13476
13477   if (!dx && !dy)
13478   {
13479     if (player->MovPos == 0)
13480     {
13481       player->is_moving = FALSE;
13482       player->is_digging = FALSE;
13483       player->is_collecting = FALSE;
13484       player->is_snapping = FALSE;
13485       player->is_pushing = FALSE;
13486     }
13487
13488     return FALSE;
13489   }
13490
13491   if (player->move_delay > 0)
13492     return FALSE;
13493
13494   player->move_delay = -1;              /* set to "uninitialized" value */
13495
13496   /* store if player is automatically moved to next field */
13497   player->is_auto_moving = (player->programmed_action != MV_NONE);
13498
13499   /* remove the last programmed player action */
13500   player->programmed_action = 0;
13501
13502   if (player->MovPos)
13503   {
13504     /* should only happen if pre-1.2 tape recordings are played */
13505     /* this is only for backward compatibility */
13506
13507     int original_move_delay_value = player->move_delay_value;
13508
13509 #if DEBUG
13510     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13511            tape.counter);
13512 #endif
13513
13514     /* scroll remaining steps with finest movement resolution */
13515     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13516
13517     while (player->MovPos)
13518     {
13519       ScrollPlayer(player, SCROLL_GO_ON);
13520       ScrollScreen(NULL, SCROLL_GO_ON);
13521
13522       AdvanceFrameAndPlayerCounters(player->index_nr);
13523
13524       DrawAllPlayers();
13525       BackToFront();
13526     }
13527
13528     player->move_delay_value = original_move_delay_value;
13529   }
13530
13531   player->is_active = FALSE;
13532
13533   if (player->last_move_dir & MV_HORIZONTAL)
13534   {
13535     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13536       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13537   }
13538   else
13539   {
13540     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13541       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13542   }
13543
13544 #if USE_FIXED_BORDER_RUNNING_GFX
13545   if (!moved && !player->is_active)
13546   {
13547     player->is_moving = FALSE;
13548     player->is_digging = FALSE;
13549     player->is_collecting = FALSE;
13550     player->is_snapping = FALSE;
13551     player->is_pushing = FALSE;
13552   }
13553 #endif
13554
13555   jx = player->jx;
13556   jy = player->jy;
13557
13558 #if 1
13559   if (moved & MP_MOVING && !ScreenMovPos &&
13560       (player->index_nr == game.centered_player_nr ||
13561        game.centered_player_nr == -1))
13562 #else
13563   if (moved & MP_MOVING && !ScreenMovPos &&
13564       (player == local_player || !options.network))
13565 #endif
13566   {
13567     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13568     int offset = game.scroll_delay_value;
13569
13570     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13571     {
13572       /* actual player has left the screen -- scroll in that direction */
13573       if (jx != old_jx)         /* player has moved horizontally */
13574         scroll_x += (jx - old_jx);
13575       else                      /* player has moved vertically */
13576         scroll_y += (jy - old_jy);
13577     }
13578     else
13579     {
13580       if (jx != old_jx)         /* player has moved horizontally */
13581       {
13582         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13583             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13584           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13585
13586         /* don't scroll over playfield boundaries */
13587         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13588           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13589
13590         /* don't scroll more than one field at a time */
13591         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13592
13593         /* don't scroll against the player's moving direction */
13594         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13595             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13596           scroll_x = old_scroll_x;
13597       }
13598       else                      /* player has moved vertically */
13599       {
13600         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13601             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13602           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13603
13604         /* don't scroll over playfield boundaries */
13605         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13606           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13607
13608         /* don't scroll more than one field at a time */
13609         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13610
13611         /* don't scroll against the player's moving direction */
13612         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13613             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13614           scroll_y = old_scroll_y;
13615       }
13616     }
13617
13618     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13619     {
13620 #if 1
13621       if (!options.network && game.centered_player_nr == -1 &&
13622           !AllPlayersInVisibleScreen())
13623       {
13624         scroll_x = old_scroll_x;
13625         scroll_y = old_scroll_y;
13626       }
13627       else
13628 #else
13629       if (!options.network && !AllPlayersInVisibleScreen())
13630       {
13631         scroll_x = old_scroll_x;
13632         scroll_y = old_scroll_y;
13633       }
13634       else
13635 #endif
13636       {
13637         ScrollScreen(player, SCROLL_INIT);
13638         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13639       }
13640     }
13641   }
13642
13643   player->StepFrame = 0;
13644
13645   if (moved & MP_MOVING)
13646   {
13647     if (old_jx != jx && old_jy == jy)
13648       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13649     else if (old_jx == jx && old_jy != jy)
13650       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13651
13652     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13653
13654     player->last_move_dir = player->MovDir;
13655     player->is_moving = TRUE;
13656     player->is_snapping = FALSE;
13657     player->is_switching = FALSE;
13658     player->is_dropping = FALSE;
13659     player->is_dropping_pressed = FALSE;
13660     player->drop_pressed_delay = 0;
13661
13662 #if 0
13663     /* should better be called here than above, but this breaks some tapes */
13664     ScrollPlayer(player, SCROLL_INIT);
13665 #endif
13666   }
13667   else
13668   {
13669     CheckGravityMovementWhenNotMoving(player);
13670
13671     player->is_moving = FALSE;
13672
13673     /* at this point, the player is allowed to move, but cannot move right now
13674        (e.g. because of something blocking the way) -- ensure that the player
13675        is also allowed to move in the next frame (in old versions before 3.1.1,
13676        the player was forced to wait again for eight frames before next try) */
13677
13678     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13679       player->move_delay = 0;   /* allow direct movement in the next frame */
13680   }
13681
13682   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13683     player->move_delay = player->move_delay_value;
13684
13685   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13686   {
13687     TestIfPlayerTouchesBadThing(jx, jy);
13688     TestIfPlayerTouchesCustomElement(jx, jy);
13689   }
13690
13691   if (!player->active)
13692     RemovePlayer(player);
13693
13694   return moved;
13695 }
13696
13697 void ScrollPlayer(struct PlayerInfo *player, int mode)
13698 {
13699   int jx = player->jx, jy = player->jy;
13700   int last_jx = player->last_jx, last_jy = player->last_jy;
13701   int move_stepsize = TILEX / player->move_delay_value;
13702
13703 #if USE_NEW_PLAYER_SPEED
13704   if (!player->active)
13705     return;
13706
13707   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13708     return;
13709 #else
13710   if (!player->active || player->MovPos == 0)
13711     return;
13712 #endif
13713
13714   if (mode == SCROLL_INIT)
13715   {
13716     player->actual_frame_counter = FrameCounter;
13717     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13718
13719     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13720         Feld[last_jx][last_jy] == EL_EMPTY)
13721     {
13722       int last_field_block_delay = 0;   /* start with no blocking at all */
13723       int block_delay_adjustment = player->block_delay_adjustment;
13724
13725       /* if player blocks last field, add delay for exactly one move */
13726       if (player->block_last_field)
13727       {
13728         last_field_block_delay += player->move_delay_value;
13729
13730         /* when blocking enabled, prevent moving up despite gravity */
13731 #if USE_PLAYER_GRAVITY
13732         if (player->gravity && player->MovDir == MV_UP)
13733           block_delay_adjustment = -1;
13734 #else
13735         if (game.gravity && player->MovDir == MV_UP)
13736           block_delay_adjustment = -1;
13737 #endif
13738       }
13739
13740       /* add block delay adjustment (also possible when not blocking) */
13741       last_field_block_delay += block_delay_adjustment;
13742
13743       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13744       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13745     }
13746
13747 #if USE_NEW_PLAYER_SPEED
13748     if (player->MovPos != 0)    /* player has not yet reached destination */
13749       return;
13750 #else
13751     return;
13752 #endif
13753   }
13754   else if (!FrameReached(&player->actual_frame_counter, 1))
13755     return;
13756
13757 #if USE_NEW_PLAYER_SPEED
13758   if (player->MovPos != 0)
13759   {
13760     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13761     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13762
13763     /* before DrawPlayer() to draw correct player graphic for this case */
13764     if (player->MovPos == 0)
13765       CheckGravityMovement(player);
13766   }
13767 #else
13768   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13769   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13770
13771   /* before DrawPlayer() to draw correct player graphic for this case */
13772   if (player->MovPos == 0)
13773     CheckGravityMovement(player);
13774 #endif
13775
13776   if (player->MovPos == 0)      /* player reached destination field */
13777   {
13778     if (player->move_delay_reset_counter > 0)
13779     {
13780       player->move_delay_reset_counter--;
13781
13782       if (player->move_delay_reset_counter == 0)
13783       {
13784         /* continue with normal speed after quickly moving through gate */
13785         HALVE_PLAYER_SPEED(player);
13786
13787         /* be able to make the next move without delay */
13788         player->move_delay = 0;
13789       }
13790     }
13791
13792     player->last_jx = jx;
13793     player->last_jy = jy;
13794
13795     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13796         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13797 #if 1
13798         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13799 #endif
13800         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13801         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13802 #if 1
13803         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13804 #endif
13805         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13806         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13807     {
13808       DrawPlayer(player);       /* needed here only to cleanup last field */
13809       RemovePlayer(player);
13810
13811       if (local_player->friends_still_needed == 0 ||
13812           IS_SP_ELEMENT(Feld[jx][jy]))
13813         PlayerWins(player);
13814     }
13815
13816     /* this breaks one level: "machine", level 000 */
13817     {
13818       int move_direction = player->MovDir;
13819       int enter_side = MV_DIR_OPPOSITE(move_direction);
13820       int leave_side = move_direction;
13821       int old_jx = last_jx;
13822       int old_jy = last_jy;
13823       int old_element = Feld[old_jx][old_jy];
13824       int new_element = Feld[jx][jy];
13825
13826       if (IS_CUSTOM_ELEMENT(old_element))
13827         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13828                                    CE_LEFT_BY_PLAYER,
13829                                    player->index_bit, leave_side);
13830
13831       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13832                                           CE_PLAYER_LEAVES_X,
13833                                           player->index_bit, leave_side);
13834
13835       if (IS_CUSTOM_ELEMENT(new_element))
13836         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13837                                    player->index_bit, enter_side);
13838
13839       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13840                                           CE_PLAYER_ENTERS_X,
13841                                           player->index_bit, enter_side);
13842
13843 #if USE_FIX_CE_ACTION_WITH_PLAYER
13844       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13845                                         CE_MOVE_OF_X, move_direction);
13846 #else
13847       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13848                                         CE_MOVE_OF_X, move_direction);
13849 #endif
13850     }
13851
13852     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13853     {
13854       TestIfPlayerTouchesBadThing(jx, jy);
13855       TestIfPlayerTouchesCustomElement(jx, jy);
13856
13857       /* needed because pushed element has not yet reached its destination,
13858          so it would trigger a change event at its previous field location */
13859       if (!player->is_pushing)
13860         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13861
13862       if (!player->active)
13863         RemovePlayer(player);
13864     }
13865
13866     if (!local_player->LevelSolved && level.use_step_counter)
13867     {
13868       int i;
13869
13870       TimePlayed++;
13871
13872       if (TimeLeft > 0)
13873       {
13874         TimeLeft--;
13875
13876         if (TimeLeft <= 10 && setup.time_limit)
13877           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13878
13879 #if 1
13880         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13881
13882         DisplayGameControlValues();
13883 #else
13884         DrawGameValue_Time(TimeLeft);
13885 #endif
13886
13887         if (!TimeLeft && setup.time_limit)
13888           for (i = 0; i < MAX_PLAYERS; i++)
13889             KillPlayer(&stored_player[i]);
13890       }
13891 #if 1
13892       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13893       {
13894         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13895
13896         DisplayGameControlValues();
13897       }
13898 #else
13899       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13900         DrawGameValue_Time(TimePlayed);
13901 #endif
13902     }
13903
13904     if (tape.single_step && tape.recording && !tape.pausing &&
13905         !player->programmed_action)
13906       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13907   }
13908 }
13909
13910 void ScrollScreen(struct PlayerInfo *player, int mode)
13911 {
13912   static unsigned long screen_frame_counter = 0;
13913
13914   if (mode == SCROLL_INIT)
13915   {
13916     /* set scrolling step size according to actual player's moving speed */
13917     ScrollStepSize = TILEX / player->move_delay_value;
13918
13919     screen_frame_counter = FrameCounter;
13920     ScreenMovDir = player->MovDir;
13921     ScreenMovPos = player->MovPos;
13922     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13923     return;
13924   }
13925   else if (!FrameReached(&screen_frame_counter, 1))
13926     return;
13927
13928   if (ScreenMovPos)
13929   {
13930     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13931     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13932     redraw_mask |= REDRAW_FIELD;
13933   }
13934   else
13935     ScreenMovDir = MV_NONE;
13936 }
13937
13938 void TestIfPlayerTouchesCustomElement(int x, int y)
13939 {
13940   static int xy[4][2] =
13941   {
13942     { 0, -1 },
13943     { -1, 0 },
13944     { +1, 0 },
13945     { 0, +1 }
13946   };
13947   static int trigger_sides[4][2] =
13948   {
13949     /* center side       border side */
13950     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13951     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13952     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13953     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13954   };
13955   static int touch_dir[4] =
13956   {
13957     MV_LEFT | MV_RIGHT,
13958     MV_UP   | MV_DOWN,
13959     MV_UP   | MV_DOWN,
13960     MV_LEFT | MV_RIGHT
13961   };
13962   int center_element = Feld[x][y];      /* should always be non-moving! */
13963   int i;
13964
13965   for (i = 0; i < NUM_DIRECTIONS; i++)
13966   {
13967     int xx = x + xy[i][0];
13968     int yy = y + xy[i][1];
13969     int center_side = trigger_sides[i][0];
13970     int border_side = trigger_sides[i][1];
13971     int border_element;
13972
13973     if (!IN_LEV_FIELD(xx, yy))
13974       continue;
13975
13976     if (IS_PLAYER(x, y))                /* player found at center element */
13977     {
13978       struct PlayerInfo *player = PLAYERINFO(x, y);
13979
13980       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13981         border_element = Feld[xx][yy];          /* may be moving! */
13982       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13983         border_element = Feld[xx][yy];
13984       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13985         border_element = MovingOrBlocked2Element(xx, yy);
13986       else
13987         continue;               /* center and border element do not touch */
13988
13989       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13990                                  player->index_bit, border_side);
13991       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13992                                           CE_PLAYER_TOUCHES_X,
13993                                           player->index_bit, border_side);
13994
13995 #if USE_FIX_CE_ACTION_WITH_PLAYER
13996       {
13997         /* use player element that is initially defined in the level playfield,
13998            not the player element that corresponds to the runtime player number
13999            (example: a level that contains EL_PLAYER_3 as the only player would
14000            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14001         int player_element = PLAYERINFO(x, y)->initial_element;
14002
14003         CheckElementChangeBySide(xx, yy, border_element, player_element,
14004                                  CE_TOUCHING_X, border_side);
14005       }
14006 #endif
14007     }
14008     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14009     {
14010       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14011
14012       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14013       {
14014         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14015           continue;             /* center and border element do not touch */
14016       }
14017
14018       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14019                                  player->index_bit, center_side);
14020       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14021                                           CE_PLAYER_TOUCHES_X,
14022                                           player->index_bit, center_side);
14023
14024 #if USE_FIX_CE_ACTION_WITH_PLAYER
14025       {
14026         /* use player element that is initially defined in the level playfield,
14027            not the player element that corresponds to the runtime player number
14028            (example: a level that contains EL_PLAYER_3 as the only player would
14029            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14030         int player_element = PLAYERINFO(xx, yy)->initial_element;
14031
14032         CheckElementChangeBySide(x, y, center_element, player_element,
14033                                  CE_TOUCHING_X, center_side);
14034       }
14035 #endif
14036
14037       break;
14038     }
14039   }
14040 }
14041
14042 #if USE_ELEMENT_TOUCHING_BUGFIX
14043
14044 void TestIfElementTouchesCustomElement(int x, int y)
14045 {
14046   static int xy[4][2] =
14047   {
14048     { 0, -1 },
14049     { -1, 0 },
14050     { +1, 0 },
14051     { 0, +1 }
14052   };
14053   static int trigger_sides[4][2] =
14054   {
14055     /* center side      border side */
14056     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14057     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14058     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14059     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14060   };
14061   static int touch_dir[4] =
14062   {
14063     MV_LEFT | MV_RIGHT,
14064     MV_UP   | MV_DOWN,
14065     MV_UP   | MV_DOWN,
14066     MV_LEFT | MV_RIGHT
14067   };
14068   boolean change_center_element = FALSE;
14069   int center_element = Feld[x][y];      /* should always be non-moving! */
14070   int border_element_old[NUM_DIRECTIONS];
14071   int i;
14072
14073   for (i = 0; i < NUM_DIRECTIONS; i++)
14074   {
14075     int xx = x + xy[i][0];
14076     int yy = y + xy[i][1];
14077     int border_element;
14078
14079     border_element_old[i] = -1;
14080
14081     if (!IN_LEV_FIELD(xx, yy))
14082       continue;
14083
14084     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14085       border_element = Feld[xx][yy];    /* may be moving! */
14086     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14087       border_element = Feld[xx][yy];
14088     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14089       border_element = MovingOrBlocked2Element(xx, yy);
14090     else
14091       continue;                 /* center and border element do not touch */
14092
14093     border_element_old[i] = border_element;
14094   }
14095
14096   for (i = 0; i < NUM_DIRECTIONS; i++)
14097   {
14098     int xx = x + xy[i][0];
14099     int yy = y + xy[i][1];
14100     int center_side = trigger_sides[i][0];
14101     int border_element = border_element_old[i];
14102
14103     if (border_element == -1)
14104       continue;
14105
14106     /* check for change of border element */
14107     CheckElementChangeBySide(xx, yy, border_element, center_element,
14108                              CE_TOUCHING_X, center_side);
14109
14110     /* (center element cannot be player, so we dont have to check this here) */
14111   }
14112
14113   for (i = 0; i < NUM_DIRECTIONS; i++)
14114   {
14115     int xx = x + xy[i][0];
14116     int yy = y + xy[i][1];
14117     int border_side = trigger_sides[i][1];
14118     int border_element = border_element_old[i];
14119
14120     if (border_element == -1)
14121       continue;
14122
14123     /* check for change of center element (but change it only once) */
14124     if (!change_center_element)
14125       change_center_element =
14126         CheckElementChangeBySide(x, y, center_element, border_element,
14127                                  CE_TOUCHING_X, border_side);
14128
14129 #if USE_FIX_CE_ACTION_WITH_PLAYER
14130     if (IS_PLAYER(xx, yy))
14131     {
14132       /* use player element that is initially defined in the level playfield,
14133          not the player element that corresponds to the runtime player number
14134          (example: a level that contains EL_PLAYER_3 as the only player would
14135          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14136       int player_element = PLAYERINFO(xx, yy)->initial_element;
14137
14138       CheckElementChangeBySide(x, y, center_element, player_element,
14139                                CE_TOUCHING_X, border_side);
14140     }
14141 #endif
14142   }
14143 }
14144
14145 #else
14146
14147 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14148 {
14149   static int xy[4][2] =
14150   {
14151     { 0, -1 },
14152     { -1, 0 },
14153     { +1, 0 },
14154     { 0, +1 }
14155   };
14156   static int trigger_sides[4][2] =
14157   {
14158     /* center side      border side */
14159     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14160     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14161     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14162     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14163   };
14164   static int touch_dir[4] =
14165   {
14166     MV_LEFT | MV_RIGHT,
14167     MV_UP   | MV_DOWN,
14168     MV_UP   | MV_DOWN,
14169     MV_LEFT | MV_RIGHT
14170   };
14171   boolean change_center_element = FALSE;
14172   int center_element = Feld[x][y];      /* should always be non-moving! */
14173   int i;
14174
14175   for (i = 0; i < NUM_DIRECTIONS; i++)
14176   {
14177     int xx = x + xy[i][0];
14178     int yy = y + xy[i][1];
14179     int center_side = trigger_sides[i][0];
14180     int border_side = trigger_sides[i][1];
14181     int border_element;
14182
14183     if (!IN_LEV_FIELD(xx, yy))
14184       continue;
14185
14186     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14187       border_element = Feld[xx][yy];    /* may be moving! */
14188     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14189       border_element = Feld[xx][yy];
14190     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14191       border_element = MovingOrBlocked2Element(xx, yy);
14192     else
14193       continue;                 /* center and border element do not touch */
14194
14195     /* check for change of center element (but change it only once) */
14196     if (!change_center_element)
14197       change_center_element =
14198         CheckElementChangeBySide(x, y, center_element, border_element,
14199                                  CE_TOUCHING_X, border_side);
14200
14201     /* check for change of border element */
14202     CheckElementChangeBySide(xx, yy, border_element, center_element,
14203                              CE_TOUCHING_X, center_side);
14204   }
14205 }
14206
14207 #endif
14208
14209 void TestIfElementHitsCustomElement(int x, int y, int direction)
14210 {
14211   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14212   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14213   int hitx = x + dx, hity = y + dy;
14214   int hitting_element = Feld[x][y];
14215   int touched_element;
14216
14217   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14218     return;
14219
14220   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14221                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14222
14223   if (IN_LEV_FIELD(hitx, hity))
14224   {
14225     int opposite_direction = MV_DIR_OPPOSITE(direction);
14226     int hitting_side = direction;
14227     int touched_side = opposite_direction;
14228     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14229                           MovDir[hitx][hity] != direction ||
14230                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14231
14232     object_hit = TRUE;
14233
14234     if (object_hit)
14235     {
14236       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14237                                CE_HITTING_X, touched_side);
14238
14239       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14240                                CE_HIT_BY_X, hitting_side);
14241
14242       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14243                                CE_HIT_BY_SOMETHING, opposite_direction);
14244
14245 #if USE_FIX_CE_ACTION_WITH_PLAYER
14246       if (IS_PLAYER(hitx, hity))
14247       {
14248         /* use player element that is initially defined in the level playfield,
14249            not the player element that corresponds to the runtime player number
14250            (example: a level that contains EL_PLAYER_3 as the only player would
14251            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14252         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14253
14254         CheckElementChangeBySide(x, y, hitting_element, player_element,
14255                                  CE_HITTING_X, touched_side);
14256       }
14257 #endif
14258     }
14259   }
14260
14261   /* "hitting something" is also true when hitting the playfield border */
14262   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14263                            CE_HITTING_SOMETHING, direction);
14264 }
14265
14266 #if 0
14267 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14268 {
14269   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14270   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14271   int hitx = x + dx, hity = y + dy;
14272   int hitting_element = Feld[x][y];
14273   int touched_element;
14274 #if 0
14275   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14276                         !IS_FREE(hitx, hity) &&
14277                         (!IS_MOVING(hitx, hity) ||
14278                          MovDir[hitx][hity] != direction ||
14279                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14280 #endif
14281
14282   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14283     return;
14284
14285 #if 0
14286   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14287     return;
14288 #endif
14289
14290   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14291                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14292
14293   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14294                            EP_CAN_SMASH_EVERYTHING, direction);
14295
14296   if (IN_LEV_FIELD(hitx, hity))
14297   {
14298     int opposite_direction = MV_DIR_OPPOSITE(direction);
14299     int hitting_side = direction;
14300     int touched_side = opposite_direction;
14301 #if 0
14302     int touched_element = MovingOrBlocked2Element(hitx, hity);
14303 #endif
14304 #if 1
14305     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14306                           MovDir[hitx][hity] != direction ||
14307                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14308
14309     object_hit = TRUE;
14310 #endif
14311
14312     if (object_hit)
14313     {
14314       int i;
14315
14316       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14317                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14318
14319       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14320                                CE_OTHER_IS_SMASHING, touched_side);
14321
14322       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14323                                CE_OTHER_GETS_SMASHED, hitting_side);
14324     }
14325   }
14326 }
14327 #endif
14328
14329 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14330 {
14331   int i, kill_x = -1, kill_y = -1;
14332
14333   int bad_element = -1;
14334   static int test_xy[4][2] =
14335   {
14336     { 0, -1 },
14337     { -1, 0 },
14338     { +1, 0 },
14339     { 0, +1 }
14340   };
14341   static int test_dir[4] =
14342   {
14343     MV_UP,
14344     MV_LEFT,
14345     MV_RIGHT,
14346     MV_DOWN
14347   };
14348
14349   for (i = 0; i < NUM_DIRECTIONS; i++)
14350   {
14351     int test_x, test_y, test_move_dir, test_element;
14352
14353     test_x = good_x + test_xy[i][0];
14354     test_y = good_y + test_xy[i][1];
14355
14356     if (!IN_LEV_FIELD(test_x, test_y))
14357       continue;
14358
14359     test_move_dir =
14360       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14361
14362     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14363
14364     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14365        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14366     */
14367     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14368         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14369     {
14370       kill_x = test_x;
14371       kill_y = test_y;
14372       bad_element = test_element;
14373
14374       break;
14375     }
14376   }
14377
14378   if (kill_x != -1 || kill_y != -1)
14379   {
14380     if (IS_PLAYER(good_x, good_y))
14381     {
14382       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14383
14384       if (player->shield_deadly_time_left > 0 &&
14385           !IS_INDESTRUCTIBLE(bad_element))
14386         Bang(kill_x, kill_y);
14387       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14388         KillPlayer(player);
14389     }
14390     else
14391       Bang(good_x, good_y);
14392   }
14393 }
14394
14395 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14396 {
14397   int i, kill_x = -1, kill_y = -1;
14398   int bad_element = Feld[bad_x][bad_y];
14399   static int test_xy[4][2] =
14400   {
14401     { 0, -1 },
14402     { -1, 0 },
14403     { +1, 0 },
14404     { 0, +1 }
14405   };
14406   static int touch_dir[4] =
14407   {
14408     MV_LEFT | MV_RIGHT,
14409     MV_UP   | MV_DOWN,
14410     MV_UP   | MV_DOWN,
14411     MV_LEFT | MV_RIGHT
14412   };
14413   static int test_dir[4] =
14414   {
14415     MV_UP,
14416     MV_LEFT,
14417     MV_RIGHT,
14418     MV_DOWN
14419   };
14420
14421   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14422     return;
14423
14424   for (i = 0; i < NUM_DIRECTIONS; i++)
14425   {
14426     int test_x, test_y, test_move_dir, test_element;
14427
14428     test_x = bad_x + test_xy[i][0];
14429     test_y = bad_y + test_xy[i][1];
14430
14431     if (!IN_LEV_FIELD(test_x, test_y))
14432       continue;
14433
14434     test_move_dir =
14435       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14436
14437     test_element = Feld[test_x][test_y];
14438
14439     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14440        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14441     */
14442     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14443         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14444     {
14445       /* good thing is player or penguin that does not move away */
14446       if (IS_PLAYER(test_x, test_y))
14447       {
14448         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14449
14450         if (bad_element == EL_ROBOT && player->is_moving)
14451           continue;     /* robot does not kill player if he is moving */
14452
14453         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14454         {
14455           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14456             continue;           /* center and border element do not touch */
14457         }
14458
14459         kill_x = test_x;
14460         kill_y = test_y;
14461
14462         break;
14463       }
14464       else if (test_element == EL_PENGUIN)
14465       {
14466         kill_x = test_x;
14467         kill_y = test_y;
14468
14469         break;
14470       }
14471     }
14472   }
14473
14474   if (kill_x != -1 || kill_y != -1)
14475   {
14476     if (IS_PLAYER(kill_x, kill_y))
14477     {
14478       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14479
14480       if (player->shield_deadly_time_left > 0 &&
14481           !IS_INDESTRUCTIBLE(bad_element))
14482         Bang(bad_x, bad_y);
14483       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14484         KillPlayer(player);
14485     }
14486     else
14487       Bang(kill_x, kill_y);
14488   }
14489 }
14490
14491 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14492 {
14493   int bad_element = Feld[bad_x][bad_y];
14494   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14495   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14496   int test_x = bad_x + dx, test_y = bad_y + dy;
14497   int test_move_dir, test_element;
14498   int kill_x = -1, kill_y = -1;
14499
14500   if (!IN_LEV_FIELD(test_x, test_y))
14501     return;
14502
14503   test_move_dir =
14504     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14505
14506   test_element = Feld[test_x][test_y];
14507
14508   if (test_move_dir != bad_move_dir)
14509   {
14510     /* good thing can be player or penguin that does not move away */
14511     if (IS_PLAYER(test_x, test_y))
14512     {
14513       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14514
14515       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14516          player as being hit when he is moving towards the bad thing, because
14517          the "get hit by" condition would be lost after the player stops) */
14518       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14519         return;         /* player moves away from bad thing */
14520
14521       kill_x = test_x;
14522       kill_y = test_y;
14523     }
14524     else if (test_element == EL_PENGUIN)
14525     {
14526       kill_x = test_x;
14527       kill_y = test_y;
14528     }
14529   }
14530
14531   if (kill_x != -1 || kill_y != -1)
14532   {
14533     if (IS_PLAYER(kill_x, kill_y))
14534     {
14535       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14536
14537       if (player->shield_deadly_time_left > 0 &&
14538           !IS_INDESTRUCTIBLE(bad_element))
14539         Bang(bad_x, bad_y);
14540       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14541         KillPlayer(player);
14542     }
14543     else
14544       Bang(kill_x, kill_y);
14545   }
14546 }
14547
14548 void TestIfPlayerTouchesBadThing(int x, int y)
14549 {
14550   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14551 }
14552
14553 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14554 {
14555   TestIfGoodThingHitsBadThing(x, y, move_dir);
14556 }
14557
14558 void TestIfBadThingTouchesPlayer(int x, int y)
14559 {
14560   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14561 }
14562
14563 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14564 {
14565   TestIfBadThingHitsGoodThing(x, y, move_dir);
14566 }
14567
14568 void TestIfFriendTouchesBadThing(int x, int y)
14569 {
14570   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14571 }
14572
14573 void TestIfBadThingTouchesFriend(int x, int y)
14574 {
14575   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14576 }
14577
14578 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14579 {
14580   int i, kill_x = bad_x, kill_y = bad_y;
14581   static int xy[4][2] =
14582   {
14583     { 0, -1 },
14584     { -1, 0 },
14585     { +1, 0 },
14586     { 0, +1 }
14587   };
14588
14589   for (i = 0; i < NUM_DIRECTIONS; i++)
14590   {
14591     int x, y, element;
14592
14593     x = bad_x + xy[i][0];
14594     y = bad_y + xy[i][1];
14595     if (!IN_LEV_FIELD(x, y))
14596       continue;
14597
14598     element = Feld[x][y];
14599     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14600         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14601     {
14602       kill_x = x;
14603       kill_y = y;
14604       break;
14605     }
14606   }
14607
14608   if (kill_x != bad_x || kill_y != bad_y)
14609     Bang(bad_x, bad_y);
14610 }
14611
14612 void KillPlayer(struct PlayerInfo *player)
14613 {
14614   int jx = player->jx, jy = player->jy;
14615
14616   if (!player->active)
14617     return;
14618
14619 #if 0
14620   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14621          player->killed, player->active, player->reanimated);
14622 #endif
14623
14624   /* the following code was introduced to prevent an infinite loop when calling
14625      -> Bang()
14626      -> CheckTriggeredElementChangeExt()
14627      -> ExecuteCustomElementAction()
14628      -> KillPlayer()
14629      -> (infinitely repeating the above sequence of function calls)
14630      which occurs when killing the player while having a CE with the setting
14631      "kill player X when explosion of <player X>"; the solution using a new
14632      field "player->killed" was chosen for backwards compatibility, although
14633      clever use of the fields "player->active" etc. would probably also work */
14634 #if 1
14635   if (player->killed)
14636     return;
14637 #endif
14638
14639   player->killed = TRUE;
14640
14641   /* remove accessible field at the player's position */
14642   Feld[jx][jy] = EL_EMPTY;
14643
14644   /* deactivate shield (else Bang()/Explode() would not work right) */
14645   player->shield_normal_time_left = 0;
14646   player->shield_deadly_time_left = 0;
14647
14648 #if 0
14649   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14650          player->killed, player->active, player->reanimated);
14651 #endif
14652
14653   Bang(jx, jy);
14654
14655 #if 0
14656   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14657          player->killed, player->active, player->reanimated);
14658 #endif
14659
14660 #if USE_PLAYER_REANIMATION
14661 #if 1
14662   if (player->reanimated)       /* killed player may have been reanimated */
14663     player->killed = player->reanimated = FALSE;
14664   else
14665     BuryPlayer(player);
14666 #else
14667   if (player->killed)           /* player may have been reanimated */
14668     BuryPlayer(player);
14669 #endif
14670 #else
14671   BuryPlayer(player);
14672 #endif
14673 }
14674
14675 static void KillPlayerUnlessEnemyProtected(int x, int y)
14676 {
14677   if (!PLAYER_ENEMY_PROTECTED(x, y))
14678     KillPlayer(PLAYERINFO(x, y));
14679 }
14680
14681 static void KillPlayerUnlessExplosionProtected(int x, int y)
14682 {
14683   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14684     KillPlayer(PLAYERINFO(x, y));
14685 }
14686
14687 void BuryPlayer(struct PlayerInfo *player)
14688 {
14689   int jx = player->jx, jy = player->jy;
14690
14691   if (!player->active)
14692     return;
14693
14694   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14695   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14696
14697   player->GameOver = TRUE;
14698   RemovePlayer(player);
14699 }
14700
14701 void RemovePlayer(struct PlayerInfo *player)
14702 {
14703   int jx = player->jx, jy = player->jy;
14704   int i, found = FALSE;
14705
14706   player->present = FALSE;
14707   player->active = FALSE;
14708
14709   if (!ExplodeField[jx][jy])
14710     StorePlayer[jx][jy] = 0;
14711
14712   if (player->is_moving)
14713     TEST_DrawLevelField(player->last_jx, player->last_jy);
14714
14715   for (i = 0; i < MAX_PLAYERS; i++)
14716     if (stored_player[i].active)
14717       found = TRUE;
14718
14719   if (!found)
14720     AllPlayersGone = TRUE;
14721
14722   ExitX = ZX = jx;
14723   ExitY = ZY = jy;
14724 }
14725
14726 #if USE_NEW_SNAP_DELAY
14727 static void setFieldForSnapping(int x, int y, int element, int direction)
14728 {
14729   struct ElementInfo *ei = &element_info[element];
14730   int direction_bit = MV_DIR_TO_BIT(direction);
14731   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14732   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14733                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14734
14735   Feld[x][y] = EL_ELEMENT_SNAPPING;
14736   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14737
14738   ResetGfxAnimation(x, y);
14739
14740   GfxElement[x][y] = element;
14741   GfxAction[x][y] = action;
14742   GfxDir[x][y] = direction;
14743   GfxFrame[x][y] = -1;
14744 }
14745 #endif
14746
14747 /*
14748   =============================================================================
14749   checkDiagonalPushing()
14750   -----------------------------------------------------------------------------
14751   check if diagonal input device direction results in pushing of object
14752   (by checking if the alternative direction is walkable, diggable, ...)
14753   =============================================================================
14754 */
14755
14756 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14757                                     int x, int y, int real_dx, int real_dy)
14758 {
14759   int jx, jy, dx, dy, xx, yy;
14760
14761   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14762     return TRUE;
14763
14764   /* diagonal direction: check alternative direction */
14765   jx = player->jx;
14766   jy = player->jy;
14767   dx = x - jx;
14768   dy = y - jy;
14769   xx = jx + (dx == 0 ? real_dx : 0);
14770   yy = jy + (dy == 0 ? real_dy : 0);
14771
14772   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14773 }
14774
14775 /*
14776   =============================================================================
14777   DigField()
14778   -----------------------------------------------------------------------------
14779   x, y:                 field next to player (non-diagonal) to try to dig to
14780   real_dx, real_dy:     direction as read from input device (can be diagonal)
14781   =============================================================================
14782 */
14783
14784 static int DigField(struct PlayerInfo *player,
14785                     int oldx, int oldy, int x, int y,
14786                     int real_dx, int real_dy, int mode)
14787 {
14788   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14789   boolean player_was_pushing = player->is_pushing;
14790   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14791   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14792   int jx = oldx, jy = oldy;
14793   int dx = x - jx, dy = y - jy;
14794   int nextx = x + dx, nexty = y + dy;
14795   int move_direction = (dx == -1 ? MV_LEFT  :
14796                         dx == +1 ? MV_RIGHT :
14797                         dy == -1 ? MV_UP    :
14798                         dy == +1 ? MV_DOWN  : MV_NONE);
14799   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14800   int dig_side = MV_DIR_OPPOSITE(move_direction);
14801   int old_element = Feld[jx][jy];
14802 #if USE_FIXED_DONT_RUN_INTO
14803   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14804 #else
14805   int element;
14806 #endif
14807   int collect_count;
14808
14809   if (is_player)                /* function can also be called by EL_PENGUIN */
14810   {
14811     if (player->MovPos == 0)
14812     {
14813       player->is_digging = FALSE;
14814       player->is_collecting = FALSE;
14815     }
14816
14817     if (player->MovPos == 0)    /* last pushing move finished */
14818       player->is_pushing = FALSE;
14819
14820     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14821     {
14822       player->is_switching = FALSE;
14823       player->push_delay = -1;
14824
14825       return MP_NO_ACTION;
14826     }
14827   }
14828
14829 #if !USE_FIXED_DONT_RUN_INTO
14830   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14831     return MP_NO_ACTION;
14832 #endif
14833
14834   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14835     old_element = Back[jx][jy];
14836
14837   /* in case of element dropped at player position, check background */
14838   else if (Back[jx][jy] != EL_EMPTY &&
14839            game.engine_version >= VERSION_IDENT(2,2,0,0))
14840     old_element = Back[jx][jy];
14841
14842   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14843     return MP_NO_ACTION;        /* field has no opening in this direction */
14844
14845   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14846     return MP_NO_ACTION;        /* field has no opening in this direction */
14847
14848 #if USE_FIXED_DONT_RUN_INTO
14849   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14850   {
14851     SplashAcid(x, y);
14852
14853     Feld[jx][jy] = player->artwork_element;
14854     InitMovingField(jx, jy, MV_DOWN);
14855     Store[jx][jy] = EL_ACID;
14856     ContinueMoving(jx, jy);
14857     BuryPlayer(player);
14858
14859     return MP_DONT_RUN_INTO;
14860   }
14861 #endif
14862
14863 #if USE_FIXED_DONT_RUN_INTO
14864   if (player_can_move && DONT_RUN_INTO(element))
14865   {
14866     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14867
14868     return MP_DONT_RUN_INTO;
14869   }
14870 #endif
14871
14872 #if USE_FIXED_DONT_RUN_INTO
14873   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14874     return MP_NO_ACTION;
14875 #endif
14876
14877 #if !USE_FIXED_DONT_RUN_INTO
14878   element = Feld[x][y];
14879 #endif
14880
14881   collect_count = element_info[element].collect_count_initial;
14882
14883   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14884     return MP_NO_ACTION;
14885
14886   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14887     player_can_move = player_can_move_or_snap;
14888
14889   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14890       game.engine_version >= VERSION_IDENT(2,2,0,0))
14891   {
14892     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14893                                player->index_bit, dig_side);
14894     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14895                                         player->index_bit, dig_side);
14896
14897     if (element == EL_DC_LANDMINE)
14898       Bang(x, y);
14899
14900     if (Feld[x][y] != element)          /* field changed by snapping */
14901       return MP_ACTION;
14902
14903     return MP_NO_ACTION;
14904   }
14905
14906 #if USE_PLAYER_GRAVITY
14907   if (player->gravity && is_player && !player->is_auto_moving &&
14908       canFallDown(player) && move_direction != MV_DOWN &&
14909       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14910     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14911 #else
14912   if (game.gravity && is_player && !player->is_auto_moving &&
14913       canFallDown(player) && move_direction != MV_DOWN &&
14914       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14915     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14916 #endif
14917
14918   if (player_can_move &&
14919       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14920   {
14921     int sound_element = SND_ELEMENT(element);
14922     int sound_action = ACTION_WALKING;
14923
14924     if (IS_RND_GATE(element))
14925     {
14926       if (!player->key[RND_GATE_NR(element)])
14927         return MP_NO_ACTION;
14928     }
14929     else if (IS_RND_GATE_GRAY(element))
14930     {
14931       if (!player->key[RND_GATE_GRAY_NR(element)])
14932         return MP_NO_ACTION;
14933     }
14934     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14935     {
14936       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14937         return MP_NO_ACTION;
14938     }
14939     else if (element == EL_EXIT_OPEN ||
14940              element == EL_EM_EXIT_OPEN ||
14941 #if 1
14942              element == EL_EM_EXIT_OPENING ||
14943 #endif
14944              element == EL_STEEL_EXIT_OPEN ||
14945              element == EL_EM_STEEL_EXIT_OPEN ||
14946 #if 1
14947              element == EL_EM_STEEL_EXIT_OPENING ||
14948 #endif
14949              element == EL_SP_EXIT_OPEN ||
14950              element == EL_SP_EXIT_OPENING)
14951     {
14952       sound_action = ACTION_PASSING;    /* player is passing exit */
14953     }
14954     else if (element == EL_EMPTY)
14955     {
14956       sound_action = ACTION_MOVING;             /* nothing to walk on */
14957     }
14958
14959     /* play sound from background or player, whatever is available */
14960     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14961       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14962     else
14963       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14964   }
14965   else if (player_can_move &&
14966            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14967   {
14968     if (!ACCESS_FROM(element, opposite_direction))
14969       return MP_NO_ACTION;      /* field not accessible from this direction */
14970
14971     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14972       return MP_NO_ACTION;
14973
14974     if (IS_EM_GATE(element))
14975     {
14976       if (!player->key[EM_GATE_NR(element)])
14977         return MP_NO_ACTION;
14978     }
14979     else if (IS_EM_GATE_GRAY(element))
14980     {
14981       if (!player->key[EM_GATE_GRAY_NR(element)])
14982         return MP_NO_ACTION;
14983     }
14984     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14985     {
14986       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14987         return MP_NO_ACTION;
14988     }
14989     else if (IS_EMC_GATE(element))
14990     {
14991       if (!player->key[EMC_GATE_NR(element)])
14992         return MP_NO_ACTION;
14993     }
14994     else if (IS_EMC_GATE_GRAY(element))
14995     {
14996       if (!player->key[EMC_GATE_GRAY_NR(element)])
14997         return MP_NO_ACTION;
14998     }
14999     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15000     {
15001       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15002         return MP_NO_ACTION;
15003     }
15004     else if (element == EL_DC_GATE_WHITE ||
15005              element == EL_DC_GATE_WHITE_GRAY ||
15006              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15007     {
15008       if (player->num_white_keys == 0)
15009         return MP_NO_ACTION;
15010
15011       player->num_white_keys--;
15012     }
15013     else if (IS_SP_PORT(element))
15014     {
15015       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15016           element == EL_SP_GRAVITY_PORT_RIGHT ||
15017           element == EL_SP_GRAVITY_PORT_UP ||
15018           element == EL_SP_GRAVITY_PORT_DOWN)
15019 #if USE_PLAYER_GRAVITY
15020         player->gravity = !player->gravity;
15021 #else
15022         game.gravity = !game.gravity;
15023 #endif
15024       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15025                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15026                element == EL_SP_GRAVITY_ON_PORT_UP ||
15027                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15028 #if USE_PLAYER_GRAVITY
15029         player->gravity = TRUE;
15030 #else
15031         game.gravity = TRUE;
15032 #endif
15033       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15034                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15035                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15036                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15037 #if USE_PLAYER_GRAVITY
15038         player->gravity = FALSE;
15039 #else
15040         game.gravity = FALSE;
15041 #endif
15042     }
15043
15044     /* automatically move to the next field with double speed */
15045     player->programmed_action = move_direction;
15046
15047     if (player->move_delay_reset_counter == 0)
15048     {
15049       player->move_delay_reset_counter = 2;     /* two double speed steps */
15050
15051       DOUBLE_PLAYER_SPEED(player);
15052     }
15053
15054     PlayLevelSoundAction(x, y, ACTION_PASSING);
15055   }
15056   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15057   {
15058     RemoveField(x, y);
15059
15060     if (mode != DF_SNAP)
15061     {
15062       GfxElement[x][y] = GFX_ELEMENT(element);
15063       player->is_digging = TRUE;
15064     }
15065
15066     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15067
15068     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15069                                         player->index_bit, dig_side);
15070
15071     if (mode == DF_SNAP)
15072     {
15073 #if USE_NEW_SNAP_DELAY
15074       if (level.block_snap_field)
15075         setFieldForSnapping(x, y, element, move_direction);
15076       else
15077         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15078 #else
15079       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15080 #endif
15081
15082       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15083                                           player->index_bit, dig_side);
15084     }
15085   }
15086   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15087   {
15088     RemoveField(x, y);
15089
15090     if (is_player && mode != DF_SNAP)
15091     {
15092       GfxElement[x][y] = element;
15093       player->is_collecting = TRUE;
15094     }
15095
15096     if (element == EL_SPEED_PILL)
15097     {
15098       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15099     }
15100     else if (element == EL_EXTRA_TIME && level.time > 0)
15101     {
15102       TimeLeft += level.extra_time;
15103
15104 #if 1
15105       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15106
15107       DisplayGameControlValues();
15108 #else
15109       DrawGameValue_Time(TimeLeft);
15110 #endif
15111     }
15112     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15113     {
15114       player->shield_normal_time_left += level.shield_normal_time;
15115       if (element == EL_SHIELD_DEADLY)
15116         player->shield_deadly_time_left += level.shield_deadly_time;
15117     }
15118     else if (element == EL_DYNAMITE ||
15119              element == EL_EM_DYNAMITE ||
15120              element == EL_SP_DISK_RED)
15121     {
15122       if (player->inventory_size < MAX_INVENTORY_SIZE)
15123         player->inventory_element[player->inventory_size++] = element;
15124
15125       DrawGameDoorValues();
15126     }
15127     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15128     {
15129       player->dynabomb_count++;
15130       player->dynabombs_left++;
15131     }
15132     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15133     {
15134       player->dynabomb_size++;
15135     }
15136     else if (element == EL_DYNABOMB_INCREASE_POWER)
15137     {
15138       player->dynabomb_xl = TRUE;
15139     }
15140     else if (IS_KEY(element))
15141     {
15142       player->key[KEY_NR(element)] = TRUE;
15143
15144       DrawGameDoorValues();
15145     }
15146     else if (element == EL_DC_KEY_WHITE)
15147     {
15148       player->num_white_keys++;
15149
15150       /* display white keys? */
15151       /* DrawGameDoorValues(); */
15152     }
15153     else if (IS_ENVELOPE(element))
15154     {
15155       player->show_envelope = element;
15156     }
15157     else if (element == EL_EMC_LENSES)
15158     {
15159       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15160
15161       RedrawAllInvisibleElementsForLenses();
15162     }
15163     else if (element == EL_EMC_MAGNIFIER)
15164     {
15165       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15166
15167       RedrawAllInvisibleElementsForMagnifier();
15168     }
15169     else if (IS_DROPPABLE(element) ||
15170              IS_THROWABLE(element))     /* can be collected and dropped */
15171     {
15172       int i;
15173
15174       if (collect_count == 0)
15175         player->inventory_infinite_element = element;
15176       else
15177         for (i = 0; i < collect_count; i++)
15178           if (player->inventory_size < MAX_INVENTORY_SIZE)
15179             player->inventory_element[player->inventory_size++] = element;
15180
15181       DrawGameDoorValues();
15182     }
15183     else if (collect_count > 0)
15184     {
15185       local_player->gems_still_needed -= collect_count;
15186       if (local_player->gems_still_needed < 0)
15187         local_player->gems_still_needed = 0;
15188
15189 #if 1
15190       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15191
15192       DisplayGameControlValues();
15193 #else
15194       DrawGameValue_Emeralds(local_player->gems_still_needed);
15195 #endif
15196     }
15197
15198     RaiseScoreElement(element);
15199     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15200
15201     if (is_player)
15202       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15203                                           player->index_bit, dig_side);
15204
15205     if (mode == DF_SNAP)
15206     {
15207 #if USE_NEW_SNAP_DELAY
15208       if (level.block_snap_field)
15209         setFieldForSnapping(x, y, element, move_direction);
15210       else
15211         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15212 #else
15213       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15214 #endif
15215
15216       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15217                                           player->index_bit, dig_side);
15218     }
15219   }
15220   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15221   {
15222     if (mode == DF_SNAP && element != EL_BD_ROCK)
15223       return MP_NO_ACTION;
15224
15225     if (CAN_FALL(element) && dy)
15226       return MP_NO_ACTION;
15227
15228     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15229         !(element == EL_SPRING && level.use_spring_bug))
15230       return MP_NO_ACTION;
15231
15232     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15233         ((move_direction & MV_VERTICAL &&
15234           ((element_info[element].move_pattern & MV_LEFT &&
15235             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15236            (element_info[element].move_pattern & MV_RIGHT &&
15237             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15238          (move_direction & MV_HORIZONTAL &&
15239           ((element_info[element].move_pattern & MV_UP &&
15240             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15241            (element_info[element].move_pattern & MV_DOWN &&
15242             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15243       return MP_NO_ACTION;
15244
15245     /* do not push elements already moving away faster than player */
15246     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15247         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15248       return MP_NO_ACTION;
15249
15250     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15251     {
15252       if (player->push_delay_value == -1 || !player_was_pushing)
15253         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15254     }
15255     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15256     {
15257       if (player->push_delay_value == -1)
15258         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15259     }
15260     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15261     {
15262       if (!player->is_pushing)
15263         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15264     }
15265
15266     player->is_pushing = TRUE;
15267     player->is_active = TRUE;
15268
15269     if (!(IN_LEV_FIELD(nextx, nexty) &&
15270           (IS_FREE(nextx, nexty) ||
15271            (IS_SB_ELEMENT(element) &&
15272             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15273            (IS_CUSTOM_ELEMENT(element) &&
15274             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15275       return MP_NO_ACTION;
15276
15277     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15278       return MP_NO_ACTION;
15279
15280     if (player->push_delay == -1)       /* new pushing; restart delay */
15281       player->push_delay = 0;
15282
15283     if (player->push_delay < player->push_delay_value &&
15284         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15285         element != EL_SPRING && element != EL_BALLOON)
15286     {
15287       /* make sure that there is no move delay before next try to push */
15288       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15289         player->move_delay = 0;
15290
15291       return MP_NO_ACTION;
15292     }
15293
15294     if (IS_CUSTOM_ELEMENT(element) &&
15295         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15296     {
15297       if (!DigFieldByCE(nextx, nexty, element))
15298         return MP_NO_ACTION;
15299     }
15300
15301     if (IS_SB_ELEMENT(element))
15302     {
15303       if (element == EL_SOKOBAN_FIELD_FULL)
15304       {
15305         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15306         local_player->sokobanfields_still_needed++;
15307       }
15308
15309       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15310       {
15311         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15312         local_player->sokobanfields_still_needed--;
15313       }
15314
15315       Feld[x][y] = EL_SOKOBAN_OBJECT;
15316
15317       if (Back[x][y] == Back[nextx][nexty])
15318         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15319       else if (Back[x][y] != 0)
15320         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15321                                     ACTION_EMPTYING);
15322       else
15323         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15324                                     ACTION_FILLING);
15325
15326       if (local_player->sokobanfields_still_needed == 0 &&
15327           game.emulation == EMU_SOKOBAN)
15328       {
15329         PlayerWins(player);
15330
15331         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15332       }
15333     }
15334     else
15335       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15336
15337     InitMovingField(x, y, move_direction);
15338     GfxAction[x][y] = ACTION_PUSHING;
15339
15340     if (mode == DF_SNAP)
15341       ContinueMoving(x, y);
15342     else
15343       MovPos[x][y] = (dx != 0 ? dx : dy);
15344
15345     Pushed[x][y] = TRUE;
15346     Pushed[nextx][nexty] = TRUE;
15347
15348     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15349       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15350     else
15351       player->push_delay_value = -1;    /* get new value later */
15352
15353     /* check for element change _after_ element has been pushed */
15354     if (game.use_change_when_pushing_bug)
15355     {
15356       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15357                                  player->index_bit, dig_side);
15358       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15359                                           player->index_bit, dig_side);
15360     }
15361   }
15362   else if (IS_SWITCHABLE(element))
15363   {
15364     if (PLAYER_SWITCHING(player, x, y))
15365     {
15366       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15367                                           player->index_bit, dig_side);
15368
15369       return MP_ACTION;
15370     }
15371
15372     player->is_switching = TRUE;
15373     player->switch_x = x;
15374     player->switch_y = y;
15375
15376     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15377
15378     if (element == EL_ROBOT_WHEEL)
15379     {
15380       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15381       ZX = x;
15382       ZY = y;
15383
15384       game.robot_wheel_active = TRUE;
15385
15386       TEST_DrawLevelField(x, y);
15387     }
15388     else if (element == EL_SP_TERMINAL)
15389     {
15390       int xx, yy;
15391
15392       SCAN_PLAYFIELD(xx, yy)
15393       {
15394         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15395           Bang(xx, yy);
15396         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15397           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15398       }
15399     }
15400     else if (IS_BELT_SWITCH(element))
15401     {
15402       ToggleBeltSwitch(x, y);
15403     }
15404     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15405              element == EL_SWITCHGATE_SWITCH_DOWN ||
15406              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15407              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15408     {
15409       ToggleSwitchgateSwitch(x, y);
15410     }
15411     else if (element == EL_LIGHT_SWITCH ||
15412              element == EL_LIGHT_SWITCH_ACTIVE)
15413     {
15414       ToggleLightSwitch(x, y);
15415     }
15416     else if (element == EL_TIMEGATE_SWITCH ||
15417              element == EL_DC_TIMEGATE_SWITCH)
15418     {
15419       ActivateTimegateSwitch(x, y);
15420     }
15421     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15422              element == EL_BALLOON_SWITCH_RIGHT ||
15423              element == EL_BALLOON_SWITCH_UP    ||
15424              element == EL_BALLOON_SWITCH_DOWN  ||
15425              element == EL_BALLOON_SWITCH_NONE  ||
15426              element == EL_BALLOON_SWITCH_ANY)
15427     {
15428       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15429                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15430                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15431                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15432                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15433                              move_direction);
15434     }
15435     else if (element == EL_LAMP)
15436     {
15437       Feld[x][y] = EL_LAMP_ACTIVE;
15438       local_player->lights_still_needed--;
15439
15440       ResetGfxAnimation(x, y);
15441       TEST_DrawLevelField(x, y);
15442     }
15443     else if (element == EL_TIME_ORB_FULL)
15444     {
15445       Feld[x][y] = EL_TIME_ORB_EMPTY;
15446
15447       if (level.time > 0 || level.use_time_orb_bug)
15448       {
15449         TimeLeft += level.time_orb_time;
15450
15451 #if 1
15452         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15453
15454         DisplayGameControlValues();
15455 #else
15456         DrawGameValue_Time(TimeLeft);
15457 #endif
15458       }
15459
15460       ResetGfxAnimation(x, y);
15461       TEST_DrawLevelField(x, y);
15462     }
15463     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15464              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15465     {
15466       int xx, yy;
15467
15468       game.ball_state = !game.ball_state;
15469
15470       SCAN_PLAYFIELD(xx, yy)
15471       {
15472         int e = Feld[xx][yy];
15473
15474         if (game.ball_state)
15475         {
15476           if (e == EL_EMC_MAGIC_BALL)
15477             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15478           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15479             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15480         }
15481         else
15482         {
15483           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15484             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15485           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15486             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15487         }
15488       }
15489     }
15490
15491     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15492                                         player->index_bit, dig_side);
15493
15494     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15495                                         player->index_bit, dig_side);
15496
15497     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15498                                         player->index_bit, dig_side);
15499
15500     return MP_ACTION;
15501   }
15502   else
15503   {
15504     if (!PLAYER_SWITCHING(player, x, y))
15505     {
15506       player->is_switching = TRUE;
15507       player->switch_x = x;
15508       player->switch_y = y;
15509
15510       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15511                                  player->index_bit, dig_side);
15512       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15513                                           player->index_bit, dig_side);
15514
15515       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15516                                  player->index_bit, dig_side);
15517       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15518                                           player->index_bit, dig_side);
15519     }
15520
15521     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15522                                player->index_bit, dig_side);
15523     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15524                                         player->index_bit, dig_side);
15525
15526     return MP_NO_ACTION;
15527   }
15528
15529   player->push_delay = -1;
15530
15531   if (is_player)                /* function can also be called by EL_PENGUIN */
15532   {
15533     if (Feld[x][y] != element)          /* really digged/collected something */
15534     {
15535       player->is_collecting = !player->is_digging;
15536       player->is_active = TRUE;
15537     }
15538   }
15539
15540   return MP_MOVING;
15541 }
15542
15543 static boolean DigFieldByCE(int x, int y, int digging_element)
15544 {
15545   int element = Feld[x][y];
15546
15547   if (!IS_FREE(x, y))
15548   {
15549     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15550                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15551                   ACTION_BREAKING);
15552
15553     /* no element can dig solid indestructible elements */
15554     if (IS_INDESTRUCTIBLE(element) &&
15555         !IS_DIGGABLE(element) &&
15556         !IS_COLLECTIBLE(element))
15557       return FALSE;
15558
15559     if (AmoebaNr[x][y] &&
15560         (element == EL_AMOEBA_FULL ||
15561          element == EL_BD_AMOEBA ||
15562          element == EL_AMOEBA_GROWING))
15563     {
15564       AmoebaCnt[AmoebaNr[x][y]]--;
15565       AmoebaCnt2[AmoebaNr[x][y]]--;
15566     }
15567
15568     if (IS_MOVING(x, y))
15569       RemoveMovingField(x, y);
15570     else
15571     {
15572       RemoveField(x, y);
15573       TEST_DrawLevelField(x, y);
15574     }
15575
15576     /* if digged element was about to explode, prevent the explosion */
15577     ExplodeField[x][y] = EX_TYPE_NONE;
15578
15579     PlayLevelSoundAction(x, y, action);
15580   }
15581
15582   Store[x][y] = EL_EMPTY;
15583
15584 #if 1
15585   /* this makes it possible to leave the removed element again */
15586   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15587     Store[x][y] = element;
15588 #else
15589   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15590   {
15591     int move_leave_element = element_info[digging_element].move_leave_element;
15592
15593     /* this makes it possible to leave the removed element again */
15594     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15595                    element : move_leave_element);
15596   }
15597 #endif
15598
15599   return TRUE;
15600 }
15601
15602 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15603 {
15604   int jx = player->jx, jy = player->jy;
15605   int x = jx + dx, y = jy + dy;
15606   int snap_direction = (dx == -1 ? MV_LEFT  :
15607                         dx == +1 ? MV_RIGHT :
15608                         dy == -1 ? MV_UP    :
15609                         dy == +1 ? MV_DOWN  : MV_NONE);
15610   boolean can_continue_snapping = (level.continuous_snapping &&
15611                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15612
15613   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15614     return FALSE;
15615
15616   if (!player->active || !IN_LEV_FIELD(x, y))
15617     return FALSE;
15618
15619   if (dx && dy)
15620     return FALSE;
15621
15622   if (!dx && !dy)
15623   {
15624     if (player->MovPos == 0)
15625       player->is_pushing = FALSE;
15626
15627     player->is_snapping = FALSE;
15628
15629     if (player->MovPos == 0)
15630     {
15631       player->is_moving = FALSE;
15632       player->is_digging = FALSE;
15633       player->is_collecting = FALSE;
15634     }
15635
15636     return FALSE;
15637   }
15638
15639 #if USE_NEW_CONTINUOUS_SNAPPING
15640   /* prevent snapping with already pressed snap key when not allowed */
15641   if (player->is_snapping && !can_continue_snapping)
15642     return FALSE;
15643 #else
15644   if (player->is_snapping)
15645     return FALSE;
15646 #endif
15647
15648   player->MovDir = snap_direction;
15649
15650   if (player->MovPos == 0)
15651   {
15652     player->is_moving = FALSE;
15653     player->is_digging = FALSE;
15654     player->is_collecting = FALSE;
15655   }
15656
15657   player->is_dropping = FALSE;
15658   player->is_dropping_pressed = FALSE;
15659   player->drop_pressed_delay = 0;
15660
15661   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15662     return FALSE;
15663
15664   player->is_snapping = TRUE;
15665   player->is_active = TRUE;
15666
15667   if (player->MovPos == 0)
15668   {
15669     player->is_moving = FALSE;
15670     player->is_digging = FALSE;
15671     player->is_collecting = FALSE;
15672   }
15673
15674   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15675     TEST_DrawLevelField(player->last_jx, player->last_jy);
15676
15677   TEST_DrawLevelField(x, y);
15678
15679   return TRUE;
15680 }
15681
15682 static boolean DropElement(struct PlayerInfo *player)
15683 {
15684   int old_element, new_element;
15685   int dropx = player->jx, dropy = player->jy;
15686   int drop_direction = player->MovDir;
15687   int drop_side = drop_direction;
15688 #if 1
15689   int drop_element = get_next_dropped_element(player);
15690 #else
15691   int drop_element = (player->inventory_size > 0 ?
15692                       player->inventory_element[player->inventory_size - 1] :
15693                       player->inventory_infinite_element != EL_UNDEFINED ?
15694                       player->inventory_infinite_element :
15695                       player->dynabombs_left > 0 ?
15696                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15697                       EL_UNDEFINED);
15698 #endif
15699
15700   player->is_dropping_pressed = TRUE;
15701
15702   /* do not drop an element on top of another element; when holding drop key
15703      pressed without moving, dropped element must move away before the next
15704      element can be dropped (this is especially important if the next element
15705      is dynamite, which can be placed on background for historical reasons) */
15706   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15707     return MP_ACTION;
15708
15709   if (IS_THROWABLE(drop_element))
15710   {
15711     dropx += GET_DX_FROM_DIR(drop_direction);
15712     dropy += GET_DY_FROM_DIR(drop_direction);
15713
15714     if (!IN_LEV_FIELD(dropx, dropy))
15715       return FALSE;
15716   }
15717
15718   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15719   new_element = drop_element;           /* default: no change when dropping */
15720
15721   /* check if player is active, not moving and ready to drop */
15722   if (!player->active || player->MovPos || player->drop_delay > 0)
15723     return FALSE;
15724
15725   /* check if player has anything that can be dropped */
15726   if (new_element == EL_UNDEFINED)
15727     return FALSE;
15728
15729   /* check if drop key was pressed long enough for EM style dynamite */
15730   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15731     return FALSE;
15732
15733   /* check if anything can be dropped at the current position */
15734   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15735     return FALSE;
15736
15737   /* collected custom elements can only be dropped on empty fields */
15738   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15739     return FALSE;
15740
15741   if (old_element != EL_EMPTY)
15742     Back[dropx][dropy] = old_element;   /* store old element on this field */
15743
15744   ResetGfxAnimation(dropx, dropy);
15745   ResetRandomAnimationValue(dropx, dropy);
15746
15747   if (player->inventory_size > 0 ||
15748       player->inventory_infinite_element != EL_UNDEFINED)
15749   {
15750     if (player->inventory_size > 0)
15751     {
15752       player->inventory_size--;
15753
15754       DrawGameDoorValues();
15755
15756       if (new_element == EL_DYNAMITE)
15757         new_element = EL_DYNAMITE_ACTIVE;
15758       else if (new_element == EL_EM_DYNAMITE)
15759         new_element = EL_EM_DYNAMITE_ACTIVE;
15760       else if (new_element == EL_SP_DISK_RED)
15761         new_element = EL_SP_DISK_RED_ACTIVE;
15762     }
15763
15764     Feld[dropx][dropy] = new_element;
15765
15766     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15767       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15768                           el2img(Feld[dropx][dropy]), 0);
15769
15770     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15771
15772     /* needed if previous element just changed to "empty" in the last frame */
15773     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15774
15775     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15776                                player->index_bit, drop_side);
15777     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15778                                         CE_PLAYER_DROPS_X,
15779                                         player->index_bit, drop_side);
15780
15781     TestIfElementTouchesCustomElement(dropx, dropy);
15782   }
15783   else          /* player is dropping a dyna bomb */
15784   {
15785     player->dynabombs_left--;
15786
15787     Feld[dropx][dropy] = new_element;
15788
15789     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15790       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15791                           el2img(Feld[dropx][dropy]), 0);
15792
15793     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15794   }
15795
15796   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15797     InitField_WithBug1(dropx, dropy, FALSE);
15798
15799   new_element = Feld[dropx][dropy];     /* element might have changed */
15800
15801   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15802       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15803   {
15804     int move_direction, nextx, nexty;
15805
15806     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15807       MovDir[dropx][dropy] = drop_direction;
15808
15809     move_direction = MovDir[dropx][dropy];
15810     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15811     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15812
15813     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15814
15815 #if USE_FIX_IMPACT_COLLISION
15816     /* do not cause impact style collision by dropping elements that can fall */
15817     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15818 #else
15819     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15820 #endif
15821   }
15822
15823   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15824   player->is_dropping = TRUE;
15825
15826   player->drop_pressed_delay = 0;
15827   player->is_dropping_pressed = FALSE;
15828
15829   player->drop_x = dropx;
15830   player->drop_y = dropy;
15831
15832   return TRUE;
15833 }
15834
15835 /* ------------------------------------------------------------------------- */
15836 /* game sound playing functions                                              */
15837 /* ------------------------------------------------------------------------- */
15838
15839 static int *loop_sound_frame = NULL;
15840 static int *loop_sound_volume = NULL;
15841
15842 void InitPlayLevelSound()
15843 {
15844   int num_sounds = getSoundListSize();
15845
15846   checked_free(loop_sound_frame);
15847   checked_free(loop_sound_volume);
15848
15849   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15850   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15851 }
15852
15853 static void PlayLevelSound(int x, int y, int nr)
15854 {
15855   int sx = SCREENX(x), sy = SCREENY(y);
15856   int volume, stereo_position;
15857   int max_distance = 8;
15858   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15859
15860   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15861       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15862     return;
15863
15864   if (!IN_LEV_FIELD(x, y) ||
15865       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15866       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15867     return;
15868
15869   volume = SOUND_MAX_VOLUME;
15870
15871   if (!IN_SCR_FIELD(sx, sy))
15872   {
15873     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15874     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15875
15876     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15877   }
15878
15879   stereo_position = (SOUND_MAX_LEFT +
15880                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15881                      (SCR_FIELDX + 2 * max_distance));
15882
15883   if (IS_LOOP_SOUND(nr))
15884   {
15885     /* This assures that quieter loop sounds do not overwrite louder ones,
15886        while restarting sound volume comparison with each new game frame. */
15887
15888     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15889       return;
15890
15891     loop_sound_volume[nr] = volume;
15892     loop_sound_frame[nr] = FrameCounter;
15893   }
15894
15895   PlaySoundExt(nr, volume, stereo_position, type);
15896 }
15897
15898 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15899 {
15900   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15901                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15902                  y < LEVELY(BY1) ? LEVELY(BY1) :
15903                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15904                  sound_action);
15905 }
15906
15907 static void PlayLevelSoundAction(int x, int y, int action)
15908 {
15909   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15910 }
15911
15912 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15913 {
15914   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15915
15916   if (sound_effect != SND_UNDEFINED)
15917     PlayLevelSound(x, y, sound_effect);
15918 }
15919
15920 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15921                                               int action)
15922 {
15923   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15924
15925   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15926     PlayLevelSound(x, y, sound_effect);
15927 }
15928
15929 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15930 {
15931   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15932
15933   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15934     PlayLevelSound(x, y, sound_effect);
15935 }
15936
15937 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15938 {
15939   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15940
15941   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15942     StopSound(sound_effect);
15943 }
15944
15945 static void PlayLevelMusic()
15946 {
15947   if (levelset.music[level_nr] != MUS_UNDEFINED)
15948     PlayMusic(levelset.music[level_nr]);        /* from config file */
15949   else
15950     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15951 }
15952
15953 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15954 {
15955   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15956   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15957   int x = xx - 1 - offset;
15958   int y = yy - 1 - offset;
15959
15960   switch (sample)
15961   {
15962     case SAMPLE_blank:
15963       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15964       break;
15965
15966     case SAMPLE_roll:
15967       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15968       break;
15969
15970     case SAMPLE_stone:
15971       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15972       break;
15973
15974     case SAMPLE_nut:
15975       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15976       break;
15977
15978     case SAMPLE_crack:
15979       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15980       break;
15981
15982     case SAMPLE_bug:
15983       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15984       break;
15985
15986     case SAMPLE_tank:
15987       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15988       break;
15989
15990     case SAMPLE_android_clone:
15991       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15992       break;
15993
15994     case SAMPLE_android_move:
15995       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15996       break;
15997
15998     case SAMPLE_spring:
15999       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16000       break;
16001
16002     case SAMPLE_slurp:
16003       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16004       break;
16005
16006     case SAMPLE_eater:
16007       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16008       break;
16009
16010     case SAMPLE_eater_eat:
16011       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16012       break;
16013
16014     case SAMPLE_alien:
16015       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16016       break;
16017
16018     case SAMPLE_collect:
16019       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16020       break;
16021
16022     case SAMPLE_diamond:
16023       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16024       break;
16025
16026     case SAMPLE_squash:
16027       /* !!! CHECK THIS !!! */
16028 #if 1
16029       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16030 #else
16031       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16032 #endif
16033       break;
16034
16035     case SAMPLE_wonderfall:
16036       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16037       break;
16038
16039     case SAMPLE_drip:
16040       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16041       break;
16042
16043     case SAMPLE_push:
16044       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16045       break;
16046
16047     case SAMPLE_dirt:
16048       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16049       break;
16050
16051     case SAMPLE_acid:
16052       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16053       break;
16054
16055     case SAMPLE_ball:
16056       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16057       break;
16058
16059     case SAMPLE_grow:
16060       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16061       break;
16062
16063     case SAMPLE_wonder:
16064       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16065       break;
16066
16067     case SAMPLE_door:
16068       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16069       break;
16070
16071     case SAMPLE_exit_open:
16072       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16073       break;
16074
16075     case SAMPLE_exit_leave:
16076       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16077       break;
16078
16079     case SAMPLE_dynamite:
16080       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16081       break;
16082
16083     case SAMPLE_tick:
16084       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16085       break;
16086
16087     case SAMPLE_press:
16088       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16089       break;
16090
16091     case SAMPLE_wheel:
16092       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16093       break;
16094
16095     case SAMPLE_boom:
16096       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16097       break;
16098
16099     case SAMPLE_die:
16100       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16101       break;
16102
16103     case SAMPLE_time:
16104       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16105       break;
16106
16107     default:
16108       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16109       break;
16110   }
16111 }
16112
16113 #if 0
16114 void ChangeTime(int value)
16115 {
16116   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16117
16118   *time += value;
16119
16120   /* EMC game engine uses value from time counter of RND game engine */
16121   level.native_em_level->lev->time = *time;
16122
16123   DrawGameValue_Time(*time);
16124 }
16125
16126 void RaiseScore(int value)
16127 {
16128   /* EMC game engine and RND game engine have separate score counters */
16129   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16130                 &level.native_em_level->lev->score : &local_player->score);
16131
16132   *score += value;
16133
16134   DrawGameValue_Score(*score);
16135 }
16136 #endif
16137
16138 void RaiseScore(int value)
16139 {
16140   local_player->score += value;
16141
16142 #if 1
16143   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16144
16145   DisplayGameControlValues();
16146 #else
16147   DrawGameValue_Score(local_player->score);
16148 #endif
16149 }
16150
16151 void RaiseScoreElement(int element)
16152 {
16153   switch (element)
16154   {
16155     case EL_EMERALD:
16156     case EL_BD_DIAMOND:
16157     case EL_EMERALD_YELLOW:
16158     case EL_EMERALD_RED:
16159     case EL_EMERALD_PURPLE:
16160     case EL_SP_INFOTRON:
16161       RaiseScore(level.score[SC_EMERALD]);
16162       break;
16163     case EL_DIAMOND:
16164       RaiseScore(level.score[SC_DIAMOND]);
16165       break;
16166     case EL_CRYSTAL:
16167       RaiseScore(level.score[SC_CRYSTAL]);
16168       break;
16169     case EL_PEARL:
16170       RaiseScore(level.score[SC_PEARL]);
16171       break;
16172     case EL_BUG:
16173     case EL_BD_BUTTERFLY:
16174     case EL_SP_ELECTRON:
16175       RaiseScore(level.score[SC_BUG]);
16176       break;
16177     case EL_SPACESHIP:
16178     case EL_BD_FIREFLY:
16179     case EL_SP_SNIKSNAK:
16180       RaiseScore(level.score[SC_SPACESHIP]);
16181       break;
16182     case EL_YAMYAM:
16183     case EL_DARK_YAMYAM:
16184       RaiseScore(level.score[SC_YAMYAM]);
16185       break;
16186     case EL_ROBOT:
16187       RaiseScore(level.score[SC_ROBOT]);
16188       break;
16189     case EL_PACMAN:
16190       RaiseScore(level.score[SC_PACMAN]);
16191       break;
16192     case EL_NUT:
16193       RaiseScore(level.score[SC_NUT]);
16194       break;
16195     case EL_DYNAMITE:
16196     case EL_EM_DYNAMITE:
16197     case EL_SP_DISK_RED:
16198     case EL_DYNABOMB_INCREASE_NUMBER:
16199     case EL_DYNABOMB_INCREASE_SIZE:
16200     case EL_DYNABOMB_INCREASE_POWER:
16201       RaiseScore(level.score[SC_DYNAMITE]);
16202       break;
16203     case EL_SHIELD_NORMAL:
16204     case EL_SHIELD_DEADLY:
16205       RaiseScore(level.score[SC_SHIELD]);
16206       break;
16207     case EL_EXTRA_TIME:
16208       RaiseScore(level.extra_time_score);
16209       break;
16210     case EL_KEY_1:
16211     case EL_KEY_2:
16212     case EL_KEY_3:
16213     case EL_KEY_4:
16214     case EL_EM_KEY_1:
16215     case EL_EM_KEY_2:
16216     case EL_EM_KEY_3:
16217     case EL_EM_KEY_4:
16218     case EL_EMC_KEY_5:
16219     case EL_EMC_KEY_6:
16220     case EL_EMC_KEY_7:
16221     case EL_EMC_KEY_8:
16222     case EL_DC_KEY_WHITE:
16223       RaiseScore(level.score[SC_KEY]);
16224       break;
16225     default:
16226       RaiseScore(element_info[element].collect_score);
16227       break;
16228   }
16229 }
16230
16231 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16232 {
16233   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16234   {
16235 #if defined(NETWORK_AVALIABLE)
16236     if (options.network)
16237       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16238     else
16239 #endif
16240     {
16241       if (quick_quit)
16242       {
16243 #if 1
16244
16245 #if 1
16246         FadeSkipNextFadeIn();
16247 #else
16248         fading = fading_none;
16249 #endif
16250
16251 #else
16252         OpenDoor(DOOR_CLOSE_1);
16253 #endif
16254
16255         game_status = GAME_MODE_MAIN;
16256
16257 #if 1
16258         DrawAndFadeInMainMenu(REDRAW_FIELD);
16259 #else
16260         DrawMainMenu();
16261 #endif
16262       }
16263       else
16264       {
16265 #if 0
16266         FadeOut(REDRAW_FIELD);
16267 #endif
16268
16269         game_status = GAME_MODE_MAIN;
16270
16271         DrawAndFadeInMainMenu(REDRAW_FIELD);
16272       }
16273     }
16274   }
16275   else          /* continue playing the game */
16276   {
16277     if (tape.playing && tape.deactivate_display)
16278       TapeDeactivateDisplayOff(TRUE);
16279
16280     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16281
16282     if (tape.playing && tape.deactivate_display)
16283       TapeDeactivateDisplayOn();
16284   }
16285 }
16286
16287 void RequestQuitGame(boolean ask_if_really_quit)
16288 {
16289   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16290   boolean skip_request = AllPlayersGone || quick_quit;
16291
16292   RequestQuitGameExt(skip_request, quick_quit,
16293                      "Do you really want to quit the game ?");
16294 }
16295
16296
16297 /* ------------------------------------------------------------------------- */
16298 /* random generator functions                                                */
16299 /* ------------------------------------------------------------------------- */
16300
16301 unsigned int InitEngineRandom_RND(long seed)
16302 {
16303   game.num_random_calls = 0;
16304
16305 #if 0
16306   unsigned int rnd_seed = InitEngineRandom(seed);
16307
16308   printf("::: START RND: %d\n", rnd_seed);
16309
16310   return rnd_seed;
16311 #else
16312
16313   return InitEngineRandom(seed);
16314
16315 #endif
16316
16317 }
16318
16319 unsigned int RND(int max)
16320 {
16321   if (max > 0)
16322   {
16323     game.num_random_calls++;
16324
16325     return GetEngineRandom(max);
16326   }
16327
16328   return 0;
16329 }
16330
16331
16332 /* ------------------------------------------------------------------------- */
16333 /* game engine snapshot handling functions                                   */
16334 /* ------------------------------------------------------------------------- */
16335
16336 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
16337
16338 struct EngineSnapshotInfo
16339 {
16340   /* runtime values for custom element collect score */
16341   int collect_score[NUM_CUSTOM_ELEMENTS];
16342
16343   /* runtime values for group element choice position */
16344   int choice_pos[NUM_GROUP_ELEMENTS];
16345
16346   /* runtime values for belt position animations */
16347   int belt_graphic[4 * NUM_BELT_PARTS];
16348   int belt_anim_mode[4 * NUM_BELT_PARTS];
16349 };
16350
16351 struct EngineSnapshotNodeInfo
16352 {
16353   void *buffer_orig;
16354   void *buffer_copy;
16355   int size;
16356 };
16357
16358 static struct EngineSnapshotInfo engine_snapshot_rnd;
16359 static ListNode *engine_snapshot_list = NULL;
16360 static char *snapshot_level_identifier = NULL;
16361 static int snapshot_level_nr = -1;
16362
16363 void FreeEngineSnapshot()
16364 {
16365   while (engine_snapshot_list != NULL)
16366     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16367                        checked_free);
16368
16369   setString(&snapshot_level_identifier, NULL);
16370   snapshot_level_nr = -1;
16371 }
16372
16373 static void SaveEngineSnapshotValues_RND()
16374 {
16375   static int belt_base_active_element[4] =
16376   {
16377     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16378     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16379     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16380     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16381   };
16382   int i, j;
16383
16384   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16385   {
16386     int element = EL_CUSTOM_START + i;
16387
16388     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16389   }
16390
16391   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16392   {
16393     int element = EL_GROUP_START + i;
16394
16395     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16396   }
16397
16398   for (i = 0; i < 4; i++)
16399   {
16400     for (j = 0; j < NUM_BELT_PARTS; j++)
16401     {
16402       int element = belt_base_active_element[i] + j;
16403       int graphic = el2img(element);
16404       int anim_mode = graphic_info[graphic].anim_mode;
16405
16406       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16407       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16408     }
16409   }
16410 }
16411
16412 static void LoadEngineSnapshotValues_RND()
16413 {
16414   unsigned long num_random_calls = game.num_random_calls;
16415   int i, j;
16416
16417   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16418   {
16419     int element = EL_CUSTOM_START + i;
16420
16421     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16422   }
16423
16424   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16425   {
16426     int element = EL_GROUP_START + i;
16427
16428     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16429   }
16430
16431   for (i = 0; i < 4; i++)
16432   {
16433     for (j = 0; j < NUM_BELT_PARTS; j++)
16434     {
16435       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16436       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16437
16438       graphic_info[graphic].anim_mode = anim_mode;
16439     }
16440   }
16441
16442   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16443   {
16444     InitRND(tape.random_seed);
16445     for (i = 0; i < num_random_calls; i++)
16446       RND(1);
16447   }
16448
16449   if (game.num_random_calls != num_random_calls)
16450   {
16451     Error(ERR_INFO, "number of random calls out of sync");
16452     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16453     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16454     Error(ERR_EXIT, "this should not happen -- please debug");
16455   }
16456 }
16457
16458 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16459 {
16460   struct EngineSnapshotNodeInfo *bi =
16461     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16462
16463   bi->buffer_orig = buffer;
16464   bi->buffer_copy = checked_malloc(size);
16465   bi->size = size;
16466
16467   memcpy(bi->buffer_copy, buffer, size);
16468
16469   addNodeToList(&engine_snapshot_list, NULL, bi);
16470 }
16471
16472 void SaveEngineSnapshot()
16473 {
16474   FreeEngineSnapshot();         /* free previous snapshot, if needed */
16475
16476   if (level_editor_test_game)   /* do not save snapshots from editor */
16477     return;
16478
16479   /* copy some special values to a structure better suited for the snapshot */
16480
16481   SaveEngineSnapshotValues_RND();
16482   SaveEngineSnapshotValues_EM();
16483
16484   /* save values stored in special snapshot structure */
16485
16486   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16487   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16488
16489   /* save further RND engine values */
16490
16491   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16492   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16493   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16494
16495   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16496   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16497   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16498   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16499
16500   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16501   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16502   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16503   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16504   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16505
16506   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16507   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16508   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16509
16510   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16511
16512   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16513
16514   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16515   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16516
16517   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16518   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16519   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16520   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16521   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16522   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16523   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16524   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16525   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16526   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16527   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16528   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16529   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16530   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16531   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16532   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16533   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16534   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16535
16536   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16537   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16538
16539   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16540   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16541   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16542
16543   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16544   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16545
16546   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16547   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16548   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16549   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16550   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16551
16552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16553   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16554
16555   /* save level identification information */
16556
16557   setString(&snapshot_level_identifier, leveldir_current->identifier);
16558   snapshot_level_nr = level_nr;
16559
16560 #if 0
16561   ListNode *node = engine_snapshot_list;
16562   int num_bytes = 0;
16563
16564   while (node != NULL)
16565   {
16566     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16567
16568     node = node->next;
16569   }
16570
16571   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16572 #endif
16573 }
16574
16575 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16576 {
16577   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16578 }
16579
16580 void LoadEngineSnapshot()
16581 {
16582   ListNode *node = engine_snapshot_list;
16583
16584   if (engine_snapshot_list == NULL)
16585     return;
16586
16587   while (node != NULL)
16588   {
16589     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16590
16591     node = node->next;
16592   }
16593
16594   /* restore special values from snapshot structure */
16595
16596   LoadEngineSnapshotValues_RND();
16597   LoadEngineSnapshotValues_EM();
16598 }
16599
16600 boolean CheckEngineSnapshot()
16601 {
16602   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16603           snapshot_level_nr == level_nr);
16604 }
16605
16606
16607 /* ---------- new game button stuff ---------------------------------------- */
16608
16609 /* graphic position values for game buttons */
16610 #define GAME_BUTTON_XSIZE       30
16611 #define GAME_BUTTON_YSIZE       30
16612 #define GAME_BUTTON_XPOS        5
16613 #define GAME_BUTTON_YPOS        215
16614 #define SOUND_BUTTON_XPOS       5
16615 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16616
16617 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16618 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16619 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16620 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16621 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16622 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16623
16624 static struct
16625 {
16626   int *x, *y;
16627   int gd_x, gd_y;
16628   int gadget_id;
16629   char *infotext;
16630 } gamebutton_info[NUM_GAME_BUTTONS] =
16631 {
16632 #if 1
16633   {
16634     &game.button.stop.x,        &game.button.stop.y,
16635     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16636     GAME_CTRL_ID_STOP,
16637     "stop game"
16638   },
16639   {
16640     &game.button.pause.x,       &game.button.pause.y,
16641     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16642     GAME_CTRL_ID_PAUSE,
16643     "pause game"
16644   },
16645   {
16646     &game.button.play.x,        &game.button.play.y,
16647     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16648     GAME_CTRL_ID_PLAY,
16649     "play game"
16650   },
16651   {
16652     &game.button.sound_music.x, &game.button.sound_music.y,
16653     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16654     SOUND_CTRL_ID_MUSIC,
16655     "background music on/off"
16656   },
16657   {
16658     &game.button.sound_loops.x, &game.button.sound_loops.y,
16659     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16660     SOUND_CTRL_ID_LOOPS,
16661     "sound loops on/off"
16662   },
16663   {
16664     &game.button.sound_simple.x,&game.button.sound_simple.y,
16665     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16666     SOUND_CTRL_ID_SIMPLE,
16667     "normal sounds on/off"
16668   }
16669 #else
16670   {
16671     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16672     GAME_CTRL_ID_STOP,
16673     "stop game"
16674   },
16675   {
16676     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16677     GAME_CTRL_ID_PAUSE,
16678     "pause game"
16679   },
16680   {
16681     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16682     GAME_CTRL_ID_PLAY,
16683     "play game"
16684   },
16685   {
16686     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16687     SOUND_CTRL_ID_MUSIC,
16688     "background music on/off"
16689   },
16690   {
16691     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16692     SOUND_CTRL_ID_LOOPS,
16693     "sound loops on/off"
16694   },
16695   {
16696     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16697     SOUND_CTRL_ID_SIMPLE,
16698     "normal sounds on/off"
16699   }
16700 #endif
16701 };
16702
16703 void CreateGameButtons()
16704 {
16705   int i;
16706
16707   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16708   {
16709     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16710     struct GadgetInfo *gi;
16711     int button_type;
16712     boolean checked;
16713     unsigned long event_mask;
16714     int x, y;
16715     int gd_xoffset, gd_yoffset;
16716     int gd_x1, gd_x2, gd_y1, gd_y2;
16717     int id = i;
16718
16719     x = DX + *gamebutton_info[i].x;
16720     y = DY + *gamebutton_info[i].y;
16721     gd_xoffset = gamebutton_info[i].gd_x;
16722     gd_yoffset = gamebutton_info[i].gd_y;
16723     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16724     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16725
16726     if (id == GAME_CTRL_ID_STOP ||
16727         id == GAME_CTRL_ID_PAUSE ||
16728         id == GAME_CTRL_ID_PLAY)
16729     {
16730       button_type = GD_TYPE_NORMAL_BUTTON;
16731       checked = FALSE;
16732       event_mask = GD_EVENT_RELEASED;
16733       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16734       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16735     }
16736     else
16737     {
16738       button_type = GD_TYPE_CHECK_BUTTON;
16739       checked =
16740         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16741          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16742          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16743       event_mask = GD_EVENT_PRESSED;
16744       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16745       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16746     }
16747
16748     gi = CreateGadget(GDI_CUSTOM_ID, id,
16749                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16750 #if 1
16751                       GDI_X, x,
16752                       GDI_Y, y,
16753 #else
16754                       GDI_X, DX + gd_xoffset,
16755                       GDI_Y, DY + gd_yoffset,
16756 #endif
16757                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16758                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16759                       GDI_TYPE, button_type,
16760                       GDI_STATE, GD_BUTTON_UNPRESSED,
16761                       GDI_CHECKED, checked,
16762                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16763                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16764                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16765                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16766                       GDI_DIRECT_DRAW, FALSE,
16767                       GDI_EVENT_MASK, event_mask,
16768                       GDI_CALLBACK_ACTION, HandleGameButtons,
16769                       GDI_END);
16770
16771     if (gi == NULL)
16772       Error(ERR_EXIT, "cannot create gadget");
16773
16774     game_gadget[id] = gi;
16775   }
16776 }
16777
16778 void FreeGameButtons()
16779 {
16780   int i;
16781
16782   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16783     FreeGadget(game_gadget[i]);
16784 }
16785
16786 static void MapGameButtons()
16787 {
16788   int i;
16789
16790   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16791     MapGadget(game_gadget[i]);
16792 }
16793
16794 void UnmapGameButtons()
16795 {
16796   int i;
16797
16798   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16799     UnmapGadget(game_gadget[i]);
16800 }
16801
16802 void RedrawGameButtons()
16803 {
16804   int i;
16805
16806   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16807     RedrawGadget(game_gadget[i]);
16808 }
16809
16810 static void HandleGameButtons(struct GadgetInfo *gi)
16811 {
16812   int id = gi->custom_id;
16813
16814   if (game_status != GAME_MODE_PLAYING)
16815     return;
16816
16817   switch (id)
16818   {
16819     case GAME_CTRL_ID_STOP:
16820       if (tape.playing)
16821         TapeStop();
16822       else
16823         RequestQuitGame(TRUE);
16824       break;
16825
16826     case GAME_CTRL_ID_PAUSE:
16827       if (options.network)
16828       {
16829 #if defined(NETWORK_AVALIABLE)
16830         if (tape.pausing)
16831           SendToServer_ContinuePlaying();
16832         else
16833           SendToServer_PausePlaying();
16834 #endif
16835       }
16836       else
16837         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16838       break;
16839
16840     case GAME_CTRL_ID_PLAY:
16841       if (tape.pausing)
16842       {
16843 #if defined(NETWORK_AVALIABLE)
16844         if (options.network)
16845           SendToServer_ContinuePlaying();
16846         else
16847 #endif
16848         {
16849           tape.pausing = FALSE;
16850           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16851         }
16852       }
16853       break;
16854
16855     case SOUND_CTRL_ID_MUSIC:
16856       if (setup.sound_music)
16857       { 
16858         setup.sound_music = FALSE;
16859         FadeMusic();
16860       }
16861       else if (audio.music_available)
16862       { 
16863         setup.sound = setup.sound_music = TRUE;
16864
16865         SetAudioMode(setup.sound);
16866
16867         PlayLevelMusic();
16868       }
16869       break;
16870
16871     case SOUND_CTRL_ID_LOOPS:
16872       if (setup.sound_loops)
16873         setup.sound_loops = FALSE;
16874       else if (audio.loops_available)
16875       {
16876         setup.sound = setup.sound_loops = TRUE;
16877         SetAudioMode(setup.sound);
16878       }
16879       break;
16880
16881     case SOUND_CTRL_ID_SIMPLE:
16882       if (setup.sound_simple)
16883         setup.sound_simple = FALSE;
16884       else if (audio.sound_available)
16885       {
16886         setup.sound = setup.sound_simple = TRUE;
16887         SetAudioMode(setup.sound);
16888       }
16889       break;
16890
16891     default:
16892       break;
16893   }
16894 }