rnd-20121014-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_DrawLevelFieldCrumbled(x, y)                       \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(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_DrawLevelFieldCrumbled(x, y)                       \
85              DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
87              DrawLevelFieldCrumbledNeighbours(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_FRAME                        35
198 #define GAME_PANEL_SHIELD_NORMAL                36
199 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
200 #define GAME_PANEL_SHIELD_DEADLY                38
201 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
202 #define GAME_PANEL_EXIT                         40
203 #define GAME_PANEL_EMC_MAGIC_BALL               41
204 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
205 #define GAME_PANEL_LIGHT_SWITCH                 43
206 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
207 #define GAME_PANEL_TIMEGATE_SWITCH              45
208 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
209 #define GAME_PANEL_SWITCHGATE_SWITCH            47
210 #define GAME_PANEL_EMC_LENSES                   48
211 #define GAME_PANEL_EMC_LENSES_TIME              49
212 #define GAME_PANEL_EMC_MAGNIFIER                50
213 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
214 #define GAME_PANEL_BALLOON_SWITCH               52
215 #define GAME_PANEL_DYNABOMB_NUMBER              53
216 #define GAME_PANEL_DYNABOMB_SIZE                54
217 #define GAME_PANEL_DYNABOMB_POWER               55
218 #define GAME_PANEL_PENGUINS                     56
219 #define GAME_PANEL_SOKOBAN_OBJECTS              57
220 #define GAME_PANEL_SOKOBAN_FIELDS               58
221 #define GAME_PANEL_ROBOT_WHEEL                  59
222 #define GAME_PANEL_CONVEYOR_BELT_1              60
223 #define GAME_PANEL_CONVEYOR_BELT_2              61
224 #define GAME_PANEL_CONVEYOR_BELT_3              62
225 #define GAME_PANEL_CONVEYOR_BELT_4              63
226 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
227 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
228 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
229 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
230 #define GAME_PANEL_MAGIC_WALL                   68
231 #define GAME_PANEL_MAGIC_WALL_TIME              69
232 #define GAME_PANEL_GRAVITY_STATE                70
233 #define GAME_PANEL_GRAPHIC_1                    71
234 #define GAME_PANEL_GRAPHIC_2                    72
235 #define GAME_PANEL_GRAPHIC_3                    73
236 #define GAME_PANEL_GRAPHIC_4                    74
237 #define GAME_PANEL_GRAPHIC_5                    75
238 #define GAME_PANEL_GRAPHIC_6                    76
239 #define GAME_PANEL_GRAPHIC_7                    77
240 #define GAME_PANEL_GRAPHIC_8                    78
241 #define GAME_PANEL_ELEMENT_1                    79
242 #define GAME_PANEL_ELEMENT_2                    80
243 #define GAME_PANEL_ELEMENT_3                    81
244 #define GAME_PANEL_ELEMENT_4                    82
245 #define GAME_PANEL_ELEMENT_5                    83
246 #define GAME_PANEL_ELEMENT_6                    84
247 #define GAME_PANEL_ELEMENT_7                    85
248 #define GAME_PANEL_ELEMENT_8                    86
249 #define GAME_PANEL_ELEMENT_COUNT_1              87
250 #define GAME_PANEL_ELEMENT_COUNT_2              88
251 #define GAME_PANEL_ELEMENT_COUNT_3              89
252 #define GAME_PANEL_ELEMENT_COUNT_4              90
253 #define GAME_PANEL_ELEMENT_COUNT_5              91
254 #define GAME_PANEL_ELEMENT_COUNT_6              92
255 #define GAME_PANEL_ELEMENT_COUNT_7              93
256 #define GAME_PANEL_ELEMENT_COUNT_8              94
257 #define GAME_PANEL_CE_SCORE_1                   95
258 #define GAME_PANEL_CE_SCORE_2                   96
259 #define GAME_PANEL_CE_SCORE_3                   97
260 #define GAME_PANEL_CE_SCORE_4                   98
261 #define GAME_PANEL_CE_SCORE_5                   99
262 #define GAME_PANEL_CE_SCORE_6                   100
263 #define GAME_PANEL_CE_SCORE_7                   101
264 #define GAME_PANEL_CE_SCORE_8                   102
265 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
266 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
267 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
268 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
269 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
270 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
271 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
272 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
273 #define GAME_PANEL_PLAYER_NAME                  111
274 #define GAME_PANEL_LEVEL_NAME                   112
275 #define GAME_PANEL_LEVEL_AUTHOR                 113
276
277 #define NUM_GAME_PANEL_CONTROLS                 114
278
279 struct GamePanelOrderInfo
280 {
281   int nr;
282   int sort_priority;
283 };
284
285 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
286
287 struct GamePanelControlInfo
288 {
289   int nr;
290
291   struct TextPosInfo *pos;
292   int type;
293
294   int value, last_value;
295   int frame, last_frame;
296   int gfx_frame;
297   int gfx_random;
298 };
299
300 static struct GamePanelControlInfo game_panel_controls[] =
301 {
302   {
303     GAME_PANEL_LEVEL_NUMBER,
304     &game.panel.level_number,
305     TYPE_INTEGER,
306   },
307   {
308     GAME_PANEL_GEMS,
309     &game.panel.gems,
310     TYPE_INTEGER,
311   },
312   {
313     GAME_PANEL_INVENTORY_COUNT,
314     &game.panel.inventory_count,
315     TYPE_INTEGER,
316   },
317   {
318     GAME_PANEL_INVENTORY_FIRST_1,
319     &game.panel.inventory_first[0],
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_PANEL_INVENTORY_FIRST_2,
324     &game.panel.inventory_first[1],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_PANEL_INVENTORY_FIRST_3,
329     &game.panel.inventory_first[2],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_PANEL_INVENTORY_FIRST_4,
334     &game.panel.inventory_first[3],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_PANEL_INVENTORY_FIRST_5,
339     &game.panel.inventory_first[4],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_PANEL_INVENTORY_FIRST_6,
344     &game.panel.inventory_first[5],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_PANEL_INVENTORY_FIRST_7,
349     &game.panel.inventory_first[6],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_PANEL_INVENTORY_FIRST_8,
354     &game.panel.inventory_first[7],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_PANEL_INVENTORY_LAST_1,
359     &game.panel.inventory_last[0],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_PANEL_INVENTORY_LAST_2,
364     &game.panel.inventory_last[1],
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_PANEL_INVENTORY_LAST_3,
369     &game.panel.inventory_last[2],
370     TYPE_ELEMENT,
371   },
372   {
373     GAME_PANEL_INVENTORY_LAST_4,
374     &game.panel.inventory_last[3],
375     TYPE_ELEMENT,
376   },
377   {
378     GAME_PANEL_INVENTORY_LAST_5,
379     &game.panel.inventory_last[4],
380     TYPE_ELEMENT,
381   },
382   {
383     GAME_PANEL_INVENTORY_LAST_6,
384     &game.panel.inventory_last[5],
385     TYPE_ELEMENT,
386   },
387   {
388     GAME_PANEL_INVENTORY_LAST_7,
389     &game.panel.inventory_last[6],
390     TYPE_ELEMENT,
391   },
392   {
393     GAME_PANEL_INVENTORY_LAST_8,
394     &game.panel.inventory_last[7],
395     TYPE_ELEMENT,
396   },
397   {
398     GAME_PANEL_KEY_1,
399     &game.panel.key[0],
400     TYPE_ELEMENT,
401   },
402   {
403     GAME_PANEL_KEY_2,
404     &game.panel.key[1],
405     TYPE_ELEMENT,
406   },
407   {
408     GAME_PANEL_KEY_3,
409     &game.panel.key[2],
410     TYPE_ELEMENT,
411   },
412   {
413     GAME_PANEL_KEY_4,
414     &game.panel.key[3],
415     TYPE_ELEMENT,
416   },
417   {
418     GAME_PANEL_KEY_5,
419     &game.panel.key[4],
420     TYPE_ELEMENT,
421   },
422   {
423     GAME_PANEL_KEY_6,
424     &game.panel.key[5],
425     TYPE_ELEMENT,
426   },
427   {
428     GAME_PANEL_KEY_7,
429     &game.panel.key[6],
430     TYPE_ELEMENT,
431   },
432   {
433     GAME_PANEL_KEY_8,
434     &game.panel.key[7],
435     TYPE_ELEMENT,
436   },
437   {
438     GAME_PANEL_KEY_WHITE,
439     &game.panel.key_white,
440     TYPE_ELEMENT,
441   },
442   {
443     GAME_PANEL_KEY_WHITE_COUNT,
444     &game.panel.key_white_count,
445     TYPE_INTEGER,
446   },
447   {
448     GAME_PANEL_SCORE,
449     &game.panel.score,
450     TYPE_INTEGER,
451   },
452   {
453     GAME_PANEL_HIGHSCORE,
454     &game.panel.highscore,
455     TYPE_INTEGER,
456   },
457   {
458     GAME_PANEL_TIME,
459     &game.panel.time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_PANEL_TIME_HH,
464     &game.panel.time_hh,
465     TYPE_INTEGER,
466   },
467   {
468     GAME_PANEL_TIME_MM,
469     &game.panel.time_mm,
470     TYPE_INTEGER,
471   },
472   {
473     GAME_PANEL_TIME_SS,
474     &game.panel.time_ss,
475     TYPE_INTEGER,
476   },
477   {
478     GAME_PANEL_FRAME,
479     &game.panel.frame,
480     TYPE_INTEGER,
481   },
482   {
483     GAME_PANEL_SHIELD_NORMAL,
484     &game.panel.shield_normal,
485     TYPE_ELEMENT,
486   },
487   {
488     GAME_PANEL_SHIELD_NORMAL_TIME,
489     &game.panel.shield_normal_time,
490     TYPE_INTEGER,
491   },
492   {
493     GAME_PANEL_SHIELD_DEADLY,
494     &game.panel.shield_deadly,
495     TYPE_ELEMENT,
496   },
497   {
498     GAME_PANEL_SHIELD_DEADLY_TIME,
499     &game.panel.shield_deadly_time,
500     TYPE_INTEGER,
501   },
502   {
503     GAME_PANEL_EXIT,
504     &game.panel.exit,
505     TYPE_ELEMENT,
506   },
507   {
508     GAME_PANEL_EMC_MAGIC_BALL,
509     &game.panel.emc_magic_ball,
510     TYPE_ELEMENT,
511   },
512   {
513     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
514     &game.panel.emc_magic_ball_switch,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_PANEL_LIGHT_SWITCH,
519     &game.panel.light_switch,
520     TYPE_ELEMENT,
521   },
522   {
523     GAME_PANEL_LIGHT_SWITCH_TIME,
524     &game.panel.light_switch_time,
525     TYPE_INTEGER,
526   },
527   {
528     GAME_PANEL_TIMEGATE_SWITCH,
529     &game.panel.timegate_switch,
530     TYPE_ELEMENT,
531   },
532   {
533     GAME_PANEL_TIMEGATE_SWITCH_TIME,
534     &game.panel.timegate_switch_time,
535     TYPE_INTEGER,
536   },
537   {
538     GAME_PANEL_SWITCHGATE_SWITCH,
539     &game.panel.switchgate_switch,
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_PANEL_EMC_LENSES,
544     &game.panel.emc_lenses,
545     TYPE_ELEMENT,
546   },
547   {
548     GAME_PANEL_EMC_LENSES_TIME,
549     &game.panel.emc_lenses_time,
550     TYPE_INTEGER,
551   },
552   {
553     GAME_PANEL_EMC_MAGNIFIER,
554     &game.panel.emc_magnifier,
555     TYPE_ELEMENT,
556   },
557   {
558     GAME_PANEL_EMC_MAGNIFIER_TIME,
559     &game.panel.emc_magnifier_time,
560     TYPE_INTEGER,
561   },
562   {
563     GAME_PANEL_BALLOON_SWITCH,
564     &game.panel.balloon_switch,
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_PANEL_DYNABOMB_NUMBER,
569     &game.panel.dynabomb_number,
570     TYPE_INTEGER,
571   },
572   {
573     GAME_PANEL_DYNABOMB_SIZE,
574     &game.panel.dynabomb_size,
575     TYPE_INTEGER,
576   },
577   {
578     GAME_PANEL_DYNABOMB_POWER,
579     &game.panel.dynabomb_power,
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_PANEL_PENGUINS,
584     &game.panel.penguins,
585     TYPE_INTEGER,
586   },
587   {
588     GAME_PANEL_SOKOBAN_OBJECTS,
589     &game.panel.sokoban_objects,
590     TYPE_INTEGER,
591   },
592   {
593     GAME_PANEL_SOKOBAN_FIELDS,
594     &game.panel.sokoban_fields,
595     TYPE_INTEGER,
596   },
597   {
598     GAME_PANEL_ROBOT_WHEEL,
599     &game.panel.robot_wheel,
600     TYPE_ELEMENT,
601   },
602   {
603     GAME_PANEL_CONVEYOR_BELT_1,
604     &game.panel.conveyor_belt[0],
605     TYPE_ELEMENT,
606   },
607   {
608     GAME_PANEL_CONVEYOR_BELT_2,
609     &game.panel.conveyor_belt[1],
610     TYPE_ELEMENT,
611   },
612   {
613     GAME_PANEL_CONVEYOR_BELT_3,
614     &game.panel.conveyor_belt[2],
615     TYPE_ELEMENT,
616   },
617   {
618     GAME_PANEL_CONVEYOR_BELT_4,
619     &game.panel.conveyor_belt[3],
620     TYPE_ELEMENT,
621   },
622   {
623     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
624     &game.panel.conveyor_belt_switch[0],
625     TYPE_ELEMENT,
626   },
627   {
628     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
629     &game.panel.conveyor_belt_switch[1],
630     TYPE_ELEMENT,
631   },
632   {
633     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
634     &game.panel.conveyor_belt_switch[2],
635     TYPE_ELEMENT,
636   },
637   {
638     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
639     &game.panel.conveyor_belt_switch[3],
640     TYPE_ELEMENT,
641   },
642   {
643     GAME_PANEL_MAGIC_WALL,
644     &game.panel.magic_wall,
645     TYPE_ELEMENT,
646   },
647   {
648     GAME_PANEL_MAGIC_WALL_TIME,
649     &game.panel.magic_wall_time,
650     TYPE_INTEGER,
651   },
652   {
653     GAME_PANEL_GRAVITY_STATE,
654     &game.panel.gravity_state,
655     TYPE_STRING,
656   },
657   {
658     GAME_PANEL_GRAPHIC_1,
659     &game.panel.graphic[0],
660     TYPE_ELEMENT,
661   },
662   {
663     GAME_PANEL_GRAPHIC_2,
664     &game.panel.graphic[1],
665     TYPE_ELEMENT,
666   },
667   {
668     GAME_PANEL_GRAPHIC_3,
669     &game.panel.graphic[2],
670     TYPE_ELEMENT,
671   },
672   {
673     GAME_PANEL_GRAPHIC_4,
674     &game.panel.graphic[3],
675     TYPE_ELEMENT,
676   },
677   {
678     GAME_PANEL_GRAPHIC_5,
679     &game.panel.graphic[4],
680     TYPE_ELEMENT,
681   },
682   {
683     GAME_PANEL_GRAPHIC_6,
684     &game.panel.graphic[5],
685     TYPE_ELEMENT,
686   },
687   {
688     GAME_PANEL_GRAPHIC_7,
689     &game.panel.graphic[6],
690     TYPE_ELEMENT,
691   },
692   {
693     GAME_PANEL_GRAPHIC_8,
694     &game.panel.graphic[7],
695     TYPE_ELEMENT,
696   },
697   {
698     GAME_PANEL_ELEMENT_1,
699     &game.panel.element[0],
700     TYPE_ELEMENT,
701   },
702   {
703     GAME_PANEL_ELEMENT_2,
704     &game.panel.element[1],
705     TYPE_ELEMENT,
706   },
707   {
708     GAME_PANEL_ELEMENT_3,
709     &game.panel.element[2],
710     TYPE_ELEMENT,
711   },
712   {
713     GAME_PANEL_ELEMENT_4,
714     &game.panel.element[3],
715     TYPE_ELEMENT,
716   },
717   {
718     GAME_PANEL_ELEMENT_5,
719     &game.panel.element[4],
720     TYPE_ELEMENT,
721   },
722   {
723     GAME_PANEL_ELEMENT_6,
724     &game.panel.element[5],
725     TYPE_ELEMENT,
726   },
727   {
728     GAME_PANEL_ELEMENT_7,
729     &game.panel.element[6],
730     TYPE_ELEMENT,
731   },
732   {
733     GAME_PANEL_ELEMENT_8,
734     &game.panel.element[7],
735     TYPE_ELEMENT,
736   },
737   {
738     GAME_PANEL_ELEMENT_COUNT_1,
739     &game.panel.element_count[0],
740     TYPE_INTEGER,
741   },
742   {
743     GAME_PANEL_ELEMENT_COUNT_2,
744     &game.panel.element_count[1],
745     TYPE_INTEGER,
746   },
747   {
748     GAME_PANEL_ELEMENT_COUNT_3,
749     &game.panel.element_count[2],
750     TYPE_INTEGER,
751   },
752   {
753     GAME_PANEL_ELEMENT_COUNT_4,
754     &game.panel.element_count[3],
755     TYPE_INTEGER,
756   },
757   {
758     GAME_PANEL_ELEMENT_COUNT_5,
759     &game.panel.element_count[4],
760     TYPE_INTEGER,
761   },
762   {
763     GAME_PANEL_ELEMENT_COUNT_6,
764     &game.panel.element_count[5],
765     TYPE_INTEGER,
766   },
767   {
768     GAME_PANEL_ELEMENT_COUNT_7,
769     &game.panel.element_count[6],
770     TYPE_INTEGER,
771   },
772   {
773     GAME_PANEL_ELEMENT_COUNT_8,
774     &game.panel.element_count[7],
775     TYPE_INTEGER,
776   },
777   {
778     GAME_PANEL_CE_SCORE_1,
779     &game.panel.ce_score[0],
780     TYPE_INTEGER,
781   },
782   {
783     GAME_PANEL_CE_SCORE_2,
784     &game.panel.ce_score[1],
785     TYPE_INTEGER,
786   },
787   {
788     GAME_PANEL_CE_SCORE_3,
789     &game.panel.ce_score[2],
790     TYPE_INTEGER,
791   },
792   {
793     GAME_PANEL_CE_SCORE_4,
794     &game.panel.ce_score[3],
795     TYPE_INTEGER,
796   },
797   {
798     GAME_PANEL_CE_SCORE_5,
799     &game.panel.ce_score[4],
800     TYPE_INTEGER,
801   },
802   {
803     GAME_PANEL_CE_SCORE_6,
804     &game.panel.ce_score[5],
805     TYPE_INTEGER,
806   },
807   {
808     GAME_PANEL_CE_SCORE_7,
809     &game.panel.ce_score[6],
810     TYPE_INTEGER,
811   },
812   {
813     GAME_PANEL_CE_SCORE_8,
814     &game.panel.ce_score[7],
815     TYPE_INTEGER,
816   },
817   {
818     GAME_PANEL_CE_SCORE_1_ELEMENT,
819     &game.panel.ce_score_element[0],
820     TYPE_ELEMENT,
821   },
822   {
823     GAME_PANEL_CE_SCORE_2_ELEMENT,
824     &game.panel.ce_score_element[1],
825     TYPE_ELEMENT,
826   },
827   {
828     GAME_PANEL_CE_SCORE_3_ELEMENT,
829     &game.panel.ce_score_element[2],
830     TYPE_ELEMENT,
831   },
832   {
833     GAME_PANEL_CE_SCORE_4_ELEMENT,
834     &game.panel.ce_score_element[3],
835     TYPE_ELEMENT,
836   },
837   {
838     GAME_PANEL_CE_SCORE_5_ELEMENT,
839     &game.panel.ce_score_element[4],
840     TYPE_ELEMENT,
841   },
842   {
843     GAME_PANEL_CE_SCORE_6_ELEMENT,
844     &game.panel.ce_score_element[5],
845     TYPE_ELEMENT,
846   },
847   {
848     GAME_PANEL_CE_SCORE_7_ELEMENT,
849     &game.panel.ce_score_element[6],
850     TYPE_ELEMENT,
851   },
852   {
853     GAME_PANEL_CE_SCORE_8_ELEMENT,
854     &game.panel.ce_score_element[7],
855     TYPE_ELEMENT,
856   },
857   {
858     GAME_PANEL_PLAYER_NAME,
859     &game.panel.player_name,
860     TYPE_STRING,
861   },
862   {
863     GAME_PANEL_LEVEL_NAME,
864     &game.panel.level_name,
865     TYPE_STRING,
866   },
867   {
868     GAME_PANEL_LEVEL_AUTHOR,
869     &game.panel.level_author,
870     TYPE_STRING,
871   },
872
873   {
874     -1,
875     NULL,
876     -1,
877   }
878 };
879 #endif
880
881
882 /* values for delayed check of falling and moving elements and for collision */
883 #define CHECK_DELAY_MOVING      3
884 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
885 #define CHECK_DELAY_COLLISION   2
886 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
887
888 /* values for initial player move delay (initial delay counter value) */
889 #define INITIAL_MOVE_DELAY_OFF  -1
890 #define INITIAL_MOVE_DELAY_ON   0
891
892 /* values for player movement speed (which is in fact a delay value) */
893 #define MOVE_DELAY_MIN_SPEED    32
894 #define MOVE_DELAY_NORMAL_SPEED 8
895 #define MOVE_DELAY_HIGH_SPEED   4
896 #define MOVE_DELAY_MAX_SPEED    1
897
898 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
899 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
900
901 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
902 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
903
904 /* values for other actions */
905 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
906 #define MOVE_STEPSIZE_MIN       (1)
907 #define MOVE_STEPSIZE_MAX       (TILEX)
908
909 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
910 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
911
912 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
913
914 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
915                                  RND(element_info[e].push_delay_random))
916 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
917                                  RND(element_info[e].drop_delay_random))
918 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
919                                  RND(element_info[e].move_delay_random))
920 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
921                                     (element_info[e].move_delay_random))
922 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
923                                  RND(element_info[e].ce_value_random_initial))
924 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
925 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
926                                  RND((c)->delay_random * (c)->delay_frames))
927 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
928                                  RND((c)->delay_random))
929
930
931 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
932          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
933
934 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
935         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
936          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
937          (be) + (e) - EL_SELF)
938
939 #define GET_PLAYER_FROM_BITS(p)                                         \
940         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
941
942 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
943         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
944          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
945          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
946          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
947          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
948          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
949          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
950          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
951          (e))
952
953 #define CAN_GROW_INTO(e)                                                \
954         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
955
956 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
957                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
962                                         (CAN_MOVE_INTO_ACID(e) &&       \
963                                          Feld[x][y] == EL_ACID) ||      \
964                                         (condition)))
965
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
967                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
968                                         (CAN_MOVE_INTO_ACID(e) &&       \
969                                          Feld[x][y] == EL_ACID) ||      \
970                                         (condition)))
971
972 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
973                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
974                                         (condition) ||                  \
975                                         (CAN_MOVE_INTO_ACID(e) &&       \
976                                          Feld[x][y] == EL_ACID) ||      \
977                                         (DONT_COLLIDE_WITH(e) &&        \
978                                          IS_PLAYER(x, y) &&             \
979                                          !PLAYER_ENEMY_PROTECTED(x, y))))
980
981 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
982         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
983
984 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
985         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
986
987 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
988         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
989
990 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
991         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
992                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
993
994 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
995         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
996
997 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
999
1000 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
1002
1003 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1005
1006 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
1007         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1008
1009 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1011                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1012                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1013                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1014                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1015 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1016         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017
1018 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1019         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1020
1021 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1022         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1023
1024 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1025         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1026                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1027
1028 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1029
1030 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1031                 (!IS_PLAYER(x, y) &&                                    \
1032                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1033
1034 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1035         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1036
1037 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1038 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1039
1040 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1041 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1042 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1043 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1044
1045 /* game button identifiers */
1046 #define GAME_CTRL_ID_STOP               0
1047 #define GAME_CTRL_ID_PAUSE              1
1048 #define GAME_CTRL_ID_PLAY               2
1049 #define SOUND_CTRL_ID_MUSIC             3
1050 #define SOUND_CTRL_ID_LOOPS             4
1051 #define SOUND_CTRL_ID_SIMPLE            5
1052
1053 #define NUM_GAME_BUTTONS                6
1054
1055
1056 /* forward declaration for internal use */
1057
1058 static void CreateField(int, int, int);
1059
1060 static void ResetGfxAnimation(int, int);
1061
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081
1082 static void TestIfPlayerTouchesCustomElement(int, int);
1083 static void TestIfElementTouchesCustomElement(int, int);
1084 static void TestIfElementHitsCustomElement(int, int, int);
1085 #if 0
1086 static void TestIfElementSmashesCustomElement(int, int, int);
1087 #endif
1088
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1094 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1097         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1099         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1101         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102
1103 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1104 #define CheckElementChange(x, y, e, te, ev)                             \
1105         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1106 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1107         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1108 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1109         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1110
1111 static void PlayLevelSound(int, int, int);
1112 static void PlayLevelSoundNearest(int, int, int);
1113 static void PlayLevelSoundAction(int, int, int);
1114 static void PlayLevelSoundElementAction(int, int, int, int);
1115 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1116 static void PlayLevelSoundActionIfLoop(int, int, int);
1117 static void StopLevelSoundActionIfLoop(int, int, int);
1118 static void PlayLevelMusic();
1119
1120 static void MapGameButtons();
1121 static void HandleGameButtons(struct GadgetInfo *);
1122
1123 int AmoebeNachbarNr(int, int);
1124 void AmoebeUmwandeln(int, int);
1125 void ContinueMoving(int, int);
1126 void Bang(int, int);
1127 void InitMovDir(int, int);
1128 void InitAmoebaNr(int, int);
1129 int NewHiScore(void);
1130
1131 void TestIfGoodThingHitsBadThing(int, int, int);
1132 void TestIfBadThingHitsGoodThing(int, int, int);
1133 void TestIfPlayerTouchesBadThing(int, int);
1134 void TestIfPlayerRunsIntoBadThing(int, int, int);
1135 void TestIfBadThingTouchesPlayer(int, int);
1136 void TestIfBadThingRunsIntoPlayer(int, int, int);
1137 void TestIfFriendTouchesBadThing(int, int);
1138 void TestIfBadThingTouchesFriend(int, int);
1139 void TestIfBadThingTouchesOtherBadThing(int, int);
1140 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1141
1142 void KillPlayer(struct PlayerInfo *);
1143 void BuryPlayer(struct PlayerInfo *);
1144 void RemovePlayer(struct PlayerInfo *);
1145
1146 static int getInvisibleActiveFromInvisibleElement(int);
1147 static int getInvisibleFromInvisibleActiveElement(int);
1148
1149 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1150
1151 /* for detection of endless loops, caused by custom element programming */
1152 /* (using maximal playfield width x 10 is just a rough approximation) */
1153 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1154
1155 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1156 {                                                                       \
1157   if (recursion_loop_detected)                                          \
1158     return (rc);                                                        \
1159                                                                         \
1160   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1161   {                                                                     \
1162     recursion_loop_detected = TRUE;                                     \
1163     recursion_loop_element = (e);                                       \
1164   }                                                                     \
1165                                                                         \
1166   recursion_loop_depth++;                                               \
1167 }
1168
1169 #define RECURSION_LOOP_DETECTION_END()                                  \
1170 {                                                                       \
1171   recursion_loop_depth--;                                               \
1172 }
1173
1174 static int recursion_loop_depth;
1175 static boolean recursion_loop_detected;
1176 static boolean recursion_loop_element;
1177
1178 static int map_player_action[MAX_PLAYERS];
1179
1180
1181 /* ------------------------------------------------------------------------- */
1182 /* definition of elements that automatically change to other elements after  */
1183 /* a specified time, eventually calling a function when changing             */
1184 /* ------------------------------------------------------------------------- */
1185
1186 /* forward declaration for changer functions */
1187 static void InitBuggyBase(int, int);
1188 static void WarnBuggyBase(int, int);
1189
1190 static void InitTrap(int, int);
1191 static void ActivateTrap(int, int);
1192 static void ChangeActiveTrap(int, int);
1193
1194 static void InitRobotWheel(int, int);
1195 static void RunRobotWheel(int, int);
1196 static void StopRobotWheel(int, int);
1197
1198 static void InitTimegateWheel(int, int);
1199 static void RunTimegateWheel(int, int);
1200
1201 static void InitMagicBallDelay(int, int);
1202 static void ActivateMagicBall(int, int);
1203
1204 struct ChangingElementInfo
1205 {
1206   int element;
1207   int target_element;
1208   int change_delay;
1209   void (*pre_change_function)(int x, int y);
1210   void (*change_function)(int x, int y);
1211   void (*post_change_function)(int x, int y);
1212 };
1213
1214 static struct ChangingElementInfo change_delay_list[] =
1215 {
1216   {
1217     EL_NUT_BREAKING,
1218     EL_EMERALD,
1219     6,
1220     NULL,
1221     NULL,
1222     NULL
1223   },
1224   {
1225     EL_PEARL_BREAKING,
1226     EL_EMPTY,
1227     8,
1228     NULL,
1229     NULL,
1230     NULL
1231   },
1232   {
1233     EL_EXIT_OPENING,
1234     EL_EXIT_OPEN,
1235     29,
1236     NULL,
1237     NULL,
1238     NULL
1239   },
1240   {
1241     EL_EXIT_CLOSING,
1242     EL_EXIT_CLOSED,
1243     29,
1244     NULL,
1245     NULL,
1246     NULL
1247   },
1248   {
1249     EL_STEEL_EXIT_OPENING,
1250     EL_STEEL_EXIT_OPEN,
1251     29,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_STEEL_EXIT_CLOSING,
1258     EL_STEEL_EXIT_CLOSED,
1259     29,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_EM_EXIT_OPENING,
1266     EL_EM_EXIT_OPEN,
1267     29,
1268     NULL,
1269     NULL,
1270     NULL
1271   },
1272   {
1273     EL_EM_EXIT_CLOSING,
1274 #if 1
1275     EL_EMPTY,
1276 #else
1277     EL_EM_EXIT_CLOSED,
1278 #endif
1279     29,
1280     NULL,
1281     NULL,
1282     NULL
1283   },
1284   {
1285     EL_EM_STEEL_EXIT_OPENING,
1286     EL_EM_STEEL_EXIT_OPEN,
1287     29,
1288     NULL,
1289     NULL,
1290     NULL
1291   },
1292   {
1293     EL_EM_STEEL_EXIT_CLOSING,
1294 #if 1
1295     EL_STEELWALL,
1296 #else
1297     EL_EM_STEEL_EXIT_CLOSED,
1298 #endif
1299     29,
1300     NULL,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_EXIT_OPENING,
1306     EL_SP_EXIT_OPEN,
1307     29,
1308     NULL,
1309     NULL,
1310     NULL
1311   },
1312   {
1313     EL_SP_EXIT_CLOSING,
1314     EL_SP_EXIT_CLOSED,
1315     29,
1316     NULL,
1317     NULL,
1318     NULL
1319   },
1320   {
1321     EL_SWITCHGATE_OPENING,
1322     EL_SWITCHGATE_OPEN,
1323     29,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_SWITCHGATE_CLOSING,
1330     EL_SWITCHGATE_CLOSED,
1331     29,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_TIMEGATE_OPENING,
1338     EL_TIMEGATE_OPEN,
1339     29,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_TIMEGATE_CLOSING,
1346     EL_TIMEGATE_CLOSED,
1347     29,
1348     NULL,
1349     NULL,
1350     NULL
1351   },
1352
1353   {
1354     EL_ACID_SPLASH_LEFT,
1355     EL_EMPTY,
1356     8,
1357     NULL,
1358     NULL,
1359     NULL
1360   },
1361   {
1362     EL_ACID_SPLASH_RIGHT,
1363     EL_EMPTY,
1364     8,
1365     NULL,
1366     NULL,
1367     NULL
1368   },
1369   {
1370     EL_SP_BUGGY_BASE,
1371     EL_SP_BUGGY_BASE_ACTIVATING,
1372     0,
1373     InitBuggyBase,
1374     NULL,
1375     NULL
1376   },
1377   {
1378     EL_SP_BUGGY_BASE_ACTIVATING,
1379     EL_SP_BUGGY_BASE_ACTIVE,
1380     0,
1381     InitBuggyBase,
1382     NULL,
1383     NULL
1384   },
1385   {
1386     EL_SP_BUGGY_BASE_ACTIVE,
1387     EL_SP_BUGGY_BASE,
1388     0,
1389     InitBuggyBase,
1390     WarnBuggyBase,
1391     NULL
1392   },
1393   {
1394     EL_TRAP,
1395     EL_TRAP_ACTIVE,
1396     0,
1397     InitTrap,
1398     NULL,
1399     ActivateTrap
1400   },
1401   {
1402     EL_TRAP_ACTIVE,
1403     EL_TRAP,
1404     31,
1405     NULL,
1406     ChangeActiveTrap,
1407     NULL
1408   },
1409   {
1410     EL_ROBOT_WHEEL_ACTIVE,
1411     EL_ROBOT_WHEEL,
1412     0,
1413     InitRobotWheel,
1414     RunRobotWheel,
1415     StopRobotWheel
1416   },
1417   {
1418     EL_TIMEGATE_SWITCH_ACTIVE,
1419     EL_TIMEGATE_SWITCH,
1420     0,
1421     InitTimegateWheel,
1422     RunTimegateWheel,
1423     NULL
1424   },
1425   {
1426     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1427     EL_DC_TIMEGATE_SWITCH,
1428     0,
1429     InitTimegateWheel,
1430     RunTimegateWheel,
1431     NULL
1432   },
1433   {
1434     EL_EMC_MAGIC_BALL_ACTIVE,
1435     EL_EMC_MAGIC_BALL_ACTIVE,
1436     0,
1437     InitMagicBallDelay,
1438     NULL,
1439     ActivateMagicBall
1440   },
1441   {
1442     EL_EMC_SPRING_BUMPER_ACTIVE,
1443     EL_EMC_SPRING_BUMPER,
1444     8,
1445     NULL,
1446     NULL,
1447     NULL
1448   },
1449   {
1450     EL_DIAGONAL_SHRINKING,
1451     EL_UNDEFINED,
1452     0,
1453     NULL,
1454     NULL,
1455     NULL
1456   },
1457   {
1458     EL_DIAGONAL_GROWING,
1459     EL_UNDEFINED,
1460     0,
1461     NULL,
1462     NULL,
1463     NULL,
1464   },
1465
1466   {
1467     EL_UNDEFINED,
1468     EL_UNDEFINED,
1469     -1,
1470     NULL,
1471     NULL,
1472     NULL
1473   }
1474 };
1475
1476 struct
1477 {
1478   int element;
1479   int push_delay_fixed, push_delay_random;
1480 }
1481 push_delay_list[] =
1482 {
1483   { EL_SPRING,                  0, 0 },
1484   { EL_BALLOON,                 0, 0 },
1485
1486   { EL_SOKOBAN_OBJECT,          2, 0 },
1487   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1488   { EL_SATELLITE,               2, 0 },
1489   { EL_SP_DISK_YELLOW,          2, 0 },
1490
1491   { EL_UNDEFINED,               0, 0 },
1492 };
1493
1494 struct
1495 {
1496   int element;
1497   int move_stepsize;
1498 }
1499 move_stepsize_list[] =
1500 {
1501   { EL_AMOEBA_DROP,             2 },
1502   { EL_AMOEBA_DROPPING,         2 },
1503   { EL_QUICKSAND_FILLING,       1 },
1504   { EL_QUICKSAND_EMPTYING,      1 },
1505   { EL_QUICKSAND_FAST_FILLING,  2 },
1506   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1507   { EL_MAGIC_WALL_FILLING,      2 },
1508   { EL_MAGIC_WALL_EMPTYING,     2 },
1509   { EL_BD_MAGIC_WALL_FILLING,   2 },
1510   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1511   { EL_DC_MAGIC_WALL_FILLING,   2 },
1512   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1513
1514   { EL_UNDEFINED,               0 },
1515 };
1516
1517 struct
1518 {
1519   int element;
1520   int count;
1521 }
1522 collect_count_list[] =
1523 {
1524   { EL_EMERALD,                 1 },
1525   { EL_BD_DIAMOND,              1 },
1526   { EL_EMERALD_YELLOW,          1 },
1527   { EL_EMERALD_RED,             1 },
1528   { EL_EMERALD_PURPLE,          1 },
1529   { EL_DIAMOND,                 3 },
1530   { EL_SP_INFOTRON,             1 },
1531   { EL_PEARL,                   5 },
1532   { EL_CRYSTAL,                 8 },
1533
1534   { EL_UNDEFINED,               0 },
1535 };
1536
1537 struct
1538 {
1539   int element;
1540   int direction;
1541 }
1542 access_direction_list[] =
1543 {
1544   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1545   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1546   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1547   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1548   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1549   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1550   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1551   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1552   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1553   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1554   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1555
1556   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1557   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1558   { EL_SP_PORT_UP,                                                   MV_DOWN },
1559   { EL_SP_PORT_DOWN,                                         MV_UP           },
1560   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1561   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1562   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1563   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1564   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1565   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1566   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1567   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1568   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1569   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1570   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1571   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1572   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1573   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1574   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1575
1576   { EL_UNDEFINED,                       MV_NONE                              }
1577 };
1578
1579 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1580
1581 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1582 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1583 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1584                                  IS_JUST_CHANGING(x, y))
1585
1586 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1587
1588 /* static variables for playfield scan mode (scanning forward or backward) */
1589 static int playfield_scan_start_x = 0;
1590 static int playfield_scan_start_y = 0;
1591 static int playfield_scan_delta_x = 1;
1592 static int playfield_scan_delta_y = 1;
1593
1594 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1595                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1596                                      (y) += playfield_scan_delta_y)     \
1597                                 for ((x) = playfield_scan_start_x;      \
1598                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1599                                      (x) += playfield_scan_delta_x)
1600
1601 #ifdef DEBUG
1602 void DEBUG_SetMaximumDynamite()
1603 {
1604   int i;
1605
1606   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1607     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1608       local_player->inventory_element[local_player->inventory_size++] =
1609         EL_DYNAMITE;
1610 }
1611 #endif
1612
1613 static void InitPlayfieldScanModeVars()
1614 {
1615   if (game.use_reverse_scan_direction)
1616   {
1617     playfield_scan_start_x = lev_fieldx - 1;
1618     playfield_scan_start_y = lev_fieldy - 1;
1619
1620     playfield_scan_delta_x = -1;
1621     playfield_scan_delta_y = -1;
1622   }
1623   else
1624   {
1625     playfield_scan_start_x = 0;
1626     playfield_scan_start_y = 0;
1627
1628     playfield_scan_delta_x = 1;
1629     playfield_scan_delta_y = 1;
1630   }
1631 }
1632
1633 static void InitPlayfieldScanMode(int mode)
1634 {
1635   game.use_reverse_scan_direction =
1636     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1637
1638   InitPlayfieldScanModeVars();
1639 }
1640
1641 static int get_move_delay_from_stepsize(int move_stepsize)
1642 {
1643   move_stepsize =
1644     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1645
1646   /* make sure that stepsize value is always a power of 2 */
1647   move_stepsize = (1 << log_2(move_stepsize));
1648
1649   return TILEX / move_stepsize;
1650 }
1651
1652 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1653                                boolean init_game)
1654 {
1655   int player_nr = player->index_nr;
1656   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1657   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1658
1659   /* do no immediately change move delay -- the player might just be moving */
1660   player->move_delay_value_next = move_delay;
1661
1662   /* information if player can move must be set separately */
1663   player->cannot_move = cannot_move;
1664
1665   if (init_game)
1666   {
1667     player->move_delay       = game.initial_move_delay[player_nr];
1668     player->move_delay_value = game.initial_move_delay_value[player_nr];
1669
1670     player->move_delay_value_next = -1;
1671
1672     player->move_delay_reset_counter = 0;
1673   }
1674 }
1675
1676 void GetPlayerConfig()
1677 {
1678   GameFrameDelay = setup.game_frame_delay;
1679
1680   if (!audio.sound_available)
1681     setup.sound_simple = FALSE;
1682
1683   if (!audio.loops_available)
1684     setup.sound_loops = FALSE;
1685
1686   if (!audio.music_available)
1687     setup.sound_music = FALSE;
1688
1689   if (!video.fullscreen_available)
1690     setup.fullscreen = FALSE;
1691
1692   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1693
1694   SetAudioMode(setup.sound);
1695   InitJoysticks();
1696 }
1697
1698 int GetElementFromGroupElement(int element)
1699 {
1700   if (IS_GROUP_ELEMENT(element))
1701   {
1702     struct ElementGroupInfo *group = element_info[element].group;
1703     int last_anim_random_frame = gfx.anim_random_frame;
1704     int element_pos;
1705
1706     if (group->choice_mode == ANIM_RANDOM)
1707       gfx.anim_random_frame = RND(group->num_elements_resolved);
1708
1709     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1710                                     group->choice_mode, 0,
1711                                     group->choice_pos);
1712
1713     if (group->choice_mode == ANIM_RANDOM)
1714       gfx.anim_random_frame = last_anim_random_frame;
1715
1716     group->choice_pos++;
1717
1718     element = group->element_resolved[element_pos];
1719   }
1720
1721   return element;
1722 }
1723
1724 static void InitPlayerField(int x, int y, int element, boolean init_game)
1725 {
1726   if (element == EL_SP_MURPHY)
1727   {
1728     if (init_game)
1729     {
1730       if (stored_player[0].present)
1731       {
1732         Feld[x][y] = EL_SP_MURPHY_CLONE;
1733
1734         return;
1735       }
1736       else
1737       {
1738         stored_player[0].initial_element = element;
1739         stored_player[0].use_murphy = TRUE;
1740
1741         if (!level.use_artwork_element[0])
1742           stored_player[0].artwork_element = EL_SP_MURPHY;
1743       }
1744
1745       Feld[x][y] = EL_PLAYER_1;
1746     }
1747   }
1748
1749   if (init_game)
1750   {
1751     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1752     int jx = player->jx, jy = player->jy;
1753
1754     player->present = TRUE;
1755
1756     player->block_last_field = (element == EL_SP_MURPHY ?
1757                                 level.sp_block_last_field :
1758                                 level.block_last_field);
1759
1760     /* ---------- initialize player's last field block delay --------------- */
1761
1762     /* always start with reliable default value (no adjustment needed) */
1763     player->block_delay_adjustment = 0;
1764
1765     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1766     if (player->block_last_field && element == EL_SP_MURPHY)
1767       player->block_delay_adjustment = 1;
1768
1769     /* special case 2: in game engines before 3.1.1, blocking was different */
1770     if (game.use_block_last_field_bug)
1771       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1772
1773     if (!options.network || player->connected)
1774     {
1775       player->active = TRUE;
1776
1777       /* remove potentially duplicate players */
1778       if (StorePlayer[jx][jy] == Feld[x][y])
1779         StorePlayer[jx][jy] = 0;
1780
1781       StorePlayer[x][y] = Feld[x][y];
1782
1783       if (options.debug)
1784       {
1785         printf("Player %d activated.\n", player->element_nr);
1786         printf("[Local player is %d and currently %s.]\n",
1787                local_player->element_nr,
1788                local_player->active ? "active" : "not active");
1789       }
1790     }
1791
1792     Feld[x][y] = EL_EMPTY;
1793
1794     player->jx = player->last_jx = x;
1795     player->jy = player->last_jy = y;
1796   }
1797
1798 #if USE_PLAYER_REANIMATION
1799   if (!init_game)
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1806   }
1807 #endif
1808 }
1809
1810 static void InitField(int x, int y, boolean init_game)
1811 {
1812   int element = Feld[x][y];
1813
1814   switch (element)
1815   {
1816     case EL_SP_MURPHY:
1817     case EL_PLAYER_1:
1818     case EL_PLAYER_2:
1819     case EL_PLAYER_3:
1820     case EL_PLAYER_4:
1821       InitPlayerField(x, y, element, init_game);
1822       break;
1823
1824     case EL_SOKOBAN_FIELD_PLAYER:
1825       element = Feld[x][y] = EL_PLAYER_1;
1826       InitField(x, y, init_game);
1827
1828       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1829       InitField(x, y, init_game);
1830       break;
1831
1832     case EL_SOKOBAN_FIELD_EMPTY:
1833       local_player->sokobanfields_still_needed++;
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1838         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1840         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1844         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888       InitMovDir(x, y);
1889       break;
1890
1891     case EL_AMOEBA_FULL:
1892     case EL_BD_AMOEBA:
1893       InitAmoebaNr(x, y);
1894       break;
1895
1896     case EL_AMOEBA_DROP:
1897       if (y == lev_fieldy - 1)
1898       {
1899         Feld[x][y] = EL_AMOEBA_GROWING;
1900         Store[x][y] = EL_AMOEBA_WET;
1901       }
1902       break;
1903
1904     case EL_DYNAMITE_ACTIVE:
1905     case EL_SP_DISK_RED_ACTIVE:
1906     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1907     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1910       MovDelay[x][y] = 96;
1911       break;
1912
1913     case EL_EM_DYNAMITE_ACTIVE:
1914       MovDelay[x][y] = 32;
1915       break;
1916
1917     case EL_LAMP:
1918       local_player->lights_still_needed++;
1919       break;
1920
1921     case EL_PENGUIN:
1922       local_player->friends_still_needed++;
1923       break;
1924
1925     case EL_PIG:
1926     case EL_DRAGON:
1927       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1928       break;
1929
1930     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1936     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1937     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1938     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1939     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1940     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1941     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1942       if (init_game)
1943       {
1944         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1945         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1946         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1947
1948         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1949         {
1950           game.belt_dir[belt_nr] = belt_dir;
1951           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1952         }
1953         else    /* more than one switch -- set it like the first switch */
1954         {
1955           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1956         }
1957       }
1958       break;
1959
1960 #if !USE_BOTH_SWITCHGATE_SWITCHES
1961     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1962       if (init_game)
1963         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1964       break;
1965
1966     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1967       if (init_game)
1968         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1969       break;
1970 #endif
1971
1972     case EL_LIGHT_SWITCH_ACTIVE:
1973       if (init_game)
1974         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1975       break;
1976
1977     case EL_INVISIBLE_STEELWALL:
1978     case EL_INVISIBLE_WALL:
1979     case EL_INVISIBLE_SAND:
1980       if (game.light_time_left > 0 ||
1981           game.lenses_time_left > 0)
1982         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1983       break;
1984
1985     case EL_EMC_MAGIC_BALL:
1986       if (game.ball_state)
1987         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1988       break;
1989
1990     case EL_EMC_MAGIC_BALL_SWITCH:
1991       if (game.ball_state)
1992         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1993       break;
1994
1995     case EL_TRIGGER_PLAYER:
1996     case EL_TRIGGER_ELEMENT:
1997     case EL_TRIGGER_CE_VALUE:
1998     case EL_TRIGGER_CE_SCORE:
1999     case EL_SELF:
2000     case EL_ANY_ELEMENT:
2001     case EL_CURRENT_CE_VALUE:
2002     case EL_CURRENT_CE_SCORE:
2003     case EL_PREV_CE_1:
2004     case EL_PREV_CE_2:
2005     case EL_PREV_CE_3:
2006     case EL_PREV_CE_4:
2007     case EL_PREV_CE_5:
2008     case EL_PREV_CE_6:
2009     case EL_PREV_CE_7:
2010     case EL_PREV_CE_8:
2011     case EL_NEXT_CE_1:
2012     case EL_NEXT_CE_2:
2013     case EL_NEXT_CE_3:
2014     case EL_NEXT_CE_4:
2015     case EL_NEXT_CE_5:
2016     case EL_NEXT_CE_6:
2017     case EL_NEXT_CE_7:
2018     case EL_NEXT_CE_8:
2019       /* reference elements should not be used on the playfield */
2020       Feld[x][y] = EL_EMPTY;
2021       break;
2022
2023     default:
2024       if (IS_CUSTOM_ELEMENT(element))
2025       {
2026         if (CAN_MOVE(element))
2027           InitMovDir(x, y);
2028
2029 #if USE_NEW_CUSTOM_VALUE
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2032 #endif
2033       }
2034       else if (IS_GROUP_ELEMENT(element))
2035       {
2036         Feld[x][y] = GetElementFromGroupElement(element);
2037
2038         InitField(x, y, init_game);
2039       }
2040
2041       break;
2042   }
2043
2044   if (!init_game)
2045     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2046 }
2047
2048 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2049 {
2050   InitField(x, y, init_game);
2051
2052   /* not needed to call InitMovDir() -- already done by InitField()! */
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(Feld[x][y]))
2055     InitMovDir(x, y);
2056 }
2057
2058 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2059 {
2060   int old_element = Feld[x][y];
2061
2062   InitField(x, y, init_game);
2063
2064   /* not needed to call InitMovDir() -- already done by InitField()! */
2065   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2066       CAN_MOVE(old_element) &&
2067       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2068     InitMovDir(x, y);
2069
2070   /* this case is in fact a combination of not less than three bugs:
2071      first, it calls InitMovDir() for elements that can move, although this is
2072      already done by InitField(); then, it checks the element that was at this
2073      field _before_ the call to InitField() (which can change it); lastly, it
2074      was not called for "mole with direction" elements, which were treated as
2075      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2076   */
2077 }
2078
2079 #if 1
2080
2081 static int get_key_element_from_nr(int key_nr)
2082 {
2083   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2084                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2085                           EL_EM_KEY_1 : EL_KEY_1);
2086
2087   return key_base_element + key_nr;
2088 }
2089
2090 static int get_next_dropped_element(struct PlayerInfo *player)
2091 {
2092   return (player->inventory_size > 0 ?
2093           player->inventory_element[player->inventory_size - 1] :
2094           player->inventory_infinite_element != EL_UNDEFINED ?
2095           player->inventory_infinite_element :
2096           player->dynabombs_left > 0 ?
2097           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2098           EL_UNDEFINED);
2099 }
2100
2101 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2102 {
2103   /* pos >= 0: get element from bottom of the stack;
2104      pos <  0: get element from top of the stack */
2105
2106   if (pos < 0)
2107   {
2108     int min_inventory_size = -pos;
2109     int inventory_pos = player->inventory_size - min_inventory_size;
2110     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2111
2112     return (player->inventory_size >= min_inventory_size ?
2113             player->inventory_element[inventory_pos] :
2114             player->inventory_infinite_element != EL_UNDEFINED ?
2115             player->inventory_infinite_element :
2116             player->dynabombs_left >= min_dynabombs_left ?
2117             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2118             EL_UNDEFINED);
2119   }
2120   else
2121   {
2122     int min_dynabombs_left = pos + 1;
2123     int min_inventory_size = pos + 1 - player->dynabombs_left;
2124     int inventory_pos = pos - player->dynabombs_left;
2125
2126     return (player->inventory_infinite_element != EL_UNDEFINED ?
2127             player->inventory_infinite_element :
2128             player->dynabombs_left >= min_dynabombs_left ?
2129             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2130             player->inventory_size >= min_inventory_size ?
2131             player->inventory_element[inventory_pos] :
2132             EL_UNDEFINED);
2133   }
2134 }
2135
2136 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2137 {
2138   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2139   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2140   int compare_result;
2141
2142   if (gpo1->sort_priority != gpo2->sort_priority)
2143     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2144   else
2145     compare_result = gpo1->nr - gpo2->nr;
2146
2147   return compare_result;
2148 }
2149
2150 void InitGameControlValues()
2151 {
2152   int i;
2153
2154   for (i = 0; game_panel_controls[i].nr != -1; i++)
2155   {
2156     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2157     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2158     struct TextPosInfo *pos = gpc->pos;
2159     int nr = gpc->nr;
2160     int type = gpc->type;
2161
2162     if (nr != i)
2163     {
2164       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2165       Error(ERR_EXIT, "this should not happen -- please debug");
2166     }
2167
2168     /* force update of game controls after initialization */
2169     gpc->value = gpc->last_value = -1;
2170     gpc->frame = gpc->last_frame = -1;
2171     gpc->gfx_frame = -1;
2172
2173     /* determine panel value width for later calculation of alignment */
2174     if (type == TYPE_INTEGER || type == TYPE_STRING)
2175     {
2176       pos->width = pos->size * getFontWidth(pos->font);
2177       pos->height = getFontHeight(pos->font);
2178     }
2179     else if (type == TYPE_ELEMENT)
2180     {
2181       pos->width = pos->size;
2182       pos->height = pos->size;
2183     }
2184
2185     /* fill structure for game panel draw order */
2186     gpo->nr = gpc->nr;
2187     gpo->sort_priority = pos->sort_priority;
2188   }
2189
2190   /* sort game panel controls according to sort_priority and control number */
2191   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2193 }
2194
2195 void UpdatePlayfieldElementCount()
2196 {
2197   boolean use_element_count = FALSE;
2198   int i, j, x, y;
2199
2200   /* first check if it is needed at all to calculate playfield element count */
2201   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203       use_element_count = TRUE;
2204
2205   if (!use_element_count)
2206     return;
2207
2208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209     element_info[i].element_count = 0;
2210
2211   SCAN_PLAYFIELD(x, y)
2212   {
2213     element_info[Feld[x][y]].element_count++;
2214   }
2215
2216   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218       if (IS_IN_GROUP(j, i))
2219         element_info[EL_GROUP_START + i].element_count +=
2220           element_info[j].element_count;
2221 }
2222
2223 void UpdateGameControlValues()
2224 {
2225   int i, k;
2226 #if 1
2227   int time = (local_player->LevelSolved ?
2228               local_player->LevelSolved_CountingTime :
2229               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2230               level.native_em_level->lev->time :
2231               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2232               level.native_sp_level->game_sp->time_played :
2233               game.no_time_limit ? TimePlayed : TimeLeft);
2234 #else
2235   int time = (local_player->LevelSolved ?
2236               local_player->LevelSolved_CountingTime :
2237               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238               level.native_em_level->lev->time :
2239               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240               level.native_sp_level->game_sp->time_played :
2241               level.time == 0 ? TimePlayed : TimeLeft);
2242 #endif
2243   int score = (local_player->LevelSolved ?
2244                local_player->LevelSolved_CountingScore :
2245                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246                level.native_em_level->lev->score :
2247                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248                level.native_sp_level->game_sp->score :
2249                local_player->score);
2250   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2251               level.native_em_level->lev->required :
2252               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2253               level.native_sp_level->game_sp->infotrons_still_needed :
2254               local_player->gems_still_needed);
2255   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2256                      level.native_em_level->lev->required > 0 :
2257                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2258                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2259                      local_player->gems_still_needed > 0 ||
2260                      local_player->sokobanfields_still_needed > 0 ||
2261                      local_player->lights_still_needed > 0);
2262
2263   UpdatePlayfieldElementCount();
2264
2265   /* update game panel control values */
2266
2267   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2268   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2269
2270   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2271   for (i = 0; i < MAX_NUM_KEYS; i++)
2272     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2273   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2274   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2275
2276   if (game.centered_player_nr == -1)
2277   {
2278     for (i = 0; i < MAX_PLAYERS; i++)
2279     {
2280       /* only one player in Supaplex game engine */
2281       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2282         break;
2283
2284       for (k = 0; k < MAX_NUM_KEYS; k++)
2285       {
2286         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2287         {
2288           if (level.native_em_level->ply[i]->keys & (1 << k))
2289             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290               get_key_element_from_nr(k);
2291         }
2292         else if (stored_player[i].key[k])
2293           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294             get_key_element_from_nr(k);
2295       }
2296
2297       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2298         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2299           level.native_em_level->ply[i]->dynamite;
2300       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2301         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302           level.native_sp_level->game_sp->red_disk_count;
2303       else
2304         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2305           stored_player[i].inventory_size;
2306
2307       if (stored_player[i].num_white_keys > 0)
2308         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2309           EL_DC_KEY_WHITE;
2310
2311       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312         stored_player[i].num_white_keys;
2313     }
2314   }
2315   else
2316   {
2317     int player_nr = game.centered_player_nr;
2318
2319     for (k = 0; k < MAX_NUM_KEYS; k++)
2320     {
2321       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2322       {
2323         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2324           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2325             get_key_element_from_nr(k);
2326       }
2327       else if (stored_player[player_nr].key[k])
2328         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2329           get_key_element_from_nr(k);
2330     }
2331
2332     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2333       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2334         level.native_em_level->ply[player_nr]->dynamite;
2335     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2336       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2337         level.native_sp_level->game_sp->red_disk_count;
2338     else
2339       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2340         stored_player[player_nr].inventory_size;
2341
2342     if (stored_player[player_nr].num_white_keys > 0)
2343       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2344
2345     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2346       stored_player[player_nr].num_white_keys;
2347   }
2348
2349   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2350   {
2351     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2352       get_inventory_element_from_pos(local_player, i);
2353     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2354       get_inventory_element_from_pos(local_player, -i - 1);
2355   }
2356
2357   game_panel_controls[GAME_PANEL_SCORE].value = score;
2358   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2359
2360   game_panel_controls[GAME_PANEL_TIME].value = time;
2361
2362   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2363   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2364   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2365
2366   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2367
2368   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2369     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2370      EL_EMPTY);
2371   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2372     local_player->shield_normal_time_left;
2373   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2374     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2375      EL_EMPTY);
2376   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2377     local_player->shield_deadly_time_left;
2378
2379   game_panel_controls[GAME_PANEL_EXIT].value =
2380     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2381
2382   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2383     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2384   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2385     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2386      EL_EMC_MAGIC_BALL_SWITCH);
2387
2388   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2389     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2390   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2391     game.light_time_left;
2392
2393   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2394     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2395   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2396     game.timegate_time_left;
2397
2398   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2399     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2400
2401   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2402     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2403   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2404     game.lenses_time_left;
2405
2406   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2407     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2408   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2409     game.magnify_time_left;
2410
2411   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2412     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2413      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2414      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2415      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2416      EL_BALLOON_SWITCH_NONE);
2417
2418   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2419     local_player->dynabomb_count;
2420   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2421     local_player->dynabomb_size;
2422   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2423     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2424
2425   game_panel_controls[GAME_PANEL_PENGUINS].value =
2426     local_player->friends_still_needed;
2427
2428   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2429     local_player->sokobanfields_still_needed;
2430   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2431     local_player->sokobanfields_still_needed;
2432
2433   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2434     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2435
2436   for (i = 0; i < NUM_BELTS; i++)
2437   {
2438     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2439       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2440        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2441     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2442       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2443   }
2444
2445   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2446     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2447   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2448     game.magic_wall_time_left;
2449
2450 #if USE_PLAYER_GRAVITY
2451   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2452     local_player->gravity;
2453 #else
2454   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2455 #endif
2456
2457   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2458     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2459
2460   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2461     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2462       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2463        game.panel.element[i].id : EL_UNDEFINED);
2464
2465   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2466     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2467       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2468        element_info[game.panel.element_count[i].id].element_count : 0);
2469
2470   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2471     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2472       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2473        element_info[game.panel.ce_score[i].id].collect_score : 0);
2474
2475   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2476     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2477       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2478        element_info[game.panel.ce_score_element[i].id].collect_score :
2479        EL_UNDEFINED);
2480
2481   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2482   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2483   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2484
2485   /* update game panel control frames */
2486
2487   for (i = 0; game_panel_controls[i].nr != -1; i++)
2488   {
2489     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2490
2491     if (gpc->type == TYPE_ELEMENT)
2492     {
2493       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2494       {
2495         int last_anim_random_frame = gfx.anim_random_frame;
2496         int element = gpc->value;
2497         int graphic = el2panelimg(element);
2498
2499         if (gpc->value != gpc->last_value)
2500         {
2501           gpc->gfx_frame = 0;
2502           gpc->gfx_random = INIT_GFX_RANDOM();
2503         }
2504         else
2505         {
2506           gpc->gfx_frame++;
2507
2508           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2509               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2510             gpc->gfx_random = INIT_GFX_RANDOM();
2511         }
2512
2513         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2514           gfx.anim_random_frame = gpc->gfx_random;
2515
2516         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2517           gpc->gfx_frame = element_info[element].collect_score;
2518
2519         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2520                                               gpc->gfx_frame);
2521
2522         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2523           gfx.anim_random_frame = last_anim_random_frame;
2524       }
2525     }
2526   }
2527 }
2528
2529 void DisplayGameControlValues()
2530 {
2531   boolean redraw_panel = FALSE;
2532   int i;
2533
2534   for (i = 0; game_panel_controls[i].nr != -1; i++)
2535   {
2536     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2537
2538     if (PANEL_DEACTIVATED(gpc->pos))
2539       continue;
2540
2541     if (gpc->value == gpc->last_value &&
2542         gpc->frame == gpc->last_frame)
2543       continue;
2544
2545     redraw_panel = TRUE;
2546   }
2547
2548   if (!redraw_panel)
2549     return;
2550
2551   /* copy default game door content to main double buffer */
2552 #if 1
2553   /* !!! CHECK AGAIN !!! */
2554   SetPanelBackground();
2555   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2556   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2557 #else
2558   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2559              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2560 #endif
2561
2562   /* redraw game control buttons */
2563 #if 1
2564   RedrawGameButtons();
2565 #else
2566   UnmapGameButtons();
2567   MapGameButtons();
2568 #endif
2569
2570   game_status = GAME_MODE_PSEUDO_PANEL;
2571
2572 #if 1
2573   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2574 #else
2575   for (i = 0; game_panel_controls[i].nr != -1; i++)
2576 #endif
2577   {
2578 #if 1
2579     int nr = game_panel_order[i].nr;
2580     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2581 #else
2582     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2583     int nr = gpc->nr;
2584 #endif
2585     struct TextPosInfo *pos = gpc->pos;
2586     int type = gpc->type;
2587     int value = gpc->value;
2588     int frame = gpc->frame;
2589 #if 0
2590     int last_value = gpc->last_value;
2591     int last_frame = gpc->last_frame;
2592 #endif
2593     int size = pos->size;
2594     int font = pos->font;
2595     boolean draw_masked = pos->draw_masked;
2596     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2597
2598     if (PANEL_DEACTIVATED(pos))
2599       continue;
2600
2601 #if 0
2602     if (value == last_value && frame == last_frame)
2603       continue;
2604 #endif
2605
2606     gpc->last_value = value;
2607     gpc->last_frame = frame;
2608
2609 #if 0
2610     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2611 #endif
2612
2613     if (type == TYPE_INTEGER)
2614     {
2615       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2616           nr == GAME_PANEL_TIME)
2617       {
2618         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2619
2620         if (use_dynamic_size)           /* use dynamic number of digits */
2621         {
2622           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2623           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2624           int size2 = size1 + 1;
2625           int font1 = pos->font;
2626           int font2 = pos->font_alt;
2627
2628           size = (value < value_change ? size1 : size2);
2629           font = (value < value_change ? font1 : font2);
2630
2631 #if 0
2632           /* clear background if value just changed its size (dynamic digits) */
2633           if ((last_value < value_change) != (value < value_change))
2634           {
2635             int width1 = size1 * getFontWidth(font1);
2636             int width2 = size2 * getFontWidth(font2);
2637             int max_width = MAX(width1, width2);
2638             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2639
2640             pos->width = max_width;
2641
2642             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2643                                        max_width, max_height);
2644           }
2645 #endif
2646         }
2647       }
2648
2649 #if 1
2650       /* correct text size if "digits" is zero or less */
2651       if (size <= 0)
2652         size = strlen(int2str(value, size));
2653
2654       /* dynamically correct text alignment */
2655       pos->width = size * getFontWidth(font);
2656 #endif
2657
2658       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2659                   int2str(value, size), font, mask_mode);
2660     }
2661     else if (type == TYPE_ELEMENT)
2662     {
2663       int element, graphic;
2664       Bitmap *src_bitmap;
2665       int src_x, src_y;
2666       int width, height;
2667       int dst_x = PANEL_XPOS(pos);
2668       int dst_y = PANEL_YPOS(pos);
2669
2670 #if 1
2671       if (value != EL_UNDEFINED && value != EL_EMPTY)
2672       {
2673         element = value;
2674         graphic = el2panelimg(value);
2675
2676         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2677
2678 #if 1
2679         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2680           size = TILESIZE;
2681 #endif
2682
2683         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2684                               &src_x, &src_y);
2685
2686         width  = graphic_info[graphic].width  * size / TILESIZE;
2687         height = graphic_info[graphic].height * size / TILESIZE;
2688
2689         if (draw_masked)
2690         {
2691           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2692                         dst_x - src_x, dst_y - src_y);
2693           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2694                            dst_x, dst_y);
2695         }
2696         else
2697         {
2698           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2699                      dst_x, dst_y);
2700         }
2701       }
2702 #else
2703       if (value == EL_UNDEFINED || value == EL_EMPTY)
2704       {
2705         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2706         graphic = el2panelimg(element);
2707
2708         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2709         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2710         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2711       }
2712       else
2713       {
2714         element = value;
2715         graphic = el2panelimg(value);
2716
2717         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2718       }
2719
2720       width  = graphic_info[graphic].width  * size / TILESIZE;
2721       height = graphic_info[graphic].height * size / TILESIZE;
2722
2723       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2724 #endif
2725     }
2726     else if (type == TYPE_STRING)
2727     {
2728       boolean active = (value != 0);
2729       char *state_normal = "off";
2730       char *state_active = "on";
2731       char *state = (active ? state_active : state_normal);
2732       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2733                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2734                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2735                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2736
2737       if (nr == GAME_PANEL_GRAVITY_STATE)
2738       {
2739         int font1 = pos->font;          /* (used for normal state) */
2740         int font2 = pos->font_alt;      /* (used for active state) */
2741 #if 0
2742         int size1 = strlen(state_normal);
2743         int size2 = strlen(state_active);
2744         int width1 = size1 * getFontWidth(font1);
2745         int width2 = size2 * getFontWidth(font2);
2746         int max_width = MAX(width1, width2);
2747         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2748
2749         pos->width = max_width;
2750
2751         /* clear background for values that may have changed its size */
2752         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2753                                    max_width, max_height);
2754 #endif
2755
2756         font = (active ? font2 : font1);
2757       }
2758
2759       if (s != NULL)
2760       {
2761         char *s_cut;
2762
2763 #if 1
2764         if (size <= 0)
2765         {
2766           /* don't truncate output if "chars" is zero or less */
2767           size = strlen(s);
2768
2769           /* dynamically correct text alignment */
2770           pos->width = size * getFontWidth(font);
2771         }
2772 #endif
2773
2774         s_cut = getStringCopyN(s, size);
2775
2776         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2777                     s_cut, font, mask_mode);
2778
2779         free(s_cut);
2780       }
2781     }
2782
2783     redraw_mask |= REDRAW_DOOR_1;
2784   }
2785
2786   game_status = GAME_MODE_PLAYING;
2787 }
2788
2789 void UpdateAndDisplayGameControlValues()
2790 {
2791   if (tape.warp_forward)
2792     return;
2793
2794   UpdateGameControlValues();
2795   DisplayGameControlValues();
2796 }
2797
2798 void DrawGameValue_Emeralds(int value)
2799 {
2800   struct TextPosInfo *pos = &game.panel.gems;
2801 #if 1
2802   int font_nr = pos->font;
2803 #else
2804   int font_nr = FONT_TEXT_2;
2805 #endif
2806   int font_width = getFontWidth(font_nr);
2807   int chars = pos->size;
2808
2809 #if 1
2810   return;       /* !!! USE NEW STUFF !!! */
2811 #endif
2812
2813   if (PANEL_DEACTIVATED(pos))
2814     return;
2815
2816   pos->width = chars * font_width;
2817
2818   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2819 }
2820
2821 void DrawGameValue_Dynamite(int value)
2822 {
2823   struct TextPosInfo *pos = &game.panel.inventory_count;
2824 #if 1
2825   int font_nr = pos->font;
2826 #else
2827   int font_nr = FONT_TEXT_2;
2828 #endif
2829   int font_width = getFontWidth(font_nr);
2830   int chars = pos->size;
2831
2832 #if 1
2833   return;       /* !!! USE NEW STUFF !!! */
2834 #endif
2835
2836   if (PANEL_DEACTIVATED(pos))
2837     return;
2838
2839   pos->width = chars * font_width;
2840
2841   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2842 }
2843
2844 void DrawGameValue_Score(int value)
2845 {
2846   struct TextPosInfo *pos = &game.panel.score;
2847 #if 1
2848   int font_nr = pos->font;
2849 #else
2850   int font_nr = FONT_TEXT_2;
2851 #endif
2852   int font_width = getFontWidth(font_nr);
2853   int chars = pos->size;
2854
2855 #if 1
2856   return;       /* !!! USE NEW STUFF !!! */
2857 #endif
2858
2859   if (PANEL_DEACTIVATED(pos))
2860     return;
2861
2862   pos->width = chars * font_width;
2863
2864   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2865 }
2866
2867 void DrawGameValue_Time(int value)
2868 {
2869   struct TextPosInfo *pos = &game.panel.time;
2870   static int last_value = -1;
2871   int chars1 = 3;
2872   int chars2 = 4;
2873   int chars = pos->size;
2874 #if 1
2875   int font1_nr = pos->font;
2876   int font2_nr = pos->font_alt;
2877 #else
2878   int font1_nr = FONT_TEXT_2;
2879   int font2_nr = FONT_TEXT_1;
2880 #endif
2881   int font_nr = font1_nr;
2882   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2883
2884 #if 1
2885   return;       /* !!! USE NEW STUFF !!! */
2886 #endif
2887
2888   if (PANEL_DEACTIVATED(pos))
2889     return;
2890
2891   if (use_dynamic_chars)                /* use dynamic number of chars */
2892   {
2893     chars   = (value < 1000 ? chars1   : chars2);
2894     font_nr = (value < 1000 ? font1_nr : font2_nr);
2895   }
2896
2897   /* clear background if value just changed its size (dynamic chars only) */
2898   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2899   {
2900     int width1 = chars1 * getFontWidth(font1_nr);
2901     int width2 = chars2 * getFontWidth(font2_nr);
2902     int max_width = MAX(width1, width2);
2903     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2904
2905     pos->width = max_width;
2906
2907     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2908                                max_width, max_height);
2909   }
2910
2911   pos->width = chars * getFontWidth(font_nr);
2912
2913   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2914
2915   last_value = value;
2916 }
2917
2918 void DrawGameValue_Level(int value)
2919 {
2920   struct TextPosInfo *pos = &game.panel.level_number;
2921   int chars1 = 2;
2922   int chars2 = 3;
2923   int chars = pos->size;
2924 #if 1
2925   int font1_nr = pos->font;
2926   int font2_nr = pos->font_alt;
2927 #else
2928   int font1_nr = FONT_TEXT_2;
2929   int font2_nr = FONT_TEXT_1;
2930 #endif
2931   int font_nr = font1_nr;
2932   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2933
2934 #if 1
2935   return;       /* !!! USE NEW STUFF !!! */
2936 #endif
2937
2938   if (PANEL_DEACTIVATED(pos))
2939     return;
2940
2941   if (use_dynamic_chars)                /* use dynamic number of chars */
2942   {
2943     chars   = (level_nr < 100 ? chars1   : chars2);
2944     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2945   }
2946
2947   pos->width = chars * getFontWidth(font_nr);
2948
2949   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2950 }
2951
2952 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2953 {
2954 #if 0
2955   struct TextPosInfo *pos = &game.panel.keys;
2956 #endif
2957 #if 0
2958   int base_key_graphic = EL_KEY_1;
2959 #endif
2960   int i;
2961
2962 #if 1
2963   return;       /* !!! USE NEW STUFF !!! */
2964 #endif
2965
2966 #if 0
2967   if (PANEL_DEACTIVATED(pos))
2968     return;
2969 #endif
2970
2971 #if 0
2972   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2973     base_key_graphic = EL_EM_KEY_1;
2974 #endif
2975
2976 #if 0
2977   pos->width = 4 * MINI_TILEX;
2978 #endif
2979
2980 #if 1
2981   for (i = 0; i < MAX_NUM_KEYS; i++)
2982 #else
2983   /* currently only 4 of 8 possible keys are displayed */
2984   for (i = 0; i < STD_NUM_KEYS; i++)
2985 #endif
2986   {
2987 #if 1
2988     struct TextPosInfo *pos = &game.panel.key[i];
2989 #endif
2990     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2991     int src_y = DOOR_GFX_PAGEY1 + 123;
2992 #if 1
2993     int dst_x = PANEL_XPOS(pos);
2994     int dst_y = PANEL_YPOS(pos);
2995 #else
2996     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2997     int dst_y = PANEL_YPOS(pos);
2998 #endif
2999
3000 #if 1
3001     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
3002                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
3003                    EL_KEY_1) + i;
3004     int graphic = el2edimg(element);
3005 #endif
3006
3007 #if 1
3008     if (PANEL_DEACTIVATED(pos))
3009       continue;
3010 #endif
3011
3012 #if 0
3013     /* masked blit with tiles from half-size scaled bitmap does not work yet
3014        (no mask bitmap created for these sizes after loading and scaling) --
3015        solution: load without creating mask, scale, then create final mask */
3016
3017     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3018                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3019
3020     if (key[i])
3021     {
3022 #if 0
3023       int graphic = el2edimg(base_key_graphic + i);
3024 #endif
3025       Bitmap *src_bitmap;
3026       int src_x, src_y;
3027
3028       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3029
3030       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3031                     dst_x - src_x, dst_y - src_y);
3032       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3033                        dst_x, dst_y);
3034     }
3035 #else
3036 #if 1
3037     if (key[i])
3038       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3039     else
3040       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3041                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3042 #else
3043     if (key[i])
3044       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3045     else
3046       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3047                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3048 #endif
3049 #endif
3050   }
3051 }
3052
3053 #else
3054
3055 void DrawGameValue_Emeralds(int value)
3056 {
3057   int font_nr = FONT_TEXT_2;
3058   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3059
3060   if (PANEL_DEACTIVATED(game.panel.gems))
3061     return;
3062
3063   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3064 }
3065
3066 void DrawGameValue_Dynamite(int value)
3067 {
3068   int font_nr = FONT_TEXT_2;
3069   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3070
3071   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3072     return;
3073
3074   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3075 }
3076
3077 void DrawGameValue_Score(int value)
3078 {
3079   int font_nr = FONT_TEXT_2;
3080   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3081
3082   if (PANEL_DEACTIVATED(game.panel.score))
3083     return;
3084
3085   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3086 }
3087
3088 void DrawGameValue_Time(int value)
3089 {
3090   int font1_nr = FONT_TEXT_2;
3091 #if 1
3092   int font2_nr = FONT_TEXT_1;
3093 #else
3094   int font2_nr = FONT_LEVEL_NUMBER;
3095 #endif
3096   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3097   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3098
3099   if (PANEL_DEACTIVATED(game.panel.time))
3100     return;
3101
3102   /* clear background if value just changed its size */
3103   if (value == 999 || value == 1000)
3104     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3105
3106   if (value < 1000)
3107     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3108   else
3109     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3110 }
3111
3112 void DrawGameValue_Level(int value)
3113 {
3114   int font1_nr = FONT_TEXT_2;
3115 #if 1
3116   int font2_nr = FONT_TEXT_1;
3117 #else
3118   int font2_nr = FONT_LEVEL_NUMBER;
3119 #endif
3120
3121   if (PANEL_DEACTIVATED(game.panel.level))
3122     return;
3123
3124   if (level_nr < 100)
3125     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3126   else
3127     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3128 }
3129
3130 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3131 {
3132   int base_key_graphic = EL_KEY_1;
3133   int i;
3134
3135   if (PANEL_DEACTIVATED(game.panel.keys))
3136     return;
3137
3138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3139     base_key_graphic = EL_EM_KEY_1;
3140
3141   /* currently only 4 of 8 possible keys are displayed */
3142   for (i = 0; i < STD_NUM_KEYS; i++)
3143   {
3144     int x = XX_KEYS + i * MINI_TILEX;
3145     int y = YY_KEYS;
3146
3147     if (key[i])
3148       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3149     else
3150       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3151                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3152   }
3153 }
3154
3155 #endif
3156
3157 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3158                        int key_bits)
3159 {
3160   int key[MAX_NUM_KEYS];
3161   int i;
3162
3163   /* prevent EM engine from updating time/score values parallel to GameWon() */
3164   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3165       local_player->LevelSolved)
3166     return;
3167
3168   for (i = 0; i < MAX_NUM_KEYS; i++)
3169     key[i] = key_bits & (1 << i);
3170
3171   DrawGameValue_Level(level_nr);
3172
3173   DrawGameValue_Emeralds(emeralds);
3174   DrawGameValue_Dynamite(dynamite);
3175   DrawGameValue_Score(score);
3176   DrawGameValue_Time(time);
3177
3178   DrawGameValue_Keys(key);
3179 }
3180
3181 void UpdateGameDoorValues()
3182 {
3183   UpdateGameControlValues();
3184 }
3185
3186 void DrawGameDoorValues()
3187 {
3188   DisplayGameControlValues();
3189 }
3190
3191 void DrawGameDoorValues_OLD()
3192 {
3193   int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
3194   int dynamite_value = 0;
3195   int score_value = (local_player->LevelSolved ? local_player->score_final :
3196                      local_player->score);
3197   int gems_value = local_player->gems_still_needed;
3198   int key_bits = 0;
3199   int i, j;
3200
3201   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3202   {
3203     DrawGameDoorValues_EM();
3204
3205     return;
3206   }
3207
3208   if (game.centered_player_nr == -1)
3209   {
3210     for (i = 0; i < MAX_PLAYERS; i++)
3211     {
3212       for (j = 0; j < MAX_NUM_KEYS; j++)
3213         if (stored_player[i].key[j])
3214           key_bits |= (1 << j);
3215
3216       dynamite_value += stored_player[i].inventory_size;
3217     }
3218   }
3219   else
3220   {
3221     int player_nr = game.centered_player_nr;
3222
3223     for (i = 0; i < MAX_NUM_KEYS; i++)
3224       if (stored_player[player_nr].key[i])
3225         key_bits |= (1 << i);
3226
3227     dynamite_value = stored_player[player_nr].inventory_size;
3228   }
3229
3230   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3231                     key_bits);
3232 }
3233
3234
3235 /*
3236   =============================================================================
3237   InitGameEngine()
3238   -----------------------------------------------------------------------------
3239   initialize game engine due to level / tape version number
3240   =============================================================================
3241 */
3242
3243 static void InitGameEngine()
3244 {
3245   int i, j, k, l, x, y;
3246
3247   /* set game engine from tape file when re-playing, else from level file */
3248   game.engine_version = (tape.playing ? tape.engine_version :
3249                          level.game_version);
3250
3251   /* ---------------------------------------------------------------------- */
3252   /* set flags for bugs and changes according to active game engine version */
3253   /* ---------------------------------------------------------------------- */
3254
3255   /*
3256     Summary of bugfix/change:
3257     Fixed handling for custom elements that change when pushed by the player.
3258
3259     Fixed/changed in version:
3260     3.1.0
3261
3262     Description:
3263     Before 3.1.0, custom elements that "change when pushing" changed directly
3264     after the player started pushing them (until then handled in "DigField()").
3265     Since 3.1.0, these custom elements are not changed until the "pushing"
3266     move of the element is finished (now handled in "ContinueMoving()").
3267
3268     Affected levels/tapes:
3269     The first condition is generally needed for all levels/tapes before version
3270     3.1.0, which might use the old behaviour before it was changed; known tapes
3271     that are affected are some tapes from the level set "Walpurgis Gardens" by
3272     Jamie Cullen.
3273     The second condition is an exception from the above case and is needed for
3274     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3275     above (including some development versions of 3.1.0), but before it was
3276     known that this change would break tapes like the above and was fixed in
3277     3.1.1, so that the changed behaviour was active although the engine version
3278     while recording maybe was before 3.1.0. There is at least one tape that is
3279     affected by this exception, which is the tape for the one-level set "Bug
3280     Machine" by Juergen Bonhagen.
3281   */
3282
3283   game.use_change_when_pushing_bug =
3284     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3285      !(tape.playing &&
3286        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3287        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3288
3289   /*
3290     Summary of bugfix/change:
3291     Fixed handling for blocking the field the player leaves when moving.
3292
3293     Fixed/changed in version:
3294     3.1.1
3295
3296     Description:
3297     Before 3.1.1, when "block last field when moving" was enabled, the field
3298     the player is leaving when moving was blocked for the time of the move,
3299     and was directly unblocked afterwards. This resulted in the last field
3300     being blocked for exactly one less than the number of frames of one player
3301     move. Additionally, even when blocking was disabled, the last field was
3302     blocked for exactly one frame.
3303     Since 3.1.1, due to changes in player movement handling, the last field
3304     is not blocked at all when blocking is disabled. When blocking is enabled,
3305     the last field is blocked for exactly the number of frames of one player
3306     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3307     last field is blocked for exactly one more than the number of frames of
3308     one player move.
3309
3310     Affected levels/tapes:
3311     (!!! yet to be determined -- probably many !!!)
3312   */
3313
3314   game.use_block_last_field_bug =
3315     (game.engine_version < VERSION_IDENT(3,1,1,0));
3316
3317   /*
3318     Summary of bugfix/change:
3319     Changed behaviour of CE changes with multiple changes per single frame.
3320
3321     Fixed/changed in version:
3322     3.2.0-6
3323
3324     Description:
3325     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3326     This resulted in race conditions where CEs seem to behave strange in some
3327     situations (where triggered CE changes were just skipped because there was
3328     already a CE change on that tile in the playfield in that engine frame).
3329     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3330     (The number of changes per frame must be limited in any case, because else
3331     it is easily possible to define CE changes that would result in an infinite
3332     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3333     should be set large enough so that it would only be reached in cases where
3334     the corresponding CE change conditions run into a loop. Therefore, it seems
3335     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3336     maximal number of change pages for custom elements.)
3337
3338     Affected levels/tapes:
3339     Probably many.
3340   */
3341
3342 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3343   game.max_num_changes_per_frame = 1;
3344 #else
3345   game.max_num_changes_per_frame =
3346     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3347 #endif
3348
3349   /* ---------------------------------------------------------------------- */
3350
3351   /* default scan direction: scan playfield from top/left to bottom/right */
3352   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3353
3354   /* dynamically adjust element properties according to game engine version */
3355   InitElementPropertiesEngine(game.engine_version);
3356
3357 #if 0
3358   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3359   printf("          tape version == %06d [%s] [file: %06d]\n",
3360          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3361          tape.file_version);
3362   printf("       => game.engine_version == %06d\n", game.engine_version);
3363 #endif
3364
3365   /* ---------- initialize player's initial move delay --------------------- */
3366
3367   /* dynamically adjust player properties according to level information */
3368   for (i = 0; i < MAX_PLAYERS; i++)
3369     game.initial_move_delay_value[i] =
3370       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3371
3372   /* dynamically adjust player properties according to game engine version */
3373   for (i = 0; i < MAX_PLAYERS; i++)
3374     game.initial_move_delay[i] =
3375       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3376        game.initial_move_delay_value[i] : 0);
3377
3378   /* ---------- initialize player's initial push delay --------------------- */
3379
3380   /* dynamically adjust player properties according to game engine version */
3381   game.initial_push_delay_value =
3382     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3383
3384   /* ---------- initialize changing elements ------------------------------- */
3385
3386   /* initialize changing elements information */
3387   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3388   {
3389     struct ElementInfo *ei = &element_info[i];
3390
3391     /* this pointer might have been changed in the level editor */
3392     ei->change = &ei->change_page[0];
3393
3394     if (!IS_CUSTOM_ELEMENT(i))
3395     {
3396       ei->change->target_element = EL_EMPTY_SPACE;
3397       ei->change->delay_fixed = 0;
3398       ei->change->delay_random = 0;
3399       ei->change->delay_frames = 1;
3400     }
3401
3402     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3403     {
3404       ei->has_change_event[j] = FALSE;
3405
3406       ei->event_page_nr[j] = 0;
3407       ei->event_page[j] = &ei->change_page[0];
3408     }
3409   }
3410
3411   /* add changing elements from pre-defined list */
3412   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3413   {
3414     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3415     struct ElementInfo *ei = &element_info[ch_delay->element];
3416
3417     ei->change->target_element       = ch_delay->target_element;
3418     ei->change->delay_fixed          = ch_delay->change_delay;
3419
3420     ei->change->pre_change_function  = ch_delay->pre_change_function;
3421     ei->change->change_function      = ch_delay->change_function;
3422     ei->change->post_change_function = ch_delay->post_change_function;
3423
3424     ei->change->can_change = TRUE;
3425     ei->change->can_change_or_has_action = TRUE;
3426
3427     ei->has_change_event[CE_DELAY] = TRUE;
3428
3429     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3430     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3431   }
3432
3433   /* ---------- initialize internal run-time variables --------------------- */
3434
3435   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3436   {
3437     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3438
3439     for (j = 0; j < ei->num_change_pages; j++)
3440     {
3441       ei->change_page[j].can_change_or_has_action =
3442         (ei->change_page[j].can_change |
3443          ei->change_page[j].has_action);
3444     }
3445   }
3446
3447   /* add change events from custom element configuration */
3448   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3449   {
3450     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3451
3452     for (j = 0; j < ei->num_change_pages; j++)
3453     {
3454       if (!ei->change_page[j].can_change_or_has_action)
3455         continue;
3456
3457       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3458       {
3459         /* only add event page for the first page found with this event */
3460         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3461         {
3462           ei->has_change_event[k] = TRUE;
3463
3464           ei->event_page_nr[k] = j;
3465           ei->event_page[k] = &ei->change_page[j];
3466         }
3467       }
3468     }
3469   }
3470
3471 #if 1
3472   /* ---------- initialize reference elements in change conditions --------- */
3473
3474   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3475   {
3476     int element = EL_CUSTOM_START + i;
3477     struct ElementInfo *ei = &element_info[element];
3478
3479     for (j = 0; j < ei->num_change_pages; j++)
3480     {
3481       int trigger_element = ei->change_page[j].initial_trigger_element;
3482
3483       if (trigger_element >= EL_PREV_CE_8 &&
3484           trigger_element <= EL_NEXT_CE_8)
3485         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3486
3487       ei->change_page[j].trigger_element = trigger_element;
3488     }
3489   }
3490 #endif
3491
3492   /* ---------- initialize run-time trigger player and element ------------- */
3493
3494   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3495   {
3496     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3497
3498     for (j = 0; j < ei->num_change_pages; j++)
3499     {
3500       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3501       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3502       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3503       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3504       ei->change_page[j].actual_trigger_ce_value = 0;
3505       ei->change_page[j].actual_trigger_ce_score = 0;
3506     }
3507   }
3508
3509   /* ---------- initialize trigger events ---------------------------------- */
3510
3511   /* initialize trigger events information */
3512   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3513     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3514       trigger_events[i][j] = FALSE;
3515
3516   /* add trigger events from element change event properties */
3517   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3518   {
3519     struct ElementInfo *ei = &element_info[i];
3520
3521     for (j = 0; j < ei->num_change_pages; j++)
3522     {
3523       if (!ei->change_page[j].can_change_or_has_action)
3524         continue;
3525
3526       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3527       {
3528         int trigger_element = ei->change_page[j].trigger_element;
3529
3530         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3531         {
3532           if (ei->change_page[j].has_event[k])
3533           {
3534             if (IS_GROUP_ELEMENT(trigger_element))
3535             {
3536               struct ElementGroupInfo *group =
3537                 element_info[trigger_element].group;
3538
3539               for (l = 0; l < group->num_elements_resolved; l++)
3540                 trigger_events[group->element_resolved[l]][k] = TRUE;
3541             }
3542             else if (trigger_element == EL_ANY_ELEMENT)
3543               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3544                 trigger_events[l][k] = TRUE;
3545             else
3546               trigger_events[trigger_element][k] = TRUE;
3547           }
3548         }
3549       }
3550     }
3551   }
3552
3553   /* ---------- initialize push delay -------------------------------------- */
3554
3555   /* initialize push delay values to default */
3556   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3557   {
3558     if (!IS_CUSTOM_ELEMENT(i))
3559     {
3560       /* set default push delay values (corrected since version 3.0.7-1) */
3561       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3562       {
3563         element_info[i].push_delay_fixed = 2;
3564         element_info[i].push_delay_random = 8;
3565       }
3566       else
3567       {
3568         element_info[i].push_delay_fixed = 8;
3569         element_info[i].push_delay_random = 8;
3570       }
3571     }
3572   }
3573
3574   /* set push delay value for certain elements from pre-defined list */
3575   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3576   {
3577     int e = push_delay_list[i].element;
3578
3579     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3580     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3581   }
3582
3583   /* set push delay value for Supaplex elements for newer engine versions */
3584   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3585   {
3586     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3587     {
3588       if (IS_SP_ELEMENT(i))
3589       {
3590         /* set SP push delay to just enough to push under a falling zonk */
3591         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3592
3593         element_info[i].push_delay_fixed  = delay;
3594         element_info[i].push_delay_random = 0;
3595       }
3596     }
3597   }
3598
3599   /* ---------- initialize move stepsize ----------------------------------- */
3600
3601   /* initialize move stepsize values to default */
3602   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3603     if (!IS_CUSTOM_ELEMENT(i))
3604       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3605
3606   /* set move stepsize value for certain elements from pre-defined list */
3607   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3608   {
3609     int e = move_stepsize_list[i].element;
3610
3611     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3612   }
3613
3614   /* ---------- initialize collect score ----------------------------------- */
3615
3616   /* initialize collect score values for custom elements from initial value */
3617   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3618     if (IS_CUSTOM_ELEMENT(i))
3619       element_info[i].collect_score = element_info[i].collect_score_initial;
3620
3621   /* ---------- initialize collect count ----------------------------------- */
3622
3623   /* initialize collect count values for non-custom elements */
3624   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3625     if (!IS_CUSTOM_ELEMENT(i))
3626       element_info[i].collect_count_initial = 0;
3627
3628   /* add collect count values for all elements from pre-defined list */
3629   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3630     element_info[collect_count_list[i].element].collect_count_initial =
3631       collect_count_list[i].count;
3632
3633   /* ---------- initialize access direction -------------------------------- */
3634
3635   /* initialize access direction values to default (access from every side) */
3636   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3637     if (!IS_CUSTOM_ELEMENT(i))
3638       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3639
3640   /* set access direction value for certain elements from pre-defined list */
3641   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3642     element_info[access_direction_list[i].element].access_direction =
3643       access_direction_list[i].direction;
3644
3645   /* ---------- initialize explosion content ------------------------------- */
3646   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3647   {
3648     if (IS_CUSTOM_ELEMENT(i))
3649       continue;
3650
3651     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3652     {
3653       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3654
3655       element_info[i].content.e[x][y] =
3656         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3657          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3658          i == EL_PLAYER_3 ? EL_EMERALD :
3659          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3660          i == EL_MOLE ? EL_EMERALD_RED :
3661          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3662          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3663          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3664          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3665          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3666          i == EL_WALL_EMERALD ? EL_EMERALD :
3667          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3668          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3669          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3670          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3671          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3672          i == EL_WALL_PEARL ? EL_PEARL :
3673          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3674          EL_EMPTY);
3675     }
3676   }
3677
3678   /* ---------- initialize recursion detection ------------------------------ */
3679   recursion_loop_depth = 0;
3680   recursion_loop_detected = FALSE;
3681   recursion_loop_element = EL_UNDEFINED;
3682
3683   /* ---------- initialize graphics engine ---------------------------------- */
3684   game.scroll_delay_value =
3685     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3686      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3687   game.scroll_delay_value =
3688     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3689 }
3690
3691 int get_num_special_action(int element, int action_first, int action_last)
3692 {
3693   int num_special_action = 0;
3694   int i, j;
3695
3696   for (i = action_first; i <= action_last; i++)
3697   {
3698     boolean found = FALSE;
3699
3700     for (j = 0; j < NUM_DIRECTIONS; j++)
3701       if (el_act_dir2img(element, i, j) !=
3702           el_act_dir2img(element, ACTION_DEFAULT, j))
3703         found = TRUE;
3704
3705     if (found)
3706       num_special_action++;
3707     else
3708       break;
3709   }
3710
3711   return num_special_action;
3712 }
3713
3714
3715 /*
3716   =============================================================================
3717   InitGame()
3718   -----------------------------------------------------------------------------
3719   initialize and start new game
3720   =============================================================================
3721 */
3722
3723 void InitGame()
3724 {
3725   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3726   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3727   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3728 #if 0
3729   boolean do_fading = (game_status == GAME_MODE_MAIN);
3730 #endif
3731 #if 1
3732   int initial_move_dir = MV_DOWN;
3733 #else
3734   int initial_move_dir = MV_NONE;
3735 #endif
3736   int i, j, x, y;
3737
3738   game_status = GAME_MODE_PLAYING;
3739
3740 #if 1
3741   /* needed if different viewport properties defined for playing */
3742   ChangeViewportPropertiesIfNeeded();
3743 #endif
3744
3745 #if 1
3746   DrawCompleteVideoDisplay();
3747 #endif
3748
3749   InitGameEngine();
3750   InitGameControlValues();
3751
3752   /* don't play tapes over network */
3753   network_playing = (options.network && !tape.playing);
3754
3755   for (i = 0; i < MAX_PLAYERS; i++)
3756   {
3757     struct PlayerInfo *player = &stored_player[i];
3758
3759     player->index_nr = i;
3760     player->index_bit = (1 << i);
3761     player->element_nr = EL_PLAYER_1 + i;
3762
3763     player->present = FALSE;
3764     player->active = FALSE;
3765     player->mapped = FALSE;
3766
3767     player->killed = FALSE;
3768     player->reanimated = FALSE;
3769
3770     player->action = 0;
3771     player->effective_action = 0;
3772     player->programmed_action = 0;
3773
3774     player->score = 0;
3775     player->score_final = 0;
3776
3777     player->gems_still_needed = level.gems_needed;
3778     player->sokobanfields_still_needed = 0;
3779     player->lights_still_needed = 0;
3780     player->friends_still_needed = 0;
3781
3782     for (j = 0; j < MAX_NUM_KEYS; j++)
3783       player->key[j] = FALSE;
3784
3785     player->num_white_keys = 0;
3786
3787     player->dynabomb_count = 0;
3788     player->dynabomb_size = 1;
3789     player->dynabombs_left = 0;
3790     player->dynabomb_xl = FALSE;
3791
3792     player->MovDir = initial_move_dir;
3793     player->MovPos = 0;
3794     player->GfxPos = 0;
3795     player->GfxDir = initial_move_dir;
3796     player->GfxAction = ACTION_DEFAULT;
3797     player->Frame = 0;
3798     player->StepFrame = 0;
3799
3800     player->initial_element = player->element_nr;
3801     player->artwork_element =
3802       (level.use_artwork_element[i] ? level.artwork_element[i] :
3803        player->element_nr);
3804     player->use_murphy = FALSE;
3805
3806     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3807     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3808
3809     player->gravity = level.initial_player_gravity[i];
3810
3811     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3812
3813     player->actual_frame_counter = 0;
3814
3815     player->step_counter = 0;
3816
3817     player->last_move_dir = initial_move_dir;
3818
3819     player->is_active = FALSE;
3820
3821     player->is_waiting = FALSE;
3822     player->is_moving = FALSE;
3823     player->is_auto_moving = FALSE;
3824     player->is_digging = FALSE;
3825     player->is_snapping = FALSE;
3826     player->is_collecting = FALSE;
3827     player->is_pushing = FALSE;
3828     player->is_switching = FALSE;
3829     player->is_dropping = FALSE;
3830     player->is_dropping_pressed = FALSE;
3831
3832     player->is_bored = FALSE;
3833     player->is_sleeping = FALSE;
3834
3835     player->frame_counter_bored = -1;
3836     player->frame_counter_sleeping = -1;
3837
3838     player->anim_delay_counter = 0;
3839     player->post_delay_counter = 0;
3840
3841     player->dir_waiting = initial_move_dir;
3842     player->action_waiting = ACTION_DEFAULT;
3843     player->last_action_waiting = ACTION_DEFAULT;
3844     player->special_action_bored = ACTION_DEFAULT;
3845     player->special_action_sleeping = ACTION_DEFAULT;
3846
3847     player->switch_x = -1;
3848     player->switch_y = -1;
3849
3850     player->drop_x = -1;
3851     player->drop_y = -1;
3852
3853     player->show_envelope = 0;
3854
3855     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3856
3857     player->push_delay       = -1;      /* initialized when pushing starts */
3858     player->push_delay_value = game.initial_push_delay_value;
3859
3860     player->drop_delay = 0;
3861     player->drop_pressed_delay = 0;
3862
3863     player->last_jx = -1;
3864     player->last_jy = -1;
3865     player->jx = -1;
3866     player->jy = -1;
3867
3868     player->shield_normal_time_left = 0;
3869     player->shield_deadly_time_left = 0;
3870
3871     player->inventory_infinite_element = EL_UNDEFINED;
3872     player->inventory_size = 0;
3873
3874     if (level.use_initial_inventory[i])
3875     {
3876       for (j = 0; j < level.initial_inventory_size[i]; j++)
3877       {
3878         int element = level.initial_inventory_content[i][j];
3879         int collect_count = element_info[element].collect_count_initial;
3880         int k;
3881
3882         if (!IS_CUSTOM_ELEMENT(element))
3883           collect_count = 1;
3884
3885         if (collect_count == 0)
3886           player->inventory_infinite_element = element;
3887         else
3888           for (k = 0; k < collect_count; k++)
3889             if (player->inventory_size < MAX_INVENTORY_SIZE)
3890               player->inventory_element[player->inventory_size++] = element;
3891       }
3892     }
3893
3894     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3895     SnapField(player, 0, 0);
3896
3897     player->LevelSolved = FALSE;
3898     player->GameOver = FALSE;
3899
3900     player->LevelSolved_GameWon = FALSE;
3901     player->LevelSolved_GameEnd = FALSE;
3902     player->LevelSolved_PanelOff = FALSE;
3903     player->LevelSolved_SaveTape = FALSE;
3904     player->LevelSolved_SaveScore = FALSE;
3905     player->LevelSolved_CountingTime = 0;
3906     player->LevelSolved_CountingScore = 0;
3907
3908     map_player_action[i] = i;
3909   }
3910
3911   network_player_action_received = FALSE;
3912
3913 #if defined(NETWORK_AVALIABLE)
3914   /* initial null action */
3915   if (network_playing)
3916     SendToServer_MovePlayer(MV_NONE);
3917 #endif
3918
3919   ZX = ZY = -1;
3920   ExitX = ExitY = -1;
3921
3922   FrameCounter = 0;
3923   TimeFrames = 0;
3924   TimePlayed = 0;
3925   TimeLeft = level.time;
3926   TapeTime = 0;
3927
3928   ScreenMovDir = MV_NONE;
3929   ScreenMovPos = 0;
3930   ScreenGfxPos = 0;
3931
3932   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3933
3934   AllPlayersGone = FALSE;
3935
3936   game.no_time_limit = (level.time == 0);
3937
3938   game.yamyam_content_nr = 0;
3939   game.robot_wheel_active = FALSE;
3940   game.magic_wall_active = FALSE;
3941   game.magic_wall_time_left = 0;
3942   game.light_time_left = 0;
3943   game.timegate_time_left = 0;
3944   game.switchgate_pos = 0;
3945   game.wind_direction = level.wind_direction_initial;
3946
3947 #if !USE_PLAYER_GRAVITY
3948   game.gravity = FALSE;
3949   game.explosions_delayed = TRUE;
3950 #endif
3951
3952   game.lenses_time_left = 0;
3953   game.magnify_time_left = 0;
3954
3955   game.ball_state = level.ball_state_initial;
3956   game.ball_content_nr = 0;
3957
3958   game.envelope_active = FALSE;
3959
3960   /* set focus to local player for network games, else to all players */
3961   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3962   game.centered_player_nr_next = game.centered_player_nr;
3963   game.set_centered_player = FALSE;
3964
3965   if (network_playing && tape.recording)
3966   {
3967     /* store client dependent player focus when recording network games */
3968     tape.centered_player_nr_next = game.centered_player_nr_next;
3969     tape.set_centered_player = TRUE;
3970   }
3971
3972   for (i = 0; i < NUM_BELTS; i++)
3973   {
3974     game.belt_dir[i] = MV_NONE;
3975     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3976   }
3977
3978   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3979     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3980
3981   SCAN_PLAYFIELD(x, y)
3982   {
3983     Feld[x][y] = level.field[x][y];
3984     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3985     ChangeDelay[x][y] = 0;
3986     ChangePage[x][y] = -1;
3987 #if USE_NEW_CUSTOM_VALUE
3988     CustomValue[x][y] = 0;              /* initialized in InitField() */
3989 #endif
3990     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3991     AmoebaNr[x][y] = 0;
3992     WasJustMoving[x][y] = 0;
3993     WasJustFalling[x][y] = 0;
3994     CheckCollision[x][y] = 0;
3995     CheckImpact[x][y] = 0;
3996     Stop[x][y] = FALSE;
3997     Pushed[x][y] = FALSE;
3998
3999     ChangeCount[x][y] = 0;
4000     ChangeEvent[x][y] = -1;
4001
4002     ExplodePhase[x][y] = 0;
4003     ExplodeDelay[x][y] = 0;
4004     ExplodeField[x][y] = EX_TYPE_NONE;
4005
4006     RunnerVisit[x][y] = 0;
4007     PlayerVisit[x][y] = 0;
4008
4009     GfxFrame[x][y] = 0;
4010     GfxRandom[x][y] = INIT_GFX_RANDOM();
4011     GfxElement[x][y] = EL_UNDEFINED;
4012     GfxAction[x][y] = ACTION_DEFAULT;
4013     GfxDir[x][y] = MV_NONE;
4014     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4015   }
4016
4017   SCAN_PLAYFIELD(x, y)
4018   {
4019     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4020       emulate_bd = FALSE;
4021     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4022       emulate_sb = FALSE;
4023     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4024       emulate_sp = FALSE;
4025
4026     InitField(x, y, TRUE);
4027
4028     ResetGfxAnimation(x, y);
4029   }
4030
4031   InitBeltMovement();
4032
4033   for (i = 0; i < MAX_PLAYERS; i++)
4034   {
4035     struct PlayerInfo *player = &stored_player[i];
4036
4037     /* set number of special actions for bored and sleeping animation */
4038     player->num_special_action_bored =
4039       get_num_special_action(player->artwork_element,
4040                              ACTION_BORING_1, ACTION_BORING_LAST);
4041     player->num_special_action_sleeping =
4042       get_num_special_action(player->artwork_element,
4043                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4044   }
4045
4046   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4047                     emulate_sb ? EMU_SOKOBAN :
4048                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4049
4050 #if USE_NEW_ALL_SLIPPERY
4051   /* initialize type of slippery elements */
4052   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4053   {
4054     if (!IS_CUSTOM_ELEMENT(i))
4055     {
4056       /* default: elements slip down either to the left or right randomly */
4057       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4058
4059       /* SP style elements prefer to slip down on the left side */
4060       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4061         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4062
4063       /* BD style elements prefer to slip down on the left side */
4064       if (game.emulation == EMU_BOULDERDASH)
4065         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4066     }
4067   }
4068 #endif
4069
4070   /* initialize explosion and ignition delay */
4071   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4072   {
4073     if (!IS_CUSTOM_ELEMENT(i))
4074     {
4075       int num_phase = 8;
4076       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4077                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4078                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4079       int last_phase = (num_phase + 1) * delay;
4080       int half_phase = (num_phase / 2) * delay;
4081
4082       element_info[i].explosion_delay = last_phase - 1;
4083       element_info[i].ignition_delay = half_phase;
4084
4085       if (i == EL_BLACK_ORB)
4086         element_info[i].ignition_delay = 1;
4087     }
4088
4089 #if 0
4090     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4091       element_info[i].explosion_delay = 1;
4092
4093     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4094       element_info[i].ignition_delay = 1;
4095 #endif
4096   }
4097
4098   /* correct non-moving belts to start moving left */
4099   for (i = 0; i < NUM_BELTS; i++)
4100     if (game.belt_dir[i] == MV_NONE)
4101       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4102
4103 #if USE_NEW_PLAYER_ASSIGNMENTS
4104   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4105   /* choose default local player */
4106   local_player = &stored_player[0];
4107
4108   for (i = 0; i < MAX_PLAYERS; i++)
4109     stored_player[i].connected = FALSE;
4110
4111   local_player->connected = TRUE;
4112   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4113
4114   if (tape.playing)
4115   {
4116     /* try to guess locally connected team mode players (needed for correct
4117        assignment of player figures from level to locally playing players) */
4118
4119     for (i = 0; i < MAX_PLAYERS; i++)
4120       if (tape.player_participates[i])
4121         stored_player[i].connected = TRUE;
4122   }
4123   else if (setup.team_mode && !options.network)
4124   {
4125     /* try to guess locally connected team mode players (needed for correct
4126        assignment of player figures from level to locally playing players) */
4127
4128     for (i = 0; i < MAX_PLAYERS; i++)
4129       if (setup.input[i].use_joystick ||
4130           setup.input[i].key.left != KSYM_UNDEFINED)
4131         stored_player[i].connected = TRUE;
4132   }
4133
4134 #if 0
4135   for (i = 0; i < MAX_PLAYERS; i++)
4136     printf("::: player %d: %s\n", i,
4137            (stored_player[i].connected ? "connected" : "not connected"));
4138
4139   for (i = 0; i < MAX_PLAYERS; i++)
4140     printf("::: player %d: %s\n", i,
4141            (stored_player[i].present ? "present" : "not present"));
4142 #endif
4143
4144   /* check if any connected player was not found in playfield */
4145   for (i = 0; i < MAX_PLAYERS; i++)
4146   {
4147     struct PlayerInfo *player = &stored_player[i];
4148
4149     if (player->connected && !player->present)
4150     {
4151       struct PlayerInfo *field_player = NULL;
4152
4153 #if 0
4154       printf("::: looking for field player for player %d ...\n", i);
4155 #endif
4156
4157       /* assign first free player found that is present in the playfield */
4158
4159       /* first try: look for unmapped playfield player that is not connected */
4160       if (field_player == NULL)
4161         for (j = 0; j < MAX_PLAYERS; j++)
4162           if (stored_player[j].present &&
4163               !stored_player[j].mapped &&
4164               !stored_player[j].connected)
4165             field_player = &stored_player[j];
4166
4167       /* second try: look for *any* unmapped playfield player */
4168       if (field_player == NULL)
4169         for (j = 0; j < MAX_PLAYERS; j++)
4170           if (stored_player[j].present &&
4171               !stored_player[j].mapped)
4172             field_player = &stored_player[j];
4173
4174       if (field_player != NULL)
4175       {
4176         int jx = field_player->jx, jy = field_player->jy;
4177
4178 #if 0
4179         printf("::: found player figure %d\n", field_player->index_nr);
4180 #endif
4181
4182         player->present = FALSE;
4183         player->active = FALSE;
4184
4185         field_player->present = TRUE;
4186         field_player->active = TRUE;
4187
4188         /*
4189         player->initial_element = field_player->initial_element;
4190         player->artwork_element = field_player->artwork_element;
4191
4192         player->block_last_field       = field_player->block_last_field;
4193         player->block_delay_adjustment = field_player->block_delay_adjustment;
4194         */
4195
4196         StorePlayer[jx][jy] = field_player->element_nr;
4197
4198         field_player->jx = field_player->last_jx = jx;
4199         field_player->jy = field_player->last_jy = jy;
4200
4201         if (local_player == player)
4202           local_player = field_player;
4203
4204         map_player_action[field_player->index_nr] = i;
4205
4206         field_player->mapped = TRUE;
4207
4208 #if 0
4209         printf("::: map_player_action[%d] == %d\n",
4210                field_player->index_nr, i);
4211 #endif
4212       }
4213     }
4214
4215     if (player->connected && player->present)
4216       player->mapped = TRUE;
4217   }
4218
4219 #else
4220
4221   /* check if any connected player was not found in playfield */
4222   for (i = 0; i < MAX_PLAYERS; i++)
4223   {
4224     struct PlayerInfo *player = &stored_player[i];
4225
4226     if (player->connected && !player->present)
4227     {
4228       for (j = 0; j < MAX_PLAYERS; j++)
4229       {
4230         struct PlayerInfo *field_player = &stored_player[j];
4231         int jx = field_player->jx, jy = field_player->jy;
4232
4233         /* assign first free player found that is present in the playfield */
4234         if (field_player->present && !field_player->connected)
4235         {
4236           player->present = TRUE;
4237           player->active = TRUE;
4238
4239           field_player->present = FALSE;
4240           field_player->active = FALSE;
4241
4242           player->initial_element = field_player->initial_element;
4243           player->artwork_element = field_player->artwork_element;
4244
4245           player->block_last_field       = field_player->block_last_field;
4246           player->block_delay_adjustment = field_player->block_delay_adjustment;
4247
4248           StorePlayer[jx][jy] = player->element_nr;
4249
4250           player->jx = player->last_jx = jx;
4251           player->jy = player->last_jy = jy;
4252
4253           break;
4254         }
4255       }
4256     }
4257   }
4258 #endif
4259
4260 #if 0
4261   printf("::: local_player->present == %d\n", local_player->present);
4262 #endif
4263
4264   if (tape.playing)
4265   {
4266     /* when playing a tape, eliminate all players who do not participate */
4267
4268 #if USE_NEW_PLAYER_ASSIGNMENTS
4269     for (i = 0; i < MAX_PLAYERS; i++)
4270     {
4271       if (stored_player[i].active &&
4272           !tape.player_participates[map_player_action[i]])
4273       {
4274         struct PlayerInfo *player = &stored_player[i];
4275         int jx = player->jx, jy = player->jy;
4276
4277         player->active = FALSE;
4278         StorePlayer[jx][jy] = 0;
4279         Feld[jx][jy] = EL_EMPTY;
4280       }
4281     }
4282 #else
4283     for (i = 0; i < MAX_PLAYERS; i++)
4284     {
4285       if (stored_player[i].active &&
4286           !tape.player_participates[i])
4287       {
4288         struct PlayerInfo *player = &stored_player[i];
4289         int jx = player->jx, jy = player->jy;
4290
4291         player->active = FALSE;
4292         StorePlayer[jx][jy] = 0;
4293         Feld[jx][jy] = EL_EMPTY;
4294       }
4295     }
4296 #endif
4297   }
4298   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4299   {
4300     /* when in single player mode, eliminate all but the first active player */
4301
4302     for (i = 0; i < MAX_PLAYERS; i++)
4303     {
4304       if (stored_player[i].active)
4305       {
4306         for (j = i + 1; j < MAX_PLAYERS; j++)
4307         {
4308           if (stored_player[j].active)
4309           {
4310             struct PlayerInfo *player = &stored_player[j];
4311             int jx = player->jx, jy = player->jy;
4312
4313             player->active = FALSE;
4314             player->present = FALSE;
4315
4316             StorePlayer[jx][jy] = 0;
4317             Feld[jx][jy] = EL_EMPTY;
4318           }
4319         }
4320       }
4321     }
4322   }
4323
4324   /* when recording the game, store which players take part in the game */
4325   if (tape.recording)
4326   {
4327 #if USE_NEW_PLAYER_ASSIGNMENTS
4328     for (i = 0; i < MAX_PLAYERS; i++)
4329       if (stored_player[i].connected)
4330         tape.player_participates[i] = TRUE;
4331 #else
4332     for (i = 0; i < MAX_PLAYERS; i++)
4333       if (stored_player[i].active)
4334         tape.player_participates[i] = TRUE;
4335 #endif
4336   }
4337
4338   if (options.debug)
4339   {
4340     for (i = 0; i < MAX_PLAYERS; i++)
4341     {
4342       struct PlayerInfo *player = &stored_player[i];
4343
4344       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4345              i+1,
4346              player->present,
4347              player->connected,
4348              player->active);
4349       if (local_player == player)
4350         printf("Player  %d is local player.\n", i+1);
4351     }
4352   }
4353
4354   if (BorderElement == EL_EMPTY)
4355   {
4356     SBX_Left = 0;
4357     SBX_Right = lev_fieldx - SCR_FIELDX;
4358     SBY_Upper = 0;
4359     SBY_Lower = lev_fieldy - SCR_FIELDY;
4360   }
4361   else
4362   {
4363     SBX_Left = -1;
4364     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4365     SBY_Upper = -1;
4366     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4367   }
4368
4369 #if NEW_TILESIZE
4370
4371   if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4372     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4373
4374   if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4375     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4376
4377   if (EVEN(SCR_FIELDX))
4378     SBX_Left--;
4379   if (EVEN(SCR_FIELDY))
4380     SBY_Upper--;
4381
4382 #else
4383
4384   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4385     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4386
4387   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4388     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4389 #endif
4390
4391   /* if local player not found, look for custom element that might create
4392      the player (make some assumptions about the right custom element) */
4393   if (!local_player->present)
4394   {
4395     int start_x = 0, start_y = 0;
4396     int found_rating = 0;
4397     int found_element = EL_UNDEFINED;
4398     int player_nr = local_player->index_nr;
4399
4400     SCAN_PLAYFIELD(x, y)
4401     {
4402       int element = Feld[x][y];
4403       int content;
4404       int xx, yy;
4405       boolean is_player;
4406
4407       if (level.use_start_element[player_nr] &&
4408           level.start_element[player_nr] == element &&
4409           found_rating < 4)
4410       {
4411         start_x = x;
4412         start_y = y;
4413
4414         found_rating = 4;
4415         found_element = element;
4416       }
4417
4418       if (!IS_CUSTOM_ELEMENT(element))
4419         continue;
4420
4421       if (CAN_CHANGE(element))
4422       {
4423         for (i = 0; i < element_info[element].num_change_pages; i++)
4424         {
4425           /* check for player created from custom element as single target */
4426           content = element_info[element].change_page[i].target_element;
4427           is_player = ELEM_IS_PLAYER(content);
4428
4429           if (is_player && (found_rating < 3 ||
4430                             (found_rating == 3 && element < found_element)))
4431           {
4432             start_x = x;
4433             start_y = y;
4434
4435             found_rating = 3;
4436             found_element = element;
4437           }
4438         }
4439       }
4440
4441       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4442       {
4443         /* check for player created from custom element as explosion content */
4444         content = element_info[element].content.e[xx][yy];
4445         is_player = ELEM_IS_PLAYER(content);
4446
4447         if (is_player && (found_rating < 2 ||
4448                           (found_rating == 2 && element < found_element)))
4449         {
4450           start_x = x + xx - 1;
4451           start_y = y + yy - 1;
4452
4453           found_rating = 2;
4454           found_element = element;
4455         }
4456
4457         if (!CAN_CHANGE(element))
4458           continue;
4459
4460         for (i = 0; i < element_info[element].num_change_pages; i++)
4461         {
4462           /* check for player created from custom element as extended target */
4463           content =
4464             element_info[element].change_page[i].target_content.e[xx][yy];
4465
4466           is_player = ELEM_IS_PLAYER(content);
4467
4468           if (is_player && (found_rating < 1 ||
4469                             (found_rating == 1 && element < found_element)))
4470           {
4471             start_x = x + xx - 1;
4472             start_y = y + yy - 1;
4473
4474             found_rating = 1;
4475             found_element = element;
4476           }
4477         }
4478       }
4479     }
4480
4481     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4482                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4483                 start_x - MIDPOSX);
4484
4485     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4486                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4487                 start_y - MIDPOSY);
4488   }
4489   else
4490   {
4491     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4492                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4493                 local_player->jx - MIDPOSX);
4494
4495     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4496                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4497                 local_player->jy - MIDPOSY);
4498   }
4499
4500 #if 0
4501   printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4502 #endif
4503
4504 #if 0
4505   /* do not use PLAYING mask for fading out from main screen */
4506   game_status = GAME_MODE_MAIN;
4507 #endif
4508
4509   StopAnimation();
4510
4511   if (!game.restart_level)
4512     CloseDoor(DOOR_CLOSE_1);
4513
4514 #if 1
4515   if (level_editor_test_game)
4516     FadeSkipNextFadeIn();
4517   else
4518     FadeSetEnterScreen();
4519 #else
4520   if (level_editor_test_game)
4521     fading = fading_none;
4522   else
4523     fading = menu.destination;
4524 #endif
4525
4526 #if 1
4527   FadeOut(REDRAW_FIELD);
4528 #else
4529   if (do_fading)
4530     FadeOut(REDRAW_FIELD);
4531 #endif
4532
4533 #if 0
4534   game_status = GAME_MODE_PLAYING;
4535 #endif
4536
4537   /* !!! FIX THIS (START) !!! */
4538   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4539   {
4540     InitGameEngine_EM();
4541
4542     /* blit playfield from scroll buffer to normal back buffer for fading in */
4543     BlitScreenToBitmap_EM(backbuffer);
4544   }
4545   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4546   {
4547     InitGameEngine_SP();
4548
4549     /* blit playfield from scroll buffer to normal back buffer for fading in */
4550     BlitScreenToBitmap_SP(backbuffer);
4551   }
4552   else
4553   {
4554     DrawLevel();
4555     DrawAllPlayers();
4556
4557     /* after drawing the level, correct some elements */
4558     if (game.timegate_time_left == 0)
4559       CloseAllOpenTimegates();
4560
4561 #if NEW_TILESIZE
4562     BlitScreenToBitmap(backbuffer);
4563 #else
4564     /* blit playfield from scroll buffer to normal back buffer for fading in */
4565     if (setup.soft_scrolling)
4566       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4567 #endif
4568
4569     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4570   }
4571   /* !!! FIX THIS (END) !!! */
4572
4573 #if 1
4574   FadeIn(REDRAW_FIELD);
4575 #else
4576   if (do_fading)
4577     FadeIn(REDRAW_FIELD);
4578
4579   BackToFront();
4580 #endif
4581
4582   if (!game.restart_level)
4583   {
4584     /* copy default game door content to main double buffer */
4585 #if 1
4586 #if 1
4587     /* !!! CHECK AGAIN !!! */
4588     SetPanelBackground();
4589     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4590     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4591 #else
4592     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4593
4594     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4595     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4596     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4597                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4598 #endif
4599 #else
4600     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4601                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4602 #endif
4603   }
4604
4605   SetPanelBackground();
4606   SetDrawBackgroundMask(REDRAW_DOOR_1);
4607
4608 #if 1
4609   UpdateAndDisplayGameControlValues();
4610 #else
4611   UpdateGameDoorValues();
4612   DrawGameDoorValues();
4613 #endif
4614
4615   if (!game.restart_level)
4616   {
4617     UnmapGameButtons();
4618     UnmapTapeButtons();
4619     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4620     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4621     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4622     MapGameButtons();
4623     MapTapeButtons();
4624
4625     /* copy actual game door content to door double buffer for OpenDoor() */
4626     BlitBitmap(drawto, bitmap_db_door,
4627                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4628
4629     OpenDoor(DOOR_OPEN_ALL);
4630
4631     PlaySound(SND_GAME_STARTING);
4632
4633     if (setup.sound_music)
4634       PlayLevelMusic();
4635
4636     KeyboardAutoRepeatOffUnlessAutoplay();
4637
4638     if (options.debug)
4639     {
4640       for (i = 0; i < MAX_PLAYERS; i++)
4641         printf("Player %d %sactive.\n",
4642                i + 1, (stored_player[i].active ? "" : "not "));
4643     }
4644   }
4645
4646 #if 1
4647   UnmapAllGadgets();
4648
4649   MapGameButtons();
4650   MapTapeButtons();
4651 #endif
4652
4653   if (!game.restart_level && !tape.playing)
4654   {
4655     LevelStats_incPlayed(level_nr);
4656
4657     SaveLevelSetup_SeriesInfo();
4658
4659 #if 0
4660     printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4661 #endif
4662   }
4663
4664   game.restart_level = FALSE;
4665 }
4666
4667 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4668 {
4669   /* this is used for non-R'n'D game engines to update certain engine values */
4670
4671   /* needed to determine if sounds are played within the visible screen area */
4672   scroll_x = actual_scroll_x;
4673   scroll_y = actual_scroll_y;
4674 }
4675
4676 void InitMovDir(int x, int y)
4677 {
4678   int i, element = Feld[x][y];
4679   static int xy[4][2] =
4680   {
4681     {  0, +1 },
4682     { +1,  0 },
4683     {  0, -1 },
4684     { -1,  0 }
4685   };
4686   static int direction[3][4] =
4687   {
4688     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4689     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4690     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4691   };
4692
4693   switch (element)
4694   {
4695     case EL_BUG_RIGHT:
4696     case EL_BUG_UP:
4697     case EL_BUG_LEFT:
4698     case EL_BUG_DOWN:
4699       Feld[x][y] = EL_BUG;
4700       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4701       break;
4702
4703     case EL_SPACESHIP_RIGHT:
4704     case EL_SPACESHIP_UP:
4705     case EL_SPACESHIP_LEFT:
4706     case EL_SPACESHIP_DOWN:
4707       Feld[x][y] = EL_SPACESHIP;
4708       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4709       break;
4710
4711     case EL_BD_BUTTERFLY_RIGHT:
4712     case EL_BD_BUTTERFLY_UP:
4713     case EL_BD_BUTTERFLY_LEFT:
4714     case EL_BD_BUTTERFLY_DOWN:
4715       Feld[x][y] = EL_BD_BUTTERFLY;
4716       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4717       break;
4718
4719     case EL_BD_FIREFLY_RIGHT:
4720     case EL_BD_FIREFLY_UP:
4721     case EL_BD_FIREFLY_LEFT:
4722     case EL_BD_FIREFLY_DOWN:
4723       Feld[x][y] = EL_BD_FIREFLY;
4724       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4725       break;
4726
4727     case EL_PACMAN_RIGHT:
4728     case EL_PACMAN_UP:
4729     case EL_PACMAN_LEFT:
4730     case EL_PACMAN_DOWN:
4731       Feld[x][y] = EL_PACMAN;
4732       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4733       break;
4734
4735     case EL_YAMYAM_LEFT:
4736     case EL_YAMYAM_RIGHT:
4737     case EL_YAMYAM_UP:
4738     case EL_YAMYAM_DOWN:
4739       Feld[x][y] = EL_YAMYAM;
4740       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4741       break;
4742
4743     case EL_SP_SNIKSNAK:
4744       MovDir[x][y] = MV_UP;
4745       break;
4746
4747     case EL_SP_ELECTRON:
4748       MovDir[x][y] = MV_LEFT;
4749       break;
4750
4751     case EL_MOLE_LEFT:
4752     case EL_MOLE_RIGHT:
4753     case EL_MOLE_UP:
4754     case EL_MOLE_DOWN:
4755       Feld[x][y] = EL_MOLE;
4756       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4757       break;
4758
4759     default:
4760       if (IS_CUSTOM_ELEMENT(element))
4761       {
4762         struct ElementInfo *ei = &element_info[element];
4763         int move_direction_initial = ei->move_direction_initial;
4764         int move_pattern = ei->move_pattern;
4765
4766         if (move_direction_initial == MV_START_PREVIOUS)
4767         {
4768           if (MovDir[x][y] != MV_NONE)
4769             return;
4770
4771           move_direction_initial = MV_START_AUTOMATIC;
4772         }
4773
4774         if (move_direction_initial == MV_START_RANDOM)
4775           MovDir[x][y] = 1 << RND(4);
4776         else if (move_direction_initial & MV_ANY_DIRECTION)
4777           MovDir[x][y] = move_direction_initial;
4778         else if (move_pattern == MV_ALL_DIRECTIONS ||
4779                  move_pattern == MV_TURNING_LEFT ||
4780                  move_pattern == MV_TURNING_RIGHT ||
4781                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4782                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4783                  move_pattern == MV_TURNING_RANDOM)
4784           MovDir[x][y] = 1 << RND(4);
4785         else if (move_pattern == MV_HORIZONTAL)
4786           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4787         else if (move_pattern == MV_VERTICAL)
4788           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4789         else if (move_pattern & MV_ANY_DIRECTION)
4790           MovDir[x][y] = element_info[element].move_pattern;
4791         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4792                  move_pattern == MV_ALONG_RIGHT_SIDE)
4793         {
4794           /* use random direction as default start direction */
4795           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4796             MovDir[x][y] = 1 << RND(4);
4797
4798           for (i = 0; i < NUM_DIRECTIONS; i++)
4799           {
4800             int x1 = x + xy[i][0];
4801             int y1 = y + xy[i][1];
4802
4803             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4804             {
4805               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4806                 MovDir[x][y] = direction[0][i];
4807               else
4808                 MovDir[x][y] = direction[1][i];
4809
4810               break;
4811             }
4812           }
4813         }                
4814       }
4815       else
4816       {
4817         MovDir[x][y] = 1 << RND(4);
4818
4819         if (element != EL_BUG &&
4820             element != EL_SPACESHIP &&
4821             element != EL_BD_BUTTERFLY &&
4822             element != EL_BD_FIREFLY)
4823           break;
4824
4825         for (i = 0; i < NUM_DIRECTIONS; i++)
4826         {
4827           int x1 = x + xy[i][0];
4828           int y1 = y + xy[i][1];
4829
4830           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4831           {
4832             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4833             {
4834               MovDir[x][y] = direction[0][i];
4835               break;
4836             }
4837             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4838                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4839             {
4840               MovDir[x][y] = direction[1][i];
4841               break;
4842             }
4843           }
4844         }
4845       }
4846       break;
4847   }
4848
4849   GfxDir[x][y] = MovDir[x][y];
4850 }
4851
4852 void InitAmoebaNr(int x, int y)
4853 {
4854   int i;
4855   int group_nr = AmoebeNachbarNr(x, y);
4856
4857   if (group_nr == 0)
4858   {
4859     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4860     {
4861       if (AmoebaCnt[i] == 0)
4862       {
4863         group_nr = i;
4864         break;
4865       }
4866     }
4867   }
4868
4869   AmoebaNr[x][y] = group_nr;
4870   AmoebaCnt[group_nr]++;
4871   AmoebaCnt2[group_nr]++;
4872 }
4873
4874 static void PlayerWins(struct PlayerInfo *player)
4875 {
4876   player->LevelSolved = TRUE;
4877   player->GameOver = TRUE;
4878
4879   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4880                          level.native_em_level->lev->score : player->score);
4881
4882   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4883                                       TimeLeft);
4884   player->LevelSolved_CountingScore = player->score_final;
4885 }
4886
4887 void GameWon()
4888 {
4889   static int time, time_final;
4890   static int score, score_final;
4891   static int game_over_delay_1 = 0;
4892   static int game_over_delay_2 = 0;
4893   int game_over_delay_value_1 = 50;
4894   int game_over_delay_value_2 = 50;
4895
4896   if (!local_player->LevelSolved_GameWon)
4897   {
4898     int i;
4899
4900     /* do not start end game actions before the player stops moving (to exit) */
4901     if (local_player->MovPos)
4902       return;
4903
4904     local_player->LevelSolved_GameWon = TRUE;
4905     local_player->LevelSolved_SaveTape = tape.recording;
4906     local_player->LevelSolved_SaveScore = !tape.playing;
4907
4908     if (!tape.playing)
4909     {
4910       LevelStats_incSolved(level_nr);
4911
4912       SaveLevelSetup_SeriesInfo();
4913
4914 #if 0
4915       printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4916 #endif
4917     }
4918
4919     if (tape.auto_play)         /* tape might already be stopped here */
4920       tape.auto_play_level_solved = TRUE;
4921
4922 #if 1
4923     TapeStop();
4924 #endif
4925
4926     game_over_delay_1 = game_over_delay_value_1;
4927     game_over_delay_2 = game_over_delay_value_2;
4928
4929     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4930     score = score_final = local_player->score_final;
4931
4932     if (TimeLeft > 0)
4933     {
4934       time_final = 0;
4935       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4936     }
4937     else if (game.no_time_limit && TimePlayed < 999)
4938     {
4939       time_final = 999;
4940       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4941     }
4942
4943     local_player->score_final = score_final;
4944
4945     if (level_editor_test_game)
4946     {
4947       time = time_final;
4948       score = score_final;
4949
4950 #if 1
4951       local_player->LevelSolved_CountingTime = time;
4952       local_player->LevelSolved_CountingScore = score;
4953
4954       game_panel_controls[GAME_PANEL_TIME].value = time;
4955       game_panel_controls[GAME_PANEL_SCORE].value = score;
4956
4957       DisplayGameControlValues();
4958 #else
4959       DrawGameValue_Time(time);
4960       DrawGameValue_Score(score);
4961 #endif
4962     }
4963
4964     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4965     {
4966       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4967       {
4968         /* close exit door after last player */
4969         if ((AllPlayersGone &&
4970              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4971               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4972               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4973             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4974             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4975         {
4976           int element = Feld[ExitX][ExitY];
4977
4978 #if 0
4979           if (element == EL_EM_EXIT_OPEN ||
4980               element == EL_EM_STEEL_EXIT_OPEN)
4981           {
4982             Bang(ExitX, ExitY);
4983           }
4984           else
4985 #endif
4986           {
4987             Feld[ExitX][ExitY] =
4988               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4989                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4990                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4991                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4992                EL_EM_STEEL_EXIT_CLOSING);
4993
4994             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4995           }
4996         }
4997
4998         /* player disappears */
4999         DrawLevelField(ExitX, ExitY);
5000       }
5001
5002       for (i = 0; i < MAX_PLAYERS; i++)
5003       {
5004         struct PlayerInfo *player = &stored_player[i];
5005
5006         if (player->present)
5007         {
5008           RemovePlayer(player);
5009
5010           /* player disappears */
5011           DrawLevelField(player->jx, player->jy);
5012         }
5013       }
5014     }
5015
5016     PlaySound(SND_GAME_WINNING);
5017   }
5018
5019   if (game_over_delay_1 > 0)
5020   {
5021     game_over_delay_1--;
5022
5023     return;
5024   }
5025
5026   if (time != time_final)
5027   {
5028     int time_to_go = ABS(time_final - time);
5029     int time_count_dir = (time < time_final ? +1 : -1);
5030     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
5031
5032     time  += time_count_steps * time_count_dir;
5033     score += time_count_steps * level.score[SC_TIME_BONUS];
5034
5035 #if 1
5036     local_player->LevelSolved_CountingTime = time;
5037     local_player->LevelSolved_CountingScore = score;
5038
5039     game_panel_controls[GAME_PANEL_TIME].value = time;
5040     game_panel_controls[GAME_PANEL_SCORE].value = score;
5041
5042     DisplayGameControlValues();
5043 #else
5044     DrawGameValue_Time(time);
5045     DrawGameValue_Score(score);
5046 #endif
5047
5048     if (time == time_final)
5049       StopSound(SND_GAME_LEVELTIME_BONUS);
5050     else if (setup.sound_loops)
5051       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5052     else
5053       PlaySound(SND_GAME_LEVELTIME_BONUS);
5054
5055     return;
5056   }
5057
5058   local_player->LevelSolved_PanelOff = TRUE;
5059
5060   if (game_over_delay_2 > 0)
5061   {
5062     game_over_delay_2--;
5063
5064     return;
5065   }
5066
5067 #if 1
5068   GameEnd();
5069 #endif
5070 }
5071
5072 void GameEnd()
5073 {
5074   int hi_pos;
5075   boolean raise_level = FALSE;
5076
5077   local_player->LevelSolved_GameEnd = TRUE;
5078
5079   CloseDoor(DOOR_CLOSE_1);
5080
5081   if (local_player->LevelSolved_SaveTape)
5082   {
5083 #if 0
5084     TapeStop();
5085 #endif
5086
5087 #if 1
5088     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5089 #else
5090     SaveTape(tape.level_nr);            /* ask to save tape */
5091 #endif
5092   }
5093
5094   if (level_editor_test_game)
5095   {
5096     game_status = GAME_MODE_MAIN;
5097
5098 #if 1
5099     DrawAndFadeInMainMenu(REDRAW_FIELD);
5100 #else
5101     DrawMainMenu();
5102 #endif
5103
5104     return;
5105   }
5106
5107   if (!local_player->LevelSolved_SaveScore)
5108   {
5109 #if 1
5110     FadeOut(REDRAW_FIELD);
5111 #endif
5112
5113     game_status = GAME_MODE_MAIN;
5114
5115     DrawAndFadeInMainMenu(REDRAW_FIELD);
5116
5117     return;
5118   }
5119
5120   if (level_nr == leveldir_current->handicap_level)
5121   {
5122     leveldir_current->handicap_level++;
5123
5124     SaveLevelSetup_SeriesInfo();
5125   }
5126
5127   if (level_nr < leveldir_current->last_level)
5128     raise_level = TRUE;                 /* advance to next level */
5129
5130   if ((hi_pos = NewHiScore()) >= 0) 
5131   {
5132     game_status = GAME_MODE_SCORES;
5133
5134     DrawHallOfFame(hi_pos);
5135
5136     if (raise_level)
5137     {
5138       level_nr++;
5139       TapeErase();
5140     }
5141   }
5142   else
5143   {
5144 #if 1
5145     FadeOut(REDRAW_FIELD);
5146 #endif
5147
5148     game_status = GAME_MODE_MAIN;
5149
5150     if (raise_level)
5151     {
5152       level_nr++;
5153       TapeErase();
5154     }
5155
5156     DrawAndFadeInMainMenu(REDRAW_FIELD);
5157   }
5158 }
5159
5160 int NewHiScore()
5161 {
5162   int k, l;
5163   int position = -1;
5164
5165   LoadScore(level_nr);
5166
5167   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5168       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5169     return -1;
5170
5171   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5172   {
5173     if (local_player->score_final > highscore[k].Score)
5174     {
5175       /* player has made it to the hall of fame */
5176
5177       if (k < MAX_SCORE_ENTRIES - 1)
5178       {
5179         int m = MAX_SCORE_ENTRIES - 1;
5180
5181 #ifdef ONE_PER_NAME
5182         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5183           if (strEqual(setup.player_name, highscore[l].Name))
5184             m = l;
5185         if (m == k)     /* player's new highscore overwrites his old one */
5186           goto put_into_list;
5187 #endif
5188
5189         for (l = m; l > k; l--)
5190         {
5191           strcpy(highscore[l].Name, highscore[l - 1].Name);
5192           highscore[l].Score = highscore[l - 1].Score;
5193         }
5194       }
5195
5196 #ifdef ONE_PER_NAME
5197       put_into_list:
5198 #endif
5199       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5200       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5201       highscore[k].Score = local_player->score_final; 
5202       position = k;
5203       break;
5204     }
5205
5206 #ifdef ONE_PER_NAME
5207     else if (!strncmp(setup.player_name, highscore[k].Name,
5208                       MAX_PLAYER_NAME_LEN))
5209       break;    /* player already there with a higher score */
5210 #endif
5211
5212   }
5213
5214   if (position >= 0) 
5215     SaveScore(level_nr);
5216
5217   return position;
5218 }
5219
5220 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5221 {
5222   int element = Feld[x][y];
5223   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5224   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5225   int horiz_move = (dx != 0);
5226   int sign = (horiz_move ? dx : dy);
5227   int step = sign * element_info[element].move_stepsize;
5228
5229   /* special values for move stepsize for spring and things on conveyor belt */
5230   if (horiz_move)
5231   {
5232     if (CAN_FALL(element) &&
5233         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5234       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5235     else if (element == EL_SPRING)
5236       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5237   }
5238
5239   return step;
5240 }
5241
5242 inline static int getElementMoveStepsize(int x, int y)
5243 {
5244   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5245 }
5246
5247 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5248 {
5249   if (player->GfxAction != action || player->GfxDir != dir)
5250   {
5251 #if 0
5252     printf("Player frame reset! (%d => %d, %d => %d)\n",
5253            player->GfxAction, action, player->GfxDir, dir);
5254 #endif
5255
5256     player->GfxAction = action;
5257     player->GfxDir = dir;
5258     player->Frame = 0;
5259     player->StepFrame = 0;
5260   }
5261 }
5262
5263 #if USE_GFX_RESET_GFX_ANIMATION
5264 static void ResetGfxFrame(int x, int y, boolean redraw)
5265 {
5266   int element = Feld[x][y];
5267   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5268   int last_gfx_frame = GfxFrame[x][y];
5269
5270   if (graphic_info[graphic].anim_global_sync)
5271     GfxFrame[x][y] = FrameCounter;
5272   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5273     GfxFrame[x][y] = CustomValue[x][y];
5274   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5275     GfxFrame[x][y] = element_info[element].collect_score;
5276   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5277     GfxFrame[x][y] = ChangeDelay[x][y];
5278
5279   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5280     DrawLevelGraphicAnimation(x, y, graphic);
5281 }
5282 #endif
5283
5284 static void ResetGfxAnimation(int x, int y)
5285 {
5286   GfxAction[x][y] = ACTION_DEFAULT;
5287   GfxDir[x][y] = MovDir[x][y];
5288   GfxFrame[x][y] = 0;
5289
5290 #if USE_GFX_RESET_GFX_ANIMATION
5291   ResetGfxFrame(x, y, FALSE);
5292 #endif
5293 }
5294
5295 static void ResetRandomAnimationValue(int x, int y)
5296 {
5297   GfxRandom[x][y] = INIT_GFX_RANDOM();
5298 }
5299
5300 void InitMovingField(int x, int y, int direction)
5301 {
5302   int element = Feld[x][y];
5303   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5304   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5305   int newx = x + dx;
5306   int newy = y + dy;
5307   boolean is_moving_before, is_moving_after;
5308 #if 0
5309   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5310 #endif
5311
5312   /* check if element was/is moving or being moved before/after mode change */
5313 #if 1
5314 #if 1
5315   is_moving_before = (WasJustMoving[x][y] != 0);
5316 #else
5317   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5318   is_moving_before = WasJustMoving[x][y];
5319 #endif
5320 #else
5321   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5322 #endif
5323   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5324
5325   /* reset animation only for moving elements which change direction of moving
5326      or which just started or stopped moving
5327      (else CEs with property "can move" / "not moving" are reset each frame) */
5328 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5329 #if 1
5330   if (is_moving_before != is_moving_after ||
5331       direction != MovDir[x][y])
5332     ResetGfxAnimation(x, y);
5333 #else
5334   if ((is_moving_before || is_moving_after) && !continues_moving)
5335     ResetGfxAnimation(x, y);
5336 #endif
5337 #else
5338   if (!continues_moving)
5339     ResetGfxAnimation(x, y);
5340 #endif
5341
5342   MovDir[x][y] = direction;
5343   GfxDir[x][y] = direction;
5344
5345 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5346   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5347                      direction == MV_DOWN && CAN_FALL(element) ?
5348                      ACTION_FALLING : ACTION_MOVING);
5349 #else
5350   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5351                      ACTION_FALLING : ACTION_MOVING);
5352 #endif
5353
5354   /* this is needed for CEs with property "can move" / "not moving" */
5355
5356   if (is_moving_after)
5357   {
5358     if (Feld[newx][newy] == EL_EMPTY)
5359       Feld[newx][newy] = EL_BLOCKED;
5360
5361     MovDir[newx][newy] = MovDir[x][y];
5362
5363 #if USE_NEW_CUSTOM_VALUE
5364     CustomValue[newx][newy] = CustomValue[x][y];
5365 #endif
5366
5367     GfxFrame[newx][newy] = GfxFrame[x][y];
5368     GfxRandom[newx][newy] = GfxRandom[x][y];
5369     GfxAction[newx][newy] = GfxAction[x][y];
5370     GfxDir[newx][newy] = GfxDir[x][y];
5371   }
5372 }
5373
5374 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5375 {
5376   int direction = MovDir[x][y];
5377   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5378   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5379
5380   *goes_to_x = newx;
5381   *goes_to_y = newy;
5382 }
5383
5384 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5385 {
5386   int oldx = x, oldy = y;
5387   int direction = MovDir[x][y];
5388
5389   if (direction == MV_LEFT)
5390     oldx++;
5391   else if (direction == MV_RIGHT)
5392     oldx--;
5393   else if (direction == MV_UP)
5394     oldy++;
5395   else if (direction == MV_DOWN)
5396     oldy--;
5397
5398   *comes_from_x = oldx;
5399   *comes_from_y = oldy;
5400 }
5401
5402 int MovingOrBlocked2Element(int x, int y)
5403 {
5404   int element = Feld[x][y];
5405
5406   if (element == EL_BLOCKED)
5407   {
5408     int oldx, oldy;
5409
5410     Blocked2Moving(x, y, &oldx, &oldy);
5411     return Feld[oldx][oldy];
5412   }
5413   else
5414     return element;
5415 }
5416
5417 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5418 {
5419   /* like MovingOrBlocked2Element(), but if element is moving
5420      and (x,y) is the field the moving element is just leaving,
5421      return EL_BLOCKED instead of the element value */
5422   int element = Feld[x][y];
5423
5424   if (IS_MOVING(x, y))
5425   {
5426     if (element == EL_BLOCKED)
5427     {
5428       int oldx, oldy;
5429
5430       Blocked2Moving(x, y, &oldx, &oldy);
5431       return Feld[oldx][oldy];
5432     }
5433     else
5434       return EL_BLOCKED;
5435   }
5436   else
5437     return element;
5438 }
5439
5440 static void RemoveField(int x, int y)
5441 {
5442   Feld[x][y] = EL_EMPTY;
5443
5444   MovPos[x][y] = 0;
5445   MovDir[x][y] = 0;
5446   MovDelay[x][y] = 0;
5447
5448 #if USE_NEW_CUSTOM_VALUE
5449   CustomValue[x][y] = 0;
5450 #endif
5451
5452   AmoebaNr[x][y] = 0;
5453   ChangeDelay[x][y] = 0;
5454   ChangePage[x][y] = -1;
5455   Pushed[x][y] = FALSE;
5456
5457 #if 0
5458   ExplodeField[x][y] = EX_TYPE_NONE;
5459 #endif
5460
5461   GfxElement[x][y] = EL_UNDEFINED;
5462   GfxAction[x][y] = ACTION_DEFAULT;
5463   GfxDir[x][y] = MV_NONE;
5464 #if 0
5465   /* !!! this would prevent the removed tile from being redrawn !!! */
5466   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5467 #endif
5468 }
5469
5470 void RemoveMovingField(int x, int y)
5471 {
5472   int oldx = x, oldy = y, newx = x, newy = y;
5473   int element = Feld[x][y];
5474   int next_element = EL_UNDEFINED;
5475
5476   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5477     return;
5478
5479   if (IS_MOVING(x, y))
5480   {
5481     Moving2Blocked(x, y, &newx, &newy);
5482
5483     if (Feld[newx][newy] != EL_BLOCKED)
5484     {
5485       /* element is moving, but target field is not free (blocked), but
5486          already occupied by something different (example: acid pool);
5487          in this case, only remove the moving field, but not the target */
5488
5489       RemoveField(oldx, oldy);
5490
5491       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5492
5493       TEST_DrawLevelField(oldx, oldy);
5494
5495       return;
5496     }
5497   }
5498   else if (element == EL_BLOCKED)
5499   {
5500     Blocked2Moving(x, y, &oldx, &oldy);
5501     if (!IS_MOVING(oldx, oldy))
5502       return;
5503   }
5504
5505   if (element == EL_BLOCKED &&
5506       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5507        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5508        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5509        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5510        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5511        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5512     next_element = get_next_element(Feld[oldx][oldy]);
5513
5514   RemoveField(oldx, oldy);
5515   RemoveField(newx, newy);
5516
5517   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5518
5519   if (next_element != EL_UNDEFINED)
5520     Feld[oldx][oldy] = next_element;
5521
5522   TEST_DrawLevelField(oldx, oldy);
5523   TEST_DrawLevelField(newx, newy);
5524 }
5525
5526 void DrawDynamite(int x, int y)
5527 {
5528   int sx = SCREENX(x), sy = SCREENY(y);
5529   int graphic = el2img(Feld[x][y]);
5530   int frame;
5531
5532   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5533     return;
5534
5535   if (IS_WALKABLE_INSIDE(Back[x][y]))
5536     return;
5537
5538   if (Back[x][y])
5539     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5540   else if (Store[x][y])
5541     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5542
5543   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5544
5545   if (Back[x][y] || Store[x][y])
5546     DrawGraphicThruMask(sx, sy, graphic, frame);
5547   else
5548     DrawGraphic(sx, sy, graphic, frame);
5549 }
5550
5551 void CheckDynamite(int x, int y)
5552 {
5553   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5554   {
5555     MovDelay[x][y]--;
5556
5557     if (MovDelay[x][y] != 0)
5558     {
5559       DrawDynamite(x, y);
5560       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5561
5562       return;
5563     }
5564   }
5565
5566   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5567
5568   Bang(x, y);
5569 }
5570
5571 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5572 {
5573   boolean num_checked_players = 0;
5574   int i;
5575
5576   for (i = 0; i < MAX_PLAYERS; i++)
5577   {
5578     if (stored_player[i].active)
5579     {
5580       int sx = stored_player[i].jx;
5581       int sy = stored_player[i].jy;
5582
5583       if (num_checked_players == 0)
5584       {
5585         *sx1 = *sx2 = sx;
5586         *sy1 = *sy2 = sy;
5587       }
5588       else
5589       {
5590         *sx1 = MIN(*sx1, sx);
5591         *sy1 = MIN(*sy1, sy);
5592         *sx2 = MAX(*sx2, sx);
5593         *sy2 = MAX(*sy2, sy);
5594       }
5595
5596       num_checked_players++;
5597     }
5598   }
5599 }
5600
5601 static boolean checkIfAllPlayersFitToScreen_RND()
5602 {
5603   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5604
5605   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5606
5607   return (sx2 - sx1 < SCR_FIELDX &&
5608           sy2 - sy1 < SCR_FIELDY);
5609 }
5610
5611 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5612 {
5613   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5614
5615   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5616
5617   *sx = (sx1 + sx2) / 2;
5618   *sy = (sy1 + sy2) / 2;
5619 }
5620
5621 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5622                         boolean center_screen, boolean quick_relocation)
5623 {
5624   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5625   boolean no_delay = (tape.warp_forward);
5626   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5627   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5628
5629   if (quick_relocation)
5630   {
5631     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5632     {
5633       if (!level.shifted_relocation || center_screen)
5634       {
5635         /* quick relocation (without scrolling), with centering of screen */
5636
5637         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5638                     x > SBX_Right + MIDPOSX ? SBX_Right :
5639                     x - MIDPOSX);
5640
5641         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5642                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5643                     y - MIDPOSY);
5644       }
5645       else
5646       {
5647         /* quick relocation (without scrolling), but do not center screen */
5648
5649         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5650                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5651                                old_x - MIDPOSX);
5652
5653         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5654                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5655                                old_y - MIDPOSY);
5656
5657         int offset_x = x + (scroll_x - center_scroll_x);
5658         int offset_y = y + (scroll_y - center_scroll_y);
5659
5660         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5661                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5662                     offset_x - MIDPOSX);
5663
5664         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5665                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5666                     offset_y - MIDPOSY);
5667       }
5668     }
5669     else
5670     {
5671 #if 1
5672       if (!level.shifted_relocation || center_screen)
5673       {
5674         /* quick relocation (without scrolling), with centering of screen */
5675
5676         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5677                     x > SBX_Right + MIDPOSX ? SBX_Right :
5678                     x - MIDPOSX);
5679
5680         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5681                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5682                     y - MIDPOSY);
5683       }
5684       else
5685       {
5686         /* quick relocation (without scrolling), but do not center screen */
5687
5688         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5689                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5690                                old_x - MIDPOSX);
5691
5692         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5693                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5694                                old_y - MIDPOSY);
5695
5696         int offset_x = x + (scroll_x - center_scroll_x);
5697         int offset_y = y + (scroll_y - center_scroll_y);
5698
5699         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5700                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5701                     offset_x - MIDPOSX);
5702
5703         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5704                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5705                     offset_y - MIDPOSY);
5706       }
5707 #else
5708       /* quick relocation (without scrolling), inside visible screen area */
5709
5710       int offset = game.scroll_delay_value;
5711
5712       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5713           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5714         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5715
5716       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5717           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5718         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5719
5720       /* don't scroll over playfield boundaries */
5721       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5722         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5723
5724       /* don't scroll over playfield boundaries */
5725       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5726         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5727 #endif
5728     }
5729
5730     RedrawPlayfield(TRUE, 0,0,0,0);
5731   }
5732   else
5733   {
5734 #if 1
5735     int scroll_xx, scroll_yy;
5736
5737     if (!level.shifted_relocation || center_screen)
5738     {
5739       /* visible relocation (with scrolling), with centering of screen */
5740
5741       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5742                    x > SBX_Right + MIDPOSX ? SBX_Right :
5743                    x - MIDPOSX);
5744
5745       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5746                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5747                    y - MIDPOSY);
5748     }
5749     else
5750     {
5751       /* visible relocation (with scrolling), but do not center screen */
5752
5753       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5754                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5755                              old_x - MIDPOSX);
5756
5757       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5758                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5759                              old_y - MIDPOSY);
5760
5761       int offset_x = x + (scroll_x - center_scroll_x);
5762       int offset_y = y + (scroll_y - center_scroll_y);
5763
5764       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5765                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5766                    offset_x - MIDPOSX);
5767
5768       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5769                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5770                    offset_y - MIDPOSY);
5771     }
5772
5773 #else
5774
5775     /* visible relocation (with scrolling), with centering of screen */
5776
5777     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5778                      x > SBX_Right + MIDPOSX ? SBX_Right :
5779                      x - MIDPOSX);
5780
5781     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5782                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5783                      y - MIDPOSY);
5784 #endif
5785
5786     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5787
5788     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5789     {
5790       int dx = 0, dy = 0;
5791       int fx = FX, fy = FY;
5792
5793       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5794       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5795
5796       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5797         break;
5798
5799       scroll_x -= dx;
5800       scroll_y -= dy;
5801
5802       fx += dx * TILEX / 2;
5803       fy += dy * TILEY / 2;
5804
5805       ScrollLevel(dx, dy);
5806       DrawAllPlayers();
5807
5808       /* scroll in two steps of half tile size to make things smoother */
5809       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5810       FlushDisplay();
5811       Delay(wait_delay_value);
5812
5813       /* scroll second step to align at full tile size */
5814       BackToFront();
5815       Delay(wait_delay_value);
5816     }
5817
5818     DrawAllPlayers();
5819     BackToFront();
5820     Delay(wait_delay_value);
5821   }
5822 }
5823
5824 void RelocatePlayer(int jx, int jy, int el_player_raw)
5825 {
5826   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5827   int player_nr = GET_PLAYER_NR(el_player);
5828   struct PlayerInfo *player = &stored_player[player_nr];
5829   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5830   boolean no_delay = (tape.warp_forward);
5831   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5832   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5833   int old_jx = player->jx;
5834   int old_jy = player->jy;
5835   int old_element = Feld[old_jx][old_jy];
5836   int element = Feld[jx][jy];
5837   boolean player_relocated = (old_jx != jx || old_jy != jy);
5838
5839   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5840   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5841   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5842   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5843   int leave_side_horiz = move_dir_horiz;
5844   int leave_side_vert  = move_dir_vert;
5845   int enter_side = enter_side_horiz | enter_side_vert;
5846   int leave_side = leave_side_horiz | leave_side_vert;
5847
5848   if (player->GameOver)         /* do not reanimate dead player */
5849     return;
5850
5851   if (!player_relocated)        /* no need to relocate the player */
5852     return;
5853
5854   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5855   {
5856     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5857     DrawLevelField(jx, jy);
5858   }
5859
5860   if (player->present)
5861   {
5862     while (player->MovPos)
5863     {
5864       ScrollPlayer(player, SCROLL_GO_ON);
5865       ScrollScreen(NULL, SCROLL_GO_ON);
5866
5867       AdvanceFrameAndPlayerCounters(player->index_nr);
5868
5869       DrawPlayer(player);
5870
5871       BackToFront();
5872       Delay(wait_delay_value);
5873     }
5874
5875     DrawPlayer(player);         /* needed here only to cleanup last field */
5876     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5877
5878     player->is_moving = FALSE;
5879   }
5880
5881   if (IS_CUSTOM_ELEMENT(old_element))
5882     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5883                                CE_LEFT_BY_PLAYER,
5884                                player->index_bit, leave_side);
5885
5886   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5887                                       CE_PLAYER_LEAVES_X,
5888                                       player->index_bit, leave_side);
5889
5890   Feld[jx][jy] = el_player;
5891   InitPlayerField(jx, jy, el_player, TRUE);
5892
5893   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5894      possible that the relocation target field did not contain a player element,
5895      but a walkable element, to which the new player was relocated -- in this
5896      case, restore that (already initialized!) element on the player field */
5897   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5898   {
5899     Feld[jx][jy] = element;     /* restore previously existing element */
5900 #if 0
5901     /* !!! do not initialize already initialized element a second time !!! */
5902     /* (this causes at least problems with "element creation" CE trigger for
5903        already existing elements, and existing Sokoban fields counted twice) */
5904     InitField(jx, jy, FALSE);
5905 #endif
5906   }
5907
5908   /* only visually relocate centered player */
5909   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5910                      FALSE, level.instant_relocation);
5911
5912   TestIfPlayerTouchesBadThing(jx, jy);
5913   TestIfPlayerTouchesCustomElement(jx, jy);
5914
5915   if (IS_CUSTOM_ELEMENT(element))
5916     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5917                                player->index_bit, enter_side);
5918
5919   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5920                                       player->index_bit, enter_side);
5921
5922 #if 1
5923   if (player->is_switching)
5924   {
5925     /* ensure that relocation while still switching an element does not cause
5926        a new element to be treated as also switched directly after relocation
5927        (this is important for teleporter switches that teleport the player to
5928        a place where another teleporter switch is in the same direction, which
5929        would then incorrectly be treated as immediately switched before the
5930        direction key that caused the switch was released) */
5931
5932     player->switch_x += jx - old_jx;
5933     player->switch_y += jy - old_jy;
5934   }
5935 #endif
5936 }
5937
5938 void Explode(int ex, int ey, int phase, int mode)
5939 {
5940   int x, y;
5941   int last_phase;
5942   int border_element;
5943
5944   /* !!! eliminate this variable !!! */
5945   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5946
5947   if (game.explosions_delayed)
5948   {
5949     ExplodeField[ex][ey] = mode;
5950     return;
5951   }
5952
5953   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5954   {
5955     int center_element = Feld[ex][ey];
5956     int artwork_element, explosion_element;     /* set these values later */
5957
5958 #if 0
5959     /* --- This is only really needed (and now handled) in "Impact()". --- */
5960     /* do not explode moving elements that left the explode field in time */
5961     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5962         center_element == EL_EMPTY &&
5963         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5964       return;
5965 #endif
5966
5967 #if 0
5968     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5969     if (mode == EX_TYPE_NORMAL ||
5970         mode == EX_TYPE_CENTER ||
5971         mode == EX_TYPE_CROSS)
5972       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5973 #endif
5974
5975     /* remove things displayed in background while burning dynamite */
5976     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5977       Back[ex][ey] = 0;
5978
5979     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5980     {
5981       /* put moving element to center field (and let it explode there) */
5982       center_element = MovingOrBlocked2Element(ex, ey);
5983       RemoveMovingField(ex, ey);
5984       Feld[ex][ey] = center_element;
5985     }
5986
5987     /* now "center_element" is finally determined -- set related values now */
5988     artwork_element = center_element;           /* for custom player artwork */
5989     explosion_element = center_element;         /* for custom player artwork */
5990
5991     if (IS_PLAYER(ex, ey))
5992     {
5993       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5994
5995       artwork_element = stored_player[player_nr].artwork_element;
5996
5997       if (level.use_explosion_element[player_nr])
5998       {
5999         explosion_element = level.explosion_element[player_nr];
6000         artwork_element = explosion_element;
6001       }
6002     }
6003
6004 #if 1
6005     if (mode == EX_TYPE_NORMAL ||
6006         mode == EX_TYPE_CENTER ||
6007         mode == EX_TYPE_CROSS)
6008       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6009 #endif
6010
6011     last_phase = element_info[explosion_element].explosion_delay + 1;
6012
6013     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6014     {
6015       int xx = x - ex + 1;
6016       int yy = y - ey + 1;
6017       int element;
6018
6019       if (!IN_LEV_FIELD(x, y) ||
6020           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6021           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6022         continue;
6023
6024       element = Feld[x][y];
6025
6026       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6027       {
6028         element = MovingOrBlocked2Element(x, y);
6029
6030         if (!IS_EXPLOSION_PROOF(element))
6031           RemoveMovingField(x, y);
6032       }
6033
6034       /* indestructible elements can only explode in center (but not flames) */
6035       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6036                                            mode == EX_TYPE_BORDER)) ||
6037           element == EL_FLAMES)
6038         continue;
6039
6040       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6041          behaviour, for example when touching a yamyam that explodes to rocks
6042          with active deadly shield, a rock is created under the player !!! */
6043       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6044 #if 0
6045       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6046           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6047            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6048 #else
6049       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6050 #endif
6051       {
6052         if (IS_ACTIVE_BOMB(element))
6053         {
6054           /* re-activate things under the bomb like gate or penguin */
6055           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6056           Back[x][y] = 0;
6057         }
6058
6059         continue;
6060       }
6061
6062       /* save walkable background elements while explosion on same tile */
6063       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6064           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6065         Back[x][y] = element;
6066
6067       /* ignite explodable elements reached by other explosion */
6068       if (element == EL_EXPLOSION)
6069         element = Store2[x][y];
6070
6071       if (AmoebaNr[x][y] &&
6072           (element == EL_AMOEBA_FULL ||
6073            element == EL_BD_AMOEBA ||
6074            element == EL_AMOEBA_GROWING))
6075       {
6076         AmoebaCnt[AmoebaNr[x][y]]--;
6077         AmoebaCnt2[AmoebaNr[x][y]]--;
6078       }
6079
6080       RemoveField(x, y);
6081
6082       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6083       {
6084         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6085
6086         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6087
6088         if (PLAYERINFO(ex, ey)->use_murphy)
6089           Store[x][y] = EL_EMPTY;
6090       }
6091
6092       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6093          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6094       else if (ELEM_IS_PLAYER(center_element))
6095         Store[x][y] = EL_EMPTY;
6096       else if (center_element == EL_YAMYAM)
6097         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6098       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6099         Store[x][y] = element_info[center_element].content.e[xx][yy];
6100 #if 1
6101       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6102          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6103          otherwise) -- FIX THIS !!! */
6104       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6105         Store[x][y] = element_info[element].content.e[1][1];
6106 #else
6107       else if (!CAN_EXPLODE(element))
6108         Store[x][y] = element_info[element].content.e[1][1];
6109 #endif
6110       else
6111         Store[x][y] = EL_EMPTY;
6112
6113       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6114           center_element == EL_AMOEBA_TO_DIAMOND)
6115         Store2[x][y] = element;
6116
6117       Feld[x][y] = EL_EXPLOSION;
6118       GfxElement[x][y] = artwork_element;
6119
6120       ExplodePhase[x][y] = 1;
6121       ExplodeDelay[x][y] = last_phase;
6122
6123       Stop[x][y] = TRUE;
6124     }
6125
6126     if (center_element == EL_YAMYAM)
6127       game.yamyam_content_nr =
6128         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6129
6130     return;
6131   }
6132
6133   if (Stop[ex][ey])
6134     return;
6135
6136   x = ex;
6137   y = ey;
6138
6139   if (phase == 1)
6140     GfxFrame[x][y] = 0;         /* restart explosion animation */
6141
6142   last_phase = ExplodeDelay[x][y];
6143
6144   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6145
6146 #ifdef DEBUG
6147
6148   /* activate this even in non-DEBUG version until cause for crash in
6149      getGraphicAnimationFrame() (see below) is found and eliminated */
6150
6151 #endif
6152 #if 1
6153
6154 #if 1
6155   /* this can happen if the player leaves an explosion just in time */
6156   if (GfxElement[x][y] == EL_UNDEFINED)
6157     GfxElement[x][y] = EL_EMPTY;
6158 #else
6159   if (GfxElement[x][y] == EL_UNDEFINED)
6160   {
6161     printf("\n\n");
6162     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6163     printf("Explode(): This should never happen!\n");
6164     printf("\n\n");
6165
6166     GfxElement[x][y] = EL_EMPTY;
6167   }
6168 #endif
6169
6170 #endif
6171
6172   border_element = Store2[x][y];
6173   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6174     border_element = StorePlayer[x][y];
6175
6176   if (phase == element_info[border_element].ignition_delay ||
6177       phase == last_phase)
6178   {
6179     boolean border_explosion = FALSE;
6180
6181     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6182         !PLAYER_EXPLOSION_PROTECTED(x, y))
6183     {
6184       KillPlayerUnlessExplosionProtected(x, y);
6185       border_explosion = TRUE;
6186     }
6187     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6188     {
6189       Feld[x][y] = Store2[x][y];
6190       Store2[x][y] = 0;
6191       Bang(x, y);
6192       border_explosion = TRUE;
6193     }
6194     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6195     {
6196       AmoebeUmwandeln(x, y);
6197       Store2[x][y] = 0;
6198       border_explosion = TRUE;
6199     }
6200
6201     /* if an element just explodes due to another explosion (chain-reaction),
6202        do not immediately end the new explosion when it was the last frame of
6203        the explosion (as it would be done in the following "if"-statement!) */
6204     if (border_explosion && phase == last_phase)
6205       return;
6206   }
6207
6208   if (phase == last_phase)
6209   {
6210     int element;
6211
6212     element = Feld[x][y] = Store[x][y];
6213     Store[x][y] = Store2[x][y] = 0;
6214     GfxElement[x][y] = EL_UNDEFINED;
6215
6216     /* player can escape from explosions and might therefore be still alive */
6217     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6218         element <= EL_PLAYER_IS_EXPLODING_4)
6219     {
6220       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6221       int explosion_element = EL_PLAYER_1 + player_nr;
6222       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6223       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6224
6225       if (level.use_explosion_element[player_nr])
6226         explosion_element = level.explosion_element[player_nr];
6227
6228       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6229                     element_info[explosion_element].content.e[xx][yy]);
6230     }
6231
6232     /* restore probably existing indestructible background element */
6233     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6234       element = Feld[x][y] = Back[x][y];
6235     Back[x][y] = 0;
6236
6237     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6238     GfxDir[x][y] = MV_NONE;
6239     ChangeDelay[x][y] = 0;
6240     ChangePage[x][y] = -1;
6241
6242 #if USE_NEW_CUSTOM_VALUE
6243     CustomValue[x][y] = 0;
6244 #endif
6245
6246     InitField_WithBug2(x, y, FALSE);
6247
6248     TEST_DrawLevelField(x, y);
6249
6250     TestIfElementTouchesCustomElement(x, y);
6251
6252     if (GFX_CRUMBLED(element))
6253       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6254
6255     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6256       StorePlayer[x][y] = 0;
6257
6258     if (ELEM_IS_PLAYER(element))
6259       RelocatePlayer(x, y, element);
6260   }
6261   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6262   {
6263     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6264     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6265
6266     if (phase == delay)
6267       TEST_DrawLevelFieldCrumbled(x, y);
6268
6269     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6270     {
6271       DrawLevelElement(x, y, Back[x][y]);
6272       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6273     }
6274     else if (IS_WALKABLE_UNDER(Back[x][y]))
6275     {
6276       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6277       DrawLevelElementThruMask(x, y, Back[x][y]);
6278     }
6279     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6280       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6281   }
6282 }
6283
6284 void DynaExplode(int ex, int ey)
6285 {
6286   int i, j;
6287   int dynabomb_element = Feld[ex][ey];
6288   int dynabomb_size = 1;
6289   boolean dynabomb_xl = FALSE;
6290   struct PlayerInfo *player;
6291   static int xy[4][2] =
6292   {
6293     { 0, -1 },
6294     { -1, 0 },
6295     { +1, 0 },
6296     { 0, +1 }
6297   };
6298
6299   if (IS_ACTIVE_BOMB(dynabomb_element))
6300   {
6301     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6302     dynabomb_size = player->dynabomb_size;
6303     dynabomb_xl = player->dynabomb_xl;
6304     player->dynabombs_left++;
6305   }
6306
6307   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6308
6309   for (i = 0; i < NUM_DIRECTIONS; i++)
6310   {
6311     for (j = 1; j <= dynabomb_size; j++)
6312     {
6313       int x = ex + j * xy[i][0];
6314       int y = ey + j * xy[i][1];
6315       int element;
6316
6317       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6318         break;
6319
6320       element = Feld[x][y];
6321
6322       /* do not restart explosions of fields with active bombs */
6323       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6324         continue;
6325
6326       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6327
6328       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6329           !IS_DIGGABLE(element) && !dynabomb_xl)
6330         break;
6331     }
6332   }
6333 }
6334
6335 void Bang(int x, int y)
6336 {
6337   int element = MovingOrBlocked2Element(x, y);
6338   int explosion_type = EX_TYPE_NORMAL;
6339
6340   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6341   {
6342     struct PlayerInfo *player = PLAYERINFO(x, y);
6343
6344 #if USE_FIX_CE_ACTION_WITH_PLAYER
6345     element = Feld[x][y] = player->initial_element;
6346 #else
6347     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6348                             player->element_nr);
6349 #endif
6350
6351     if (level.use_explosion_element[player->index_nr])
6352     {
6353       int explosion_element = level.explosion_element[player->index_nr];
6354
6355       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6356         explosion_type = EX_TYPE_CROSS;
6357       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6358         explosion_type = EX_TYPE_CENTER;
6359     }
6360   }
6361
6362   switch (element)
6363   {
6364     case EL_BUG:
6365     case EL_SPACESHIP:
6366     case EL_BD_BUTTERFLY:
6367     case EL_BD_FIREFLY:
6368     case EL_YAMYAM:
6369     case EL_DARK_YAMYAM:
6370     case EL_ROBOT:
6371     case EL_PACMAN:
6372     case EL_MOLE:
6373       RaiseScoreElement(element);
6374       break;
6375
6376     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6377     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6378     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6379     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6380     case EL_DYNABOMB_INCREASE_NUMBER:
6381     case EL_DYNABOMB_INCREASE_SIZE:
6382     case EL_DYNABOMB_INCREASE_POWER:
6383       explosion_type = EX_TYPE_DYNA;
6384       break;
6385
6386     case EL_DC_LANDMINE:
6387 #if 0
6388     case EL_EM_EXIT_OPEN:
6389     case EL_EM_STEEL_EXIT_OPEN:
6390 #endif
6391       explosion_type = EX_TYPE_CENTER;
6392       break;
6393
6394     case EL_PENGUIN:
6395     case EL_LAMP:
6396     case EL_LAMP_ACTIVE:
6397     case EL_AMOEBA_TO_DIAMOND:
6398       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6399         explosion_type = EX_TYPE_CENTER;
6400       break;
6401
6402     default:
6403       if (element_info[element].explosion_type == EXPLODES_CROSS)
6404         explosion_type = EX_TYPE_CROSS;
6405       else if (element_info[element].explosion_type == EXPLODES_1X1)
6406         explosion_type = EX_TYPE_CENTER;
6407       break;
6408   }
6409
6410   if (explosion_type == EX_TYPE_DYNA)
6411     DynaExplode(x, y);
6412   else
6413     Explode(x, y, EX_PHASE_START, explosion_type);
6414
6415   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6416 }
6417
6418 void SplashAcid(int x, int y)
6419 {
6420   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6421       (!IN_LEV_FIELD(x - 1, y - 2) ||
6422        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6423     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6424
6425   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6426       (!IN_LEV_FIELD(x + 1, y - 2) ||
6427        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6428     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6429
6430   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6431 }
6432
6433 static void InitBeltMovement()
6434 {
6435   static int belt_base_element[4] =
6436   {
6437     EL_CONVEYOR_BELT_1_LEFT,
6438     EL_CONVEYOR_BELT_2_LEFT,
6439     EL_CONVEYOR_BELT_3_LEFT,
6440     EL_CONVEYOR_BELT_4_LEFT
6441   };
6442   static int belt_base_active_element[4] =
6443   {
6444     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6445     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6446     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6447     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6448   };
6449
6450   int x, y, i, j;
6451
6452   /* set frame order for belt animation graphic according to belt direction */
6453   for (i = 0; i < NUM_BELTS; i++)
6454   {
6455     int belt_nr = i;
6456
6457     for (j = 0; j < NUM_BELT_PARTS; j++)
6458     {
6459       int element = belt_base_active_element[belt_nr] + j;
6460       int graphic_1 = el2img(element);
6461       int graphic_2 = el2panelimg(element);
6462
6463       if (game.belt_dir[i] == MV_LEFT)
6464       {
6465         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6466         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6467       }
6468       else
6469       {
6470         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6471         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6472       }
6473     }
6474   }
6475
6476   SCAN_PLAYFIELD(x, y)
6477   {
6478     int element = Feld[x][y];
6479
6480     for (i = 0; i < NUM_BELTS; i++)
6481     {
6482       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6483       {
6484         int e_belt_nr = getBeltNrFromBeltElement(element);
6485         int belt_nr = i;
6486
6487         if (e_belt_nr == belt_nr)
6488         {
6489           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6490
6491           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6492         }
6493       }
6494     }
6495   }
6496 }
6497
6498 static void ToggleBeltSwitch(int x, int y)
6499 {
6500   static int belt_base_element[4] =
6501   {
6502     EL_CONVEYOR_BELT_1_LEFT,
6503     EL_CONVEYOR_BELT_2_LEFT,
6504     EL_CONVEYOR_BELT_3_LEFT,
6505     EL_CONVEYOR_BELT_4_LEFT
6506   };
6507   static int belt_base_active_element[4] =
6508   {
6509     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6510     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6511     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6512     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6513   };
6514   static int belt_base_switch_element[4] =
6515   {
6516     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6517     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6518     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6519     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6520   };
6521   static int belt_move_dir[4] =
6522   {
6523     MV_LEFT,
6524     MV_NONE,
6525     MV_RIGHT,
6526     MV_NONE,
6527   };
6528
6529   int element = Feld[x][y];
6530   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6531   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6532   int belt_dir = belt_move_dir[belt_dir_nr];
6533   int xx, yy, i;
6534
6535   if (!IS_BELT_SWITCH(element))
6536     return;
6537
6538   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6539   game.belt_dir[belt_nr] = belt_dir;
6540
6541   if (belt_dir_nr == 3)
6542     belt_dir_nr = 1;
6543
6544   /* set frame order for belt animation graphic according to belt direction */
6545   for (i = 0; i < NUM_BELT_PARTS; i++)
6546   {
6547     int element = belt_base_active_element[belt_nr] + i;
6548     int graphic_1 = el2img(element);
6549     int graphic_2 = el2panelimg(element);
6550
6551     if (belt_dir == MV_LEFT)
6552     {
6553       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6554       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6555     }
6556     else
6557     {
6558       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6559       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6560     }
6561   }
6562
6563   SCAN_PLAYFIELD(xx, yy)
6564   {
6565     int element = Feld[xx][yy];
6566
6567     if (IS_BELT_SWITCH(element))
6568     {
6569       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6570
6571       if (e_belt_nr == belt_nr)
6572       {
6573         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6574         TEST_DrawLevelField(xx, yy);
6575       }
6576     }
6577     else if (IS_BELT(element) && belt_dir != MV_NONE)
6578     {
6579       int e_belt_nr = getBeltNrFromBeltElement(element);
6580
6581       if (e_belt_nr == belt_nr)
6582       {
6583         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6584
6585         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6586         TEST_DrawLevelField(xx, yy);
6587       }
6588     }
6589     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6590     {
6591       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6592
6593       if (e_belt_nr == belt_nr)
6594       {
6595         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6596
6597         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6598         TEST_DrawLevelField(xx, yy);
6599       }
6600     }
6601   }
6602 }
6603
6604 static void ToggleSwitchgateSwitch(int x, int y)
6605 {
6606   int xx, yy;
6607
6608   game.switchgate_pos = !game.switchgate_pos;
6609
6610   SCAN_PLAYFIELD(xx, yy)
6611   {
6612     int element = Feld[xx][yy];
6613
6614 #if !USE_BOTH_SWITCHGATE_SWITCHES
6615     if (element == EL_SWITCHGATE_SWITCH_UP ||
6616         element == EL_SWITCHGATE_SWITCH_DOWN)
6617     {
6618       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6619       TEST_DrawLevelField(xx, yy);
6620     }
6621     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6622              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6623     {
6624       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6625       TEST_DrawLevelField(xx, yy);
6626     }
6627 #else
6628     if (element == EL_SWITCHGATE_SWITCH_UP)
6629     {
6630       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6631       TEST_DrawLevelField(xx, yy);
6632     }
6633     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6634     {
6635       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6636       TEST_DrawLevelField(xx, yy);
6637     }
6638     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6639     {
6640       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6641       TEST_DrawLevelField(xx, yy);
6642     }
6643     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6644     {
6645       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6646       TEST_DrawLevelField(xx, yy);
6647     }
6648 #endif
6649     else if (element == EL_SWITCHGATE_OPEN ||
6650              element == EL_SWITCHGATE_OPENING)
6651     {
6652       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6653
6654       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6655     }
6656     else if (element == EL_SWITCHGATE_CLOSED ||
6657              element == EL_SWITCHGATE_CLOSING)
6658     {
6659       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6660
6661       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6662     }
6663   }
6664 }
6665
6666 static int getInvisibleActiveFromInvisibleElement(int element)
6667 {
6668   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6669           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6670           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6671           element);
6672 }
6673
6674 static int getInvisibleFromInvisibleActiveElement(int element)
6675 {
6676   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6677           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6678           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6679           element);
6680 }
6681
6682 static void RedrawAllLightSwitchesAndInvisibleElements()
6683 {
6684   int x, y;
6685
6686   SCAN_PLAYFIELD(x, y)
6687   {
6688     int element = Feld[x][y];
6689
6690     if (element == EL_LIGHT_SWITCH &&
6691         game.light_time_left > 0)
6692     {
6693       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6694       TEST_DrawLevelField(x, y);
6695     }
6696     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6697              game.light_time_left == 0)
6698     {
6699       Feld[x][y] = EL_LIGHT_SWITCH;
6700       TEST_DrawLevelField(x, y);
6701     }
6702     else if (element == EL_EMC_DRIPPER &&
6703              game.light_time_left > 0)
6704     {
6705       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6706       TEST_DrawLevelField(x, y);
6707     }
6708     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6709              game.light_time_left == 0)
6710     {
6711       Feld[x][y] = EL_EMC_DRIPPER;
6712       TEST_DrawLevelField(x, y);
6713     }
6714     else if (element == EL_INVISIBLE_STEELWALL ||
6715              element == EL_INVISIBLE_WALL ||
6716              element == EL_INVISIBLE_SAND)
6717     {
6718       if (game.light_time_left > 0)
6719         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6720
6721       TEST_DrawLevelField(x, y);
6722
6723       /* uncrumble neighbour fields, if needed */
6724       if (element == EL_INVISIBLE_SAND)
6725         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6726     }
6727     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6728              element == EL_INVISIBLE_WALL_ACTIVE ||
6729              element == EL_INVISIBLE_SAND_ACTIVE)
6730     {
6731       if (game.light_time_left == 0)
6732         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6733
6734       TEST_DrawLevelField(x, y);
6735
6736       /* re-crumble neighbour fields, if needed */
6737       if (element == EL_INVISIBLE_SAND)
6738         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6739     }
6740   }
6741 }
6742
6743 static void RedrawAllInvisibleElementsForLenses()
6744 {
6745   int x, y;
6746
6747   SCAN_PLAYFIELD(x, y)
6748   {
6749     int element = Feld[x][y];
6750
6751     if (element == EL_EMC_DRIPPER &&
6752         game.lenses_time_left > 0)
6753     {
6754       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6755       TEST_DrawLevelField(x, y);
6756     }
6757     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6758              game.lenses_time_left == 0)
6759     {
6760       Feld[x][y] = EL_EMC_DRIPPER;
6761       TEST_DrawLevelField(x, y);
6762     }
6763     else if (element == EL_INVISIBLE_STEELWALL ||
6764              element == EL_INVISIBLE_WALL ||
6765              element == EL_INVISIBLE_SAND)
6766     {
6767       if (game.lenses_time_left > 0)
6768         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6769
6770       TEST_DrawLevelField(x, y);
6771
6772       /* uncrumble neighbour fields, if needed */
6773       if (element == EL_INVISIBLE_SAND)
6774         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6775     }
6776     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6777              element == EL_INVISIBLE_WALL_ACTIVE ||
6778              element == EL_INVISIBLE_SAND_ACTIVE)
6779     {
6780       if (game.lenses_time_left == 0)
6781         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6782
6783       TEST_DrawLevelField(x, y);
6784
6785       /* re-crumble neighbour fields, if needed */
6786       if (element == EL_INVISIBLE_SAND)
6787         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6788     }
6789   }
6790 }
6791
6792 static void RedrawAllInvisibleElementsForMagnifier()
6793 {
6794   int x, y;
6795
6796   SCAN_PLAYFIELD(x, y)
6797   {
6798     int element = Feld[x][y];
6799
6800     if (element == EL_EMC_FAKE_GRASS &&
6801         game.magnify_time_left > 0)
6802     {
6803       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6804       TEST_DrawLevelField(x, y);
6805     }
6806     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6807              game.magnify_time_left == 0)
6808     {
6809       Feld[x][y] = EL_EMC_FAKE_GRASS;
6810       TEST_DrawLevelField(x, y);
6811     }
6812     else if (IS_GATE_GRAY(element) &&
6813              game.magnify_time_left > 0)
6814     {
6815       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6816                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6817                     IS_EM_GATE_GRAY(element) ?
6818                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6819                     IS_EMC_GATE_GRAY(element) ?
6820                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6821                     IS_DC_GATE_GRAY(element) ?
6822                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6823                     element);
6824       TEST_DrawLevelField(x, y);
6825     }
6826     else if (IS_GATE_GRAY_ACTIVE(element) &&
6827              game.magnify_time_left == 0)
6828     {
6829       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6830                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6831                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6832                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6833                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6834                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6835                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6836                     EL_DC_GATE_WHITE_GRAY :
6837                     element);
6838       TEST_DrawLevelField(x, y);
6839     }
6840   }
6841 }
6842
6843 static void ToggleLightSwitch(int x, int y)
6844 {
6845   int element = Feld[x][y];
6846
6847   game.light_time_left =
6848     (element == EL_LIGHT_SWITCH ?
6849      level.time_light * FRAMES_PER_SECOND : 0);
6850
6851   RedrawAllLightSwitchesAndInvisibleElements();
6852 }
6853
6854 static void ActivateTimegateSwitch(int x, int y)
6855 {
6856   int xx, yy;
6857
6858   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6859
6860   SCAN_PLAYFIELD(xx, yy)
6861   {
6862     int element = Feld[xx][yy];
6863
6864     if (element == EL_TIMEGATE_CLOSED ||
6865         element == EL_TIMEGATE_CLOSING)
6866     {
6867       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6868       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6869     }
6870
6871     /*
6872     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6873     {
6874       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6875       TEST_DrawLevelField(xx, yy);
6876     }
6877     */
6878
6879   }
6880
6881 #if 1
6882   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6883                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6884 #else
6885   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6886 #endif
6887 }
6888
6889 void Impact(int x, int y)
6890 {
6891   boolean last_line = (y == lev_fieldy - 1);
6892   boolean object_hit = FALSE;
6893   boolean impact = (last_line || object_hit);
6894   int element = Feld[x][y];
6895   int smashed = EL_STEELWALL;
6896
6897   if (!last_line)       /* check if element below was hit */
6898   {
6899     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6900       return;
6901
6902     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6903                                          MovDir[x][y + 1] != MV_DOWN ||
6904                                          MovPos[x][y + 1] <= TILEY / 2));
6905
6906     /* do not smash moving elements that left the smashed field in time */
6907     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6908         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6909       object_hit = FALSE;
6910
6911 #if USE_QUICKSAND_IMPACT_BUGFIX
6912     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6913     {
6914       RemoveMovingField(x, y + 1);
6915       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6916       Feld[x][y + 2] = EL_ROCK;
6917       TEST_DrawLevelField(x, y + 2);
6918
6919       object_hit = TRUE;
6920     }
6921
6922     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6923     {
6924       RemoveMovingField(x, y + 1);
6925       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6926       Feld[x][y + 2] = EL_ROCK;
6927       TEST_DrawLevelField(x, y + 2);
6928
6929       object_hit = TRUE;
6930     }
6931 #endif
6932
6933     if (object_hit)
6934       smashed = MovingOrBlocked2Element(x, y + 1);
6935
6936     impact = (last_line || object_hit);
6937   }
6938
6939   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6940   {
6941     SplashAcid(x, y + 1);
6942     return;
6943   }
6944
6945   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6946   /* only reset graphic animation if graphic really changes after impact */
6947   if (impact &&
6948       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6949   {
6950     ResetGfxAnimation(x, y);
6951     TEST_DrawLevelField(x, y);
6952   }
6953
6954   if (impact && CAN_EXPLODE_IMPACT(element))
6955   {
6956     Bang(x, y);
6957     return;
6958   }
6959   else if (impact && element == EL_PEARL &&
6960            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6961   {
6962     ResetGfxAnimation(x, y);
6963
6964     Feld[x][y] = EL_PEARL_BREAKING;
6965     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6966     return;
6967   }
6968   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6969   {
6970     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6971
6972     return;
6973   }
6974
6975   if (impact && element == EL_AMOEBA_DROP)
6976   {
6977     if (object_hit && IS_PLAYER(x, y + 1))
6978       KillPlayerUnlessEnemyProtected(x, y + 1);
6979     else if (object_hit && smashed == EL_PENGUIN)
6980       Bang(x, y + 1);
6981     else
6982     {
6983       Feld[x][y] = EL_AMOEBA_GROWING;
6984       Store[x][y] = EL_AMOEBA_WET;
6985
6986       ResetRandomAnimationValue(x, y);
6987     }
6988     return;
6989   }
6990
6991   if (object_hit)               /* check which object was hit */
6992   {
6993     if ((CAN_PASS_MAGIC_WALL(element) && 
6994          (smashed == EL_MAGIC_WALL ||
6995           smashed == EL_BD_MAGIC_WALL)) ||
6996         (CAN_PASS_DC_MAGIC_WALL(element) &&
6997          smashed == EL_DC_MAGIC_WALL))
6998     {
6999       int xx, yy;
7000       int activated_magic_wall =
7001         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
7002          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
7003          EL_DC_MAGIC_WALL_ACTIVE);
7004
7005       /* activate magic wall / mill */
7006       SCAN_PLAYFIELD(xx, yy)
7007       {
7008         if (Feld[xx][yy] == smashed)
7009           Feld[xx][yy] = activated_magic_wall;
7010       }
7011
7012       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
7013       game.magic_wall_active = TRUE;
7014
7015       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7016                             SND_MAGIC_WALL_ACTIVATING :
7017                             smashed == EL_BD_MAGIC_WALL ?
7018                             SND_BD_MAGIC_WALL_ACTIVATING :
7019                             SND_DC_MAGIC_WALL_ACTIVATING));
7020     }
7021
7022     if (IS_PLAYER(x, y + 1))
7023     {
7024       if (CAN_SMASH_PLAYER(element))
7025       {
7026         KillPlayerUnlessEnemyProtected(x, y + 1);
7027         return;
7028       }
7029     }
7030     else if (smashed == EL_PENGUIN)
7031     {
7032       if (CAN_SMASH_PLAYER(element))
7033       {
7034         Bang(x, y + 1);
7035         return;
7036       }
7037     }
7038     else if (element == EL_BD_DIAMOND)
7039     {
7040       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7041       {
7042         Bang(x, y + 1);
7043         return;
7044       }
7045     }
7046     else if (((element == EL_SP_INFOTRON ||
7047                element == EL_SP_ZONK) &&
7048               (smashed == EL_SP_SNIKSNAK ||
7049                smashed == EL_SP_ELECTRON ||
7050                smashed == EL_SP_DISK_ORANGE)) ||
7051              (element == EL_SP_INFOTRON &&
7052               smashed == EL_SP_DISK_YELLOW))
7053     {
7054       Bang(x, y + 1);
7055       return;
7056     }
7057     else if (CAN_SMASH_EVERYTHING(element))
7058     {
7059       if (IS_CLASSIC_ENEMY(smashed) ||
7060           CAN_EXPLODE_SMASHED(smashed))
7061       {
7062         Bang(x, y + 1);
7063         return;
7064       }
7065       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7066       {
7067         if (smashed == EL_LAMP ||
7068             smashed == EL_LAMP_ACTIVE)
7069         {
7070           Bang(x, y + 1);
7071           return;
7072         }
7073         else if (smashed == EL_NUT)
7074         {
7075           Feld[x][y + 1] = EL_NUT_BREAKING;
7076           PlayLevelSound(x, y, SND_NUT_BREAKING);
7077           RaiseScoreElement(EL_NUT);
7078           return;
7079         }
7080         else if (smashed == EL_PEARL)
7081         {
7082           ResetGfxAnimation(x, y);
7083
7084           Feld[x][y + 1] = EL_PEARL_BREAKING;
7085           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7086           return;
7087         }
7088         else if (smashed == EL_DIAMOND)
7089         {
7090           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7091           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7092           return;
7093         }
7094         else if (IS_BELT_SWITCH(smashed))
7095         {
7096           ToggleBeltSwitch(x, y + 1);
7097         }
7098         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7099                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7100                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7101                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7102         {
7103           ToggleSwitchgateSwitch(x, y + 1);
7104         }
7105         else if (smashed == EL_LIGHT_SWITCH ||
7106                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7107         {
7108           ToggleLightSwitch(x, y + 1);
7109         }
7110         else
7111         {
7112 #if 0
7113           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7114 #endif
7115
7116           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7117
7118           CheckElementChangeBySide(x, y + 1, smashed, element,
7119                                    CE_SWITCHED, CH_SIDE_TOP);
7120           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7121                                             CH_SIDE_TOP);
7122         }
7123       }
7124       else
7125       {
7126         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7127       }
7128     }
7129   }
7130
7131   /* play sound of magic wall / mill */
7132   if (!last_line &&
7133       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7134        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7135        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7136   {
7137     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7138       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7139     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7140       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7141     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7142       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7143
7144     return;
7145   }
7146
7147   /* play sound of object that hits the ground */
7148   if (last_line || object_hit)
7149     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7150 }
7151
7152 inline static void TurnRoundExt(int x, int y)
7153 {
7154   static struct
7155   {
7156     int dx, dy;
7157   } move_xy[] =
7158   {
7159     {  0,  0 },
7160     { -1,  0 },
7161     { +1,  0 },
7162     {  0,  0 },
7163     {  0, -1 },
7164     {  0,  0 }, { 0, 0 }, { 0, 0 },
7165     {  0, +1 }
7166   };
7167   static struct
7168   {
7169     int left, right, back;
7170   } turn[] =
7171   {
7172     { 0,        0,              0        },
7173     { MV_DOWN,  MV_UP,          MV_RIGHT },
7174     { MV_UP,    MV_DOWN,        MV_LEFT  },
7175     { 0,        0,              0        },
7176     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7177     { 0,        0,              0        },
7178     { 0,        0,              0        },
7179     { 0,        0,              0        },
7180     { MV_RIGHT, MV_LEFT,        MV_UP    }
7181   };
7182
7183   int element = Feld[x][y];
7184   int move_pattern = element_info[element].move_pattern;
7185
7186   int old_move_dir = MovDir[x][y];
7187   int left_dir  = turn[old_move_dir].left;
7188   int right_dir = turn[old_move_dir].right;
7189   int back_dir  = turn[old_move_dir].back;
7190
7191   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7192   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7193   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7194   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7195
7196   int left_x  = x + left_dx,  left_y  = y + left_dy;
7197   int right_x = x + right_dx, right_y = y + right_dy;
7198   int move_x  = x + move_dx,  move_y  = y + move_dy;
7199
7200   int xx, yy;
7201
7202   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7203   {
7204     TestIfBadThingTouchesOtherBadThing(x, y);
7205
7206     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7207       MovDir[x][y] = right_dir;
7208     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7209       MovDir[x][y] = left_dir;
7210
7211     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7212       MovDelay[x][y] = 9;
7213     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7214       MovDelay[x][y] = 1;
7215   }
7216   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7217   {
7218     TestIfBadThingTouchesOtherBadThing(x, y);
7219
7220     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7221       MovDir[x][y] = left_dir;
7222     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7223       MovDir[x][y] = right_dir;
7224
7225     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7226       MovDelay[x][y] = 9;
7227     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7228       MovDelay[x][y] = 1;
7229   }
7230   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7231   {
7232     TestIfBadThingTouchesOtherBadThing(x, y);
7233
7234     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7235       MovDir[x][y] = left_dir;
7236     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7237       MovDir[x][y] = right_dir;
7238
7239     if (MovDir[x][y] != old_move_dir)
7240       MovDelay[x][y] = 9;
7241   }
7242   else if (element == EL_YAMYAM)
7243   {
7244     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7245     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7246
7247     if (can_turn_left && can_turn_right)
7248       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7249     else if (can_turn_left)
7250       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7251     else if (can_turn_right)
7252       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7253     else
7254       MovDir[x][y] = back_dir;
7255
7256     MovDelay[x][y] = 16 + 16 * RND(3);
7257   }
7258   else if (element == EL_DARK_YAMYAM)
7259   {
7260     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7261                                                          left_x, left_y);
7262     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7263                                                          right_x, right_y);
7264
7265     if (can_turn_left && can_turn_right)
7266       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7267     else if (can_turn_left)
7268       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7269     else if (can_turn_right)
7270       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7271     else
7272       MovDir[x][y] = back_dir;
7273
7274     MovDelay[x][y] = 16 + 16 * RND(3);
7275   }
7276   else if (element == EL_PACMAN)
7277   {
7278     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7279     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7280
7281     if (can_turn_left && can_turn_right)
7282       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7283     else if (can_turn_left)
7284       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7285     else if (can_turn_right)
7286       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7287     else
7288       MovDir[x][y] = back_dir;
7289
7290     MovDelay[x][y] = 6 + RND(40);
7291   }
7292   else if (element == EL_PIG)
7293   {
7294     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7295     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7296     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7297     boolean should_turn_left, should_turn_right, should_move_on;
7298     int rnd_value = 24;
7299     int rnd = RND(rnd_value);
7300
7301     should_turn_left = (can_turn_left &&
7302                         (!can_move_on ||
7303                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7304                                                    y + back_dy + left_dy)));
7305     should_turn_right = (can_turn_right &&
7306                          (!can_move_on ||
7307                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7308                                                     y + back_dy + right_dy)));
7309     should_move_on = (can_move_on &&
7310                       (!can_turn_left ||
7311                        !can_turn_right ||
7312                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7313                                                  y + move_dy + left_dy) ||
7314                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7315                                                  y + move_dy + right_dy)));
7316
7317     if (should_turn_left || should_turn_right || should_move_on)
7318     {
7319       if (should_turn_left && should_turn_right && should_move_on)
7320         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7321                         rnd < 2 * rnd_value / 3 ? right_dir :
7322                         old_move_dir);
7323       else if (should_turn_left && should_turn_right)
7324         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7325       else if (should_turn_left && should_move_on)
7326         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7327       else if (should_turn_right && should_move_on)
7328         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7329       else if (should_turn_left)
7330         MovDir[x][y] = left_dir;
7331       else if (should_turn_right)
7332         MovDir[x][y] = right_dir;
7333       else if (should_move_on)
7334         MovDir[x][y] = old_move_dir;
7335     }
7336     else if (can_move_on && rnd > rnd_value / 8)
7337       MovDir[x][y] = old_move_dir;
7338     else if (can_turn_left && can_turn_right)
7339       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7340     else if (can_turn_left && rnd > rnd_value / 8)
7341       MovDir[x][y] = left_dir;
7342     else if (can_turn_right && rnd > rnd_value/8)
7343       MovDir[x][y] = right_dir;
7344     else
7345       MovDir[x][y] = back_dir;
7346
7347     xx = x + move_xy[MovDir[x][y]].dx;
7348     yy = y + move_xy[MovDir[x][y]].dy;
7349
7350     if (!IN_LEV_FIELD(xx, yy) ||
7351         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7352       MovDir[x][y] = old_move_dir;
7353
7354     MovDelay[x][y] = 0;
7355   }
7356   else if (element == EL_DRAGON)
7357   {
7358     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7359     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7360     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7361     int rnd_value = 24;
7362     int rnd = RND(rnd_value);
7363
7364     if (can_move_on && rnd > rnd_value / 8)
7365       MovDir[x][y] = old_move_dir;
7366     else if (can_turn_left && can_turn_right)
7367       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7368     else if (can_turn_left && rnd > rnd_value / 8)
7369       MovDir[x][y] = left_dir;
7370     else if (can_turn_right && rnd > rnd_value / 8)
7371       MovDir[x][y] = right_dir;
7372     else
7373       MovDir[x][y] = back_dir;
7374
7375     xx = x + move_xy[MovDir[x][y]].dx;
7376     yy = y + move_xy[MovDir[x][y]].dy;
7377
7378     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7379       MovDir[x][y] = old_move_dir;
7380
7381     MovDelay[x][y] = 0;
7382   }
7383   else if (element == EL_MOLE)
7384   {
7385     boolean can_move_on =
7386       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7387                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7388                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7389     if (!can_move_on)
7390     {
7391       boolean can_turn_left =
7392         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7393                               IS_AMOEBOID(Feld[left_x][left_y])));
7394
7395       boolean can_turn_right =
7396         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7397                               IS_AMOEBOID(Feld[right_x][right_y])));
7398
7399       if (can_turn_left && can_turn_right)
7400         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7401       else if (can_turn_left)
7402         MovDir[x][y] = left_dir;
7403       else
7404         MovDir[x][y] = right_dir;
7405     }
7406
7407     if (MovDir[x][y] != old_move_dir)
7408       MovDelay[x][y] = 9;
7409   }
7410   else if (element == EL_BALLOON)
7411   {
7412     MovDir[x][y] = game.wind_direction;
7413     MovDelay[x][y] = 0;
7414   }
7415   else if (element == EL_SPRING)
7416   {
7417 #if USE_NEW_SPRING_BUMPER
7418     if (MovDir[x][y] & MV_HORIZONTAL)
7419     {
7420       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7421           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7422       {
7423         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7424         ResetGfxAnimation(move_x, move_y);
7425         TEST_DrawLevelField(move_x, move_y);
7426
7427         MovDir[x][y] = back_dir;
7428       }
7429       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7430                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7431         MovDir[x][y] = MV_NONE;
7432     }
7433 #else
7434     if (MovDir[x][y] & MV_HORIZONTAL &&
7435         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7436          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7437       MovDir[x][y] = MV_NONE;
7438 #endif
7439
7440     MovDelay[x][y] = 0;
7441   }
7442   else if (element == EL_ROBOT ||
7443            element == EL_SATELLITE ||
7444            element == EL_PENGUIN ||
7445            element == EL_EMC_ANDROID)
7446   {
7447     int attr_x = -1, attr_y = -1;
7448
7449     if (AllPlayersGone)
7450     {
7451       attr_x = ExitX;
7452       attr_y = ExitY;
7453     }
7454     else
7455     {
7456       int i;
7457
7458       for (i = 0; i < MAX_PLAYERS; i++)
7459       {
7460         struct PlayerInfo *player = &stored_player[i];
7461         int jx = player->jx, jy = player->jy;
7462
7463         if (!player->active)
7464           continue;
7465
7466         if (attr_x == -1 ||
7467             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7468         {
7469           attr_x = jx;
7470           attr_y = jy;
7471         }
7472       }
7473     }
7474
7475     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7476         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7477          game.engine_version < VERSION_IDENT(3,1,0,0)))
7478     {
7479       attr_x = ZX;
7480       attr_y = ZY;
7481     }
7482
7483     if (element == EL_PENGUIN)
7484     {
7485       int i;
7486       static int xy[4][2] =
7487       {
7488         { 0, -1 },
7489         { -1, 0 },
7490         { +1, 0 },
7491         { 0, +1 }
7492       };
7493
7494       for (i = 0; i < NUM_DIRECTIONS; i++)
7495       {
7496         int ex = x + xy[i][0];
7497         int ey = y + xy[i][1];
7498
7499         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7500                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7501                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7502                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7503         {
7504           attr_x = ex;
7505           attr_y = ey;
7506           break;
7507         }
7508       }
7509     }
7510
7511     MovDir[x][y] = MV_NONE;
7512     if (attr_x < x)
7513       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7514     else if (attr_x > x)
7515       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7516     if (attr_y < y)
7517       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7518     else if (attr_y > y)
7519       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7520
7521     if (element == EL_ROBOT)
7522     {
7523       int newx, newy;
7524
7525       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7526         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7527       Moving2Blocked(x, y, &newx, &newy);
7528
7529       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7530         MovDelay[x][y] = 8 + 8 * !RND(3);
7531       else
7532         MovDelay[x][y] = 16;
7533     }
7534     else if (element == EL_PENGUIN)
7535     {
7536       int newx, newy;
7537
7538       MovDelay[x][y] = 1;
7539
7540       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7541       {
7542         boolean first_horiz = RND(2);
7543         int new_move_dir = MovDir[x][y];
7544
7545         MovDir[x][y] =
7546           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7547         Moving2Blocked(x, y, &newx, &newy);
7548
7549         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7550           return;
7551
7552         MovDir[x][y] =
7553           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7554         Moving2Blocked(x, y, &newx, &newy);
7555
7556         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7557           return;
7558
7559         MovDir[x][y] = old_move_dir;
7560         return;
7561       }
7562     }
7563     else if (element == EL_SATELLITE)
7564     {
7565       int newx, newy;
7566
7567       MovDelay[x][y] = 1;
7568
7569       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7570       {
7571         boolean first_horiz = RND(2);
7572         int new_move_dir = MovDir[x][y];
7573
7574         MovDir[x][y] =
7575           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7576         Moving2Blocked(x, y, &newx, &newy);
7577
7578         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7579           return;
7580
7581         MovDir[x][y] =
7582           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7583         Moving2Blocked(x, y, &newx, &newy);
7584
7585         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7586           return;
7587
7588         MovDir[x][y] = old_move_dir;
7589         return;
7590       }
7591     }
7592     else if (element == EL_EMC_ANDROID)
7593     {
7594       static int check_pos[16] =
7595       {
7596         -1,             /*  0 => (invalid)          */
7597         7,              /*  1 => MV_LEFT            */
7598         3,              /*  2 => MV_RIGHT           */
7599         -1,             /*  3 => (invalid)          */
7600         1,              /*  4 =>            MV_UP   */
7601         0,              /*  5 => MV_LEFT  | MV_UP   */
7602         2,              /*  6 => MV_RIGHT | MV_UP   */
7603         -1,             /*  7 => (invalid)          */
7604         5,              /*  8 =>            MV_DOWN */
7605         6,              /*  9 => MV_LEFT  | MV_DOWN */
7606         4,              /* 10 => MV_RIGHT | MV_DOWN */
7607         -1,             /* 11 => (invalid)          */
7608         -1,             /* 12 => (invalid)          */
7609         -1,             /* 13 => (invalid)          */
7610         -1,             /* 14 => (invalid)          */
7611         -1,             /* 15 => (invalid)          */
7612       };
7613       static struct
7614       {
7615         int dx, dy;
7616         int dir;
7617       } check_xy[8] =
7618       {
7619         { -1, -1,       MV_LEFT  | MV_UP   },
7620         {  0, -1,                  MV_UP   },
7621         { +1, -1,       MV_RIGHT | MV_UP   },
7622         { +1,  0,       MV_RIGHT           },
7623         { +1, +1,       MV_RIGHT | MV_DOWN },
7624         {  0, +1,                  MV_DOWN },
7625         { -1, +1,       MV_LEFT  | MV_DOWN },
7626         { -1,  0,       MV_LEFT            },
7627       };
7628       int start_pos, check_order;
7629       boolean can_clone = FALSE;
7630       int i;
7631
7632       /* check if there is any free field around current position */
7633       for (i = 0; i < 8; i++)
7634       {
7635         int newx = x + check_xy[i].dx;
7636         int newy = y + check_xy[i].dy;
7637
7638         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7639         {
7640           can_clone = TRUE;
7641
7642           break;
7643         }
7644       }
7645
7646       if (can_clone)            /* randomly find an element to clone */
7647       {
7648         can_clone = FALSE;
7649
7650         start_pos = check_pos[RND(8)];
7651         check_order = (RND(2) ? -1 : +1);
7652
7653         for (i = 0; i < 8; i++)
7654         {
7655           int pos_raw = start_pos + i * check_order;
7656           int pos = (pos_raw + 8) % 8;
7657           int newx = x + check_xy[pos].dx;
7658           int newy = y + check_xy[pos].dy;
7659
7660           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7661           {
7662             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7663             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7664
7665             Store[x][y] = Feld[newx][newy];
7666
7667             can_clone = TRUE;
7668
7669             break;
7670           }
7671         }
7672       }
7673
7674       if (can_clone)            /* randomly find a direction to move */
7675       {
7676         can_clone = FALSE;
7677
7678         start_pos = check_pos[RND(8)];
7679         check_order = (RND(2) ? -1 : +1);
7680
7681         for (i = 0; i < 8; i++)
7682         {
7683           int pos_raw = start_pos + i * check_order;
7684           int pos = (pos_raw + 8) % 8;
7685           int newx = x + check_xy[pos].dx;
7686           int newy = y + check_xy[pos].dy;
7687           int new_move_dir = check_xy[pos].dir;
7688
7689           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7690           {
7691             MovDir[x][y] = new_move_dir;
7692             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7693
7694             can_clone = TRUE;
7695
7696             break;
7697           }
7698         }
7699       }
7700
7701       if (can_clone)            /* cloning and moving successful */
7702         return;
7703
7704       /* cannot clone -- try to move towards player */
7705
7706       start_pos = check_pos[MovDir[x][y] & 0x0f];
7707       check_order = (RND(2) ? -1 : +1);
7708
7709       for (i = 0; i < 3; i++)
7710       {
7711         /* first check start_pos, then previous/next or (next/previous) pos */
7712         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7713         int pos = (pos_raw + 8) % 8;
7714         int newx = x + check_xy[pos].dx;
7715         int newy = y + check_xy[pos].dy;
7716         int new_move_dir = check_xy[pos].dir;
7717
7718         if (IS_PLAYER(newx, newy))
7719           break;
7720
7721         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7722         {
7723           MovDir[x][y] = new_move_dir;
7724           MovDelay[x][y] = level.android_move_time * 8 + 1;
7725
7726           break;
7727         }
7728       }
7729     }
7730   }
7731   else if (move_pattern == MV_TURNING_LEFT ||
7732            move_pattern == MV_TURNING_RIGHT ||
7733            move_pattern == MV_TURNING_LEFT_RIGHT ||
7734            move_pattern == MV_TURNING_RIGHT_LEFT ||
7735            move_pattern == MV_TURNING_RANDOM ||
7736            move_pattern == MV_ALL_DIRECTIONS)
7737   {
7738     boolean can_turn_left =
7739       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7740     boolean can_turn_right =
7741       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7742
7743     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7744       return;
7745
7746     if (move_pattern == MV_TURNING_LEFT)
7747       MovDir[x][y] = left_dir;
7748     else if (move_pattern == MV_TURNING_RIGHT)
7749       MovDir[x][y] = right_dir;
7750     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7751       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7752     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7753       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7754     else if (move_pattern == MV_TURNING_RANDOM)
7755       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7756                       can_turn_right && !can_turn_left ? right_dir :
7757                       RND(2) ? left_dir : right_dir);
7758     else if (can_turn_left && can_turn_right)
7759       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7760     else if (can_turn_left)
7761       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7762     else if (can_turn_right)
7763       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7764     else
7765       MovDir[x][y] = back_dir;
7766
7767     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7768   }
7769   else if (move_pattern == MV_HORIZONTAL ||
7770            move_pattern == MV_VERTICAL)
7771   {
7772     if (move_pattern & old_move_dir)
7773       MovDir[x][y] = back_dir;
7774     else if (move_pattern == MV_HORIZONTAL)
7775       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7776     else if (move_pattern == MV_VERTICAL)
7777       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7778
7779     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7780   }
7781   else if (move_pattern & MV_ANY_DIRECTION)
7782   {
7783     MovDir[x][y] = move_pattern;
7784     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7785   }
7786   else if (move_pattern & MV_WIND_DIRECTION)
7787   {
7788     MovDir[x][y] = game.wind_direction;
7789     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7790   }
7791   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7792   {
7793     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7794       MovDir[x][y] = left_dir;
7795     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7796       MovDir[x][y] = right_dir;
7797
7798     if (MovDir[x][y] != old_move_dir)
7799       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7800   }
7801   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7802   {
7803     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7804       MovDir[x][y] = right_dir;
7805     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7806       MovDir[x][y] = left_dir;
7807
7808     if (MovDir[x][y] != old_move_dir)
7809       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7810   }
7811   else if (move_pattern == MV_TOWARDS_PLAYER ||
7812            move_pattern == MV_AWAY_FROM_PLAYER)
7813   {
7814     int attr_x = -1, attr_y = -1;
7815     int newx, newy;
7816     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7817
7818     if (AllPlayersGone)
7819     {
7820       attr_x = ExitX;
7821       attr_y = ExitY;
7822     }
7823     else
7824     {
7825       int i;
7826
7827       for (i = 0; i < MAX_PLAYERS; i++)
7828       {
7829         struct PlayerInfo *player = &stored_player[i];
7830         int jx = player->jx, jy = player->jy;
7831
7832         if (!player->active)
7833           continue;
7834
7835         if (attr_x == -1 ||
7836             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7837         {
7838           attr_x = jx;
7839           attr_y = jy;
7840         }
7841       }
7842     }
7843
7844     MovDir[x][y] = MV_NONE;
7845     if (attr_x < x)
7846       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7847     else if (attr_x > x)
7848       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7849     if (attr_y < y)
7850       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7851     else if (attr_y > y)
7852       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7853
7854     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7855
7856     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7857     {
7858       boolean first_horiz = RND(2);
7859       int new_move_dir = MovDir[x][y];
7860
7861       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7862       {
7863         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7864         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7865
7866         return;
7867       }
7868
7869       MovDir[x][y] =
7870         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7871       Moving2Blocked(x, y, &newx, &newy);
7872
7873       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7874         return;
7875
7876       MovDir[x][y] =
7877         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7878       Moving2Blocked(x, y, &newx, &newy);
7879
7880       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7881         return;
7882
7883       MovDir[x][y] = old_move_dir;
7884     }
7885   }
7886   else if (move_pattern == MV_WHEN_PUSHED ||
7887            move_pattern == MV_WHEN_DROPPED)
7888   {
7889     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7890       MovDir[x][y] = MV_NONE;
7891
7892     MovDelay[x][y] = 0;
7893   }
7894   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7895   {
7896     static int test_xy[7][2] =
7897     {
7898       { 0, -1 },
7899       { -1, 0 },
7900       { +1, 0 },
7901       { 0, +1 },
7902       { 0, -1 },
7903       { -1, 0 },
7904       { +1, 0 },
7905     };
7906     static int test_dir[7] =
7907     {
7908       MV_UP,
7909       MV_LEFT,
7910       MV_RIGHT,
7911       MV_DOWN,
7912       MV_UP,
7913       MV_LEFT,
7914       MV_RIGHT,
7915     };
7916     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7917     int move_preference = -1000000;     /* start with very low preference */
7918     int new_move_dir = MV_NONE;
7919     int start_test = RND(4);
7920     int i;
7921
7922     for (i = 0; i < NUM_DIRECTIONS; i++)
7923     {
7924       int move_dir = test_dir[start_test + i];
7925       int move_dir_preference;
7926
7927       xx = x + test_xy[start_test + i][0];
7928       yy = y + test_xy[start_test + i][1];
7929
7930       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7931           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7932       {
7933         new_move_dir = move_dir;
7934
7935         break;
7936       }
7937
7938       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7939         continue;
7940
7941       move_dir_preference = -1 * RunnerVisit[xx][yy];
7942       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7943         move_dir_preference = PlayerVisit[xx][yy];
7944
7945       if (move_dir_preference > move_preference)
7946       {
7947         /* prefer field that has not been visited for the longest time */
7948         move_preference = move_dir_preference;
7949         new_move_dir = move_dir;
7950       }
7951       else if (move_dir_preference == move_preference &&
7952                move_dir == old_move_dir)
7953       {
7954         /* prefer last direction when all directions are preferred equally */
7955         move_preference = move_dir_preference;
7956         new_move_dir = move_dir;
7957       }
7958     }
7959
7960     MovDir[x][y] = new_move_dir;
7961     if (old_move_dir != new_move_dir)
7962       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7963   }
7964 }
7965
7966 static void TurnRound(int x, int y)
7967 {
7968   int direction = MovDir[x][y];
7969
7970   TurnRoundExt(x, y);
7971
7972   GfxDir[x][y] = MovDir[x][y];
7973
7974   if (direction != MovDir[x][y])
7975     GfxFrame[x][y] = 0;
7976
7977   if (MovDelay[x][y])
7978     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7979
7980   ResetGfxFrame(x, y, FALSE);
7981 }
7982
7983 static boolean JustBeingPushed(int x, int y)
7984 {
7985   int i;
7986
7987   for (i = 0; i < MAX_PLAYERS; i++)
7988   {
7989     struct PlayerInfo *player = &stored_player[i];
7990
7991     if (player->active && player->is_pushing && player->MovPos)
7992     {
7993       int next_jx = player->jx + (player->jx - player->last_jx);
7994       int next_jy = player->jy + (player->jy - player->last_jy);
7995
7996       if (x == next_jx && y == next_jy)
7997         return TRUE;
7998     }
7999   }
8000
8001   return FALSE;
8002 }
8003
8004 void StartMoving(int x, int y)
8005 {
8006   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
8007   int element = Feld[x][y];
8008
8009   if (Stop[x][y])
8010     return;
8011
8012   if (MovDelay[x][y] == 0)
8013     GfxAction[x][y] = ACTION_DEFAULT;
8014
8015   if (CAN_FALL(element) && y < lev_fieldy - 1)
8016   {
8017     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
8018         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
8019       if (JustBeingPushed(x, y))
8020         return;
8021
8022     if (element == EL_QUICKSAND_FULL)
8023     {
8024       if (IS_FREE(x, y + 1))
8025       {
8026         InitMovingField(x, y, MV_DOWN);
8027         started_moving = TRUE;
8028
8029         Feld[x][y] = EL_QUICKSAND_EMPTYING;
8030 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8031         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8032           Store[x][y] = EL_ROCK;
8033 #else
8034         Store[x][y] = EL_ROCK;
8035 #endif
8036
8037         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8038       }
8039       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8040       {
8041         if (!MovDelay[x][y])
8042         {
8043           MovDelay[x][y] = TILEY + 1;
8044
8045           ResetGfxAnimation(x, y);
8046           ResetGfxAnimation(x, y + 1);
8047         }
8048
8049         if (MovDelay[x][y])
8050         {
8051           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8052           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8053
8054           MovDelay[x][y]--;
8055           if (MovDelay[x][y])
8056             return;
8057         }
8058
8059         Feld[x][y] = EL_QUICKSAND_EMPTY;
8060         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8061         Store[x][y + 1] = Store[x][y];
8062         Store[x][y] = 0;
8063
8064         PlayLevelSoundAction(x, y, ACTION_FILLING);
8065       }
8066       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8067       {
8068         if (!MovDelay[x][y])
8069         {
8070           MovDelay[x][y] = TILEY + 1;
8071
8072           ResetGfxAnimation(x, y);
8073           ResetGfxAnimation(x, y + 1);
8074         }
8075
8076         if (MovDelay[x][y])
8077         {
8078           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8079           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8080
8081           MovDelay[x][y]--;
8082           if (MovDelay[x][y])
8083             return;
8084         }
8085
8086         Feld[x][y] = EL_QUICKSAND_EMPTY;
8087         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8088         Store[x][y + 1] = Store[x][y];
8089         Store[x][y] = 0;
8090
8091         PlayLevelSoundAction(x, y, ACTION_FILLING);
8092       }
8093     }
8094     else if (element == EL_QUICKSAND_FAST_FULL)
8095     {
8096       if (IS_FREE(x, y + 1))
8097       {
8098         InitMovingField(x, y, MV_DOWN);
8099         started_moving = TRUE;
8100
8101         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8102 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8103         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8104           Store[x][y] = EL_ROCK;
8105 #else
8106         Store[x][y] = EL_ROCK;
8107 #endif
8108
8109         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8110       }
8111       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8112       {
8113         if (!MovDelay[x][y])
8114         {
8115           MovDelay[x][y] = TILEY + 1;
8116
8117           ResetGfxAnimation(x, y);
8118           ResetGfxAnimation(x, y + 1);
8119         }
8120
8121         if (MovDelay[x][y])
8122         {
8123           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8124           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8125
8126           MovDelay[x][y]--;
8127           if (MovDelay[x][y])
8128             return;
8129         }
8130
8131         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8132         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8133         Store[x][y + 1] = Store[x][y];
8134         Store[x][y] = 0;
8135
8136         PlayLevelSoundAction(x, y, ACTION_FILLING);
8137       }
8138       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8139       {
8140         if (!MovDelay[x][y])
8141         {
8142           MovDelay[x][y] = TILEY + 1;
8143
8144           ResetGfxAnimation(x, y);
8145           ResetGfxAnimation(x, y + 1);
8146         }
8147
8148         if (MovDelay[x][y])
8149         {
8150           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8151           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8152
8153           MovDelay[x][y]--;
8154           if (MovDelay[x][y])
8155             return;
8156         }
8157
8158         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8159         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8160         Store[x][y + 1] = Store[x][y];
8161         Store[x][y] = 0;
8162
8163         PlayLevelSoundAction(x, y, ACTION_FILLING);
8164       }
8165     }
8166     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8167              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8168     {
8169       InitMovingField(x, y, MV_DOWN);
8170       started_moving = TRUE;
8171
8172       Feld[x][y] = EL_QUICKSAND_FILLING;
8173       Store[x][y] = element;
8174
8175       PlayLevelSoundAction(x, y, ACTION_FILLING);
8176     }
8177     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8178              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8179     {
8180       InitMovingField(x, y, MV_DOWN);
8181       started_moving = TRUE;
8182
8183       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8184       Store[x][y] = element;
8185
8186       PlayLevelSoundAction(x, y, ACTION_FILLING);
8187     }
8188     else if (element == EL_MAGIC_WALL_FULL)
8189     {
8190       if (IS_FREE(x, y + 1))
8191       {
8192         InitMovingField(x, y, MV_DOWN);
8193         started_moving = TRUE;
8194
8195         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8196         Store[x][y] = EL_CHANGED(Store[x][y]);
8197       }
8198       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8199       {
8200         if (!MovDelay[x][y])
8201           MovDelay[x][y] = TILEY / 4 + 1;
8202
8203         if (MovDelay[x][y])
8204         {
8205           MovDelay[x][y]--;
8206           if (MovDelay[x][y])
8207             return;
8208         }
8209
8210         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8211         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8212         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8213         Store[x][y] = 0;
8214       }
8215     }
8216     else if (element == EL_BD_MAGIC_WALL_FULL)
8217     {
8218       if (IS_FREE(x, y + 1))
8219       {
8220         InitMovingField(x, y, MV_DOWN);
8221         started_moving = TRUE;
8222
8223         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8224         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8225       }
8226       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8227       {
8228         if (!MovDelay[x][y])
8229           MovDelay[x][y] = TILEY / 4 + 1;
8230
8231         if (MovDelay[x][y])
8232         {
8233           MovDelay[x][y]--;
8234           if (MovDelay[x][y])
8235             return;
8236         }
8237
8238         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8239         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8240         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8241         Store[x][y] = 0;
8242       }
8243     }
8244     else if (element == EL_DC_MAGIC_WALL_FULL)
8245     {
8246       if (IS_FREE(x, y + 1))
8247       {
8248         InitMovingField(x, y, MV_DOWN);
8249         started_moving = TRUE;
8250
8251         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8252         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8253       }
8254       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8255       {
8256         if (!MovDelay[x][y])
8257           MovDelay[x][y] = TILEY / 4 + 1;
8258
8259         if (MovDelay[x][y])
8260         {
8261           MovDelay[x][y]--;
8262           if (MovDelay[x][y])
8263             return;
8264         }
8265
8266         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8267         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8268         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8269         Store[x][y] = 0;
8270       }
8271     }
8272     else if ((CAN_PASS_MAGIC_WALL(element) &&
8273               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8274                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8275              (CAN_PASS_DC_MAGIC_WALL(element) &&
8276               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8277
8278     {
8279       InitMovingField(x, y, MV_DOWN);
8280       started_moving = TRUE;
8281
8282       Feld[x][y] =
8283         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8284          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8285          EL_DC_MAGIC_WALL_FILLING);
8286       Store[x][y] = element;
8287     }
8288     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8289     {
8290       SplashAcid(x, y + 1);
8291
8292       InitMovingField(x, y, MV_DOWN);
8293       started_moving = TRUE;
8294
8295       Store[x][y] = EL_ACID;
8296     }
8297     else if (
8298 #if USE_FIX_IMPACT_COLLISION
8299              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8300               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8301 #else
8302              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8303               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8304 #endif
8305              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8306               CAN_FALL(element) && WasJustFalling[x][y] &&
8307               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8308
8309              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8310               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8311               (Feld[x][y + 1] == EL_BLOCKED)))
8312     {
8313       /* this is needed for a special case not covered by calling "Impact()"
8314          from "ContinueMoving()": if an element moves to a tile directly below
8315          another element which was just falling on that tile (which was empty
8316          in the previous frame), the falling element above would just stop
8317          instead of smashing the element below (in previous version, the above
8318          element was just checked for "moving" instead of "falling", resulting
8319          in incorrect smashes caused by horizontal movement of the above
8320          element; also, the case of the player being the element to smash was
8321          simply not covered here... :-/ ) */
8322
8323       CheckCollision[x][y] = 0;
8324       CheckImpact[x][y] = 0;
8325
8326       Impact(x, y);
8327     }
8328     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8329     {
8330       if (MovDir[x][y] == MV_NONE)
8331       {
8332         InitMovingField(x, y, MV_DOWN);
8333         started_moving = TRUE;
8334       }
8335     }
8336     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8337     {
8338       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8339         MovDir[x][y] = MV_DOWN;
8340
8341       InitMovingField(x, y, MV_DOWN);
8342       started_moving = TRUE;
8343     }
8344     else if (element == EL_AMOEBA_DROP)
8345     {
8346       Feld[x][y] = EL_AMOEBA_GROWING;
8347       Store[x][y] = EL_AMOEBA_WET;
8348     }
8349     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8350               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8351              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8352              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8353     {
8354       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8355                                 (IS_FREE(x - 1, y + 1) ||
8356                                  Feld[x - 1][y + 1] == EL_ACID));
8357       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8358                                 (IS_FREE(x + 1, y + 1) ||
8359                                  Feld[x + 1][y + 1] == EL_ACID));
8360       boolean can_fall_any  = (can_fall_left || can_fall_right);
8361       boolean can_fall_both = (can_fall_left && can_fall_right);
8362       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8363
8364 #if USE_NEW_ALL_SLIPPERY
8365       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8366       {
8367         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8368           can_fall_right = FALSE;
8369         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8370           can_fall_left = FALSE;
8371         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8372           can_fall_right = FALSE;
8373         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8374           can_fall_left = FALSE;
8375
8376         can_fall_any  = (can_fall_left || can_fall_right);
8377         can_fall_both = FALSE;
8378       }
8379 #else
8380       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8381       {
8382         if (slippery_type == SLIPPERY_ONLY_LEFT)
8383           can_fall_right = FALSE;
8384         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8385           can_fall_left = FALSE;
8386         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8387           can_fall_right = FALSE;
8388         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8389           can_fall_left = FALSE;
8390
8391         can_fall_any  = (can_fall_left || can_fall_right);
8392         can_fall_both = (can_fall_left && can_fall_right);
8393       }
8394 #endif
8395
8396 #if USE_NEW_ALL_SLIPPERY
8397 #else
8398 #if USE_NEW_SP_SLIPPERY
8399       /* !!! better use the same properties as for custom elements here !!! */
8400       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8401                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8402       {
8403         can_fall_right = FALSE;         /* slip down on left side */
8404         can_fall_both = FALSE;
8405       }
8406 #endif
8407 #endif
8408
8409 #if USE_NEW_ALL_SLIPPERY
8410       if (can_fall_both)
8411       {
8412         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8413           can_fall_right = FALSE;       /* slip down on left side */
8414         else
8415           can_fall_left = !(can_fall_right = RND(2));
8416
8417         can_fall_both = FALSE;
8418       }
8419 #else
8420       if (can_fall_both)
8421       {
8422         if (game.emulation == EMU_BOULDERDASH ||
8423             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8424           can_fall_right = FALSE;       /* slip down on left side */
8425         else
8426           can_fall_left = !(can_fall_right = RND(2));
8427
8428         can_fall_both = FALSE;
8429       }
8430 #endif
8431
8432       if (can_fall_any)
8433       {
8434         /* if not determined otherwise, prefer left side for slipping down */
8435         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8436         started_moving = TRUE;
8437       }
8438     }
8439 #if 0
8440     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8441 #else
8442     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8443 #endif
8444     {
8445       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8446       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8447       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8448       int belt_dir = game.belt_dir[belt_nr];
8449
8450       if ((belt_dir == MV_LEFT  && left_is_free) ||
8451           (belt_dir == MV_RIGHT && right_is_free))
8452       {
8453         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8454
8455         InitMovingField(x, y, belt_dir);
8456         started_moving = TRUE;
8457
8458         Pushed[x][y] = TRUE;
8459         Pushed[nextx][y] = TRUE;
8460
8461         GfxAction[x][y] = ACTION_DEFAULT;
8462       }
8463       else
8464       {
8465         MovDir[x][y] = 0;       /* if element was moving, stop it */
8466       }
8467     }
8468   }
8469
8470   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8471 #if 0
8472   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8473 #else
8474   if (CAN_MOVE(element) && !started_moving)
8475 #endif
8476   {
8477     int move_pattern = element_info[element].move_pattern;
8478     int newx, newy;
8479
8480 #if 0
8481 #if DEBUG
8482     if (MovDir[x][y] == MV_NONE)
8483     {
8484       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8485              x, y, element, element_info[element].token_name);
8486       printf("StartMoving(): This should never happen!\n");
8487     }
8488 #endif
8489 #endif
8490
8491     Moving2Blocked(x, y, &newx, &newy);
8492
8493     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8494       return;
8495
8496     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8497         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8498     {
8499       WasJustMoving[x][y] = 0;
8500       CheckCollision[x][y] = 0;
8501
8502       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8503
8504       if (Feld[x][y] != element)        /* element has changed */
8505         return;
8506     }
8507
8508     if (!MovDelay[x][y])        /* start new movement phase */
8509     {
8510       /* all objects that can change their move direction after each step
8511          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8512
8513       if (element != EL_YAMYAM &&
8514           element != EL_DARK_YAMYAM &&
8515           element != EL_PACMAN &&
8516           !(move_pattern & MV_ANY_DIRECTION) &&
8517           move_pattern != MV_TURNING_LEFT &&
8518           move_pattern != MV_TURNING_RIGHT &&
8519           move_pattern != MV_TURNING_LEFT_RIGHT &&
8520           move_pattern != MV_TURNING_RIGHT_LEFT &&
8521           move_pattern != MV_TURNING_RANDOM)
8522       {
8523         TurnRound(x, y);
8524
8525         if (MovDelay[x][y] && (element == EL_BUG ||
8526                                element == EL_SPACESHIP ||
8527                                element == EL_SP_SNIKSNAK ||
8528                                element == EL_SP_ELECTRON ||
8529                                element == EL_MOLE))
8530           TEST_DrawLevelField(x, y);
8531       }
8532     }
8533
8534     if (MovDelay[x][y])         /* wait some time before next movement */
8535     {
8536       MovDelay[x][y]--;
8537
8538       if (element == EL_ROBOT ||
8539           element == EL_YAMYAM ||
8540           element == EL_DARK_YAMYAM)
8541       {
8542         DrawLevelElementAnimationIfNeeded(x, y, element);
8543         PlayLevelSoundAction(x, y, ACTION_WAITING);
8544       }
8545       else if (element == EL_SP_ELECTRON)
8546         DrawLevelElementAnimationIfNeeded(x, y, element);
8547       else if (element == EL_DRAGON)
8548       {
8549         int i;
8550         int dir = MovDir[x][y];
8551         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8552         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8553         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8554                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8555                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8556                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8557         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8558
8559         GfxAction[x][y] = ACTION_ATTACKING;
8560
8561         if (IS_PLAYER(x, y))
8562           DrawPlayerField(x, y);
8563         else
8564           TEST_DrawLevelField(x, y);
8565
8566         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8567
8568         for (i = 1; i <= 3; i++)
8569         {
8570           int xx = x + i * dx;
8571           int yy = y + i * dy;
8572           int sx = SCREENX(xx);
8573           int sy = SCREENY(yy);
8574           int flame_graphic = graphic + (i - 1);
8575
8576           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8577             break;
8578
8579           if (MovDelay[x][y])
8580           {
8581             int flamed = MovingOrBlocked2Element(xx, yy);
8582
8583             /* !!! */
8584 #if 0
8585             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8586               Bang(xx, yy);
8587             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8588               RemoveMovingField(xx, yy);
8589             else
8590               RemoveField(xx, yy);
8591 #else
8592             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8593               Bang(xx, yy);
8594             else
8595               RemoveMovingField(xx, yy);
8596 #endif
8597
8598             ChangeDelay[xx][yy] = 0;
8599
8600             Feld[xx][yy] = EL_FLAMES;
8601
8602             if (IN_SCR_FIELD(sx, sy))
8603             {
8604               TEST_DrawLevelFieldCrumbled(xx, yy);
8605               DrawGraphic(sx, sy, flame_graphic, frame);
8606             }
8607           }
8608           else
8609           {
8610             if (Feld[xx][yy] == EL_FLAMES)
8611               Feld[xx][yy] = EL_EMPTY;
8612             TEST_DrawLevelField(xx, yy);
8613           }
8614         }
8615       }
8616
8617       if (MovDelay[x][y])       /* element still has to wait some time */
8618       {
8619         PlayLevelSoundAction(x, y, ACTION_WAITING);
8620
8621         return;
8622       }
8623     }
8624
8625     /* now make next step */
8626
8627     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8628
8629     if (DONT_COLLIDE_WITH(element) &&
8630         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8631         !PLAYER_ENEMY_PROTECTED(newx, newy))
8632     {
8633       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8634
8635       return;
8636     }
8637
8638     else if (CAN_MOVE_INTO_ACID(element) &&
8639              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8640              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8641              (MovDir[x][y] == MV_DOWN ||
8642               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8643     {
8644       SplashAcid(newx, newy);
8645       Store[x][y] = EL_ACID;
8646     }
8647     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8648     {
8649       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8650           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8651           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8652           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8653       {
8654         RemoveField(x, y);
8655         TEST_DrawLevelField(x, y);
8656
8657         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8658         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8659           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8660
8661         local_player->friends_still_needed--;
8662         if (!local_player->friends_still_needed &&
8663             !local_player->GameOver && AllPlayersGone)
8664           PlayerWins(local_player);
8665
8666         return;
8667       }
8668       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8669       {
8670         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8671           TEST_DrawLevelField(newx, newy);
8672         else
8673           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8674       }
8675       else if (!IS_FREE(newx, newy))
8676       {
8677         GfxAction[x][y] = ACTION_WAITING;
8678
8679         if (IS_PLAYER(x, y))
8680           DrawPlayerField(x, y);
8681         else
8682           TEST_DrawLevelField(x, y);
8683
8684         return;
8685       }
8686     }
8687     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8688     {
8689       if (IS_FOOD_PIG(Feld[newx][newy]))
8690       {
8691         if (IS_MOVING(newx, newy))
8692           RemoveMovingField(newx, newy);
8693         else
8694         {
8695           Feld[newx][newy] = EL_EMPTY;
8696           TEST_DrawLevelField(newx, newy);
8697         }
8698
8699         PlayLevelSound(x, y, SND_PIG_DIGGING);
8700       }
8701       else if (!IS_FREE(newx, newy))
8702       {
8703         if (IS_PLAYER(x, y))
8704           DrawPlayerField(x, y);
8705         else
8706           TEST_DrawLevelField(x, y);
8707
8708         return;
8709       }
8710     }
8711     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8712     {
8713       if (Store[x][y] != EL_EMPTY)
8714       {
8715         boolean can_clone = FALSE;
8716         int xx, yy;
8717
8718         /* check if element to clone is still there */
8719         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8720         {
8721           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8722           {
8723             can_clone = TRUE;
8724
8725             break;
8726           }
8727         }
8728
8729         /* cannot clone or target field not free anymore -- do not clone */
8730         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8731           Store[x][y] = EL_EMPTY;
8732       }
8733
8734       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8735       {
8736         if (IS_MV_DIAGONAL(MovDir[x][y]))
8737         {
8738           int diagonal_move_dir = MovDir[x][y];
8739           int stored = Store[x][y];
8740           int change_delay = 8;
8741           int graphic;
8742
8743           /* android is moving diagonally */
8744
8745           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8746
8747           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8748           GfxElement[x][y] = EL_EMC_ANDROID;
8749           GfxAction[x][y] = ACTION_SHRINKING;
8750           GfxDir[x][y] = diagonal_move_dir;
8751           ChangeDelay[x][y] = change_delay;
8752
8753           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8754                                    GfxDir[x][y]);
8755
8756           DrawLevelGraphicAnimation(x, y, graphic);
8757           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8758
8759           if (Feld[newx][newy] == EL_ACID)
8760           {
8761             SplashAcid(newx, newy);
8762
8763             return;
8764           }
8765
8766           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8767
8768           Store[newx][newy] = EL_EMC_ANDROID;
8769           GfxElement[newx][newy] = EL_EMC_ANDROID;
8770           GfxAction[newx][newy] = ACTION_GROWING;
8771           GfxDir[newx][newy] = diagonal_move_dir;
8772           ChangeDelay[newx][newy] = change_delay;
8773
8774           graphic = el_act_dir2img(GfxElement[newx][newy],
8775                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8776
8777           DrawLevelGraphicAnimation(newx, newy, graphic);
8778           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8779
8780           return;
8781         }
8782         else
8783         {
8784           Feld[newx][newy] = EL_EMPTY;
8785           TEST_DrawLevelField(newx, newy);
8786
8787           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8788         }
8789       }
8790       else if (!IS_FREE(newx, newy))
8791       {
8792 #if 0
8793         if (IS_PLAYER(x, y))
8794           DrawPlayerField(x, y);
8795         else
8796           TEST_DrawLevelField(x, y);
8797 #endif
8798
8799         return;
8800       }
8801     }
8802     else if (IS_CUSTOM_ELEMENT(element) &&
8803              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8804     {
8805 #if 1
8806       if (!DigFieldByCE(newx, newy, element))
8807         return;
8808 #else
8809       int new_element = Feld[newx][newy];
8810
8811       if (!IS_FREE(newx, newy))
8812       {
8813         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8814                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8815                       ACTION_BREAKING);
8816
8817         /* no element can dig solid indestructible elements */
8818         if (IS_INDESTRUCTIBLE(new_element) &&
8819             !IS_DIGGABLE(new_element) &&
8820             !IS_COLLECTIBLE(new_element))
8821           return;
8822
8823         if (AmoebaNr[newx][newy] &&
8824             (new_element == EL_AMOEBA_FULL ||
8825              new_element == EL_BD_AMOEBA ||
8826              new_element == EL_AMOEBA_GROWING))
8827         {
8828           AmoebaCnt[AmoebaNr[newx][newy]]--;
8829           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8830         }
8831
8832         if (IS_MOVING(newx, newy))
8833           RemoveMovingField(newx, newy);
8834         else
8835         {
8836           RemoveField(newx, newy);
8837           TEST_DrawLevelField(newx, newy);
8838         }
8839
8840         /* if digged element was about to explode, prevent the explosion */
8841         ExplodeField[newx][newy] = EX_TYPE_NONE;
8842
8843         PlayLevelSoundAction(x, y, action);
8844       }
8845
8846       Store[newx][newy] = EL_EMPTY;
8847
8848 #if 1
8849       /* this makes it possible to leave the removed element again */
8850       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8851         Store[newx][newy] = new_element;
8852 #else
8853       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8854       {
8855         int move_leave_element = element_info[element].move_leave_element;
8856
8857         /* this makes it possible to leave the removed element again */
8858         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8859                              new_element : move_leave_element);
8860       }
8861 #endif
8862
8863 #endif
8864
8865       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8866       {
8867         RunnerVisit[x][y] = FrameCounter;
8868         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8869       }
8870     }
8871     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8872     {
8873       if (!IS_FREE(newx, newy))
8874       {
8875         if (IS_PLAYER(x, y))
8876           DrawPlayerField(x, y);
8877         else
8878           TEST_DrawLevelField(x, y);
8879
8880         return;
8881       }
8882       else
8883       {
8884         boolean wanna_flame = !RND(10);
8885         int dx = newx - x, dy = newy - y;
8886         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8887         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8888         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8889                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8890         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8891                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8892
8893         if ((wanna_flame ||
8894              IS_CLASSIC_ENEMY(element1) ||
8895              IS_CLASSIC_ENEMY(element2)) &&
8896             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8897             element1 != EL_FLAMES && element2 != EL_FLAMES)
8898         {
8899           ResetGfxAnimation(x, y);
8900           GfxAction[x][y] = ACTION_ATTACKING;
8901
8902           if (IS_PLAYER(x, y))
8903             DrawPlayerField(x, y);
8904           else
8905             TEST_DrawLevelField(x, y);
8906
8907           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8908
8909           MovDelay[x][y] = 50;
8910
8911           /* !!! */
8912 #if 0
8913           RemoveField(newx, newy);
8914 #endif
8915           Feld[newx][newy] = EL_FLAMES;
8916           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8917           {
8918 #if 0
8919             RemoveField(newx1, newy1);
8920 #endif
8921             Feld[newx1][newy1] = EL_FLAMES;
8922           }
8923           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8924           {
8925 #if 0
8926             RemoveField(newx2, newy2);
8927 #endif
8928             Feld[newx2][newy2] = EL_FLAMES;
8929           }
8930
8931           return;
8932         }
8933       }
8934     }
8935     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8936              Feld[newx][newy] == EL_DIAMOND)
8937     {
8938       if (IS_MOVING(newx, newy))
8939         RemoveMovingField(newx, newy);
8940       else
8941       {
8942         Feld[newx][newy] = EL_EMPTY;
8943         TEST_DrawLevelField(newx, newy);
8944       }
8945
8946       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8947     }
8948     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8949              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8950     {
8951       if (AmoebaNr[newx][newy])
8952       {
8953         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8954         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8955             Feld[newx][newy] == EL_BD_AMOEBA)
8956           AmoebaCnt[AmoebaNr[newx][newy]]--;
8957       }
8958
8959 #if 0
8960       /* !!! test !!! */
8961       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8962       {
8963         RemoveMovingField(newx, newy);
8964       }
8965 #else
8966       if (IS_MOVING(newx, newy))
8967       {
8968         RemoveMovingField(newx, newy);
8969       }
8970 #endif
8971       else
8972       {
8973         Feld[newx][newy] = EL_EMPTY;
8974         TEST_DrawLevelField(newx, newy);
8975       }
8976
8977       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8978     }
8979     else if ((element == EL_PACMAN || element == EL_MOLE)
8980              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8981     {
8982       if (AmoebaNr[newx][newy])
8983       {
8984         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8985         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8986             Feld[newx][newy] == EL_BD_AMOEBA)
8987           AmoebaCnt[AmoebaNr[newx][newy]]--;
8988       }
8989
8990       if (element == EL_MOLE)
8991       {
8992         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8993         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8994
8995         ResetGfxAnimation(x, y);
8996         GfxAction[x][y] = ACTION_DIGGING;
8997         TEST_DrawLevelField(x, y);
8998
8999         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
9000
9001         return;                         /* wait for shrinking amoeba */
9002       }
9003       else      /* element == EL_PACMAN */
9004       {
9005         Feld[newx][newy] = EL_EMPTY;
9006         TEST_DrawLevelField(newx, newy);
9007         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
9008       }
9009     }
9010     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
9011              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
9012               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
9013     {
9014       /* wait for shrinking amoeba to completely disappear */
9015       return;
9016     }
9017     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
9018     {
9019       /* object was running against a wall */
9020
9021       TurnRound(x, y);
9022
9023 #if 0
9024       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
9025       if (move_pattern & MV_ANY_DIRECTION &&
9026           move_pattern == MovDir[x][y])
9027       {
9028         int blocking_element =
9029           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
9030
9031         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
9032                                  MovDir[x][y]);
9033
9034         element = Feld[x][y];   /* element might have changed */
9035       }
9036 #endif
9037
9038       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
9039         DrawLevelElementAnimation(x, y, element);
9040
9041       if (DONT_TOUCH(element))
9042         TestIfBadThingTouchesPlayer(x, y);
9043
9044       return;
9045     }
9046
9047     InitMovingField(x, y, MovDir[x][y]);
9048
9049     PlayLevelSoundAction(x, y, ACTION_MOVING);
9050   }
9051
9052   if (MovDir[x][y])
9053     ContinueMoving(x, y);
9054 }
9055
9056 void ContinueMoving(int x, int y)
9057 {
9058   int element = Feld[x][y];
9059   struct ElementInfo *ei = &element_info[element];
9060   int direction = MovDir[x][y];
9061   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9062   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9063   int newx = x + dx, newy = y + dy;
9064   int stored = Store[x][y];
9065   int stored_new = Store[newx][newy];
9066   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
9067   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9068   boolean last_line = (newy == lev_fieldy - 1);
9069
9070   MovPos[x][y] += getElementMoveStepsize(x, y);
9071
9072   if (pushed_by_player) /* special case: moving object pushed by player */
9073     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9074
9075   if (ABS(MovPos[x][y]) < TILEX)
9076   {
9077 #if 0
9078     int ee = Feld[x][y];
9079     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9080     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9081
9082     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9083            x, y, ABS(MovPos[x][y]),
9084            ee, gg, ff,
9085            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9086 #endif
9087
9088     TEST_DrawLevelField(x, y);
9089
9090     return;     /* element is still moving */
9091   }
9092
9093   /* element reached destination field */
9094
9095   Feld[x][y] = EL_EMPTY;
9096   Feld[newx][newy] = element;
9097   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9098
9099   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9100   {
9101     element = Feld[newx][newy] = EL_ACID;
9102   }
9103   else if (element == EL_MOLE)
9104   {
9105     Feld[x][y] = EL_SAND;
9106
9107     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9108   }
9109   else if (element == EL_QUICKSAND_FILLING)
9110   {
9111     element = Feld[newx][newy] = get_next_element(element);
9112     Store[newx][newy] = Store[x][y];
9113   }
9114   else if (element == EL_QUICKSAND_EMPTYING)
9115   {
9116     Feld[x][y] = get_next_element(element);
9117     element = Feld[newx][newy] = Store[x][y];
9118   }
9119   else if (element == EL_QUICKSAND_FAST_FILLING)
9120   {
9121     element = Feld[newx][newy] = get_next_element(element);
9122     Store[newx][newy] = Store[x][y];
9123   }
9124   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9125   {
9126     Feld[x][y] = get_next_element(element);
9127     element = Feld[newx][newy] = Store[x][y];
9128   }
9129   else if (element == EL_MAGIC_WALL_FILLING)
9130   {
9131     element = Feld[newx][newy] = get_next_element(element);
9132     if (!game.magic_wall_active)
9133       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9134     Store[newx][newy] = Store[x][y];
9135   }
9136   else if (element == EL_MAGIC_WALL_EMPTYING)
9137   {
9138     Feld[x][y] = get_next_element(element);
9139     if (!game.magic_wall_active)
9140       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9141     element = Feld[newx][newy] = Store[x][y];
9142
9143 #if USE_NEW_CUSTOM_VALUE
9144     InitField(newx, newy, FALSE);
9145 #endif
9146   }
9147   else if (element == EL_BD_MAGIC_WALL_FILLING)
9148   {
9149     element = Feld[newx][newy] = get_next_element(element);
9150     if (!game.magic_wall_active)
9151       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9152     Store[newx][newy] = Store[x][y];
9153   }
9154   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9155   {
9156     Feld[x][y] = get_next_element(element);
9157     if (!game.magic_wall_active)
9158       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9159     element = Feld[newx][newy] = Store[x][y];
9160
9161 #if USE_NEW_CUSTOM_VALUE
9162     InitField(newx, newy, FALSE);
9163 #endif
9164   }
9165   else if (element == EL_DC_MAGIC_WALL_FILLING)
9166   {
9167     element = Feld[newx][newy] = get_next_element(element);
9168     if (!game.magic_wall_active)
9169       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9170     Store[newx][newy] = Store[x][y];
9171   }
9172   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9173   {
9174     Feld[x][y] = get_next_element(element);
9175     if (!game.magic_wall_active)
9176       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9177     element = Feld[newx][newy] = Store[x][y];
9178
9179 #if USE_NEW_CUSTOM_VALUE
9180     InitField(newx, newy, FALSE);
9181 #endif
9182   }
9183   else if (element == EL_AMOEBA_DROPPING)
9184   {
9185     Feld[x][y] = get_next_element(element);
9186     element = Feld[newx][newy] = Store[x][y];
9187   }
9188   else if (element == EL_SOKOBAN_OBJECT)
9189   {
9190     if (Back[x][y])
9191       Feld[x][y] = Back[x][y];
9192
9193     if (Back[newx][newy])
9194       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9195
9196     Back[x][y] = Back[newx][newy] = 0;
9197   }
9198
9199   Store[x][y] = EL_EMPTY;
9200   MovPos[x][y] = 0;
9201   MovDir[x][y] = 0;
9202   MovDelay[x][y] = 0;
9203
9204   MovDelay[newx][newy] = 0;
9205
9206   if (CAN_CHANGE_OR_HAS_ACTION(element))
9207   {
9208     /* copy element change control values to new field */
9209     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9210     ChangePage[newx][newy]  = ChangePage[x][y];
9211     ChangeCount[newx][newy] = ChangeCount[x][y];
9212     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9213   }
9214
9215 #if USE_NEW_CUSTOM_VALUE
9216   CustomValue[newx][newy] = CustomValue[x][y];
9217 #endif
9218
9219   ChangeDelay[x][y] = 0;
9220   ChangePage[x][y] = -1;
9221   ChangeCount[x][y] = 0;
9222   ChangeEvent[x][y] = -1;
9223
9224 #if USE_NEW_CUSTOM_VALUE
9225   CustomValue[x][y] = 0;
9226 #endif
9227
9228   /* copy animation control values to new field */
9229   GfxFrame[newx][newy]  = GfxFrame[x][y];
9230   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9231   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9232   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9233
9234   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9235
9236   /* some elements can leave other elements behind after moving */
9237 #if 1
9238   if (ei->move_leave_element != EL_EMPTY &&
9239       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9240       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9241 #else
9242   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9243       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9244       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9245 #endif
9246   {
9247     int move_leave_element = ei->move_leave_element;
9248
9249 #if 1
9250 #if 1
9251     /* this makes it possible to leave the removed element again */
9252     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9253       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9254 #else
9255     /* this makes it possible to leave the removed element again */
9256     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9257       move_leave_element = stored;
9258 #endif
9259 #else
9260     /* this makes it possible to leave the removed element again */
9261     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9262         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9263       move_leave_element = stored;
9264 #endif
9265
9266     Feld[x][y] = move_leave_element;
9267
9268     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9269       MovDir[x][y] = direction;
9270
9271     InitField(x, y, FALSE);
9272
9273     if (GFX_CRUMBLED(Feld[x][y]))
9274       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9275
9276     if (ELEM_IS_PLAYER(move_leave_element))
9277       RelocatePlayer(x, y, move_leave_element);
9278   }
9279
9280   /* do this after checking for left-behind element */
9281   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9282
9283   if (!CAN_MOVE(element) ||
9284       (CAN_FALL(element) && direction == MV_DOWN &&
9285        (element == EL_SPRING ||
9286         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9287         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9288     GfxDir[x][y] = MovDir[newx][newy] = 0;
9289
9290   TEST_DrawLevelField(x, y);
9291   TEST_DrawLevelField(newx, newy);
9292
9293   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9294
9295   /* prevent pushed element from moving on in pushed direction */
9296   if (pushed_by_player && CAN_MOVE(element) &&
9297       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9298       !(element_info[element].move_pattern & direction))
9299     TurnRound(newx, newy);
9300
9301   /* prevent elements on conveyor belt from moving on in last direction */
9302   if (pushed_by_conveyor && CAN_FALL(element) &&
9303       direction & MV_HORIZONTAL)
9304     MovDir[newx][newy] = 0;
9305
9306   if (!pushed_by_player)
9307   {
9308     int nextx = newx + dx, nexty = newy + dy;
9309     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9310
9311     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9312
9313     if (CAN_FALL(element) && direction == MV_DOWN)
9314       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9315
9316     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9317       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9318
9319 #if USE_FIX_IMPACT_COLLISION
9320     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9321       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9322 #endif
9323   }
9324
9325   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9326   {
9327     TestIfBadThingTouchesPlayer(newx, newy);
9328     TestIfBadThingTouchesFriend(newx, newy);
9329
9330     if (!IS_CUSTOM_ELEMENT(element))
9331       TestIfBadThingTouchesOtherBadThing(newx, newy);
9332   }
9333   else if (element == EL_PENGUIN)
9334     TestIfFriendTouchesBadThing(newx, newy);
9335
9336   if (DONT_GET_HIT_BY(element))
9337   {
9338     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9339   }
9340
9341   /* give the player one last chance (one more frame) to move away */
9342   if (CAN_FALL(element) && direction == MV_DOWN &&
9343       (last_line || (!IS_FREE(x, newy + 1) &&
9344                      (!IS_PLAYER(x, newy + 1) ||
9345                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9346     Impact(x, newy);
9347
9348   if (pushed_by_player && !game.use_change_when_pushing_bug)
9349   {
9350     int push_side = MV_DIR_OPPOSITE(direction);
9351     struct PlayerInfo *player = PLAYERINFO(x, y);
9352
9353     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9354                                player->index_bit, push_side);
9355     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9356                                         player->index_bit, push_side);
9357   }
9358
9359   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9360     MovDelay[newx][newy] = 1;
9361
9362   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9363
9364   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9365
9366 #if 0
9367   if (ChangePage[newx][newy] != -1)             /* delayed change */
9368   {
9369     int page = ChangePage[newx][newy];
9370     struct ElementChangeInfo *change = &ei->change_page[page];
9371
9372     ChangePage[newx][newy] = -1;
9373
9374     if (change->can_change)
9375     {
9376       if (ChangeElement(newx, newy, element, page))
9377       {
9378         if (change->post_change_function)
9379           change->post_change_function(newx, newy);
9380       }
9381     }
9382
9383     if (change->has_action)
9384       ExecuteCustomElementAction(newx, newy, element, page);
9385   }
9386 #endif
9387
9388   TestIfElementHitsCustomElement(newx, newy, direction);
9389   TestIfPlayerTouchesCustomElement(newx, newy);
9390   TestIfElementTouchesCustomElement(newx, newy);
9391
9392   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9393       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9394     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9395                              MV_DIR_OPPOSITE(direction));
9396 }
9397
9398 int AmoebeNachbarNr(int ax, int ay)
9399 {
9400   int i;
9401   int element = Feld[ax][ay];
9402   int group_nr = 0;
9403   static int xy[4][2] =
9404   {
9405     { 0, -1 },
9406     { -1, 0 },
9407     { +1, 0 },
9408     { 0, +1 }
9409   };
9410
9411   for (i = 0; i < NUM_DIRECTIONS; i++)
9412   {
9413     int x = ax + xy[i][0];
9414     int y = ay + xy[i][1];
9415
9416     if (!IN_LEV_FIELD(x, y))
9417       continue;
9418
9419     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9420       group_nr = AmoebaNr[x][y];
9421   }
9422
9423   return group_nr;
9424 }
9425
9426 void AmoebenVereinigen(int ax, int ay)
9427 {
9428   int i, x, y, xx, yy;
9429   int new_group_nr = AmoebaNr[ax][ay];
9430   static int xy[4][2] =
9431   {
9432     { 0, -1 },
9433     { -1, 0 },
9434     { +1, 0 },
9435     { 0, +1 }
9436   };
9437
9438   if (new_group_nr == 0)
9439     return;
9440
9441   for (i = 0; i < NUM_DIRECTIONS; i++)
9442   {
9443     x = ax + xy[i][0];
9444     y = ay + xy[i][1];
9445
9446     if (!IN_LEV_FIELD(x, y))
9447       continue;
9448
9449     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9450          Feld[x][y] == EL_BD_AMOEBA ||
9451          Feld[x][y] == EL_AMOEBA_DEAD) &&
9452         AmoebaNr[x][y] != new_group_nr)
9453     {
9454       int old_group_nr = AmoebaNr[x][y];
9455
9456       if (old_group_nr == 0)
9457         return;
9458
9459       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9460       AmoebaCnt[old_group_nr] = 0;
9461       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9462       AmoebaCnt2[old_group_nr] = 0;
9463
9464       SCAN_PLAYFIELD(xx, yy)
9465       {
9466         if (AmoebaNr[xx][yy] == old_group_nr)
9467           AmoebaNr[xx][yy] = new_group_nr;
9468       }
9469     }
9470   }
9471 }
9472
9473 void AmoebeUmwandeln(int ax, int ay)
9474 {
9475   int i, x, y;
9476
9477   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9478   {
9479     int group_nr = AmoebaNr[ax][ay];
9480
9481 #ifdef DEBUG
9482     if (group_nr == 0)
9483     {
9484       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9485       printf("AmoebeUmwandeln(): This should never happen!\n");
9486       return;
9487     }
9488 #endif
9489
9490     SCAN_PLAYFIELD(x, y)
9491     {
9492       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9493       {
9494         AmoebaNr[x][y] = 0;
9495         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9496       }
9497     }
9498
9499     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9500                             SND_AMOEBA_TURNING_TO_GEM :
9501                             SND_AMOEBA_TURNING_TO_ROCK));
9502     Bang(ax, ay);
9503   }
9504   else
9505   {
9506     static int xy[4][2] =
9507     {
9508       { 0, -1 },
9509       { -1, 0 },
9510       { +1, 0 },
9511       { 0, +1 }
9512     };
9513
9514     for (i = 0; i < NUM_DIRECTIONS; i++)
9515     {
9516       x = ax + xy[i][0];
9517       y = ay + xy[i][1];
9518
9519       if (!IN_LEV_FIELD(x, y))
9520         continue;
9521
9522       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9523       {
9524         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9525                               SND_AMOEBA_TURNING_TO_GEM :
9526                               SND_AMOEBA_TURNING_TO_ROCK));
9527         Bang(x, y);
9528       }
9529     }
9530   }
9531 }
9532
9533 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9534 {
9535   int x, y;
9536   int group_nr = AmoebaNr[ax][ay];
9537   boolean done = FALSE;
9538
9539 #ifdef DEBUG
9540   if (group_nr == 0)
9541   {
9542     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9543     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9544     return;
9545   }
9546 #endif
9547
9548   SCAN_PLAYFIELD(x, y)
9549   {
9550     if (AmoebaNr[x][y] == group_nr &&
9551         (Feld[x][y] == EL_AMOEBA_DEAD ||
9552          Feld[x][y] == EL_BD_AMOEBA ||
9553          Feld[x][y] == EL_AMOEBA_GROWING))
9554     {
9555       AmoebaNr[x][y] = 0;
9556       Feld[x][y] = new_element;
9557       InitField(x, y, FALSE);
9558       TEST_DrawLevelField(x, y);
9559       done = TRUE;
9560     }
9561   }
9562
9563   if (done)
9564     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9565                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9566                             SND_BD_AMOEBA_TURNING_TO_GEM));
9567 }
9568
9569 void AmoebeWaechst(int x, int y)
9570 {
9571   static unsigned int sound_delay = 0;
9572   static unsigned int sound_delay_value = 0;
9573
9574   if (!MovDelay[x][y])          /* start new growing cycle */
9575   {
9576     MovDelay[x][y] = 7;
9577
9578     if (DelayReached(&sound_delay, sound_delay_value))
9579     {
9580       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9581       sound_delay_value = 30;
9582     }
9583   }
9584
9585   if (MovDelay[x][y])           /* wait some time before growing bigger */
9586   {
9587     MovDelay[x][y]--;
9588     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9589     {
9590       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9591                                            6 - MovDelay[x][y]);
9592
9593       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9594     }
9595
9596     if (!MovDelay[x][y])
9597     {
9598       Feld[x][y] = Store[x][y];
9599       Store[x][y] = 0;
9600       TEST_DrawLevelField(x, y);
9601     }
9602   }
9603 }
9604
9605 void AmoebaDisappearing(int x, int y)
9606 {
9607   static unsigned int sound_delay = 0;
9608   static unsigned int sound_delay_value = 0;
9609
9610   if (!MovDelay[x][y])          /* start new shrinking cycle */
9611   {
9612     MovDelay[x][y] = 7;
9613
9614     if (DelayReached(&sound_delay, sound_delay_value))
9615       sound_delay_value = 30;
9616   }
9617
9618   if (MovDelay[x][y])           /* wait some time before shrinking */
9619   {
9620     MovDelay[x][y]--;
9621     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9622     {
9623       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9624                                            6 - MovDelay[x][y]);
9625
9626       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9627     }
9628
9629     if (!MovDelay[x][y])
9630     {
9631       Feld[x][y] = EL_EMPTY;
9632       TEST_DrawLevelField(x, y);
9633
9634       /* don't let mole enter this field in this cycle;
9635          (give priority to objects falling to this field from above) */
9636       Stop[x][y] = TRUE;
9637     }
9638   }
9639 }
9640
9641 void AmoebeAbleger(int ax, int ay)
9642 {
9643   int i;
9644   int element = Feld[ax][ay];
9645   int graphic = el2img(element);
9646   int newax = ax, neway = ay;
9647   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9648   static int xy[4][2] =
9649   {
9650     { 0, -1 },
9651     { -1, 0 },
9652     { +1, 0 },
9653     { 0, +1 }
9654   };
9655
9656   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9657   {
9658     Feld[ax][ay] = EL_AMOEBA_DEAD;
9659     TEST_DrawLevelField(ax, ay);
9660     return;
9661   }
9662
9663   if (IS_ANIMATED(graphic))
9664     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9665
9666   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9667     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9668
9669   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9670   {
9671     MovDelay[ax][ay]--;
9672     if (MovDelay[ax][ay])
9673       return;
9674   }
9675
9676   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9677   {
9678     int start = RND(4);
9679     int x = ax + xy[start][0];
9680     int y = ay + xy[start][1];
9681
9682     if (!IN_LEV_FIELD(x, y))
9683       return;
9684
9685     if (IS_FREE(x, y) ||
9686         CAN_GROW_INTO(Feld[x][y]) ||
9687         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9688         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9689     {
9690       newax = x;
9691       neway = y;
9692     }
9693
9694     if (newax == ax && neway == ay)
9695       return;
9696   }
9697   else                          /* normal or "filled" (BD style) amoeba */
9698   {
9699     int start = RND(4);
9700     boolean waiting_for_player = FALSE;
9701
9702     for (i = 0; i < NUM_DIRECTIONS; i++)
9703     {
9704       int j = (start + i) % 4;
9705       int x = ax + xy[j][0];
9706       int y = ay + xy[j][1];
9707
9708       if (!IN_LEV_FIELD(x, y))
9709         continue;
9710
9711       if (IS_FREE(x, y) ||
9712           CAN_GROW_INTO(Feld[x][y]) ||
9713           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9714           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9715       {
9716         newax = x;
9717         neway = y;
9718         break;
9719       }
9720       else if (IS_PLAYER(x, y))
9721         waiting_for_player = TRUE;
9722     }
9723
9724     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9725     {
9726       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9727       {
9728         Feld[ax][ay] = EL_AMOEBA_DEAD;
9729         TEST_DrawLevelField(ax, ay);
9730         AmoebaCnt[AmoebaNr[ax][ay]]--;
9731
9732         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9733         {
9734           if (element == EL_AMOEBA_FULL)
9735             AmoebeUmwandeln(ax, ay);
9736           else if (element == EL_BD_AMOEBA)
9737             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9738         }
9739       }
9740       return;
9741     }
9742     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9743     {
9744       /* amoeba gets larger by growing in some direction */
9745
9746       int new_group_nr = AmoebaNr[ax][ay];
9747
9748 #ifdef DEBUG
9749   if (new_group_nr == 0)
9750   {
9751     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9752     printf("AmoebeAbleger(): This should never happen!\n");
9753     return;
9754   }
9755 #endif
9756
9757       AmoebaNr[newax][neway] = new_group_nr;
9758       AmoebaCnt[new_group_nr]++;
9759       AmoebaCnt2[new_group_nr]++;
9760
9761       /* if amoeba touches other amoeba(s) after growing, unify them */
9762       AmoebenVereinigen(newax, neway);
9763
9764       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9765       {
9766         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9767         return;
9768       }
9769     }
9770   }
9771
9772   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9773       (neway == lev_fieldy - 1 && newax != ax))
9774   {
9775     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9776     Store[newax][neway] = element;
9777   }
9778   else if (neway == ay || element == EL_EMC_DRIPPER)
9779   {
9780     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9781
9782     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9783   }
9784   else
9785   {
9786     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9787     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9788     Store[ax][ay] = EL_AMOEBA_DROP;
9789     ContinueMoving(ax, ay);
9790     return;
9791   }
9792
9793   TEST_DrawLevelField(newax, neway);
9794 }
9795
9796 void Life(int ax, int ay)
9797 {
9798   int x1, y1, x2, y2;
9799   int life_time = 40;
9800   int element = Feld[ax][ay];
9801   int graphic = el2img(element);
9802   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9803                          level.biomaze);
9804   boolean changed = FALSE;
9805
9806   if (IS_ANIMATED(graphic))
9807     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9808
9809   if (Stop[ax][ay])
9810     return;
9811
9812   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9813     MovDelay[ax][ay] = life_time;
9814
9815   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9816   {
9817     MovDelay[ax][ay]--;
9818     if (MovDelay[ax][ay])
9819       return;
9820   }
9821
9822   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9823   {
9824     int xx = ax+x1, yy = ay+y1;
9825     int nachbarn = 0;
9826
9827     if (!IN_LEV_FIELD(xx, yy))
9828       continue;
9829
9830     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9831     {
9832       int x = xx+x2, y = yy+y2;
9833
9834       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9835         continue;
9836
9837       if (((Feld[x][y] == element ||
9838             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9839            !Stop[x][y]) ||
9840           (IS_FREE(x, y) && Stop[x][y]))
9841         nachbarn++;
9842     }
9843
9844     if (xx == ax && yy == ay)           /* field in the middle */
9845     {
9846       if (nachbarn < life_parameter[0] ||
9847           nachbarn > life_parameter[1])
9848       {
9849         Feld[xx][yy] = EL_EMPTY;
9850         if (!Stop[xx][yy])
9851           TEST_DrawLevelField(xx, yy);
9852         Stop[xx][yy] = TRUE;
9853         changed = TRUE;
9854       }
9855     }
9856     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9857     {                                   /* free border field */
9858       if (nachbarn >= life_parameter[2] &&
9859           nachbarn <= life_parameter[3])
9860       {
9861         Feld[xx][yy] = element;
9862         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9863         if (!Stop[xx][yy])
9864           TEST_DrawLevelField(xx, yy);
9865         Stop[xx][yy] = TRUE;
9866         changed = TRUE;
9867       }
9868     }
9869   }
9870
9871   if (changed)
9872     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9873                    SND_GAME_OF_LIFE_GROWING);
9874 }
9875
9876 static void InitRobotWheel(int x, int y)
9877 {
9878   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9879 }
9880
9881 static void RunRobotWheel(int x, int y)
9882 {
9883   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9884 }
9885
9886 static void StopRobotWheel(int x, int y)
9887 {
9888   if (ZX == x && ZY == y)
9889   {
9890     ZX = ZY = -1;
9891
9892     game.robot_wheel_active = FALSE;
9893   }
9894 }
9895
9896 static void InitTimegateWheel(int x, int y)
9897 {
9898   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9899 }
9900
9901 static void RunTimegateWheel(int x, int y)
9902 {
9903   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9904 }
9905
9906 static void InitMagicBallDelay(int x, int y)
9907 {
9908 #if 1
9909   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9910 #else
9911   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9912 #endif
9913 }
9914
9915 static void ActivateMagicBall(int bx, int by)
9916 {
9917   int x, y;
9918
9919   if (level.ball_random)
9920   {
9921     int pos_border = RND(8);    /* select one of the eight border elements */
9922     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9923     int xx = pos_content % 3;
9924     int yy = pos_content / 3;
9925
9926     x = bx - 1 + xx;
9927     y = by - 1 + yy;
9928
9929     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9930       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9931   }
9932   else
9933   {
9934     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9935     {
9936       int xx = x - bx + 1;
9937       int yy = y - by + 1;
9938
9939       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9940         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9941     }
9942   }
9943
9944   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9945 }
9946
9947 void CheckExit(int x, int y)
9948 {
9949   if (local_player->gems_still_needed > 0 ||
9950       local_player->sokobanfields_still_needed > 0 ||
9951       local_player->lights_still_needed > 0)
9952   {
9953     int element = Feld[x][y];
9954     int graphic = el2img(element);
9955
9956     if (IS_ANIMATED(graphic))
9957       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9958
9959     return;
9960   }
9961
9962   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9963     return;
9964
9965   Feld[x][y] = EL_EXIT_OPENING;
9966
9967   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9968 }
9969
9970 void CheckExitEM(int x, int y)
9971 {
9972   if (local_player->gems_still_needed > 0 ||
9973       local_player->sokobanfields_still_needed > 0 ||
9974       local_player->lights_still_needed > 0)
9975   {
9976     int element = Feld[x][y];
9977     int graphic = el2img(element);
9978
9979     if (IS_ANIMATED(graphic))
9980       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9981
9982     return;
9983   }
9984
9985   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9986     return;
9987
9988   Feld[x][y] = EL_EM_EXIT_OPENING;
9989
9990   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9991 }
9992
9993 void CheckExitSteel(int x, int y)
9994 {
9995   if (local_player->gems_still_needed > 0 ||
9996       local_player->sokobanfields_still_needed > 0 ||
9997       local_player->lights_still_needed > 0)
9998   {
9999     int element = Feld[x][y];
10000     int graphic = el2img(element);
10001
10002     if (IS_ANIMATED(graphic))
10003       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10004
10005     return;
10006   }
10007
10008   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10009     return;
10010
10011   Feld[x][y] = EL_STEEL_EXIT_OPENING;
10012
10013   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
10014 }
10015
10016 void CheckExitSteelEM(int x, int y)
10017 {
10018   if (local_player->gems_still_needed > 0 ||
10019       local_player->sokobanfields_still_needed > 0 ||
10020       local_player->lights_still_needed > 0)
10021   {
10022     int element = Feld[x][y];
10023     int graphic = el2img(element);
10024
10025     if (IS_ANIMATED(graphic))
10026       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10027
10028     return;
10029   }
10030
10031   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10032     return;
10033
10034   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
10035
10036   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
10037 }
10038
10039 void CheckExitSP(int x, int y)
10040 {
10041   if (local_player->gems_still_needed > 0)
10042   {
10043     int element = Feld[x][y];
10044     int graphic = el2img(element);
10045
10046     if (IS_ANIMATED(graphic))
10047       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10048
10049     return;
10050   }
10051
10052   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10053     return;
10054
10055   Feld[x][y] = EL_SP_EXIT_OPENING;
10056
10057   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10058 }
10059
10060 static void CloseAllOpenTimegates()
10061 {
10062   int x, y;
10063
10064   SCAN_PLAYFIELD(x, y)
10065   {
10066     int element = Feld[x][y];
10067
10068     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10069     {
10070       Feld[x][y] = EL_TIMEGATE_CLOSING;
10071
10072       PlayLevelSoundAction(x, y, ACTION_CLOSING);
10073     }
10074   }
10075 }
10076
10077 void DrawTwinkleOnField(int x, int y)
10078 {
10079   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10080     return;
10081
10082   if (Feld[x][y] == EL_BD_DIAMOND)
10083     return;
10084
10085   if (MovDelay[x][y] == 0)      /* next animation frame */
10086     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10087
10088   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10089   {
10090     MovDelay[x][y]--;
10091
10092     DrawLevelElementAnimation(x, y, Feld[x][y]);
10093
10094     if (MovDelay[x][y] != 0)
10095     {
10096       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10097                                            10 - MovDelay[x][y]);
10098
10099       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10100     }
10101   }
10102 }
10103
10104 void MauerWaechst(int x, int y)
10105 {
10106   int delay = 6;
10107
10108   if (!MovDelay[x][y])          /* next animation frame */
10109     MovDelay[x][y] = 3 * delay;
10110
10111   if (MovDelay[x][y])           /* wait some time before next frame */
10112   {
10113     MovDelay[x][y]--;
10114
10115     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10116     {
10117       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10118       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10119
10120       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10121     }
10122
10123     if (!MovDelay[x][y])
10124     {
10125       if (MovDir[x][y] == MV_LEFT)
10126       {
10127         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10128           TEST_DrawLevelField(x - 1, y);
10129       }
10130       else if (MovDir[x][y] == MV_RIGHT)
10131       {
10132         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10133           TEST_DrawLevelField(x + 1, y);
10134       }
10135       else if (MovDir[x][y] == MV_UP)
10136       {
10137         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10138           TEST_DrawLevelField(x, y - 1);
10139       }
10140       else
10141       {
10142         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10143           TEST_DrawLevelField(x, y + 1);
10144       }
10145
10146       Feld[x][y] = Store[x][y];
10147       Store[x][y] = 0;
10148       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10149       TEST_DrawLevelField(x, y);
10150     }
10151   }
10152 }
10153
10154 void MauerAbleger(int ax, int ay)
10155 {
10156   int element = Feld[ax][ay];
10157   int graphic = el2img(element);
10158   boolean oben_frei = FALSE, unten_frei = FALSE;
10159   boolean links_frei = FALSE, rechts_frei = FALSE;
10160   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10161   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10162   boolean new_wall = FALSE;
10163
10164   if (IS_ANIMATED(graphic))
10165     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10166
10167   if (!MovDelay[ax][ay])        /* start building new wall */
10168     MovDelay[ax][ay] = 6;
10169
10170   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10171   {
10172     MovDelay[ax][ay]--;
10173     if (MovDelay[ax][ay])
10174       return;
10175   }
10176
10177   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10178     oben_frei = TRUE;
10179   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10180     unten_frei = TRUE;
10181   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10182     links_frei = TRUE;
10183   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10184     rechts_frei = TRUE;
10185
10186   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10187       element == EL_EXPANDABLE_WALL_ANY)
10188   {
10189     if (oben_frei)
10190     {
10191       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10192       Store[ax][ay-1] = element;
10193       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10194       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10195         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10196                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10197       new_wall = TRUE;
10198     }
10199     if (unten_frei)
10200     {
10201       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10202       Store[ax][ay+1] = element;
10203       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10204       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10205         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10206                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10207       new_wall = TRUE;
10208     }
10209   }
10210
10211   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10212       element == EL_EXPANDABLE_WALL_ANY ||
10213       element == EL_EXPANDABLE_WALL ||
10214       element == EL_BD_EXPANDABLE_WALL)
10215   {
10216     if (links_frei)
10217     {
10218       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10219       Store[ax-1][ay] = element;
10220       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10221       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10222         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10223                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10224       new_wall = TRUE;
10225     }
10226
10227     if (rechts_frei)
10228     {
10229       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10230       Store[ax+1][ay] = element;
10231       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10232       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10233         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10234                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10235       new_wall = TRUE;
10236     }
10237   }
10238
10239   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10240     TEST_DrawLevelField(ax, ay);
10241
10242   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10243     oben_massiv = TRUE;
10244   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10245     unten_massiv = TRUE;
10246   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10247     links_massiv = TRUE;
10248   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10249     rechts_massiv = TRUE;
10250
10251   if (((oben_massiv && unten_massiv) ||
10252        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10253        element == EL_EXPANDABLE_WALL) &&
10254       ((links_massiv && rechts_massiv) ||
10255        element == EL_EXPANDABLE_WALL_VERTICAL))
10256     Feld[ax][ay] = EL_WALL;
10257
10258   if (new_wall)
10259     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10260 }
10261
10262 void MauerAblegerStahl(int ax, int ay)
10263 {
10264   int element = Feld[ax][ay];
10265   int graphic = el2img(element);
10266   boolean oben_frei = FALSE, unten_frei = FALSE;
10267   boolean links_frei = FALSE, rechts_frei = FALSE;
10268   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10269   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10270   boolean new_wall = FALSE;
10271
10272   if (IS_ANIMATED(graphic))
10273     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10274
10275   if (!MovDelay[ax][ay])        /* start building new wall */
10276     MovDelay[ax][ay] = 6;
10277
10278   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10279   {
10280     MovDelay[ax][ay]--;
10281     if (MovDelay[ax][ay])
10282       return;
10283   }
10284
10285   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10286     oben_frei = TRUE;
10287   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10288     unten_frei = TRUE;
10289   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10290     links_frei = TRUE;
10291   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10292     rechts_frei = TRUE;
10293
10294   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10295       element == EL_EXPANDABLE_STEELWALL_ANY)
10296   {
10297     if (oben_frei)
10298     {
10299       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10300       Store[ax][ay-1] = element;
10301       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10302       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10303         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10304                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10305       new_wall = TRUE;
10306     }
10307     if (unten_frei)
10308     {
10309       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10310       Store[ax][ay+1] = element;
10311       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10312       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10313         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10314                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10315       new_wall = TRUE;
10316     }
10317   }
10318
10319   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10320       element == EL_EXPANDABLE_STEELWALL_ANY)
10321   {
10322     if (links_frei)
10323     {
10324       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10325       Store[ax-1][ay] = element;
10326       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10327       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10328         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10329                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10330       new_wall = TRUE;
10331     }
10332
10333     if (rechts_frei)
10334     {
10335       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10336       Store[ax+1][ay] = element;
10337       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10338       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10339         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10340                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10341       new_wall = TRUE;
10342     }
10343   }
10344
10345   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10346     oben_massiv = TRUE;
10347   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10348     unten_massiv = TRUE;
10349   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10350     links_massiv = TRUE;
10351   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10352     rechts_massiv = TRUE;
10353
10354   if (((oben_massiv && unten_massiv) ||
10355        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10356       ((links_massiv && rechts_massiv) ||
10357        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10358     Feld[ax][ay] = EL_STEELWALL;
10359
10360   if (new_wall)
10361     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10362 }
10363
10364 void CheckForDragon(int x, int y)
10365 {
10366   int i, j;
10367   boolean dragon_found = FALSE;
10368   static int xy[4][2] =
10369   {
10370     { 0, -1 },
10371     { -1, 0 },
10372     { +1, 0 },
10373     { 0, +1 }
10374   };
10375
10376   for (i = 0; i < NUM_DIRECTIONS; i++)
10377   {
10378     for (j = 0; j < 4; j++)
10379     {
10380       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10381
10382       if (IN_LEV_FIELD(xx, yy) &&
10383           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10384       {
10385         if (Feld[xx][yy] == EL_DRAGON)
10386           dragon_found = TRUE;
10387       }
10388       else
10389         break;
10390     }
10391   }
10392
10393   if (!dragon_found)
10394   {
10395     for (i = 0; i < NUM_DIRECTIONS; i++)
10396     {
10397       for (j = 0; j < 3; j++)
10398       {
10399         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10400   
10401         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10402         {
10403           Feld[xx][yy] = EL_EMPTY;
10404           TEST_DrawLevelField(xx, yy);
10405         }
10406         else
10407           break;
10408       }
10409     }
10410   }
10411 }
10412
10413 static void InitBuggyBase(int x, int y)
10414 {
10415   int element = Feld[x][y];
10416   int activating_delay = FRAMES_PER_SECOND / 4;
10417
10418   ChangeDelay[x][y] =
10419     (element == EL_SP_BUGGY_BASE ?
10420      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10421      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10422      activating_delay :
10423      element == EL_SP_BUGGY_BASE_ACTIVE ?
10424      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10425 }
10426
10427 static void WarnBuggyBase(int x, int y)
10428 {
10429   int i;
10430   static int xy[4][2] =
10431   {
10432     { 0, -1 },
10433     { -1, 0 },
10434     { +1, 0 },
10435     { 0, +1 }
10436   };
10437
10438   for (i = 0; i < NUM_DIRECTIONS; i++)
10439   {
10440     int xx = x + xy[i][0];
10441     int yy = y + xy[i][1];
10442
10443     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10444     {
10445       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10446
10447       break;
10448     }
10449   }
10450 }
10451
10452 static void InitTrap(int x, int y)
10453 {
10454   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10455 }
10456
10457 static void ActivateTrap(int x, int y)
10458 {
10459   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10460 }
10461
10462 static void ChangeActiveTrap(int x, int y)
10463 {
10464   int graphic = IMG_TRAP_ACTIVE;
10465
10466   /* if new animation frame was drawn, correct crumbled sand border */
10467   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10468     TEST_DrawLevelFieldCrumbled(x, y);
10469 }
10470
10471 static int getSpecialActionElement(int element, int number, int base_element)
10472 {
10473   return (element != EL_EMPTY ? element :
10474           number != -1 ? base_element + number - 1 :
10475           EL_EMPTY);
10476 }
10477
10478 static int getModifiedActionNumber(int value_old, int operator, int operand,
10479                                    int value_min, int value_max)
10480 {
10481   int value_new = (operator == CA_MODE_SET      ? operand :
10482                    operator == CA_MODE_ADD      ? value_old + operand :
10483                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10484                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10485                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10486                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10487                    value_old);
10488
10489   return (value_new < value_min ? value_min :
10490           value_new > value_max ? value_max :
10491           value_new);
10492 }
10493
10494 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10495 {
10496   struct ElementInfo *ei = &element_info[element];
10497   struct ElementChangeInfo *change = &ei->change_page[page];
10498   int target_element = change->target_element;
10499   int action_type = change->action_type;
10500   int action_mode = change->action_mode;
10501   int action_arg = change->action_arg;
10502   int action_element = change->action_element;
10503   int i;
10504
10505   if (!change->has_action)
10506     return;
10507
10508   /* ---------- determine action paramater values -------------------------- */
10509
10510   int level_time_value =
10511     (level.time > 0 ? TimeLeft :
10512      TimePlayed);
10513
10514   int action_arg_element_raw =
10515     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10516      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10517      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10518      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10519      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10520      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10521      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10522      EL_EMPTY);
10523   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10524
10525 #if 0
10526   if (action_arg_element_raw == EL_GROUP_START)
10527     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10528 #endif
10529
10530   int action_arg_direction =
10531     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10532      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10533      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10534      change->actual_trigger_side :
10535      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10536      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10537      MV_NONE);
10538
10539   int action_arg_number_min =
10540     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10541      CA_ARG_MIN);
10542
10543   int action_arg_number_max =
10544     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10545      action_type == CA_SET_LEVEL_GEMS ? 999 :
10546      action_type == CA_SET_LEVEL_TIME ? 9999 :
10547      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10548      action_type == CA_SET_CE_VALUE ? 9999 :
10549      action_type == CA_SET_CE_SCORE ? 9999 :
10550      CA_ARG_MAX);
10551
10552   int action_arg_number_reset =
10553     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10554      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10555      action_type == CA_SET_LEVEL_TIME ? level.time :
10556      action_type == CA_SET_LEVEL_SCORE ? 0 :
10557 #if USE_NEW_CUSTOM_VALUE
10558      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10559 #else
10560      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10561 #endif
10562      action_type == CA_SET_CE_SCORE ? 0 :
10563      0);
10564
10565   int action_arg_number =
10566     (action_arg <= CA_ARG_MAX ? action_arg :
10567      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10568      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10569      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10570      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10571      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10572      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10573 #if USE_NEW_CUSTOM_VALUE
10574      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10575 #else
10576      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10577 #endif
10578      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10579      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10580      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10581      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10582      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10583      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10584      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10585      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10586      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10587      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10588      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10589      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10590      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10591      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10592      -1);
10593
10594   int action_arg_number_old =
10595     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10596      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10597      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10598      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10599      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10600      0);
10601
10602   int action_arg_number_new =
10603     getModifiedActionNumber(action_arg_number_old,
10604                             action_mode, action_arg_number,
10605                             action_arg_number_min, action_arg_number_max);
10606
10607 #if 1
10608   int trigger_player_bits =
10609     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10610      change->actual_trigger_player_bits : change->trigger_player);
10611 #else
10612   int trigger_player_bits =
10613     (change->actual_trigger_player >= EL_PLAYER_1 &&
10614      change->actual_trigger_player <= EL_PLAYER_4 ?
10615      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10616      PLAYER_BITS_ANY);
10617 #endif
10618
10619   int action_arg_player_bits =
10620     (action_arg >= CA_ARG_PLAYER_1 &&
10621      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10622      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10623      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10624      PLAYER_BITS_ANY);
10625
10626   /* ---------- execute action  -------------------------------------------- */
10627
10628   switch (action_type)
10629   {
10630     case CA_NO_ACTION:
10631     {
10632       return;
10633     }
10634
10635     /* ---------- level actions  ------------------------------------------- */
10636
10637     case CA_RESTART_LEVEL:
10638     {
10639       game.restart_level = TRUE;
10640
10641       break;
10642     }
10643
10644     case CA_SHOW_ENVELOPE:
10645     {
10646       int element = getSpecialActionElement(action_arg_element,
10647                                             action_arg_number, EL_ENVELOPE_1);
10648
10649       if (IS_ENVELOPE(element))
10650         local_player->show_envelope = element;
10651
10652       break;
10653     }
10654
10655     case CA_SET_LEVEL_TIME:
10656     {
10657       if (level.time > 0)       /* only modify limited time value */
10658       {
10659         TimeLeft = action_arg_number_new;
10660
10661 #if 1
10662         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10663
10664         DisplayGameControlValues();
10665 #else
10666         DrawGameValue_Time(TimeLeft);
10667 #endif
10668
10669         if (!TimeLeft && setup.time_limit)
10670           for (i = 0; i < MAX_PLAYERS; i++)
10671             KillPlayer(&stored_player[i]);
10672       }
10673
10674       break;
10675     }
10676
10677     case CA_SET_LEVEL_SCORE:
10678     {
10679       local_player->score = action_arg_number_new;
10680
10681 #if 1
10682       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10683
10684       DisplayGameControlValues();
10685 #else
10686       DrawGameValue_Score(local_player->score);
10687 #endif
10688
10689       break;
10690     }
10691
10692     case CA_SET_LEVEL_GEMS:
10693     {
10694       local_player->gems_still_needed = action_arg_number_new;
10695
10696 #if 1
10697       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10698
10699       DisplayGameControlValues();
10700 #else
10701       DrawGameValue_Emeralds(local_player->gems_still_needed);
10702 #endif
10703
10704       break;
10705     }
10706
10707 #if !USE_PLAYER_GRAVITY
10708     case CA_SET_LEVEL_GRAVITY:
10709     {
10710       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10711                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10712                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10713                       game.gravity);
10714       break;
10715     }
10716 #endif
10717
10718     case CA_SET_LEVEL_WIND:
10719     {
10720       game.wind_direction = action_arg_direction;
10721
10722       break;
10723     }
10724
10725     case CA_SET_LEVEL_RANDOM_SEED:
10726     {
10727 #if 1
10728       /* ensure that setting a new random seed while playing is predictable */
10729       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10730 #else
10731       InitRND(action_arg_number_new);
10732 #endif
10733
10734 #if 0
10735       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10736 #endif
10737
10738 #if 0
10739       {
10740         int i;
10741
10742         printf("::: ");
10743         for (i = 0; i < 9; i++)
10744           printf("%d, ", RND(2));
10745         printf("\n");
10746       }
10747 #endif
10748
10749       break;
10750     }
10751
10752     /* ---------- player actions  ------------------------------------------ */
10753
10754     case CA_MOVE_PLAYER:
10755     {
10756       /* automatically move to the next field in specified direction */
10757       for (i = 0; i < MAX_PLAYERS; i++)
10758         if (trigger_player_bits & (1 << i))
10759           stored_player[i].programmed_action = action_arg_direction;
10760
10761       break;
10762     }
10763
10764     case CA_EXIT_PLAYER:
10765     {
10766       for (i = 0; i < MAX_PLAYERS; i++)
10767         if (action_arg_player_bits & (1 << i))
10768           PlayerWins(&stored_player[i]);
10769
10770       break;
10771     }
10772
10773     case CA_KILL_PLAYER:
10774     {
10775       for (i = 0; i < MAX_PLAYERS; i++)
10776         if (action_arg_player_bits & (1 << i))
10777           KillPlayer(&stored_player[i]);
10778
10779       break;
10780     }
10781
10782     case CA_SET_PLAYER_KEYS:
10783     {
10784       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10785       int element = getSpecialActionElement(action_arg_element,
10786                                             action_arg_number, EL_KEY_1);
10787
10788       if (IS_KEY(element))
10789       {
10790         for (i = 0; i < MAX_PLAYERS; i++)
10791         {
10792           if (trigger_player_bits & (1 << i))
10793           {
10794             stored_player[i].key[KEY_NR(element)] = key_state;
10795
10796             DrawGameDoorValues();
10797           }
10798         }
10799       }
10800
10801       break;
10802     }
10803
10804     case CA_SET_PLAYER_SPEED:
10805     {
10806 #if 0
10807       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10808 #endif
10809
10810       for (i = 0; i < MAX_PLAYERS; i++)
10811       {
10812         if (trigger_player_bits & (1 << i))
10813         {
10814           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10815
10816           if (action_arg == CA_ARG_SPEED_FASTER &&
10817               stored_player[i].cannot_move)
10818           {
10819             action_arg_number = STEPSIZE_VERY_SLOW;
10820           }
10821           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10822                    action_arg == CA_ARG_SPEED_FASTER)
10823           {
10824             action_arg_number = 2;
10825             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10826                            CA_MODE_MULTIPLY);
10827           }
10828           else if (action_arg == CA_ARG_NUMBER_RESET)
10829           {
10830             action_arg_number = level.initial_player_stepsize[i];
10831           }
10832
10833           move_stepsize =
10834             getModifiedActionNumber(move_stepsize,
10835                                     action_mode,
10836                                     action_arg_number,
10837                                     action_arg_number_min,
10838                                     action_arg_number_max);
10839
10840           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10841         }
10842       }
10843
10844       break;
10845     }
10846
10847     case CA_SET_PLAYER_SHIELD:
10848     {
10849       for (i = 0; i < MAX_PLAYERS; i++)
10850       {
10851         if (trigger_player_bits & (1 << i))
10852         {
10853           if (action_arg == CA_ARG_SHIELD_OFF)
10854           {
10855             stored_player[i].shield_normal_time_left = 0;
10856             stored_player[i].shield_deadly_time_left = 0;
10857           }
10858           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10859           {
10860             stored_player[i].shield_normal_time_left = 999999;
10861           }
10862           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10863           {
10864             stored_player[i].shield_normal_time_left = 999999;
10865             stored_player[i].shield_deadly_time_left = 999999;
10866           }
10867         }
10868       }
10869
10870       break;
10871     }
10872
10873 #if USE_PLAYER_GRAVITY
10874     case CA_SET_PLAYER_GRAVITY:
10875     {
10876       for (i = 0; i < MAX_PLAYERS; i++)
10877       {
10878         if (trigger_player_bits & (1 << i))
10879         {
10880           stored_player[i].gravity =
10881             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10882              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10883              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10884              stored_player[i].gravity);
10885         }
10886       }
10887
10888       break;
10889     }
10890 #endif
10891
10892     case CA_SET_PLAYER_ARTWORK:
10893     {
10894       for (i = 0; i < MAX_PLAYERS; i++)
10895       {
10896         if (trigger_player_bits & (1 << i))
10897         {
10898           int artwork_element = action_arg_element;
10899
10900           if (action_arg == CA_ARG_ELEMENT_RESET)
10901             artwork_element =
10902               (level.use_artwork_element[i] ? level.artwork_element[i] :
10903                stored_player[i].element_nr);
10904
10905 #if USE_GFX_RESET_PLAYER_ARTWORK
10906           if (stored_player[i].artwork_element != artwork_element)
10907             stored_player[i].Frame = 0;
10908 #endif
10909
10910           stored_player[i].artwork_element = artwork_element;
10911
10912           SetPlayerWaiting(&stored_player[i], FALSE);
10913
10914           /* set number of special actions for bored and sleeping animation */
10915           stored_player[i].num_special_action_bored =
10916             get_num_special_action(artwork_element,
10917                                    ACTION_BORING_1, ACTION_BORING_LAST);
10918           stored_player[i].num_special_action_sleeping =
10919             get_num_special_action(artwork_element,
10920                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10921         }
10922       }
10923
10924       break;
10925     }
10926
10927     case CA_SET_PLAYER_INVENTORY:
10928     {
10929       for (i = 0; i < MAX_PLAYERS; i++)
10930       {
10931         struct PlayerInfo *player = &stored_player[i];
10932         int j, k;
10933
10934         if (trigger_player_bits & (1 << i))
10935         {
10936           int inventory_element = action_arg_element;
10937
10938           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10939               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10940               action_arg == CA_ARG_ELEMENT_ACTION)
10941           {
10942             int element = inventory_element;
10943             int collect_count = element_info[element].collect_count_initial;
10944
10945             if (!IS_CUSTOM_ELEMENT(element))
10946               collect_count = 1;
10947
10948             if (collect_count == 0)
10949               player->inventory_infinite_element = element;
10950             else
10951               for (k = 0; k < collect_count; k++)
10952                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10953                   player->inventory_element[player->inventory_size++] =
10954                     element;
10955           }
10956           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10957                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10958                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10959           {
10960             if (player->inventory_infinite_element != EL_UNDEFINED &&
10961                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10962                                      action_arg_element_raw))
10963               player->inventory_infinite_element = EL_UNDEFINED;
10964
10965             for (k = 0, j = 0; j < player->inventory_size; j++)
10966             {
10967               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10968                                         action_arg_element_raw))
10969                 player->inventory_element[k++] = player->inventory_element[j];
10970             }
10971
10972             player->inventory_size = k;
10973           }
10974           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10975           {
10976             if (player->inventory_size > 0)
10977             {
10978               for (j = 0; j < player->inventory_size - 1; j++)
10979                 player->inventory_element[j] = player->inventory_element[j + 1];
10980
10981               player->inventory_size--;
10982             }
10983           }
10984           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10985           {
10986             if (player->inventory_size > 0)
10987               player->inventory_size--;
10988           }
10989           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10990           {
10991             player->inventory_infinite_element = EL_UNDEFINED;
10992             player->inventory_size = 0;
10993           }
10994           else if (action_arg == CA_ARG_INVENTORY_RESET)
10995           {
10996             player->inventory_infinite_element = EL_UNDEFINED;
10997             player->inventory_size = 0;
10998
10999             if (level.use_initial_inventory[i])
11000             {
11001               for (j = 0; j < level.initial_inventory_size[i]; j++)
11002               {
11003                 int element = level.initial_inventory_content[i][j];
11004                 int collect_count = element_info[element].collect_count_initial;
11005
11006                 if (!IS_CUSTOM_ELEMENT(element))
11007                   collect_count = 1;
11008
11009                 if (collect_count == 0)
11010                   player->inventory_infinite_element = element;
11011                 else
11012                   for (k = 0; k < collect_count; k++)
11013                     if (player->inventory_size < MAX_INVENTORY_SIZE)
11014                       player->inventory_element[player->inventory_size++] =
11015                         element;
11016               }
11017             }
11018           }
11019         }
11020       }
11021
11022       break;
11023     }
11024
11025     /* ---------- CE actions  ---------------------------------------------- */
11026
11027     case CA_SET_CE_VALUE:
11028     {
11029 #if USE_NEW_CUSTOM_VALUE
11030       int last_ce_value = CustomValue[x][y];
11031
11032       CustomValue[x][y] = action_arg_number_new;
11033
11034       if (CustomValue[x][y] != last_ce_value)
11035       {
11036         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
11037         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
11038
11039         if (CustomValue[x][y] == 0)
11040         {
11041           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
11042           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11043         }
11044       }
11045 #endif
11046
11047       break;
11048     }
11049
11050     case CA_SET_CE_SCORE:
11051     {
11052 #if USE_NEW_CUSTOM_VALUE
11053       int last_ce_score = ei->collect_score;
11054
11055       ei->collect_score = action_arg_number_new;
11056
11057       if (ei->collect_score != last_ce_score)
11058       {
11059         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11060         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11061
11062         if (ei->collect_score == 0)
11063         {
11064           int xx, yy;
11065
11066           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11067           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11068
11069           /*
11070             This is a very special case that seems to be a mixture between
11071             CheckElementChange() and CheckTriggeredElementChange(): while
11072             the first one only affects single elements that are triggered
11073             directly, the second one affects multiple elements in the playfield
11074             that are triggered indirectly by another element. This is a third
11075             case: Changing the CE score always affects multiple identical CEs,
11076             so every affected CE must be checked, not only the single CE for
11077             which the CE score was changed in the first place (as every instance
11078             of that CE shares the same CE score, and therefore also can change)!
11079           */
11080           SCAN_PLAYFIELD(xx, yy)
11081           {
11082             if (Feld[xx][yy] == element)
11083               CheckElementChange(xx, yy, element, EL_UNDEFINED,
11084                                  CE_SCORE_GETS_ZERO);
11085           }
11086         }
11087       }
11088 #endif
11089
11090       break;
11091     }
11092
11093     case CA_SET_CE_ARTWORK:
11094     {
11095       int artwork_element = action_arg_element;
11096       boolean reset_frame = FALSE;
11097       int xx, yy;
11098
11099       if (action_arg == CA_ARG_ELEMENT_RESET)
11100         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11101                            element);
11102
11103       if (ei->gfx_element != artwork_element)
11104         reset_frame = TRUE;
11105
11106       ei->gfx_element = artwork_element;
11107
11108       SCAN_PLAYFIELD(xx, yy)
11109       {
11110         if (Feld[xx][yy] == element)
11111         {
11112           if (reset_frame)
11113           {
11114             ResetGfxAnimation(xx, yy);
11115             ResetRandomAnimationValue(xx, yy);
11116           }
11117
11118           TEST_DrawLevelField(xx, yy);
11119         }
11120       }
11121
11122       break;
11123     }
11124
11125     /* ---------- engine actions  ------------------------------------------ */
11126
11127     case CA_SET_ENGINE_SCAN_MODE:
11128     {
11129       InitPlayfieldScanMode(action_arg);
11130
11131       break;
11132     }
11133
11134     default:
11135       break;
11136   }
11137 }
11138
11139 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11140 {
11141   int old_element = Feld[x][y];
11142   int new_element = GetElementFromGroupElement(element);
11143   int previous_move_direction = MovDir[x][y];
11144 #if USE_NEW_CUSTOM_VALUE
11145   int last_ce_value = CustomValue[x][y];
11146 #endif
11147   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11148   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11149   boolean add_player_onto_element = (new_element_is_player &&
11150 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11151                                      /* this breaks SnakeBite when a snake is
11152                                         halfway through a door that closes */
11153                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11154                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11155 #endif
11156                                      IS_WALKABLE(old_element));
11157
11158 #if 0
11159   /* check if element under the player changes from accessible to unaccessible
11160      (needed for special case of dropping element which then changes) */
11161   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11162       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11163   {
11164     Bang(x, y);
11165
11166     return;
11167   }
11168 #endif
11169
11170   if (!add_player_onto_element)
11171   {
11172     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11173       RemoveMovingField(x, y);
11174     else
11175       RemoveField(x, y);
11176
11177     Feld[x][y] = new_element;
11178
11179 #if !USE_GFX_RESET_GFX_ANIMATION
11180     ResetGfxAnimation(x, y);
11181     ResetRandomAnimationValue(x, y);
11182 #endif
11183
11184     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11185       MovDir[x][y] = previous_move_direction;
11186
11187 #if USE_NEW_CUSTOM_VALUE
11188     if (element_info[new_element].use_last_ce_value)
11189       CustomValue[x][y] = last_ce_value;
11190 #endif
11191
11192     InitField_WithBug1(x, y, FALSE);
11193
11194     new_element = Feld[x][y];   /* element may have changed */
11195
11196 #if USE_GFX_RESET_GFX_ANIMATION
11197     ResetGfxAnimation(x, y);
11198     ResetRandomAnimationValue(x, y);
11199 #endif
11200
11201     TEST_DrawLevelField(x, y);
11202
11203     if (GFX_CRUMBLED(new_element))
11204       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11205   }
11206
11207 #if 1
11208   /* check if element under the player changes from accessible to unaccessible
11209      (needed for special case of dropping element which then changes) */
11210   /* (must be checked after creating new element for walkable group elements) */
11211 #if USE_FIX_KILLED_BY_NON_WALKABLE
11212   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11213       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11214   {
11215     Bang(x, y);
11216
11217     return;
11218   }
11219 #else
11220   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11221       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11222   {
11223     Bang(x, y);
11224
11225     return;
11226   }
11227 #endif
11228 #endif
11229
11230   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11231   if (new_element_is_player)
11232     RelocatePlayer(x, y, new_element);
11233
11234   if (is_change)
11235     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11236
11237   TestIfBadThingTouchesPlayer(x, y);
11238   TestIfPlayerTouchesCustomElement(x, y);
11239   TestIfElementTouchesCustomElement(x, y);
11240 }
11241
11242 static void CreateField(int x, int y, int element)
11243 {
11244   CreateFieldExt(x, y, element, FALSE);
11245 }
11246
11247 static void CreateElementFromChange(int x, int y, int element)
11248 {
11249   element = GET_VALID_RUNTIME_ELEMENT(element);
11250
11251 #if USE_STOP_CHANGED_ELEMENTS
11252   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11253   {
11254     int old_element = Feld[x][y];
11255
11256     /* prevent changed element from moving in same engine frame
11257        unless both old and new element can either fall or move */
11258     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11259         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11260       Stop[x][y] = TRUE;
11261   }
11262 #endif
11263
11264   CreateFieldExt(x, y, element, TRUE);
11265 }
11266
11267 static boolean ChangeElement(int x, int y, int element, int page)
11268 {
11269   struct ElementInfo *ei = &element_info[element];
11270   struct ElementChangeInfo *change = &ei->change_page[page];
11271   int ce_value = CustomValue[x][y];
11272   int ce_score = ei->collect_score;
11273   int target_element;
11274   int old_element = Feld[x][y];
11275
11276   /* always use default change event to prevent running into a loop */
11277   if (ChangeEvent[x][y] == -1)
11278     ChangeEvent[x][y] = CE_DELAY;
11279
11280   if (ChangeEvent[x][y] == CE_DELAY)
11281   {
11282     /* reset actual trigger element, trigger player and action element */
11283     change->actual_trigger_element = EL_EMPTY;
11284     change->actual_trigger_player = EL_EMPTY;
11285     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11286     change->actual_trigger_side = CH_SIDE_NONE;
11287     change->actual_trigger_ce_value = 0;
11288     change->actual_trigger_ce_score = 0;
11289   }
11290
11291   /* do not change elements more than a specified maximum number of changes */
11292   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11293     return FALSE;
11294
11295   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11296
11297   if (change->explode)
11298   {
11299     Bang(x, y);
11300
11301     return TRUE;
11302   }
11303
11304   if (change->use_target_content)
11305   {
11306     boolean complete_replace = TRUE;
11307     boolean can_replace[3][3];
11308     int xx, yy;
11309
11310     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11311     {
11312       boolean is_empty;
11313       boolean is_walkable;
11314       boolean is_diggable;
11315       boolean is_collectible;
11316       boolean is_removable;
11317       boolean is_destructible;
11318       int ex = x + xx - 1;
11319       int ey = y + yy - 1;
11320       int content_element = change->target_content.e[xx][yy];
11321       int e;
11322
11323       can_replace[xx][yy] = TRUE;
11324
11325       if (ex == x && ey == y)   /* do not check changing element itself */
11326         continue;
11327
11328       if (content_element == EL_EMPTY_SPACE)
11329       {
11330         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11331
11332         continue;
11333       }
11334
11335       if (!IN_LEV_FIELD(ex, ey))
11336       {
11337         can_replace[xx][yy] = FALSE;
11338         complete_replace = FALSE;
11339
11340         continue;
11341       }
11342
11343       e = Feld[ex][ey];
11344
11345       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11346         e = MovingOrBlocked2Element(ex, ey);
11347
11348       is_empty = (IS_FREE(ex, ey) ||
11349                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11350
11351       is_walkable     = (is_empty || IS_WALKABLE(e));
11352       is_diggable     = (is_empty || IS_DIGGABLE(e));
11353       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11354       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11355       is_removable    = (is_diggable || is_collectible);
11356
11357       can_replace[xx][yy] =
11358         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11359           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11360           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11361           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11362           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11363           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11364          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11365
11366       if (!can_replace[xx][yy])
11367         complete_replace = FALSE;
11368     }
11369
11370     if (!change->only_if_complete || complete_replace)
11371     {
11372       boolean something_has_changed = FALSE;
11373
11374       if (change->only_if_complete && change->use_random_replace &&
11375           RND(100) < change->random_percentage)
11376         return FALSE;
11377
11378       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11379       {
11380         int ex = x + xx - 1;
11381         int ey = y + yy - 1;
11382         int content_element;
11383
11384         if (can_replace[xx][yy] && (!change->use_random_replace ||
11385                                     RND(100) < change->random_percentage))
11386         {
11387           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11388             RemoveMovingField(ex, ey);
11389
11390           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11391
11392           content_element = change->target_content.e[xx][yy];
11393           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11394                                               ce_value, ce_score);
11395
11396           CreateElementFromChange(ex, ey, target_element);
11397
11398           something_has_changed = TRUE;
11399
11400           /* for symmetry reasons, freeze newly created border elements */
11401           if (ex != x || ey != y)
11402             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11403         }
11404       }
11405
11406       if (something_has_changed)
11407       {
11408         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11409         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11410       }
11411     }
11412   }
11413   else
11414   {
11415     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11416                                         ce_value, ce_score);
11417
11418     if (element == EL_DIAGONAL_GROWING ||
11419         element == EL_DIAGONAL_SHRINKING)
11420     {
11421       target_element = Store[x][y];
11422
11423       Store[x][y] = EL_EMPTY;
11424     }
11425
11426     CreateElementFromChange(x, y, target_element);
11427
11428     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11429     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11430   }
11431
11432   /* this uses direct change before indirect change */
11433   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11434
11435   return TRUE;
11436 }
11437
11438 #if USE_NEW_DELAYED_ACTION
11439
11440 static void HandleElementChange(int x, int y, int page)
11441 {
11442   int element = MovingOrBlocked2Element(x, y);
11443   struct ElementInfo *ei = &element_info[element];
11444   struct ElementChangeInfo *change = &ei->change_page[page];
11445   boolean handle_action_before_change = FALSE;
11446
11447 #ifdef DEBUG
11448   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11449       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11450   {
11451     printf("\n\n");
11452     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11453            x, y, element, element_info[element].token_name);
11454     printf("HandleElementChange(): This should never happen!\n");
11455     printf("\n\n");
11456   }
11457 #endif
11458
11459   /* this can happen with classic bombs on walkable, changing elements */
11460   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11461   {
11462 #if 0
11463     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11464       ChangeDelay[x][y] = 0;
11465 #endif
11466
11467     return;
11468   }
11469
11470   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11471   {
11472     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11473
11474     if (change->can_change)
11475     {
11476 #if 1
11477       /* !!! not clear why graphic animation should be reset at all here !!! */
11478       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11479 #if USE_GFX_RESET_WHEN_NOT_MOVING
11480       /* when a custom element is about to change (for example by change delay),
11481          do not reset graphic animation when the custom element is moving */
11482       if (!IS_MOVING(x, y))
11483 #endif
11484       {
11485         ResetGfxAnimation(x, y);
11486         ResetRandomAnimationValue(x, y);
11487       }
11488 #endif
11489
11490       if (change->pre_change_function)
11491         change->pre_change_function(x, y);
11492     }
11493   }
11494
11495   ChangeDelay[x][y]--;
11496
11497   if (ChangeDelay[x][y] != 0)           /* continue element change */
11498   {
11499     if (change->can_change)
11500     {
11501       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11502
11503       if (IS_ANIMATED(graphic))
11504         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11505
11506       if (change->change_function)
11507         change->change_function(x, y);
11508     }
11509   }
11510   else                                  /* finish element change */
11511   {
11512     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11513     {
11514       page = ChangePage[x][y];
11515       ChangePage[x][y] = -1;
11516
11517       change = &ei->change_page[page];
11518     }
11519
11520     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11521     {
11522       ChangeDelay[x][y] = 1;            /* try change after next move step */
11523       ChangePage[x][y] = page;          /* remember page to use for change */
11524
11525       return;
11526     }
11527
11528 #if 1
11529     /* special case: set new level random seed before changing element */
11530     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11531       handle_action_before_change = TRUE;
11532
11533     if (change->has_action && handle_action_before_change)
11534       ExecuteCustomElementAction(x, y, element, page);
11535 #endif
11536
11537     if (change->can_change)
11538     {
11539       if (ChangeElement(x, y, element, page))
11540       {
11541         if (change->post_change_function)
11542           change->post_change_function(x, y);
11543       }
11544     }
11545
11546     if (change->has_action && !handle_action_before_change)
11547       ExecuteCustomElementAction(x, y, element, page);
11548   }
11549 }
11550
11551 #else
11552
11553 static void HandleElementChange(int x, int y, int page)
11554 {
11555   int element = MovingOrBlocked2Element(x, y);
11556   struct ElementInfo *ei = &element_info[element];
11557   struct ElementChangeInfo *change = &ei->change_page[page];
11558
11559 #ifdef DEBUG
11560   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11561   {
11562     printf("\n\n");
11563     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11564            x, y, element, element_info[element].token_name);
11565     printf("HandleElementChange(): This should never happen!\n");
11566     printf("\n\n");
11567   }
11568 #endif
11569
11570   /* this can happen with classic bombs on walkable, changing elements */
11571   if (!CAN_CHANGE(element))
11572   {
11573 #if 0
11574     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11575       ChangeDelay[x][y] = 0;
11576 #endif
11577
11578     return;
11579   }
11580
11581   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11582   {
11583     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11584
11585     ResetGfxAnimation(x, y);
11586     ResetRandomAnimationValue(x, y);
11587
11588     if (change->pre_change_function)
11589       change->pre_change_function(x, y);
11590   }
11591
11592   ChangeDelay[x][y]--;
11593
11594   if (ChangeDelay[x][y] != 0)           /* continue element change */
11595   {
11596     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11597
11598     if (IS_ANIMATED(graphic))
11599       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11600
11601     if (change->change_function)
11602       change->change_function(x, y);
11603   }
11604   else                                  /* finish element change */
11605   {
11606     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11607     {
11608       page = ChangePage[x][y];
11609       ChangePage[x][y] = -1;
11610
11611       change = &ei->change_page[page];
11612     }
11613
11614     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11615     {
11616       ChangeDelay[x][y] = 1;            /* try change after next move step */
11617       ChangePage[x][y] = page;          /* remember page to use for change */
11618
11619       return;
11620     }
11621
11622     if (ChangeElement(x, y, element, page))
11623     {
11624       if (change->post_change_function)
11625         change->post_change_function(x, y);
11626     }
11627   }
11628 }
11629
11630 #endif
11631
11632 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11633                                               int trigger_element,
11634                                               int trigger_event,
11635                                               int trigger_player,
11636                                               int trigger_side,
11637                                               int trigger_page)
11638 {
11639   boolean change_done_any = FALSE;
11640   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11641   int i;
11642
11643   if (!(trigger_events[trigger_element][trigger_event]))
11644     return FALSE;
11645
11646 #if 0
11647   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11648          trigger_event, recursion_loop_depth, recursion_loop_detected,
11649          recursion_loop_element, EL_NAME(recursion_loop_element));
11650 #endif
11651
11652   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11653
11654   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11655   {
11656     int element = EL_CUSTOM_START + i;
11657     boolean change_done = FALSE;
11658     int p;
11659
11660     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11661         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11662       continue;
11663
11664     for (p = 0; p < element_info[element].num_change_pages; p++)
11665     {
11666       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11667
11668       if (change->can_change_or_has_action &&
11669           change->has_event[trigger_event] &&
11670           change->trigger_side & trigger_side &&
11671           change->trigger_player & trigger_player &&
11672           change->trigger_page & trigger_page_bits &&
11673           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11674       {
11675         change->actual_trigger_element = trigger_element;
11676         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11677         change->actual_trigger_player_bits = trigger_player;
11678         change->actual_trigger_side = trigger_side;
11679         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11680         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11681
11682 #if 0
11683         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11684                element, EL_NAME(element), p);
11685 #endif
11686
11687         if ((change->can_change && !change_done) || change->has_action)
11688         {
11689           int x, y;
11690
11691           SCAN_PLAYFIELD(x, y)
11692           {
11693             if (Feld[x][y] == element)
11694             {
11695               if (change->can_change && !change_done)
11696               {
11697 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11698                 /* if element already changed in this frame, not only prevent
11699                    another element change (checked in ChangeElement()), but
11700                    also prevent additional element actions for this element */
11701
11702                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11703                     !level.use_action_after_change_bug)
11704                   continue;
11705 #endif
11706
11707 #if 0
11708                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11709                        element, EL_NAME(element), p);
11710 #endif
11711
11712                 ChangeDelay[x][y] = 1;
11713                 ChangeEvent[x][y] = trigger_event;
11714
11715                 HandleElementChange(x, y, p);
11716               }
11717 #if USE_NEW_DELAYED_ACTION
11718               else if (change->has_action)
11719               {
11720 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11721                 /* if element already changed in this frame, not only prevent
11722                    another element change (checked in ChangeElement()), but
11723                    also prevent additional element actions for this element */
11724
11725                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11726                     !level.use_action_after_change_bug)
11727                   continue;
11728 #endif
11729
11730
11731 #if 0
11732                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11733                        element, EL_NAME(element), p);
11734 #endif
11735
11736                 ExecuteCustomElementAction(x, y, element, p);
11737                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11738               }
11739 #else
11740               if (change->has_action)
11741               {
11742                 ExecuteCustomElementAction(x, y, element, p);
11743                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11744               }
11745 #endif
11746             }
11747           }
11748
11749           if (change->can_change)
11750           {
11751             change_done = TRUE;
11752             change_done_any = TRUE;
11753
11754 #if 0
11755             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11756                    element, EL_NAME(element), p);
11757 #endif
11758
11759           }
11760         }
11761       }
11762     }
11763   }
11764
11765   RECURSION_LOOP_DETECTION_END();
11766
11767   return change_done_any;
11768 }
11769
11770 static boolean CheckElementChangeExt(int x, int y,
11771                                      int element,
11772                                      int trigger_element,
11773                                      int trigger_event,
11774                                      int trigger_player,
11775                                      int trigger_side)
11776 {
11777   boolean change_done = FALSE;
11778   int p;
11779
11780   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11781       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11782     return FALSE;
11783
11784   if (Feld[x][y] == EL_BLOCKED)
11785   {
11786     Blocked2Moving(x, y, &x, &y);
11787     element = Feld[x][y];
11788   }
11789
11790 #if 0
11791   /* check if element has already changed */
11792   if (Feld[x][y] != element)
11793     return FALSE;
11794 #else
11795   /* check if element has already changed or is about to change after moving */
11796   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11797        Feld[x][y] != element) ||
11798
11799       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11800        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11801         ChangePage[x][y] != -1)))
11802     return FALSE;
11803 #endif
11804
11805 #if 0
11806   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11807          trigger_event, recursion_loop_depth, recursion_loop_detected,
11808          recursion_loop_element, EL_NAME(recursion_loop_element));
11809 #endif
11810
11811   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11812
11813 #if 0
11814   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11815 #endif
11816
11817   for (p = 0; p < element_info[element].num_change_pages; p++)
11818   {
11819     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11820
11821     /* check trigger element for all events where the element that is checked
11822        for changing interacts with a directly adjacent element -- this is
11823        different to element changes that affect other elements to change on the
11824        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11825     boolean check_trigger_element =
11826       (trigger_event == CE_TOUCHING_X ||
11827        trigger_event == CE_HITTING_X ||
11828        trigger_event == CE_HIT_BY_X ||
11829 #if 1
11830        /* this one was forgotten until 3.2.3 */
11831        trigger_event == CE_DIGGING_X);
11832 #endif
11833
11834     if (change->can_change_or_has_action &&
11835         change->has_event[trigger_event] &&
11836         change->trigger_side & trigger_side &&
11837         change->trigger_player & trigger_player &&
11838         (!check_trigger_element ||
11839          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11840     {
11841       change->actual_trigger_element = trigger_element;
11842       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11843       change->actual_trigger_player_bits = trigger_player;
11844       change->actual_trigger_side = trigger_side;
11845       change->actual_trigger_ce_value = CustomValue[x][y];
11846       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11847
11848       /* special case: trigger element not at (x,y) position for some events */
11849       if (check_trigger_element)
11850       {
11851         static struct
11852         {
11853           int dx, dy;
11854         } move_xy[] =
11855           {
11856             {  0,  0 },
11857             { -1,  0 },
11858             { +1,  0 },
11859             {  0,  0 },
11860             {  0, -1 },
11861             {  0,  0 }, { 0, 0 }, { 0, 0 },
11862             {  0, +1 }
11863           };
11864
11865         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11866         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11867
11868         change->actual_trigger_ce_value = CustomValue[xx][yy];
11869         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11870       }
11871
11872       if (change->can_change && !change_done)
11873       {
11874         ChangeDelay[x][y] = 1;
11875         ChangeEvent[x][y] = trigger_event;
11876
11877         HandleElementChange(x, y, p);
11878
11879         change_done = TRUE;
11880       }
11881 #if USE_NEW_DELAYED_ACTION
11882       else if (change->has_action)
11883       {
11884         ExecuteCustomElementAction(x, y, element, p);
11885         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11886       }
11887 #else
11888       if (change->has_action)
11889       {
11890         ExecuteCustomElementAction(x, y, element, p);
11891         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11892       }
11893 #endif
11894     }
11895   }
11896
11897   RECURSION_LOOP_DETECTION_END();
11898
11899   return change_done;
11900 }
11901
11902 static void PlayPlayerSound(struct PlayerInfo *player)
11903 {
11904   int jx = player->jx, jy = player->jy;
11905   int sound_element = player->artwork_element;
11906   int last_action = player->last_action_waiting;
11907   int action = player->action_waiting;
11908
11909   if (player->is_waiting)
11910   {
11911     if (action != last_action)
11912       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11913     else
11914       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11915   }
11916   else
11917   {
11918     if (action != last_action)
11919       StopSound(element_info[sound_element].sound[last_action]);
11920
11921     if (last_action == ACTION_SLEEPING)
11922       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11923   }
11924 }
11925
11926 static void PlayAllPlayersSound()
11927 {
11928   int i;
11929
11930   for (i = 0; i < MAX_PLAYERS; i++)
11931     if (stored_player[i].active)
11932       PlayPlayerSound(&stored_player[i]);
11933 }
11934
11935 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11936 {
11937   boolean last_waiting = player->is_waiting;
11938   int move_dir = player->MovDir;
11939
11940   player->dir_waiting = move_dir;
11941   player->last_action_waiting = player->action_waiting;
11942
11943   if (is_waiting)
11944   {
11945     if (!last_waiting)          /* not waiting -> waiting */
11946     {
11947       player->is_waiting = TRUE;
11948
11949       player->frame_counter_bored =
11950         FrameCounter +
11951         game.player_boring_delay_fixed +
11952         GetSimpleRandom(game.player_boring_delay_random);
11953       player->frame_counter_sleeping =
11954         FrameCounter +
11955         game.player_sleeping_delay_fixed +
11956         GetSimpleRandom(game.player_sleeping_delay_random);
11957
11958       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11959     }
11960
11961     if (game.player_sleeping_delay_fixed +
11962         game.player_sleeping_delay_random > 0 &&
11963         player->anim_delay_counter == 0 &&
11964         player->post_delay_counter == 0 &&
11965         FrameCounter >= player->frame_counter_sleeping)
11966       player->is_sleeping = TRUE;
11967     else if (game.player_boring_delay_fixed +
11968              game.player_boring_delay_random > 0 &&
11969              FrameCounter >= player->frame_counter_bored)
11970       player->is_bored = TRUE;
11971
11972     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11973                               player->is_bored ? ACTION_BORING :
11974                               ACTION_WAITING);
11975
11976     if (player->is_sleeping && player->use_murphy)
11977     {
11978       /* special case for sleeping Murphy when leaning against non-free tile */
11979
11980       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11981           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11982            !IS_MOVING(player->jx - 1, player->jy)))
11983         move_dir = MV_LEFT;
11984       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11985                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11986                 !IS_MOVING(player->jx + 1, player->jy)))
11987         move_dir = MV_RIGHT;
11988       else
11989         player->is_sleeping = FALSE;
11990
11991       player->dir_waiting = move_dir;
11992     }
11993
11994     if (player->is_sleeping)
11995     {
11996       if (player->num_special_action_sleeping > 0)
11997       {
11998         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11999         {
12000           int last_special_action = player->special_action_sleeping;
12001           int num_special_action = player->num_special_action_sleeping;
12002           int special_action =
12003             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
12004              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
12005              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
12006              last_special_action + 1 : ACTION_SLEEPING);
12007           int special_graphic =
12008             el_act_dir2img(player->artwork_element, special_action, move_dir);
12009
12010           player->anim_delay_counter =
12011             graphic_info[special_graphic].anim_delay_fixed +
12012             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12013           player->post_delay_counter =
12014             graphic_info[special_graphic].post_delay_fixed +
12015             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12016
12017           player->special_action_sleeping = special_action;
12018         }
12019
12020         if (player->anim_delay_counter > 0)
12021         {
12022           player->action_waiting = player->special_action_sleeping;
12023           player->anim_delay_counter--;
12024         }
12025         else if (player->post_delay_counter > 0)
12026         {
12027           player->post_delay_counter--;
12028         }
12029       }
12030     }
12031     else if (player->is_bored)
12032     {
12033       if (player->num_special_action_bored > 0)
12034       {
12035         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12036         {
12037           int special_action =
12038             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
12039           int special_graphic =
12040             el_act_dir2img(player->artwork_element, special_action, move_dir);
12041
12042           player->anim_delay_counter =
12043             graphic_info[special_graphic].anim_delay_fixed +
12044             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12045           player->post_delay_counter =
12046             graphic_info[special_graphic].post_delay_fixed +
12047             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12048
12049           player->special_action_bored = special_action;
12050         }
12051
12052         if (player->anim_delay_counter > 0)
12053         {
12054           player->action_waiting = player->special_action_bored;
12055           player->anim_delay_counter--;
12056         }
12057         else if (player->post_delay_counter > 0)
12058         {
12059           player->post_delay_counter--;
12060         }
12061       }
12062     }
12063   }
12064   else if (last_waiting)        /* waiting -> not waiting */
12065   {
12066     player->is_waiting = FALSE;
12067     player->is_bored = FALSE;
12068     player->is_sleeping = FALSE;
12069
12070     player->frame_counter_bored = -1;
12071     player->frame_counter_sleeping = -1;
12072
12073     player->anim_delay_counter = 0;
12074     player->post_delay_counter = 0;
12075
12076     player->dir_waiting = player->MovDir;
12077     player->action_waiting = ACTION_DEFAULT;
12078
12079     player->special_action_bored = ACTION_DEFAULT;
12080     player->special_action_sleeping = ACTION_DEFAULT;
12081   }
12082 }
12083
12084 static void CheckSingleStepMode(struct PlayerInfo *player)
12085 {
12086   if (tape.single_step && tape.recording && !tape.pausing)
12087   {
12088     /* as it is called "single step mode", just return to pause mode when the
12089        player stopped moving after one tile (or never starts moving at all) */
12090     if (!player->is_moving && !player->is_pushing)
12091     {
12092       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12093       SnapField(player, 0, 0);                  /* stop snapping */
12094     }
12095   }
12096 }
12097
12098 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12099 {
12100   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12101   int left      = player_action & JOY_LEFT;
12102   int right     = player_action & JOY_RIGHT;
12103   int up        = player_action & JOY_UP;
12104   int down      = player_action & JOY_DOWN;
12105   int button1   = player_action & JOY_BUTTON_1;
12106   int button2   = player_action & JOY_BUTTON_2;
12107   int dx        = (left ? -1 : right ? 1 : 0);
12108   int dy        = (up   ? -1 : down  ? 1 : 0);
12109
12110   if (!player->active || tape.pausing)
12111     return 0;
12112
12113   if (player_action)
12114   {
12115     if (button1)
12116       snapped = SnapField(player, dx, dy);
12117     else
12118     {
12119       if (button2)
12120         dropped = DropElement(player);
12121
12122       moved = MovePlayer(player, dx, dy);
12123     }
12124
12125     CheckSingleStepMode(player);
12126
12127     SetPlayerWaiting(player, FALSE);
12128
12129     return player_action;
12130   }
12131   else
12132   {
12133     /* no actions for this player (no input at player's configured device) */
12134
12135     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12136     SnapField(player, 0, 0);
12137     CheckGravityMovementWhenNotMoving(player);
12138
12139     if (player->MovPos == 0)
12140       SetPlayerWaiting(player, TRUE);
12141
12142     if (player->MovPos == 0)    /* needed for tape.playing */
12143       player->is_moving = FALSE;
12144
12145     player->is_dropping = FALSE;
12146     player->is_dropping_pressed = FALSE;
12147     player->drop_pressed_delay = 0;
12148
12149     CheckSingleStepMode(player);
12150
12151     return 0;
12152   }
12153 }
12154
12155 static void CheckLevelTime()
12156 {
12157   int i;
12158
12159   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12160   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12161   {
12162     if (level.native_em_level->lev->home == 0)  /* all players at home */
12163     {
12164       PlayerWins(local_player);
12165
12166       AllPlayersGone = TRUE;
12167
12168       level.native_em_level->lev->home = -1;
12169     }
12170
12171     if (level.native_em_level->ply[0]->alive == 0 &&
12172         level.native_em_level->ply[1]->alive == 0 &&
12173         level.native_em_level->ply[2]->alive == 0 &&
12174         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12175       AllPlayersGone = TRUE;
12176   }
12177   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12178   {
12179     if (game_sp.LevelSolved &&
12180         !game_sp.GameOver)                              /* game won */
12181     {
12182       PlayerWins(local_player);
12183
12184       game_sp.GameOver = TRUE;
12185
12186       AllPlayersGone = TRUE;
12187     }
12188
12189     if (game_sp.GameOver)                               /* game lost */
12190       AllPlayersGone = TRUE;
12191   }
12192
12193   if (TimeFrames >= FRAMES_PER_SECOND)
12194   {
12195     TimeFrames = 0;
12196     TapeTime++;
12197
12198     for (i = 0; i < MAX_PLAYERS; i++)
12199     {
12200       struct PlayerInfo *player = &stored_player[i];
12201
12202       if (SHIELD_ON(player))
12203       {
12204         player->shield_normal_time_left--;
12205
12206         if (player->shield_deadly_time_left > 0)
12207           player->shield_deadly_time_left--;
12208       }
12209     }
12210
12211     if (!local_player->LevelSolved && !level.use_step_counter)
12212     {
12213       TimePlayed++;
12214
12215       if (TimeLeft > 0)
12216       {
12217         TimeLeft--;
12218
12219         if (TimeLeft <= 10 && setup.time_limit)
12220           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12221
12222 #if 1
12223         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12224            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12225
12226         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12227
12228         /* (already called by UpdateAndDisplayGameControlValues() below) */
12229         // DisplayGameControlValues();
12230 #else
12231         DrawGameValue_Time(TimeLeft);
12232 #endif
12233
12234         if (!TimeLeft && setup.time_limit)
12235         {
12236           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12237             level.native_em_level->lev->killed_out_of_time = TRUE;
12238           else
12239             for (i = 0; i < MAX_PLAYERS; i++)
12240               KillPlayer(&stored_player[i]);
12241         }
12242       }
12243 #if 1
12244       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12245       {
12246         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12247
12248         /* (already called by UpdateAndDisplayGameControlValues() below) */
12249         // DisplayGameControlValues();
12250       }
12251 #else
12252       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12253         DrawGameValue_Time(TimePlayed);
12254 #endif
12255
12256       level.native_em_level->lev->time =
12257         (game.no_time_limit ? TimePlayed : TimeLeft);
12258     }
12259
12260     if (tape.recording || tape.playing)
12261       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12262   }
12263
12264 #if 1
12265   UpdateAndDisplayGameControlValues();
12266 #else
12267   UpdateGameDoorValues();
12268   DrawGameDoorValues();
12269 #endif
12270 }
12271
12272 void AdvanceFrameAndPlayerCounters(int player_nr)
12273 {
12274   int i;
12275
12276   /* advance frame counters (global frame counter and time frame counter) */
12277   FrameCounter++;
12278   TimeFrames++;
12279
12280   /* advance player counters (counters for move delay, move animation etc.) */
12281   for (i = 0; i < MAX_PLAYERS; i++)
12282   {
12283     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12284     int move_delay_value = stored_player[i].move_delay_value;
12285     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12286
12287     if (!advance_player_counters)       /* not all players may be affected */
12288       continue;
12289
12290 #if USE_NEW_PLAYER_ANIM
12291     if (move_frames == 0)       /* less than one move per game frame */
12292     {
12293       int stepsize = TILEX / move_delay_value;
12294       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12295       int count = (stored_player[i].is_moving ?
12296                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12297
12298       if (count % delay == 0)
12299         move_frames = 1;
12300     }
12301 #endif
12302
12303     stored_player[i].Frame += move_frames;
12304
12305     if (stored_player[i].MovPos != 0)
12306       stored_player[i].StepFrame += move_frames;
12307
12308     if (stored_player[i].move_delay > 0)
12309       stored_player[i].move_delay--;
12310
12311     /* due to bugs in previous versions, counter must count up, not down */
12312     if (stored_player[i].push_delay != -1)
12313       stored_player[i].push_delay++;
12314
12315     if (stored_player[i].drop_delay > 0)
12316       stored_player[i].drop_delay--;
12317
12318     if (stored_player[i].is_dropping_pressed)
12319       stored_player[i].drop_pressed_delay++;
12320   }
12321 }
12322
12323 void StartGameActions(boolean init_network_game, boolean record_tape,
12324                       int random_seed)
12325 {
12326   unsigned int new_random_seed = InitRND(random_seed);
12327
12328   if (record_tape)
12329     TapeStartRecording(new_random_seed);
12330
12331 #if defined(NETWORK_AVALIABLE)
12332   if (init_network_game)
12333   {
12334     SendToServer_StartPlaying();
12335
12336     return;
12337   }
12338 #endif
12339
12340   InitGame();
12341 }
12342
12343 void GameActions()
12344 {
12345   static unsigned int game_frame_delay = 0;
12346   unsigned int game_frame_delay_value;
12347   byte *recorded_player_action;
12348   byte summarized_player_action = 0;
12349   byte tape_action[MAX_PLAYERS];
12350   int i;
12351
12352   /* detect endless loops, caused by custom element programming */
12353   if (recursion_loop_detected && recursion_loop_depth == 0)
12354   {
12355     char *message = getStringCat3("Internal Error ! Element ",
12356                                   EL_NAME(recursion_loop_element),
12357                                   " caused endless loop ! Quit the game ?");
12358
12359     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12360           EL_NAME(recursion_loop_element));
12361
12362     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12363
12364     recursion_loop_detected = FALSE;    /* if game should be continued */
12365
12366     free(message);
12367
12368     return;
12369   }
12370
12371   if (game.restart_level)
12372     StartGameActions(options.network, setup.autorecord, level.random_seed);
12373
12374   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12375   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12376   {
12377     if (level.native_em_level->lev->home == 0)  /* all players at home */
12378     {
12379       PlayerWins(local_player);
12380
12381       AllPlayersGone = TRUE;
12382
12383       level.native_em_level->lev->home = -1;
12384     }
12385
12386     if (level.native_em_level->ply[0]->alive == 0 &&
12387         level.native_em_level->ply[1]->alive == 0 &&
12388         level.native_em_level->ply[2]->alive == 0 &&
12389         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12390       AllPlayersGone = TRUE;
12391   }
12392   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12393   {
12394     if (game_sp.LevelSolved &&
12395         !game_sp.GameOver)                              /* game won */
12396     {
12397       PlayerWins(local_player);
12398
12399       game_sp.GameOver = TRUE;
12400
12401       AllPlayersGone = TRUE;
12402     }
12403
12404     if (game_sp.GameOver)                               /* game lost */
12405       AllPlayersGone = TRUE;
12406   }
12407
12408   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12409     GameWon();
12410
12411   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12412     TapeStop();
12413
12414   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12415     return;
12416
12417   game_frame_delay_value =
12418     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12419
12420   if (tape.playing && tape.warp_forward && !tape.pausing)
12421     game_frame_delay_value = 0;
12422
12423   /* ---------- main game synchronization point ---------- */
12424
12425   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12426
12427   if (network_playing && !network_player_action_received)
12428   {
12429     /* try to get network player actions in time */
12430
12431 #if defined(NETWORK_AVALIABLE)
12432     /* last chance to get network player actions without main loop delay */
12433     HandleNetworking();
12434 #endif
12435
12436     /* game was quit by network peer */
12437     if (game_status != GAME_MODE_PLAYING)
12438       return;
12439
12440     if (!network_player_action_received)
12441       return;           /* failed to get network player actions in time */
12442
12443     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12444   }
12445
12446   if (tape.pausing)
12447     return;
12448
12449   /* at this point we know that we really continue executing the game */
12450
12451   network_player_action_received = FALSE;
12452
12453   /* when playing tape, read previously recorded player input from tape data */
12454   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12455
12456 #if 1
12457   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12458   if (tape.pausing)
12459     return;
12460 #endif
12461
12462   if (tape.set_centered_player)
12463   {
12464     game.centered_player_nr_next = tape.centered_player_nr_next;
12465     game.set_centered_player = TRUE;
12466   }
12467
12468   for (i = 0; i < MAX_PLAYERS; i++)
12469   {
12470     summarized_player_action |= stored_player[i].action;
12471
12472     if (!network_playing)
12473       stored_player[i].effective_action = stored_player[i].action;
12474   }
12475
12476 #if defined(NETWORK_AVALIABLE)
12477   if (network_playing)
12478     SendToServer_MovePlayer(summarized_player_action);
12479 #endif
12480
12481   if (!options.network && !setup.team_mode)
12482     local_player->effective_action = summarized_player_action;
12483
12484   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12485   {
12486     for (i = 0; i < MAX_PLAYERS; i++)
12487       stored_player[i].effective_action =
12488         (i == game.centered_player_nr ? summarized_player_action : 0);
12489   }
12490
12491   if (recorded_player_action != NULL)
12492     for (i = 0; i < MAX_PLAYERS; i++)
12493       stored_player[i].effective_action = recorded_player_action[i];
12494
12495   for (i = 0; i < MAX_PLAYERS; i++)
12496   {
12497     tape_action[i] = stored_player[i].effective_action;
12498
12499     /* (this can only happen in the R'n'D game engine) */
12500     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12501       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12502   }
12503
12504   /* only record actions from input devices, but not programmed actions */
12505   if (tape.recording)
12506     TapeRecordAction(tape_action);
12507
12508 #if USE_NEW_PLAYER_ASSIGNMENTS
12509   {
12510     byte mapped_action[MAX_PLAYERS];
12511
12512     for (i = 0; i < MAX_PLAYERS; i++)
12513       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12514
12515     for (i = 0; i < MAX_PLAYERS; i++)
12516       stored_player[i].effective_action = mapped_action[i];
12517   }
12518 #endif
12519
12520   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12521   {
12522     GameActions_EM_Main();
12523   }
12524   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12525   {
12526     GameActions_SP_Main();
12527   }
12528   else
12529   {
12530     GameActions_RND();
12531   }
12532 }
12533
12534 void GameActions_EM_Main()
12535 {
12536   byte effective_action[MAX_PLAYERS];
12537   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12538   int i;
12539
12540   for (i = 0; i < MAX_PLAYERS; i++)
12541     effective_action[i] = stored_player[i].effective_action;
12542
12543   GameActions_EM(effective_action, warp_mode);
12544
12545   CheckLevelTime();
12546
12547   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12548 }
12549
12550 void GameActions_SP_Main()
12551 {
12552   byte effective_action[MAX_PLAYERS];
12553   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12554   int i;
12555
12556   for (i = 0; i < MAX_PLAYERS; i++)
12557     effective_action[i] = stored_player[i].effective_action;
12558
12559   GameActions_SP(effective_action, warp_mode);
12560
12561   CheckLevelTime();
12562
12563   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12564 }
12565
12566 void GameActions_RND()
12567 {
12568   int magic_wall_x = 0, magic_wall_y = 0;
12569   int i, x, y, element, graphic;
12570
12571   InitPlayfieldScanModeVars();
12572
12573 #if USE_ONE_MORE_CHANGE_PER_FRAME
12574   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12575   {
12576     SCAN_PLAYFIELD(x, y)
12577     {
12578       ChangeCount[x][y] = 0;
12579       ChangeEvent[x][y] = -1;
12580     }
12581   }
12582 #endif
12583
12584   if (game.set_centered_player)
12585   {
12586     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12587
12588     /* switching to "all players" only possible if all players fit to screen */
12589     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12590     {
12591       game.centered_player_nr_next = game.centered_player_nr;
12592       game.set_centered_player = FALSE;
12593     }
12594
12595     /* do not switch focus to non-existing (or non-active) player */
12596     if (game.centered_player_nr_next >= 0 &&
12597         !stored_player[game.centered_player_nr_next].active)
12598     {
12599       game.centered_player_nr_next = game.centered_player_nr;
12600       game.set_centered_player = FALSE;
12601     }
12602   }
12603
12604   if (game.set_centered_player &&
12605       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12606   {
12607     int sx, sy;
12608
12609     if (game.centered_player_nr_next == -1)
12610     {
12611       setScreenCenteredToAllPlayers(&sx, &sy);
12612     }
12613     else
12614     {
12615       sx = stored_player[game.centered_player_nr_next].jx;
12616       sy = stored_player[game.centered_player_nr_next].jy;
12617     }
12618
12619     game.centered_player_nr = game.centered_player_nr_next;
12620     game.set_centered_player = FALSE;
12621
12622     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12623     DrawGameDoorValues();
12624   }
12625
12626   for (i = 0; i < MAX_PLAYERS; i++)
12627   {
12628     int actual_player_action = stored_player[i].effective_action;
12629
12630 #if 1
12631     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12632        - rnd_equinox_tetrachloride 048
12633        - rnd_equinox_tetrachloride_ii 096
12634        - rnd_emanuel_schmieg 002
12635        - doctor_sloan_ww 001, 020
12636     */
12637     if (stored_player[i].MovPos == 0)
12638       CheckGravityMovement(&stored_player[i]);
12639 #endif
12640
12641     /* overwrite programmed action with tape action */
12642     if (stored_player[i].programmed_action)
12643       actual_player_action = stored_player[i].programmed_action;
12644
12645     PlayerActions(&stored_player[i], actual_player_action);
12646
12647     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12648   }
12649
12650   ScrollScreen(NULL, SCROLL_GO_ON);
12651
12652   /* for backwards compatibility, the following code emulates a fixed bug that
12653      occured when pushing elements (causing elements that just made their last
12654      pushing step to already (if possible) make their first falling step in the
12655      same game frame, which is bad); this code is also needed to use the famous
12656      "spring push bug" which is used in older levels and might be wanted to be
12657      used also in newer levels, but in this case the buggy pushing code is only
12658      affecting the "spring" element and no other elements */
12659
12660   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12661   {
12662     for (i = 0; i < MAX_PLAYERS; i++)
12663     {
12664       struct PlayerInfo *player = &stored_player[i];
12665       int x = player->jx;
12666       int y = player->jy;
12667
12668       if (player->active && player->is_pushing && player->is_moving &&
12669           IS_MOVING(x, y) &&
12670           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12671            Feld[x][y] == EL_SPRING))
12672       {
12673         ContinueMoving(x, y);
12674
12675         /* continue moving after pushing (this is actually a bug) */
12676         if (!IS_MOVING(x, y))
12677           Stop[x][y] = FALSE;
12678       }
12679     }
12680   }
12681
12682 #if 0
12683   debug_print_timestamp(0, "start main loop profiling");
12684 #endif
12685
12686   SCAN_PLAYFIELD(x, y)
12687   {
12688     ChangeCount[x][y] = 0;
12689     ChangeEvent[x][y] = -1;
12690
12691     /* this must be handled before main playfield loop */
12692     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12693     {
12694       MovDelay[x][y]--;
12695       if (MovDelay[x][y] <= 0)
12696         RemoveField(x, y);
12697     }
12698
12699 #if USE_NEW_SNAP_DELAY
12700     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12701     {
12702       MovDelay[x][y]--;
12703       if (MovDelay[x][y] <= 0)
12704       {
12705         RemoveField(x, y);
12706         TEST_DrawLevelField(x, y);
12707
12708         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12709       }
12710     }
12711 #endif
12712
12713 #if DEBUG
12714     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12715     {
12716       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12717       printf("GameActions(): This should never happen!\n");
12718
12719       ChangePage[x][y] = -1;
12720     }
12721 #endif
12722
12723     Stop[x][y] = FALSE;
12724     if (WasJustMoving[x][y] > 0)
12725       WasJustMoving[x][y]--;
12726     if (WasJustFalling[x][y] > 0)
12727       WasJustFalling[x][y]--;
12728     if (CheckCollision[x][y] > 0)
12729       CheckCollision[x][y]--;
12730     if (CheckImpact[x][y] > 0)
12731       CheckImpact[x][y]--;
12732
12733     GfxFrame[x][y]++;
12734
12735     /* reset finished pushing action (not done in ContinueMoving() to allow
12736        continuous pushing animation for elements with zero push delay) */
12737     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12738     {
12739       ResetGfxAnimation(x, y);
12740       TEST_DrawLevelField(x, y);
12741     }
12742
12743 #if DEBUG
12744     if (IS_BLOCKED(x, y))
12745     {
12746       int oldx, oldy;
12747
12748       Blocked2Moving(x, y, &oldx, &oldy);
12749       if (!IS_MOVING(oldx, oldy))
12750       {
12751         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12752         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12753         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12754         printf("GameActions(): This should never happen!\n");
12755       }
12756     }
12757 #endif
12758   }
12759
12760 #if 0
12761   debug_print_timestamp(0, "- time for pre-main loop:");
12762 #endif
12763
12764 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12765   SCAN_PLAYFIELD(x, y)
12766   {
12767     element = Feld[x][y];
12768     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12769
12770 #if 1
12771     {
12772 #if 1
12773       int element2 = element;
12774       int graphic2 = graphic;
12775 #else
12776       int element2 = Feld[x][y];
12777       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12778 #endif
12779       int last_gfx_frame = GfxFrame[x][y];
12780
12781       if (graphic_info[graphic2].anim_global_sync)
12782         GfxFrame[x][y] = FrameCounter;
12783       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12784         GfxFrame[x][y] = CustomValue[x][y];
12785       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12786         GfxFrame[x][y] = element_info[element2].collect_score;
12787       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12788         GfxFrame[x][y] = ChangeDelay[x][y];
12789
12790       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12791         DrawLevelGraphicAnimation(x, y, graphic2);
12792     }
12793 #else
12794     ResetGfxFrame(x, y, TRUE);
12795 #endif
12796
12797 #if 1
12798     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12799         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12800       ResetRandomAnimationValue(x, y);
12801 #endif
12802
12803 #if 1
12804     SetRandomAnimationValue(x, y);
12805 #endif
12806
12807 #if 1
12808     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12809 #endif
12810   }
12811 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12812
12813 #if 0
12814   debug_print_timestamp(0, "- time for TEST loop:     -->");
12815 #endif
12816
12817   SCAN_PLAYFIELD(x, y)
12818   {
12819     element = Feld[x][y];
12820     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12821
12822     ResetGfxFrame(x, y, TRUE);
12823
12824     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12825         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12826       ResetRandomAnimationValue(x, y);
12827
12828     SetRandomAnimationValue(x, y);
12829
12830     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12831
12832     if (IS_INACTIVE(element))
12833     {
12834       if (IS_ANIMATED(graphic))
12835         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12836
12837       continue;
12838     }
12839
12840     /* this may take place after moving, so 'element' may have changed */
12841     if (IS_CHANGING(x, y) &&
12842         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12843     {
12844       int page = element_info[element].event_page_nr[CE_DELAY];
12845
12846 #if 1
12847       HandleElementChange(x, y, page);
12848 #else
12849       if (CAN_CHANGE(element))
12850         HandleElementChange(x, y, page);
12851
12852       if (HAS_ACTION(element))
12853         ExecuteCustomElementAction(x, y, element, page);
12854 #endif
12855
12856       element = Feld[x][y];
12857       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12858     }
12859
12860 #if 0   // ---------------------------------------------------------------------
12861
12862     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12863     {
12864       StartMoving(x, y);
12865
12866       element = Feld[x][y];
12867       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12868
12869       if (IS_ANIMATED(graphic) &&
12870           !IS_MOVING(x, y) &&
12871           !Stop[x][y])
12872         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12873
12874       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12875         TEST_DrawTwinkleOnField(x, y);
12876     }
12877     else if (IS_MOVING(x, y))
12878       ContinueMoving(x, y);
12879     else
12880     {
12881       switch (element)
12882       {
12883         case EL_ACID:
12884         case EL_EXIT_OPEN:
12885         case EL_EM_EXIT_OPEN:
12886         case EL_SP_EXIT_OPEN:
12887         case EL_STEEL_EXIT_OPEN:
12888         case EL_EM_STEEL_EXIT_OPEN:
12889         case EL_SP_TERMINAL:
12890         case EL_SP_TERMINAL_ACTIVE:
12891         case EL_EXTRA_TIME:
12892         case EL_SHIELD_NORMAL:
12893         case EL_SHIELD_DEADLY:
12894           if (IS_ANIMATED(graphic))
12895             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12896           break;
12897
12898         case EL_DYNAMITE_ACTIVE:
12899         case EL_EM_DYNAMITE_ACTIVE:
12900         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12901         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12902         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12903         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12904         case EL_SP_DISK_RED_ACTIVE:
12905           CheckDynamite(x, y);
12906           break;
12907
12908         case EL_AMOEBA_GROWING:
12909           AmoebeWaechst(x, y);
12910           break;
12911
12912         case EL_AMOEBA_SHRINKING:
12913           AmoebaDisappearing(x, y);
12914           break;
12915
12916 #if !USE_NEW_AMOEBA_CODE
12917         case EL_AMOEBA_WET:
12918         case EL_AMOEBA_DRY:
12919         case EL_AMOEBA_FULL:
12920         case EL_BD_AMOEBA:
12921         case EL_EMC_DRIPPER:
12922           AmoebeAbleger(x, y);
12923           break;
12924 #endif
12925
12926         case EL_GAME_OF_LIFE:
12927         case EL_BIOMAZE:
12928           Life(x, y);
12929           break;
12930
12931         case EL_EXIT_CLOSED:
12932           CheckExit(x, y);
12933           break;
12934
12935         case EL_EM_EXIT_CLOSED:
12936           CheckExitEM(x, y);
12937           break;
12938
12939         case EL_STEEL_EXIT_CLOSED:
12940           CheckExitSteel(x, y);
12941           break;
12942
12943         case EL_EM_STEEL_EXIT_CLOSED:
12944           CheckExitSteelEM(x, y);
12945           break;
12946
12947         case EL_SP_EXIT_CLOSED:
12948           CheckExitSP(x, y);
12949           break;
12950
12951         case EL_EXPANDABLE_WALL_GROWING:
12952         case EL_EXPANDABLE_STEELWALL_GROWING:
12953           MauerWaechst(x, y);
12954           break;
12955
12956         case EL_EXPANDABLE_WALL:
12957         case EL_EXPANDABLE_WALL_HORIZONTAL:
12958         case EL_EXPANDABLE_WALL_VERTICAL:
12959         case EL_EXPANDABLE_WALL_ANY:
12960         case EL_BD_EXPANDABLE_WALL:
12961           MauerAbleger(x, y);
12962           break;
12963
12964         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12965         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12966         case EL_EXPANDABLE_STEELWALL_ANY:
12967           MauerAblegerStahl(x, y);
12968           break;
12969
12970         case EL_FLAMES:
12971           CheckForDragon(x, y);
12972           break;
12973
12974         case EL_EXPLOSION:
12975           break;
12976
12977         case EL_ELEMENT_SNAPPING:
12978         case EL_DIAGONAL_SHRINKING:
12979         case EL_DIAGONAL_GROWING:
12980         {
12981           graphic =
12982             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12983
12984           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12985           break;
12986         }
12987
12988         default:
12989           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12990             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12991           break;
12992       }
12993     }
12994
12995 #else   // ---------------------------------------------------------------------
12996
12997     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12998     {
12999       StartMoving(x, y);
13000
13001       element = Feld[x][y];
13002       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13003
13004       if (IS_ANIMATED(graphic) &&
13005           !IS_MOVING(x, y) &&
13006           !Stop[x][y])
13007         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13008
13009       if (IS_GEM(element) || element == EL_SP_INFOTRON)
13010         TEST_DrawTwinkleOnField(x, y);
13011     }
13012     else if ((element == EL_ACID ||
13013               element == EL_EXIT_OPEN ||
13014               element == EL_EM_EXIT_OPEN ||
13015               element == EL_SP_EXIT_OPEN ||
13016               element == EL_STEEL_EXIT_OPEN ||
13017               element == EL_EM_STEEL_EXIT_OPEN ||
13018               element == EL_SP_TERMINAL ||
13019               element == EL_SP_TERMINAL_ACTIVE ||
13020               element == EL_EXTRA_TIME ||
13021               element == EL_SHIELD_NORMAL ||
13022               element == EL_SHIELD_DEADLY) &&
13023              IS_ANIMATED(graphic))
13024       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13025     else if (IS_MOVING(x, y))
13026       ContinueMoving(x, y);
13027     else if (IS_ACTIVE_BOMB(element))
13028       CheckDynamite(x, y);
13029     else if (element == EL_AMOEBA_GROWING)
13030       AmoebeWaechst(x, y);
13031     else if (element == EL_AMOEBA_SHRINKING)
13032       AmoebaDisappearing(x, y);
13033
13034 #if !USE_NEW_AMOEBA_CODE
13035     else if (IS_AMOEBALIVE(element))
13036       AmoebeAbleger(x, y);
13037 #endif
13038
13039     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13040       Life(x, y);
13041     else if (element == EL_EXIT_CLOSED)
13042       CheckExit(x, y);
13043     else if (element == EL_EM_EXIT_CLOSED)
13044       CheckExitEM(x, y);
13045     else if (element == EL_STEEL_EXIT_CLOSED)
13046       CheckExitSteel(x, y);
13047     else if (element == EL_EM_STEEL_EXIT_CLOSED)
13048       CheckExitSteelEM(x, y);
13049     else if (element == EL_SP_EXIT_CLOSED)
13050       CheckExitSP(x, y);
13051     else if (element == EL_EXPANDABLE_WALL_GROWING ||
13052              element == EL_EXPANDABLE_STEELWALL_GROWING)
13053       MauerWaechst(x, y);
13054     else if (element == EL_EXPANDABLE_WALL ||
13055              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13056              element == EL_EXPANDABLE_WALL_VERTICAL ||
13057              element == EL_EXPANDABLE_WALL_ANY ||
13058              element == EL_BD_EXPANDABLE_WALL)
13059       MauerAbleger(x, y);
13060     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13061              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13062              element == EL_EXPANDABLE_STEELWALL_ANY)
13063       MauerAblegerStahl(x, y);
13064     else if (element == EL_FLAMES)
13065       CheckForDragon(x, y);
13066     else if (element == EL_EXPLOSION)
13067       ; /* drawing of correct explosion animation is handled separately */
13068     else if (element == EL_ELEMENT_SNAPPING ||
13069              element == EL_DIAGONAL_SHRINKING ||
13070              element == EL_DIAGONAL_GROWING)
13071     {
13072       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13073
13074       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13075     }
13076     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13077       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13078
13079 #endif  // ---------------------------------------------------------------------
13080
13081     if (IS_BELT_ACTIVE(element))
13082       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13083
13084     if (game.magic_wall_active)
13085     {
13086       int jx = local_player->jx, jy = local_player->jy;
13087
13088       /* play the element sound at the position nearest to the player */
13089       if ((element == EL_MAGIC_WALL_FULL ||
13090            element == EL_MAGIC_WALL_ACTIVE ||
13091            element == EL_MAGIC_WALL_EMPTYING ||
13092            element == EL_BD_MAGIC_WALL_FULL ||
13093            element == EL_BD_MAGIC_WALL_ACTIVE ||
13094            element == EL_BD_MAGIC_WALL_EMPTYING ||
13095            element == EL_DC_MAGIC_WALL_FULL ||
13096            element == EL_DC_MAGIC_WALL_ACTIVE ||
13097            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13098           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13099       {
13100         magic_wall_x = x;
13101         magic_wall_y = y;
13102       }
13103     }
13104   }
13105
13106 #if 0
13107   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13108 #endif
13109
13110 #if USE_NEW_AMOEBA_CODE
13111   /* new experimental amoeba growth stuff */
13112   if (!(FrameCounter % 8))
13113   {
13114     static unsigned int random = 1684108901;
13115
13116     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13117     {
13118       x = RND(lev_fieldx);
13119       y = RND(lev_fieldy);
13120       element = Feld[x][y];
13121
13122       if (!IS_PLAYER(x,y) &&
13123           (element == EL_EMPTY ||
13124            CAN_GROW_INTO(element) ||
13125            element == EL_QUICKSAND_EMPTY ||
13126            element == EL_QUICKSAND_FAST_EMPTY ||
13127            element == EL_ACID_SPLASH_LEFT ||
13128            element == EL_ACID_SPLASH_RIGHT))
13129       {
13130         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13131             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13132             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13133             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13134           Feld[x][y] = EL_AMOEBA_DROP;
13135       }
13136
13137       random = random * 129 + 1;
13138     }
13139   }
13140 #endif
13141
13142 #if 0
13143   if (game.explosions_delayed)
13144 #endif
13145   {
13146     game.explosions_delayed = FALSE;
13147
13148     SCAN_PLAYFIELD(x, y)
13149     {
13150       element = Feld[x][y];
13151
13152       if (ExplodeField[x][y])
13153         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13154       else if (element == EL_EXPLOSION)
13155         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13156
13157       ExplodeField[x][y] = EX_TYPE_NONE;
13158     }
13159
13160     game.explosions_delayed = TRUE;
13161   }
13162
13163   if (game.magic_wall_active)
13164   {
13165     if (!(game.magic_wall_time_left % 4))
13166     {
13167       int element = Feld[magic_wall_x][magic_wall_y];
13168
13169       if (element == EL_BD_MAGIC_WALL_FULL ||
13170           element == EL_BD_MAGIC_WALL_ACTIVE ||
13171           element == EL_BD_MAGIC_WALL_EMPTYING)
13172         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13173       else if (element == EL_DC_MAGIC_WALL_FULL ||
13174                element == EL_DC_MAGIC_WALL_ACTIVE ||
13175                element == EL_DC_MAGIC_WALL_EMPTYING)
13176         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13177       else
13178         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13179     }
13180
13181     if (game.magic_wall_time_left > 0)
13182     {
13183       game.magic_wall_time_left--;
13184
13185       if (!game.magic_wall_time_left)
13186       {
13187         SCAN_PLAYFIELD(x, y)
13188         {
13189           element = Feld[x][y];
13190
13191           if (element == EL_MAGIC_WALL_ACTIVE ||
13192               element == EL_MAGIC_WALL_FULL)
13193           {
13194             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13195             TEST_DrawLevelField(x, y);
13196           }
13197           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13198                    element == EL_BD_MAGIC_WALL_FULL)
13199           {
13200             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13201             TEST_DrawLevelField(x, y);
13202           }
13203           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13204                    element == EL_DC_MAGIC_WALL_FULL)
13205           {
13206             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13207             TEST_DrawLevelField(x, y);
13208           }
13209         }
13210
13211         game.magic_wall_active = FALSE;
13212       }
13213     }
13214   }
13215
13216   if (game.light_time_left > 0)
13217   {
13218     game.light_time_left--;
13219
13220     if (game.light_time_left == 0)
13221       RedrawAllLightSwitchesAndInvisibleElements();
13222   }
13223
13224   if (game.timegate_time_left > 0)
13225   {
13226     game.timegate_time_left--;
13227
13228     if (game.timegate_time_left == 0)
13229       CloseAllOpenTimegates();
13230   }
13231
13232   if (game.lenses_time_left > 0)
13233   {
13234     game.lenses_time_left--;
13235
13236     if (game.lenses_time_left == 0)
13237       RedrawAllInvisibleElementsForLenses();
13238   }
13239
13240   if (game.magnify_time_left > 0)
13241   {
13242     game.magnify_time_left--;
13243
13244     if (game.magnify_time_left == 0)
13245       RedrawAllInvisibleElementsForMagnifier();
13246   }
13247
13248   for (i = 0; i < MAX_PLAYERS; i++)
13249   {
13250     struct PlayerInfo *player = &stored_player[i];
13251
13252     if (SHIELD_ON(player))
13253     {
13254       if (player->shield_deadly_time_left)
13255         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13256       else if (player->shield_normal_time_left)
13257         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13258     }
13259   }
13260
13261 #if USE_DELAYED_GFX_REDRAW
13262   SCAN_PLAYFIELD(x, y)
13263   {
13264 #if 1
13265     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13266 #else
13267     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13268         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13269 #endif
13270     {
13271       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13272          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13273
13274       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13275         DrawLevelField(x, y);
13276
13277       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13278         DrawLevelFieldCrumbled(x, y);
13279
13280       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13281         DrawLevelFieldCrumbledNeighbours(x, y);
13282
13283       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13284         DrawTwinkleOnField(x, y);
13285     }
13286
13287     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13288   }
13289 #endif
13290
13291   CheckLevelTime();
13292
13293   DrawAllPlayers();
13294   PlayAllPlayersSound();
13295
13296   if (options.debug)                    /* calculate frames per second */
13297   {
13298     static unsigned int fps_counter = 0;
13299     static int fps_frames = 0;
13300     unsigned int fps_delay_ms = Counter() - fps_counter;
13301
13302     fps_frames++;
13303
13304     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13305     {
13306       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13307
13308       fps_frames = 0;
13309       fps_counter = Counter();
13310     }
13311
13312     redraw_mask |= REDRAW_FPS;
13313   }
13314
13315   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13316
13317   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13318   {
13319     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13320
13321     local_player->show_envelope = 0;
13322   }
13323
13324 #if 0
13325   debug_print_timestamp(0, "stop main loop profiling ");
13326   printf("----------------------------------------------------------\n");
13327 #endif
13328
13329   /* use random number generator in every frame to make it less predictable */
13330   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13331     RND(1);
13332 }
13333
13334 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13335 {
13336   int min_x = x, min_y = y, max_x = x, max_y = y;
13337   int i;
13338
13339   for (i = 0; i < MAX_PLAYERS; i++)
13340   {
13341     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13342
13343     if (!stored_player[i].active || &stored_player[i] == player)
13344       continue;
13345
13346     min_x = MIN(min_x, jx);
13347     min_y = MIN(min_y, jy);
13348     max_x = MAX(max_x, jx);
13349     max_y = MAX(max_y, jy);
13350   }
13351
13352   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13353 }
13354
13355 static boolean AllPlayersInVisibleScreen()
13356 {
13357   int i;
13358
13359   for (i = 0; i < MAX_PLAYERS; i++)
13360   {
13361     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13362
13363     if (!stored_player[i].active)
13364       continue;
13365
13366     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13367       return FALSE;
13368   }
13369
13370   return TRUE;
13371 }
13372
13373 void ScrollLevel(int dx, int dy)
13374 {
13375 #if 0
13376   /* (directly solved in BlitBitmap() now) */
13377   static Bitmap *bitmap_db_field2 = NULL;
13378   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13379   int x, y;
13380 #else
13381   int x, y;
13382 #endif
13383
13384 #if 0
13385   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13386   /* only horizontal XOR vertical scroll direction allowed */
13387   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13388     return;
13389 #endif
13390
13391 #if 0
13392   /* (directly solved in BlitBitmap() now) */
13393   if (bitmap_db_field2 == NULL)
13394     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13395
13396   /* needed when blitting directly to same bitmap -- should not be needed with
13397      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13398   BlitBitmap(drawto_field, bitmap_db_field2,
13399              FX + TILEX * (dx == -1) - softscroll_offset,
13400              FY + TILEY * (dy == -1) - softscroll_offset,
13401              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13402              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13403              FX + TILEX * (dx == 1) - softscroll_offset,
13404              FY + TILEY * (dy == 1) - softscroll_offset);
13405   BlitBitmap(bitmap_db_field2, drawto_field,
13406              FX + TILEX * (dx == 1) - softscroll_offset,
13407              FY + TILEY * (dy == 1) - softscroll_offset,
13408              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13409              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13410              FX + TILEX * (dx == 1) - softscroll_offset,
13411              FY + TILEY * (dy == 1) - softscroll_offset);
13412
13413 #else
13414
13415 #if 0
13416   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13417   int xsize = (BX2 - BX1 + 1);
13418   int ysize = (BY2 - BY1 + 1);
13419   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13420   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13421   int step  = (start < end ? +1 : -1);
13422
13423   for (i = start; i != end; i += step)
13424   {
13425     BlitBitmap(drawto_field, drawto_field,
13426                FX + TILEX * (dx != 0 ? i + step : 0),
13427                FY + TILEY * (dy != 0 ? i + step : 0),
13428                TILEX * (dx != 0 ? 1 : xsize),
13429                TILEY * (dy != 0 ? 1 : ysize),
13430                FX + TILEX * (dx != 0 ? i : 0),
13431                FY + TILEY * (dy != 0 ? i : 0));
13432   }
13433
13434 #else
13435
13436 #if NEW_TILESIZE
13437 #if NEW_SCROLL
13438   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13439 #else
13440   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13441 #endif
13442 #else
13443 #if NEW_SCROLL
13444   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13445 #else
13446   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13447 #endif
13448 #endif
13449
13450 #if NEW_TILESIZE
13451   BlitBitmap(drawto_field, drawto_field,
13452              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13453              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13454              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13455              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13456              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13457              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13458 #else
13459   BlitBitmap(drawto_field, drawto_field,
13460              FX + TILEX * (dx == -1) - softscroll_offset,
13461              FY + TILEY * (dy == -1) - softscroll_offset,
13462              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13463              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13464              FX + TILEX * (dx == 1) - softscroll_offset,
13465              FY + TILEY * (dy == 1) - softscroll_offset);
13466 #endif
13467
13468 #endif
13469 #endif
13470
13471   if (dx != 0)
13472   {
13473     x = (dx == 1 ? BX1 : BX2);
13474     for (y = BY1; y <= BY2; y++)
13475       DrawScreenField(x, y);
13476   }
13477
13478   if (dy != 0)
13479   {
13480     y = (dy == 1 ? BY1 : BY2);
13481     for (x = BX1; x <= BX2; x++)
13482       DrawScreenField(x, y);
13483   }
13484
13485   redraw_mask |= REDRAW_FIELD;
13486 }
13487
13488 static boolean canFallDown(struct PlayerInfo *player)
13489 {
13490   int jx = player->jx, jy = player->jy;
13491
13492   return (IN_LEV_FIELD(jx, jy + 1) &&
13493           (IS_FREE(jx, jy + 1) ||
13494            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13495           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13496           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13497 }
13498
13499 static boolean canPassField(int x, int y, int move_dir)
13500 {
13501   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13502   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13503   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13504   int nextx = x + dx;
13505   int nexty = y + dy;
13506   int element = Feld[x][y];
13507
13508   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13509           !CAN_MOVE(element) &&
13510           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13511           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13512           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13513 }
13514
13515 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13516 {
13517   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13518   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13519   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13520   int newx = x + dx;
13521   int newy = y + dy;
13522
13523   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13524           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13525           (IS_DIGGABLE(Feld[newx][newy]) ||
13526            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13527            canPassField(newx, newy, move_dir)));
13528 }
13529
13530 static void CheckGravityMovement(struct PlayerInfo *player)
13531 {
13532 #if USE_PLAYER_GRAVITY
13533   if (player->gravity && !player->programmed_action)
13534 #else
13535   if (game.gravity && !player->programmed_action)
13536 #endif
13537   {
13538     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13539     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13540     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13541     int jx = player->jx, jy = player->jy;
13542     boolean player_is_moving_to_valid_field =
13543       (!player_is_snapping &&
13544        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13545         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13546     boolean player_can_fall_down = canFallDown(player);
13547
13548     if (player_can_fall_down &&
13549         !player_is_moving_to_valid_field)
13550       player->programmed_action = MV_DOWN;
13551   }
13552 }
13553
13554 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13555 {
13556   return CheckGravityMovement(player);
13557
13558 #if USE_PLAYER_GRAVITY
13559   if (player->gravity && !player->programmed_action)
13560 #else
13561   if (game.gravity && !player->programmed_action)
13562 #endif
13563   {
13564     int jx = player->jx, jy = player->jy;
13565     boolean field_under_player_is_free =
13566       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13567     boolean player_is_standing_on_valid_field =
13568       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13569        (IS_WALKABLE(Feld[jx][jy]) &&
13570         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13571
13572     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13573       player->programmed_action = MV_DOWN;
13574   }
13575 }
13576
13577 /*
13578   MovePlayerOneStep()
13579   -----------------------------------------------------------------------------
13580   dx, dy:               direction (non-diagonal) to try to move the player to
13581   real_dx, real_dy:     direction as read from input device (can be diagonal)
13582 */
13583
13584 boolean MovePlayerOneStep(struct PlayerInfo *player,
13585                           int dx, int dy, int real_dx, int real_dy)
13586 {
13587   int jx = player->jx, jy = player->jy;
13588   int new_jx = jx + dx, new_jy = jy + dy;
13589 #if !USE_FIXED_DONT_RUN_INTO
13590   int element;
13591 #endif
13592   int can_move;
13593   boolean player_can_move = !player->cannot_move;
13594
13595   if (!player->active || (!dx && !dy))
13596     return MP_NO_ACTION;
13597
13598   player->MovDir = (dx < 0 ? MV_LEFT :
13599                     dx > 0 ? MV_RIGHT :
13600                     dy < 0 ? MV_UP :
13601                     dy > 0 ? MV_DOWN :  MV_NONE);
13602
13603   if (!IN_LEV_FIELD(new_jx, new_jy))
13604     return MP_NO_ACTION;
13605
13606   if (!player_can_move)
13607   {
13608     if (player->MovPos == 0)
13609     {
13610       player->is_moving = FALSE;
13611       player->is_digging = FALSE;
13612       player->is_collecting = FALSE;
13613       player->is_snapping = FALSE;
13614       player->is_pushing = FALSE;
13615     }
13616   }
13617
13618 #if 1
13619   if (!options.network && game.centered_player_nr == -1 &&
13620       !AllPlayersInSight(player, new_jx, new_jy))
13621     return MP_NO_ACTION;
13622 #else
13623   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13624     return MP_NO_ACTION;
13625 #endif
13626
13627 #if !USE_FIXED_DONT_RUN_INTO
13628   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13629
13630   /* (moved to DigField()) */
13631   if (player_can_move && DONT_RUN_INTO(element))
13632   {
13633     if (element == EL_ACID && dx == 0 && dy == 1)
13634     {
13635       SplashAcid(new_jx, new_jy);
13636       Feld[jx][jy] = EL_PLAYER_1;
13637       InitMovingField(jx, jy, MV_DOWN);
13638       Store[jx][jy] = EL_ACID;
13639       ContinueMoving(jx, jy);
13640       BuryPlayer(player);
13641     }
13642     else
13643       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13644
13645     return MP_MOVING;
13646   }
13647 #endif
13648
13649   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13650   if (can_move != MP_MOVING)
13651     return can_move;
13652
13653   /* check if DigField() has caused relocation of the player */
13654   if (player->jx != jx || player->jy != jy)
13655     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13656
13657   StorePlayer[jx][jy] = 0;
13658   player->last_jx = jx;
13659   player->last_jy = jy;
13660   player->jx = new_jx;
13661   player->jy = new_jy;
13662   StorePlayer[new_jx][new_jy] = player->element_nr;
13663
13664   if (player->move_delay_value_next != -1)
13665   {
13666     player->move_delay_value = player->move_delay_value_next;
13667     player->move_delay_value_next = -1;
13668   }
13669
13670   player->MovPos =
13671     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13672
13673   player->step_counter++;
13674
13675   PlayerVisit[jx][jy] = FrameCounter;
13676
13677 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13678   player->is_moving = TRUE;
13679 #endif
13680
13681 #if 1
13682   /* should better be called in MovePlayer(), but this breaks some tapes */
13683   ScrollPlayer(player, SCROLL_INIT);
13684 #endif
13685
13686   return MP_MOVING;
13687 }
13688
13689 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13690 {
13691   int jx = player->jx, jy = player->jy;
13692   int old_jx = jx, old_jy = jy;
13693   int moved = MP_NO_ACTION;
13694
13695   if (!player->active)
13696     return FALSE;
13697
13698   if (!dx && !dy)
13699   {
13700     if (player->MovPos == 0)
13701     {
13702       player->is_moving = FALSE;
13703       player->is_digging = FALSE;
13704       player->is_collecting = FALSE;
13705       player->is_snapping = FALSE;
13706       player->is_pushing = FALSE;
13707     }
13708
13709     return FALSE;
13710   }
13711
13712   if (player->move_delay > 0)
13713     return FALSE;
13714
13715   player->move_delay = -1;              /* set to "uninitialized" value */
13716
13717   /* store if player is automatically moved to next field */
13718   player->is_auto_moving = (player->programmed_action != MV_NONE);
13719
13720   /* remove the last programmed player action */
13721   player->programmed_action = 0;
13722
13723   if (player->MovPos)
13724   {
13725     /* should only happen if pre-1.2 tape recordings are played */
13726     /* this is only for backward compatibility */
13727
13728     int original_move_delay_value = player->move_delay_value;
13729
13730 #if DEBUG
13731     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13732            tape.counter);
13733 #endif
13734
13735     /* scroll remaining steps with finest movement resolution */
13736     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13737
13738     while (player->MovPos)
13739     {
13740       ScrollPlayer(player, SCROLL_GO_ON);
13741       ScrollScreen(NULL, SCROLL_GO_ON);
13742
13743       AdvanceFrameAndPlayerCounters(player->index_nr);
13744
13745       DrawAllPlayers();
13746       BackToFront();
13747     }
13748
13749     player->move_delay_value = original_move_delay_value;
13750   }
13751
13752   player->is_active = FALSE;
13753
13754   if (player->last_move_dir & MV_HORIZONTAL)
13755   {
13756     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13757       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13758   }
13759   else
13760   {
13761     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13762       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13763   }
13764
13765 #if USE_FIXED_BORDER_RUNNING_GFX
13766   if (!moved && !player->is_active)
13767   {
13768     player->is_moving = FALSE;
13769     player->is_digging = FALSE;
13770     player->is_collecting = FALSE;
13771     player->is_snapping = FALSE;
13772     player->is_pushing = FALSE;
13773   }
13774 #endif
13775
13776   jx = player->jx;
13777   jy = player->jy;
13778
13779 #if 1
13780   if (moved & MP_MOVING && !ScreenMovPos &&
13781       (player->index_nr == game.centered_player_nr ||
13782        game.centered_player_nr == -1))
13783 #else
13784   if (moved & MP_MOVING && !ScreenMovPos &&
13785       (player == local_player || !options.network))
13786 #endif
13787   {
13788     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13789     int offset = game.scroll_delay_value;
13790
13791     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13792     {
13793       /* actual player has left the screen -- scroll in that direction */
13794       if (jx != old_jx)         /* player has moved horizontally */
13795         scroll_x += (jx - old_jx);
13796       else                      /* player has moved vertically */
13797         scroll_y += (jy - old_jy);
13798     }
13799     else
13800     {
13801       if (jx != old_jx)         /* player has moved horizontally */
13802       {
13803         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13804             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13805           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13806
13807         /* don't scroll over playfield boundaries */
13808         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13809           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13810
13811         /* don't scroll more than one field at a time */
13812         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13813
13814         /* don't scroll against the player's moving direction */
13815         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13816             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13817           scroll_x = old_scroll_x;
13818       }
13819       else                      /* player has moved vertically */
13820       {
13821         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13822             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13823           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13824
13825         /* don't scroll over playfield boundaries */
13826         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13827           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13828
13829         /* don't scroll more than one field at a time */
13830         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13831
13832         /* don't scroll against the player's moving direction */
13833         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13834             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13835           scroll_y = old_scroll_y;
13836       }
13837     }
13838
13839     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13840     {
13841 #if 1
13842       if (!options.network && game.centered_player_nr == -1 &&
13843           !AllPlayersInVisibleScreen())
13844       {
13845         scroll_x = old_scroll_x;
13846         scroll_y = old_scroll_y;
13847       }
13848       else
13849 #else
13850       if (!options.network && !AllPlayersInVisibleScreen())
13851       {
13852         scroll_x = old_scroll_x;
13853         scroll_y = old_scroll_y;
13854       }
13855       else
13856 #endif
13857       {
13858         ScrollScreen(player, SCROLL_INIT);
13859         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13860       }
13861     }
13862   }
13863
13864   player->StepFrame = 0;
13865
13866   if (moved & MP_MOVING)
13867   {
13868     if (old_jx != jx && old_jy == jy)
13869       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13870     else if (old_jx == jx && old_jy != jy)
13871       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13872
13873     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13874
13875     player->last_move_dir = player->MovDir;
13876     player->is_moving = TRUE;
13877     player->is_snapping = FALSE;
13878     player->is_switching = FALSE;
13879     player->is_dropping = FALSE;
13880     player->is_dropping_pressed = FALSE;
13881     player->drop_pressed_delay = 0;
13882
13883 #if 0
13884     /* should better be called here than above, but this breaks some tapes */
13885     ScrollPlayer(player, SCROLL_INIT);
13886 #endif
13887   }
13888   else
13889   {
13890     CheckGravityMovementWhenNotMoving(player);
13891
13892     player->is_moving = FALSE;
13893
13894     /* at this point, the player is allowed to move, but cannot move right now
13895        (e.g. because of something blocking the way) -- ensure that the player
13896        is also allowed to move in the next frame (in old versions before 3.1.1,
13897        the player was forced to wait again for eight frames before next try) */
13898
13899     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13900       player->move_delay = 0;   /* allow direct movement in the next frame */
13901   }
13902
13903   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13904     player->move_delay = player->move_delay_value;
13905
13906   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13907   {
13908     TestIfPlayerTouchesBadThing(jx, jy);
13909     TestIfPlayerTouchesCustomElement(jx, jy);
13910   }
13911
13912   if (!player->active)
13913     RemovePlayer(player);
13914
13915   return moved;
13916 }
13917
13918 void ScrollPlayer(struct PlayerInfo *player, int mode)
13919 {
13920   int jx = player->jx, jy = player->jy;
13921   int last_jx = player->last_jx, last_jy = player->last_jy;
13922   int move_stepsize = TILEX / player->move_delay_value;
13923
13924 #if USE_NEW_PLAYER_SPEED
13925   if (!player->active)
13926     return;
13927
13928   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13929     return;
13930 #else
13931   if (!player->active || player->MovPos == 0)
13932     return;
13933 #endif
13934
13935   if (mode == SCROLL_INIT)
13936   {
13937     player->actual_frame_counter = FrameCounter;
13938     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13939
13940     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13941         Feld[last_jx][last_jy] == EL_EMPTY)
13942     {
13943       int last_field_block_delay = 0;   /* start with no blocking at all */
13944       int block_delay_adjustment = player->block_delay_adjustment;
13945
13946       /* if player blocks last field, add delay for exactly one move */
13947       if (player->block_last_field)
13948       {
13949         last_field_block_delay += player->move_delay_value;
13950
13951         /* when blocking enabled, prevent moving up despite gravity */
13952 #if USE_PLAYER_GRAVITY
13953         if (player->gravity && player->MovDir == MV_UP)
13954           block_delay_adjustment = -1;
13955 #else
13956         if (game.gravity && player->MovDir == MV_UP)
13957           block_delay_adjustment = -1;
13958 #endif
13959       }
13960
13961       /* add block delay adjustment (also possible when not blocking) */
13962       last_field_block_delay += block_delay_adjustment;
13963
13964       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13965       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13966     }
13967
13968 #if USE_NEW_PLAYER_SPEED
13969     if (player->MovPos != 0)    /* player has not yet reached destination */
13970       return;
13971 #else
13972     return;
13973 #endif
13974   }
13975   else if (!FrameReached(&player->actual_frame_counter, 1))
13976     return;
13977
13978 #if USE_NEW_PLAYER_SPEED
13979   if (player->MovPos != 0)
13980   {
13981     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13982     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13983
13984     /* before DrawPlayer() to draw correct player graphic for this case */
13985     if (player->MovPos == 0)
13986       CheckGravityMovement(player);
13987   }
13988 #else
13989   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13990   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13991
13992   /* before DrawPlayer() to draw correct player graphic for this case */
13993   if (player->MovPos == 0)
13994     CheckGravityMovement(player);
13995 #endif
13996
13997   if (player->MovPos == 0)      /* player reached destination field */
13998   {
13999     if (player->move_delay_reset_counter > 0)
14000     {
14001       player->move_delay_reset_counter--;
14002
14003       if (player->move_delay_reset_counter == 0)
14004       {
14005         /* continue with normal speed after quickly moving through gate */
14006         HALVE_PLAYER_SPEED(player);
14007
14008         /* be able to make the next move without delay */
14009         player->move_delay = 0;
14010       }
14011     }
14012
14013     player->last_jx = jx;
14014     player->last_jy = jy;
14015
14016     if (Feld[jx][jy] == EL_EXIT_OPEN ||
14017         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14018 #if 1
14019         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14020 #endif
14021         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14022         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14023 #if 1
14024         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14025 #endif
14026         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14027         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
14028     {
14029       DrawPlayer(player);       /* needed here only to cleanup last field */
14030       RemovePlayer(player);
14031
14032       if (local_player->friends_still_needed == 0 ||
14033           IS_SP_ELEMENT(Feld[jx][jy]))
14034         PlayerWins(player);
14035     }
14036
14037     /* this breaks one level: "machine", level 000 */
14038     {
14039       int move_direction = player->MovDir;
14040       int enter_side = MV_DIR_OPPOSITE(move_direction);
14041       int leave_side = move_direction;
14042       int old_jx = last_jx;
14043       int old_jy = last_jy;
14044       int old_element = Feld[old_jx][old_jy];
14045       int new_element = Feld[jx][jy];
14046
14047       if (IS_CUSTOM_ELEMENT(old_element))
14048         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14049                                    CE_LEFT_BY_PLAYER,
14050                                    player->index_bit, leave_side);
14051
14052       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14053                                           CE_PLAYER_LEAVES_X,
14054                                           player->index_bit, leave_side);
14055
14056       if (IS_CUSTOM_ELEMENT(new_element))
14057         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14058                                    player->index_bit, enter_side);
14059
14060       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14061                                           CE_PLAYER_ENTERS_X,
14062                                           player->index_bit, enter_side);
14063
14064 #if USE_FIX_CE_ACTION_WITH_PLAYER
14065       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14066                                         CE_MOVE_OF_X, move_direction);
14067 #else
14068       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14069                                         CE_MOVE_OF_X, move_direction);
14070 #endif
14071     }
14072
14073     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14074     {
14075       TestIfPlayerTouchesBadThing(jx, jy);
14076       TestIfPlayerTouchesCustomElement(jx, jy);
14077
14078       /* needed because pushed element has not yet reached its destination,
14079          so it would trigger a change event at its previous field location */
14080       if (!player->is_pushing)
14081         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14082
14083       if (!player->active)
14084         RemovePlayer(player);
14085     }
14086
14087     if (!local_player->LevelSolved && level.use_step_counter)
14088     {
14089       int i;
14090
14091       TimePlayed++;
14092
14093       if (TimeLeft > 0)
14094       {
14095         TimeLeft--;
14096
14097         if (TimeLeft <= 10 && setup.time_limit)
14098           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14099
14100 #if 1
14101         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14102
14103         DisplayGameControlValues();
14104 #else
14105         DrawGameValue_Time(TimeLeft);
14106 #endif
14107
14108         if (!TimeLeft && setup.time_limit)
14109           for (i = 0; i < MAX_PLAYERS; i++)
14110             KillPlayer(&stored_player[i]);
14111       }
14112 #if 1
14113       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14114       {
14115         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14116
14117         DisplayGameControlValues();
14118       }
14119 #else
14120       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14121         DrawGameValue_Time(TimePlayed);
14122 #endif
14123     }
14124
14125     if (tape.single_step && tape.recording && !tape.pausing &&
14126         !player->programmed_action)
14127       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14128   }
14129 }
14130
14131 void ScrollScreen(struct PlayerInfo *player, int mode)
14132 {
14133   static unsigned int screen_frame_counter = 0;
14134
14135   if (mode == SCROLL_INIT)
14136   {
14137     /* set scrolling step size according to actual player's moving speed */
14138     ScrollStepSize = TILEX / player->move_delay_value;
14139
14140     screen_frame_counter = FrameCounter;
14141     ScreenMovDir = player->MovDir;
14142     ScreenMovPos = player->MovPos;
14143     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14144     return;
14145   }
14146   else if (!FrameReached(&screen_frame_counter, 1))
14147     return;
14148
14149   if (ScreenMovPos)
14150   {
14151     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14152     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14153     redraw_mask |= REDRAW_FIELD;
14154   }
14155   else
14156     ScreenMovDir = MV_NONE;
14157 }
14158
14159 void TestIfPlayerTouchesCustomElement(int x, int y)
14160 {
14161   static int xy[4][2] =
14162   {
14163     { 0, -1 },
14164     { -1, 0 },
14165     { +1, 0 },
14166     { 0, +1 }
14167   };
14168   static int trigger_sides[4][2] =
14169   {
14170     /* center side       border side */
14171     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14172     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14173     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14174     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14175   };
14176   static int touch_dir[4] =
14177   {
14178     MV_LEFT | MV_RIGHT,
14179     MV_UP   | MV_DOWN,
14180     MV_UP   | MV_DOWN,
14181     MV_LEFT | MV_RIGHT
14182   };
14183   int center_element = Feld[x][y];      /* should always be non-moving! */
14184   int i;
14185
14186   for (i = 0; i < NUM_DIRECTIONS; i++)
14187   {
14188     int xx = x + xy[i][0];
14189     int yy = y + xy[i][1];
14190     int center_side = trigger_sides[i][0];
14191     int border_side = trigger_sides[i][1];
14192     int border_element;
14193
14194     if (!IN_LEV_FIELD(xx, yy))
14195       continue;
14196
14197     if (IS_PLAYER(x, y))                /* player found at center element */
14198     {
14199       struct PlayerInfo *player = PLAYERINFO(x, y);
14200
14201       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14202         border_element = Feld[xx][yy];          /* may be moving! */
14203       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14204         border_element = Feld[xx][yy];
14205       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14206         border_element = MovingOrBlocked2Element(xx, yy);
14207       else
14208         continue;               /* center and border element do not touch */
14209
14210       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14211                                  player->index_bit, border_side);
14212       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14213                                           CE_PLAYER_TOUCHES_X,
14214                                           player->index_bit, border_side);
14215
14216 #if USE_FIX_CE_ACTION_WITH_PLAYER
14217       {
14218         /* use player element that is initially defined in the level playfield,
14219            not the player element that corresponds to the runtime player number
14220            (example: a level that contains EL_PLAYER_3 as the only player would
14221            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14222         int player_element = PLAYERINFO(x, y)->initial_element;
14223
14224         CheckElementChangeBySide(xx, yy, border_element, player_element,
14225                                  CE_TOUCHING_X, border_side);
14226       }
14227 #endif
14228     }
14229     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14230     {
14231       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14232
14233       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14234       {
14235         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14236           continue;             /* center and border element do not touch */
14237       }
14238
14239       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14240                                  player->index_bit, center_side);
14241       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14242                                           CE_PLAYER_TOUCHES_X,
14243                                           player->index_bit, center_side);
14244
14245 #if USE_FIX_CE_ACTION_WITH_PLAYER
14246       {
14247         /* use player element that is initially defined in the level playfield,
14248            not the player element that corresponds to the runtime player number
14249            (example: a level that contains EL_PLAYER_3 as the only player would
14250            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14251         int player_element = PLAYERINFO(xx, yy)->initial_element;
14252
14253         CheckElementChangeBySide(x, y, center_element, player_element,
14254                                  CE_TOUCHING_X, center_side);
14255       }
14256 #endif
14257
14258       break;
14259     }
14260   }
14261 }
14262
14263 #if USE_ELEMENT_TOUCHING_BUGFIX
14264
14265 void TestIfElementTouchesCustomElement(int x, int y)
14266 {
14267   static int xy[4][2] =
14268   {
14269     { 0, -1 },
14270     { -1, 0 },
14271     { +1, 0 },
14272     { 0, +1 }
14273   };
14274   static int trigger_sides[4][2] =
14275   {
14276     /* center side      border side */
14277     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14278     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14279     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14280     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14281   };
14282   static int touch_dir[4] =
14283   {
14284     MV_LEFT | MV_RIGHT,
14285     MV_UP   | MV_DOWN,
14286     MV_UP   | MV_DOWN,
14287     MV_LEFT | MV_RIGHT
14288   };
14289   boolean change_center_element = FALSE;
14290   int center_element = Feld[x][y];      /* should always be non-moving! */
14291   int border_element_old[NUM_DIRECTIONS];
14292   int i;
14293
14294   for (i = 0; i < NUM_DIRECTIONS; i++)
14295   {
14296     int xx = x + xy[i][0];
14297     int yy = y + xy[i][1];
14298     int border_element;
14299
14300     border_element_old[i] = -1;
14301
14302     if (!IN_LEV_FIELD(xx, yy))
14303       continue;
14304
14305     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14306       border_element = Feld[xx][yy];    /* may be moving! */
14307     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14308       border_element = Feld[xx][yy];
14309     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14310       border_element = MovingOrBlocked2Element(xx, yy);
14311     else
14312       continue;                 /* center and border element do not touch */
14313
14314     border_element_old[i] = border_element;
14315   }
14316
14317   for (i = 0; i < NUM_DIRECTIONS; i++)
14318   {
14319     int xx = x + xy[i][0];
14320     int yy = y + xy[i][1];
14321     int center_side = trigger_sides[i][0];
14322     int border_element = border_element_old[i];
14323
14324     if (border_element == -1)
14325       continue;
14326
14327     /* check for change of border element */
14328     CheckElementChangeBySide(xx, yy, border_element, center_element,
14329                              CE_TOUCHING_X, center_side);
14330
14331     /* (center element cannot be player, so we dont have to check this here) */
14332   }
14333
14334   for (i = 0; i < NUM_DIRECTIONS; i++)
14335   {
14336     int xx = x + xy[i][0];
14337     int yy = y + xy[i][1];
14338     int border_side = trigger_sides[i][1];
14339     int border_element = border_element_old[i];
14340
14341     if (border_element == -1)
14342       continue;
14343
14344     /* check for change of center element (but change it only once) */
14345     if (!change_center_element)
14346       change_center_element =
14347         CheckElementChangeBySide(x, y, center_element, border_element,
14348                                  CE_TOUCHING_X, border_side);
14349
14350 #if USE_FIX_CE_ACTION_WITH_PLAYER
14351     if (IS_PLAYER(xx, yy))
14352     {
14353       /* use player element that is initially defined in the level playfield,
14354          not the player element that corresponds to the runtime player number
14355          (example: a level that contains EL_PLAYER_3 as the only player would
14356          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14357       int player_element = PLAYERINFO(xx, yy)->initial_element;
14358
14359       CheckElementChangeBySide(x, y, center_element, player_element,
14360                                CE_TOUCHING_X, border_side);
14361     }
14362 #endif
14363   }
14364 }
14365
14366 #else
14367
14368 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14369 {
14370   static int xy[4][2] =
14371   {
14372     { 0, -1 },
14373     { -1, 0 },
14374     { +1, 0 },
14375     { 0, +1 }
14376   };
14377   static int trigger_sides[4][2] =
14378   {
14379     /* center side      border side */
14380     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14381     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14382     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14383     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14384   };
14385   static int touch_dir[4] =
14386   {
14387     MV_LEFT | MV_RIGHT,
14388     MV_UP   | MV_DOWN,
14389     MV_UP   | MV_DOWN,
14390     MV_LEFT | MV_RIGHT
14391   };
14392   boolean change_center_element = FALSE;
14393   int center_element = Feld[x][y];      /* should always be non-moving! */
14394   int i;
14395
14396   for (i = 0; i < NUM_DIRECTIONS; i++)
14397   {
14398     int xx = x + xy[i][0];
14399     int yy = y + xy[i][1];
14400     int center_side = trigger_sides[i][0];
14401     int border_side = trigger_sides[i][1];
14402     int border_element;
14403
14404     if (!IN_LEV_FIELD(xx, yy))
14405       continue;
14406
14407     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14408       border_element = Feld[xx][yy];    /* may be moving! */
14409     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14410       border_element = Feld[xx][yy];
14411     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14412       border_element = MovingOrBlocked2Element(xx, yy);
14413     else
14414       continue;                 /* center and border element do not touch */
14415
14416     /* check for change of center element (but change it only once) */
14417     if (!change_center_element)
14418       change_center_element =
14419         CheckElementChangeBySide(x, y, center_element, border_element,
14420                                  CE_TOUCHING_X, border_side);
14421
14422     /* check for change of border element */
14423     CheckElementChangeBySide(xx, yy, border_element, center_element,
14424                              CE_TOUCHING_X, center_side);
14425   }
14426 }
14427
14428 #endif
14429
14430 void TestIfElementHitsCustomElement(int x, int y, int direction)
14431 {
14432   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14433   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14434   int hitx = x + dx, hity = y + dy;
14435   int hitting_element = Feld[x][y];
14436   int touched_element;
14437
14438   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14439     return;
14440
14441   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14442                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14443
14444   if (IN_LEV_FIELD(hitx, hity))
14445   {
14446     int opposite_direction = MV_DIR_OPPOSITE(direction);
14447     int hitting_side = direction;
14448     int touched_side = opposite_direction;
14449     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14450                           MovDir[hitx][hity] != direction ||
14451                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14452
14453     object_hit = TRUE;
14454
14455     if (object_hit)
14456     {
14457       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14458                                CE_HITTING_X, touched_side);
14459
14460       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14461                                CE_HIT_BY_X, hitting_side);
14462
14463       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14464                                CE_HIT_BY_SOMETHING, opposite_direction);
14465
14466 #if USE_FIX_CE_ACTION_WITH_PLAYER
14467       if (IS_PLAYER(hitx, hity))
14468       {
14469         /* use player element that is initially defined in the level playfield,
14470            not the player element that corresponds to the runtime player number
14471            (example: a level that contains EL_PLAYER_3 as the only player would
14472            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14473         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14474
14475         CheckElementChangeBySide(x, y, hitting_element, player_element,
14476                                  CE_HITTING_X, touched_side);
14477       }
14478 #endif
14479     }
14480   }
14481
14482   /* "hitting something" is also true when hitting the playfield border */
14483   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14484                            CE_HITTING_SOMETHING, direction);
14485 }
14486
14487 #if 0
14488 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14489 {
14490   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14491   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14492   int hitx = x + dx, hity = y + dy;
14493   int hitting_element = Feld[x][y];
14494   int touched_element;
14495 #if 0
14496   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14497                         !IS_FREE(hitx, hity) &&
14498                         (!IS_MOVING(hitx, hity) ||
14499                          MovDir[hitx][hity] != direction ||
14500                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14501 #endif
14502
14503   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14504     return;
14505
14506 #if 0
14507   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14508     return;
14509 #endif
14510
14511   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14512                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14513
14514   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14515                            EP_CAN_SMASH_EVERYTHING, direction);
14516
14517   if (IN_LEV_FIELD(hitx, hity))
14518   {
14519     int opposite_direction = MV_DIR_OPPOSITE(direction);
14520     int hitting_side = direction;
14521     int touched_side = opposite_direction;
14522 #if 0
14523     int touched_element = MovingOrBlocked2Element(hitx, hity);
14524 #endif
14525 #if 1
14526     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14527                           MovDir[hitx][hity] != direction ||
14528                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14529
14530     object_hit = TRUE;
14531 #endif
14532
14533     if (object_hit)
14534     {
14535       int i;
14536
14537       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14538                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14539
14540       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14541                                CE_OTHER_IS_SMASHING, touched_side);
14542
14543       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14544                                CE_OTHER_GETS_SMASHED, hitting_side);
14545     }
14546   }
14547 }
14548 #endif
14549
14550 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14551 {
14552   int i, kill_x = -1, kill_y = -1;
14553
14554   int bad_element = -1;
14555   static int test_xy[4][2] =
14556   {
14557     { 0, -1 },
14558     { -1, 0 },
14559     { +1, 0 },
14560     { 0, +1 }
14561   };
14562   static int test_dir[4] =
14563   {
14564     MV_UP,
14565     MV_LEFT,
14566     MV_RIGHT,
14567     MV_DOWN
14568   };
14569
14570   for (i = 0; i < NUM_DIRECTIONS; i++)
14571   {
14572     int test_x, test_y, test_move_dir, test_element;
14573
14574     test_x = good_x + test_xy[i][0];
14575     test_y = good_y + test_xy[i][1];
14576
14577     if (!IN_LEV_FIELD(test_x, test_y))
14578       continue;
14579
14580     test_move_dir =
14581       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14582
14583     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14584
14585     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14586        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14587     */
14588     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14589         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14590     {
14591       kill_x = test_x;
14592       kill_y = test_y;
14593       bad_element = test_element;
14594
14595       break;
14596     }
14597   }
14598
14599   if (kill_x != -1 || kill_y != -1)
14600   {
14601     if (IS_PLAYER(good_x, good_y))
14602     {
14603       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14604
14605       if (player->shield_deadly_time_left > 0 &&
14606           !IS_INDESTRUCTIBLE(bad_element))
14607         Bang(kill_x, kill_y);
14608       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14609         KillPlayer(player);
14610     }
14611     else
14612       Bang(good_x, good_y);
14613   }
14614 }
14615
14616 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14617 {
14618   int i, kill_x = -1, kill_y = -1;
14619   int bad_element = Feld[bad_x][bad_y];
14620   static int test_xy[4][2] =
14621   {
14622     { 0, -1 },
14623     { -1, 0 },
14624     { +1, 0 },
14625     { 0, +1 }
14626   };
14627   static int touch_dir[4] =
14628   {
14629     MV_LEFT | MV_RIGHT,
14630     MV_UP   | MV_DOWN,
14631     MV_UP   | MV_DOWN,
14632     MV_LEFT | MV_RIGHT
14633   };
14634   static int test_dir[4] =
14635   {
14636     MV_UP,
14637     MV_LEFT,
14638     MV_RIGHT,
14639     MV_DOWN
14640   };
14641
14642   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14643     return;
14644
14645   for (i = 0; i < NUM_DIRECTIONS; i++)
14646   {
14647     int test_x, test_y, test_move_dir, test_element;
14648
14649     test_x = bad_x + test_xy[i][0];
14650     test_y = bad_y + test_xy[i][1];
14651
14652     if (!IN_LEV_FIELD(test_x, test_y))
14653       continue;
14654
14655     test_move_dir =
14656       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14657
14658     test_element = Feld[test_x][test_y];
14659
14660     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14661        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14662     */
14663     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14664         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14665     {
14666       /* good thing is player or penguin that does not move away */
14667       if (IS_PLAYER(test_x, test_y))
14668       {
14669         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14670
14671         if (bad_element == EL_ROBOT && player->is_moving)
14672           continue;     /* robot does not kill player if he is moving */
14673
14674         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14675         {
14676           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14677             continue;           /* center and border element do not touch */
14678         }
14679
14680         kill_x = test_x;
14681         kill_y = test_y;
14682
14683         break;
14684       }
14685       else if (test_element == EL_PENGUIN)
14686       {
14687         kill_x = test_x;
14688         kill_y = test_y;
14689
14690         break;
14691       }
14692     }
14693   }
14694
14695   if (kill_x != -1 || kill_y != -1)
14696   {
14697     if (IS_PLAYER(kill_x, kill_y))
14698     {
14699       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14700
14701       if (player->shield_deadly_time_left > 0 &&
14702           !IS_INDESTRUCTIBLE(bad_element))
14703         Bang(bad_x, bad_y);
14704       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14705         KillPlayer(player);
14706     }
14707     else
14708       Bang(kill_x, kill_y);
14709   }
14710 }
14711
14712 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14713 {
14714   int bad_element = Feld[bad_x][bad_y];
14715   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14716   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14717   int test_x = bad_x + dx, test_y = bad_y + dy;
14718   int test_move_dir, test_element;
14719   int kill_x = -1, kill_y = -1;
14720
14721   if (!IN_LEV_FIELD(test_x, test_y))
14722     return;
14723
14724   test_move_dir =
14725     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14726
14727   test_element = Feld[test_x][test_y];
14728
14729   if (test_move_dir != bad_move_dir)
14730   {
14731     /* good thing can be player or penguin that does not move away */
14732     if (IS_PLAYER(test_x, test_y))
14733     {
14734       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14735
14736       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14737          player as being hit when he is moving towards the bad thing, because
14738          the "get hit by" condition would be lost after the player stops) */
14739       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14740         return;         /* player moves away from bad thing */
14741
14742       kill_x = test_x;
14743       kill_y = test_y;
14744     }
14745     else if (test_element == EL_PENGUIN)
14746     {
14747       kill_x = test_x;
14748       kill_y = test_y;
14749     }
14750   }
14751
14752   if (kill_x != -1 || kill_y != -1)
14753   {
14754     if (IS_PLAYER(kill_x, kill_y))
14755     {
14756       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14757
14758       if (player->shield_deadly_time_left > 0 &&
14759           !IS_INDESTRUCTIBLE(bad_element))
14760         Bang(bad_x, bad_y);
14761       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14762         KillPlayer(player);
14763     }
14764     else
14765       Bang(kill_x, kill_y);
14766   }
14767 }
14768
14769 void TestIfPlayerTouchesBadThing(int x, int y)
14770 {
14771   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14772 }
14773
14774 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14775 {
14776   TestIfGoodThingHitsBadThing(x, y, move_dir);
14777 }
14778
14779 void TestIfBadThingTouchesPlayer(int x, int y)
14780 {
14781   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14782 }
14783
14784 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14785 {
14786   TestIfBadThingHitsGoodThing(x, y, move_dir);
14787 }
14788
14789 void TestIfFriendTouchesBadThing(int x, int y)
14790 {
14791   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14792 }
14793
14794 void TestIfBadThingTouchesFriend(int x, int y)
14795 {
14796   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14797 }
14798
14799 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14800 {
14801   int i, kill_x = bad_x, kill_y = bad_y;
14802   static int xy[4][2] =
14803   {
14804     { 0, -1 },
14805     { -1, 0 },
14806     { +1, 0 },
14807     { 0, +1 }
14808   };
14809
14810   for (i = 0; i < NUM_DIRECTIONS; i++)
14811   {
14812     int x, y, element;
14813
14814     x = bad_x + xy[i][0];
14815     y = bad_y + xy[i][1];
14816     if (!IN_LEV_FIELD(x, y))
14817       continue;
14818
14819     element = Feld[x][y];
14820     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14821         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14822     {
14823       kill_x = x;
14824       kill_y = y;
14825       break;
14826     }
14827   }
14828
14829   if (kill_x != bad_x || kill_y != bad_y)
14830     Bang(bad_x, bad_y);
14831 }
14832
14833 void KillPlayer(struct PlayerInfo *player)
14834 {
14835   int jx = player->jx, jy = player->jy;
14836
14837   if (!player->active)
14838     return;
14839
14840 #if 0
14841   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14842          player->killed, player->active, player->reanimated);
14843 #endif
14844
14845   /* the following code was introduced to prevent an infinite loop when calling
14846      -> Bang()
14847      -> CheckTriggeredElementChangeExt()
14848      -> ExecuteCustomElementAction()
14849      -> KillPlayer()
14850      -> (infinitely repeating the above sequence of function calls)
14851      which occurs when killing the player while having a CE with the setting
14852      "kill player X when explosion of <player X>"; the solution using a new
14853      field "player->killed" was chosen for backwards compatibility, although
14854      clever use of the fields "player->active" etc. would probably also work */
14855 #if 1
14856   if (player->killed)
14857     return;
14858 #endif
14859
14860   player->killed = TRUE;
14861
14862   /* remove accessible field at the player's position */
14863   Feld[jx][jy] = EL_EMPTY;
14864
14865   /* deactivate shield (else Bang()/Explode() would not work right) */
14866   player->shield_normal_time_left = 0;
14867   player->shield_deadly_time_left = 0;
14868
14869 #if 0
14870   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14871          player->killed, player->active, player->reanimated);
14872 #endif
14873
14874   Bang(jx, jy);
14875
14876 #if 0
14877   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14878          player->killed, player->active, player->reanimated);
14879 #endif
14880
14881 #if USE_PLAYER_REANIMATION
14882 #if 1
14883   if (player->reanimated)       /* killed player may have been reanimated */
14884     player->killed = player->reanimated = FALSE;
14885   else
14886     BuryPlayer(player);
14887 #else
14888   if (player->killed)           /* player may have been reanimated */
14889     BuryPlayer(player);
14890 #endif
14891 #else
14892   BuryPlayer(player);
14893 #endif
14894 }
14895
14896 static void KillPlayerUnlessEnemyProtected(int x, int y)
14897 {
14898   if (!PLAYER_ENEMY_PROTECTED(x, y))
14899     KillPlayer(PLAYERINFO(x, y));
14900 }
14901
14902 static void KillPlayerUnlessExplosionProtected(int x, int y)
14903 {
14904   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14905     KillPlayer(PLAYERINFO(x, y));
14906 }
14907
14908 void BuryPlayer(struct PlayerInfo *player)
14909 {
14910   int jx = player->jx, jy = player->jy;
14911
14912   if (!player->active)
14913     return;
14914
14915   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14916   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14917
14918   player->GameOver = TRUE;
14919   RemovePlayer(player);
14920 }
14921
14922 void RemovePlayer(struct PlayerInfo *player)
14923 {
14924   int jx = player->jx, jy = player->jy;
14925   int i, found = FALSE;
14926
14927   player->present = FALSE;
14928   player->active = FALSE;
14929
14930   if (!ExplodeField[jx][jy])
14931     StorePlayer[jx][jy] = 0;
14932
14933   if (player->is_moving)
14934     TEST_DrawLevelField(player->last_jx, player->last_jy);
14935
14936   for (i = 0; i < MAX_PLAYERS; i++)
14937     if (stored_player[i].active)
14938       found = TRUE;
14939
14940   if (!found)
14941     AllPlayersGone = TRUE;
14942
14943   ExitX = ZX = jx;
14944   ExitY = ZY = jy;
14945 }
14946
14947 #if USE_NEW_SNAP_DELAY
14948 static void setFieldForSnapping(int x, int y, int element, int direction)
14949 {
14950   struct ElementInfo *ei = &element_info[element];
14951   int direction_bit = MV_DIR_TO_BIT(direction);
14952   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14953   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14954                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14955
14956   Feld[x][y] = EL_ELEMENT_SNAPPING;
14957   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14958
14959   ResetGfxAnimation(x, y);
14960
14961   GfxElement[x][y] = element;
14962   GfxAction[x][y] = action;
14963   GfxDir[x][y] = direction;
14964   GfxFrame[x][y] = -1;
14965 }
14966 #endif
14967
14968 /*
14969   =============================================================================
14970   checkDiagonalPushing()
14971   -----------------------------------------------------------------------------
14972   check if diagonal input device direction results in pushing of object
14973   (by checking if the alternative direction is walkable, diggable, ...)
14974   =============================================================================
14975 */
14976
14977 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14978                                     int x, int y, int real_dx, int real_dy)
14979 {
14980   int jx, jy, dx, dy, xx, yy;
14981
14982   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14983     return TRUE;
14984
14985   /* diagonal direction: check alternative direction */
14986   jx = player->jx;
14987   jy = player->jy;
14988   dx = x - jx;
14989   dy = y - jy;
14990   xx = jx + (dx == 0 ? real_dx : 0);
14991   yy = jy + (dy == 0 ? real_dy : 0);
14992
14993   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14994 }
14995
14996 /*
14997   =============================================================================
14998   DigField()
14999   -----------------------------------------------------------------------------
15000   x, y:                 field next to player (non-diagonal) to try to dig to
15001   real_dx, real_dy:     direction as read from input device (can be diagonal)
15002   =============================================================================
15003 */
15004
15005 static int DigField(struct PlayerInfo *player,
15006                     int oldx, int oldy, int x, int y,
15007                     int real_dx, int real_dy, int mode)
15008 {
15009   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15010   boolean player_was_pushing = player->is_pushing;
15011   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15012   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15013   int jx = oldx, jy = oldy;
15014   int dx = x - jx, dy = y - jy;
15015   int nextx = x + dx, nexty = y + dy;
15016   int move_direction = (dx == -1 ? MV_LEFT  :
15017                         dx == +1 ? MV_RIGHT :
15018                         dy == -1 ? MV_UP    :
15019                         dy == +1 ? MV_DOWN  : MV_NONE);
15020   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15021   int dig_side = MV_DIR_OPPOSITE(move_direction);
15022   int old_element = Feld[jx][jy];
15023 #if USE_FIXED_DONT_RUN_INTO
15024   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15025 #else
15026   int element;
15027 #endif
15028   int collect_count;
15029
15030   if (is_player)                /* function can also be called by EL_PENGUIN */
15031   {
15032     if (player->MovPos == 0)
15033     {
15034       player->is_digging = FALSE;
15035       player->is_collecting = FALSE;
15036     }
15037
15038     if (player->MovPos == 0)    /* last pushing move finished */
15039       player->is_pushing = FALSE;
15040
15041     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
15042     {
15043       player->is_switching = FALSE;
15044       player->push_delay = -1;
15045
15046       return MP_NO_ACTION;
15047     }
15048   }
15049
15050 #if !USE_FIXED_DONT_RUN_INTO
15051   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15052     return MP_NO_ACTION;
15053 #endif
15054
15055   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15056     old_element = Back[jx][jy];
15057
15058   /* in case of element dropped at player position, check background */
15059   else if (Back[jx][jy] != EL_EMPTY &&
15060            game.engine_version >= VERSION_IDENT(2,2,0,0))
15061     old_element = Back[jx][jy];
15062
15063   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15064     return MP_NO_ACTION;        /* field has no opening in this direction */
15065
15066   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15067     return MP_NO_ACTION;        /* field has no opening in this direction */
15068
15069 #if USE_FIXED_DONT_RUN_INTO
15070   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15071   {
15072     SplashAcid(x, y);
15073
15074     Feld[jx][jy] = player->artwork_element;
15075     InitMovingField(jx, jy, MV_DOWN);
15076     Store[jx][jy] = EL_ACID;
15077     ContinueMoving(jx, jy);
15078     BuryPlayer(player);
15079
15080     return MP_DONT_RUN_INTO;
15081   }
15082 #endif
15083
15084 #if USE_FIXED_DONT_RUN_INTO
15085   if (player_can_move && DONT_RUN_INTO(element))
15086   {
15087     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15088
15089     return MP_DONT_RUN_INTO;
15090   }
15091 #endif
15092
15093 #if USE_FIXED_DONT_RUN_INTO
15094   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15095     return MP_NO_ACTION;
15096 #endif
15097
15098 #if !USE_FIXED_DONT_RUN_INTO
15099   element = Feld[x][y];
15100 #endif
15101
15102   collect_count = element_info[element].collect_count_initial;
15103
15104   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15105     return MP_NO_ACTION;
15106
15107   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15108     player_can_move = player_can_move_or_snap;
15109
15110   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15111       game.engine_version >= VERSION_IDENT(2,2,0,0))
15112   {
15113     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15114                                player->index_bit, dig_side);
15115     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15116                                         player->index_bit, dig_side);
15117
15118     if (element == EL_DC_LANDMINE)
15119       Bang(x, y);
15120
15121     if (Feld[x][y] != element)          /* field changed by snapping */
15122       return MP_ACTION;
15123
15124     return MP_NO_ACTION;
15125   }
15126
15127 #if USE_PLAYER_GRAVITY
15128   if (player->gravity && is_player && !player->is_auto_moving &&
15129       canFallDown(player) && move_direction != MV_DOWN &&
15130       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15131     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15132 #else
15133   if (game.gravity && is_player && !player->is_auto_moving &&
15134       canFallDown(player) && move_direction != MV_DOWN &&
15135       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15136     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15137 #endif
15138
15139   if (player_can_move &&
15140       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15141   {
15142     int sound_element = SND_ELEMENT(element);
15143     int sound_action = ACTION_WALKING;
15144
15145     if (IS_RND_GATE(element))
15146     {
15147       if (!player->key[RND_GATE_NR(element)])
15148         return MP_NO_ACTION;
15149     }
15150     else if (IS_RND_GATE_GRAY(element))
15151     {
15152       if (!player->key[RND_GATE_GRAY_NR(element)])
15153         return MP_NO_ACTION;
15154     }
15155     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15156     {
15157       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15158         return MP_NO_ACTION;
15159     }
15160     else if (element == EL_EXIT_OPEN ||
15161              element == EL_EM_EXIT_OPEN ||
15162 #if 1
15163              element == EL_EM_EXIT_OPENING ||
15164 #endif
15165              element == EL_STEEL_EXIT_OPEN ||
15166              element == EL_EM_STEEL_EXIT_OPEN ||
15167 #if 1
15168              element == EL_EM_STEEL_EXIT_OPENING ||
15169 #endif
15170              element == EL_SP_EXIT_OPEN ||
15171              element == EL_SP_EXIT_OPENING)
15172     {
15173       sound_action = ACTION_PASSING;    /* player is passing exit */
15174     }
15175     else if (element == EL_EMPTY)
15176     {
15177       sound_action = ACTION_MOVING;             /* nothing to walk on */
15178     }
15179
15180     /* play sound from background or player, whatever is available */
15181     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15182       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15183     else
15184       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15185   }
15186   else if (player_can_move &&
15187            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15188   {
15189     if (!ACCESS_FROM(element, opposite_direction))
15190       return MP_NO_ACTION;      /* field not accessible from this direction */
15191
15192     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15193       return MP_NO_ACTION;
15194
15195     if (IS_EM_GATE(element))
15196     {
15197       if (!player->key[EM_GATE_NR(element)])
15198         return MP_NO_ACTION;
15199     }
15200     else if (IS_EM_GATE_GRAY(element))
15201     {
15202       if (!player->key[EM_GATE_GRAY_NR(element)])
15203         return MP_NO_ACTION;
15204     }
15205     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15206     {
15207       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15208         return MP_NO_ACTION;
15209     }
15210     else if (IS_EMC_GATE(element))
15211     {
15212       if (!player->key[EMC_GATE_NR(element)])
15213         return MP_NO_ACTION;
15214     }
15215     else if (IS_EMC_GATE_GRAY(element))
15216     {
15217       if (!player->key[EMC_GATE_GRAY_NR(element)])
15218         return MP_NO_ACTION;
15219     }
15220     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15221     {
15222       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15223         return MP_NO_ACTION;
15224     }
15225     else if (element == EL_DC_GATE_WHITE ||
15226              element == EL_DC_GATE_WHITE_GRAY ||
15227              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15228     {
15229       if (player->num_white_keys == 0)
15230         return MP_NO_ACTION;
15231
15232       player->num_white_keys--;
15233     }
15234     else if (IS_SP_PORT(element))
15235     {
15236       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15237           element == EL_SP_GRAVITY_PORT_RIGHT ||
15238           element == EL_SP_GRAVITY_PORT_UP ||
15239           element == EL_SP_GRAVITY_PORT_DOWN)
15240 #if USE_PLAYER_GRAVITY
15241         player->gravity = !player->gravity;
15242 #else
15243         game.gravity = !game.gravity;
15244 #endif
15245       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15246                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15247                element == EL_SP_GRAVITY_ON_PORT_UP ||
15248                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15249 #if USE_PLAYER_GRAVITY
15250         player->gravity = TRUE;
15251 #else
15252         game.gravity = TRUE;
15253 #endif
15254       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15255                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15256                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15257                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15258 #if USE_PLAYER_GRAVITY
15259         player->gravity = FALSE;
15260 #else
15261         game.gravity = FALSE;
15262 #endif
15263     }
15264
15265     /* automatically move to the next field with double speed */
15266     player->programmed_action = move_direction;
15267
15268     if (player->move_delay_reset_counter == 0)
15269     {
15270       player->move_delay_reset_counter = 2;     /* two double speed steps */
15271
15272       DOUBLE_PLAYER_SPEED(player);
15273     }
15274
15275     PlayLevelSoundAction(x, y, ACTION_PASSING);
15276   }
15277   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15278   {
15279     RemoveField(x, y);
15280
15281     if (mode != DF_SNAP)
15282     {
15283       GfxElement[x][y] = GFX_ELEMENT(element);
15284       player->is_digging = TRUE;
15285     }
15286
15287     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15288
15289     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15290                                         player->index_bit, dig_side);
15291
15292     if (mode == DF_SNAP)
15293     {
15294 #if USE_NEW_SNAP_DELAY
15295       if (level.block_snap_field)
15296         setFieldForSnapping(x, y, element, move_direction);
15297       else
15298         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15299 #else
15300       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15301 #endif
15302
15303       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15304                                           player->index_bit, dig_side);
15305     }
15306   }
15307   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15308   {
15309     RemoveField(x, y);
15310
15311     if (is_player && mode != DF_SNAP)
15312     {
15313       GfxElement[x][y] = element;
15314       player->is_collecting = TRUE;
15315     }
15316
15317     if (element == EL_SPEED_PILL)
15318     {
15319       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15320     }
15321     else if (element == EL_EXTRA_TIME && level.time > 0)
15322     {
15323       TimeLeft += level.extra_time;
15324
15325 #if 1
15326       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15327
15328       DisplayGameControlValues();
15329 #else
15330       DrawGameValue_Time(TimeLeft);
15331 #endif
15332     }
15333     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15334     {
15335       player->shield_normal_time_left += level.shield_normal_time;
15336       if (element == EL_SHIELD_DEADLY)
15337         player->shield_deadly_time_left += level.shield_deadly_time;
15338     }
15339     else if (element == EL_DYNAMITE ||
15340              element == EL_EM_DYNAMITE ||
15341              element == EL_SP_DISK_RED)
15342     {
15343       if (player->inventory_size < MAX_INVENTORY_SIZE)
15344         player->inventory_element[player->inventory_size++] = element;
15345
15346       DrawGameDoorValues();
15347     }
15348     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15349     {
15350       player->dynabomb_count++;
15351       player->dynabombs_left++;
15352     }
15353     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15354     {
15355       player->dynabomb_size++;
15356     }
15357     else if (element == EL_DYNABOMB_INCREASE_POWER)
15358     {
15359       player->dynabomb_xl = TRUE;
15360     }
15361     else if (IS_KEY(element))
15362     {
15363       player->key[KEY_NR(element)] = TRUE;
15364
15365       DrawGameDoorValues();
15366     }
15367     else if (element == EL_DC_KEY_WHITE)
15368     {
15369       player->num_white_keys++;
15370
15371       /* display white keys? */
15372       /* DrawGameDoorValues(); */
15373     }
15374     else if (IS_ENVELOPE(element))
15375     {
15376       player->show_envelope = element;
15377     }
15378     else if (element == EL_EMC_LENSES)
15379     {
15380       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15381
15382       RedrawAllInvisibleElementsForLenses();
15383     }
15384     else if (element == EL_EMC_MAGNIFIER)
15385     {
15386       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15387
15388       RedrawAllInvisibleElementsForMagnifier();
15389     }
15390     else if (IS_DROPPABLE(element) ||
15391              IS_THROWABLE(element))     /* can be collected and dropped */
15392     {
15393       int i;
15394
15395       if (collect_count == 0)
15396         player->inventory_infinite_element = element;
15397       else
15398         for (i = 0; i < collect_count; i++)
15399           if (player->inventory_size < MAX_INVENTORY_SIZE)
15400             player->inventory_element[player->inventory_size++] = element;
15401
15402       DrawGameDoorValues();
15403     }
15404     else if (collect_count > 0)
15405     {
15406       local_player->gems_still_needed -= collect_count;
15407       if (local_player->gems_still_needed < 0)
15408         local_player->gems_still_needed = 0;
15409
15410 #if 1
15411       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15412
15413       DisplayGameControlValues();
15414 #else
15415       DrawGameValue_Emeralds(local_player->gems_still_needed);
15416 #endif
15417     }
15418
15419     RaiseScoreElement(element);
15420     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15421
15422     if (is_player)
15423       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15424                                           player->index_bit, dig_side);
15425
15426     if (mode == DF_SNAP)
15427     {
15428 #if USE_NEW_SNAP_DELAY
15429       if (level.block_snap_field)
15430         setFieldForSnapping(x, y, element, move_direction);
15431       else
15432         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15433 #else
15434       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15435 #endif
15436
15437       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15438                                           player->index_bit, dig_side);
15439     }
15440   }
15441   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15442   {
15443     if (mode == DF_SNAP && element != EL_BD_ROCK)
15444       return MP_NO_ACTION;
15445
15446     if (CAN_FALL(element) && dy)
15447       return MP_NO_ACTION;
15448
15449     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15450         !(element == EL_SPRING && level.use_spring_bug))
15451       return MP_NO_ACTION;
15452
15453     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15454         ((move_direction & MV_VERTICAL &&
15455           ((element_info[element].move_pattern & MV_LEFT &&
15456             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15457            (element_info[element].move_pattern & MV_RIGHT &&
15458             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15459          (move_direction & MV_HORIZONTAL &&
15460           ((element_info[element].move_pattern & MV_UP &&
15461             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15462            (element_info[element].move_pattern & MV_DOWN &&
15463             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15464       return MP_NO_ACTION;
15465
15466     /* do not push elements already moving away faster than player */
15467     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15468         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15469       return MP_NO_ACTION;
15470
15471     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15472     {
15473       if (player->push_delay_value == -1 || !player_was_pushing)
15474         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15475     }
15476     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15477     {
15478       if (player->push_delay_value == -1)
15479         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15480     }
15481     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15482     {
15483       if (!player->is_pushing)
15484         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15485     }
15486
15487     player->is_pushing = TRUE;
15488     player->is_active = TRUE;
15489
15490     if (!(IN_LEV_FIELD(nextx, nexty) &&
15491           (IS_FREE(nextx, nexty) ||
15492            (IS_SB_ELEMENT(element) &&
15493             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15494            (IS_CUSTOM_ELEMENT(element) &&
15495             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15496       return MP_NO_ACTION;
15497
15498     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15499       return MP_NO_ACTION;
15500
15501     if (player->push_delay == -1)       /* new pushing; restart delay */
15502       player->push_delay = 0;
15503
15504     if (player->push_delay < player->push_delay_value &&
15505         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15506         element != EL_SPRING && element != EL_BALLOON)
15507     {
15508       /* make sure that there is no move delay before next try to push */
15509       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15510         player->move_delay = 0;
15511
15512       return MP_NO_ACTION;
15513     }
15514
15515     if (IS_CUSTOM_ELEMENT(element) &&
15516         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15517     {
15518       if (!DigFieldByCE(nextx, nexty, element))
15519         return MP_NO_ACTION;
15520     }
15521
15522     if (IS_SB_ELEMENT(element))
15523     {
15524       if (element == EL_SOKOBAN_FIELD_FULL)
15525       {
15526         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15527         local_player->sokobanfields_still_needed++;
15528       }
15529
15530       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15531       {
15532         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15533         local_player->sokobanfields_still_needed--;
15534       }
15535
15536       Feld[x][y] = EL_SOKOBAN_OBJECT;
15537
15538       if (Back[x][y] == Back[nextx][nexty])
15539         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15540       else if (Back[x][y] != 0)
15541         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15542                                     ACTION_EMPTYING);
15543       else
15544         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15545                                     ACTION_FILLING);
15546
15547 #if 1
15548       if (local_player->sokobanfields_still_needed == 0 &&
15549           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15550 #else
15551       if (local_player->sokobanfields_still_needed == 0 &&
15552           game.emulation == EMU_SOKOBAN)
15553 #endif
15554       {
15555         PlayerWins(player);
15556
15557         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15558       }
15559     }
15560     else
15561       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15562
15563     InitMovingField(x, y, move_direction);
15564     GfxAction[x][y] = ACTION_PUSHING;
15565
15566     if (mode == DF_SNAP)
15567       ContinueMoving(x, y);
15568     else
15569       MovPos[x][y] = (dx != 0 ? dx : dy);
15570
15571     Pushed[x][y] = TRUE;
15572     Pushed[nextx][nexty] = TRUE;
15573
15574     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15575       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15576     else
15577       player->push_delay_value = -1;    /* get new value later */
15578
15579     /* check for element change _after_ element has been pushed */
15580     if (game.use_change_when_pushing_bug)
15581     {
15582       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15583                                  player->index_bit, dig_side);
15584       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15585                                           player->index_bit, dig_side);
15586     }
15587   }
15588   else if (IS_SWITCHABLE(element))
15589   {
15590     if (PLAYER_SWITCHING(player, x, y))
15591     {
15592       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15593                                           player->index_bit, dig_side);
15594
15595       return MP_ACTION;
15596     }
15597
15598     player->is_switching = TRUE;
15599     player->switch_x = x;
15600     player->switch_y = y;
15601
15602     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15603
15604     if (element == EL_ROBOT_WHEEL)
15605     {
15606       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15607       ZX = x;
15608       ZY = y;
15609
15610       game.robot_wheel_active = TRUE;
15611
15612       TEST_DrawLevelField(x, y);
15613     }
15614     else if (element == EL_SP_TERMINAL)
15615     {
15616       int xx, yy;
15617
15618       SCAN_PLAYFIELD(xx, yy)
15619       {
15620         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15621           Bang(xx, yy);
15622         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15623           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15624       }
15625     }
15626     else if (IS_BELT_SWITCH(element))
15627     {
15628       ToggleBeltSwitch(x, y);
15629     }
15630     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15631              element == EL_SWITCHGATE_SWITCH_DOWN ||
15632              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15633              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15634     {
15635       ToggleSwitchgateSwitch(x, y);
15636     }
15637     else if (element == EL_LIGHT_SWITCH ||
15638              element == EL_LIGHT_SWITCH_ACTIVE)
15639     {
15640       ToggleLightSwitch(x, y);
15641     }
15642     else if (element == EL_TIMEGATE_SWITCH ||
15643              element == EL_DC_TIMEGATE_SWITCH)
15644     {
15645       ActivateTimegateSwitch(x, y);
15646     }
15647     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15648              element == EL_BALLOON_SWITCH_RIGHT ||
15649              element == EL_BALLOON_SWITCH_UP    ||
15650              element == EL_BALLOON_SWITCH_DOWN  ||
15651              element == EL_BALLOON_SWITCH_NONE  ||
15652              element == EL_BALLOON_SWITCH_ANY)
15653     {
15654       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15655                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15656                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15657                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15658                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15659                              move_direction);
15660     }
15661     else if (element == EL_LAMP)
15662     {
15663       Feld[x][y] = EL_LAMP_ACTIVE;
15664       local_player->lights_still_needed--;
15665
15666       ResetGfxAnimation(x, y);
15667       TEST_DrawLevelField(x, y);
15668     }
15669     else if (element == EL_TIME_ORB_FULL)
15670     {
15671       Feld[x][y] = EL_TIME_ORB_EMPTY;
15672
15673       if (level.time > 0 || level.use_time_orb_bug)
15674       {
15675         TimeLeft += level.time_orb_time;
15676         game.no_time_limit = FALSE;
15677
15678 #if 1
15679         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15680
15681         DisplayGameControlValues();
15682 #else
15683         DrawGameValue_Time(TimeLeft);
15684 #endif
15685       }
15686
15687       ResetGfxAnimation(x, y);
15688       TEST_DrawLevelField(x, y);
15689     }
15690     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15691              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15692     {
15693       int xx, yy;
15694
15695       game.ball_state = !game.ball_state;
15696
15697       SCAN_PLAYFIELD(xx, yy)
15698       {
15699         int e = Feld[xx][yy];
15700
15701         if (game.ball_state)
15702         {
15703           if (e == EL_EMC_MAGIC_BALL)
15704             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15705           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15706             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15707         }
15708         else
15709         {
15710           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15711             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15712           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15713             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15714         }
15715       }
15716     }
15717
15718     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15719                                         player->index_bit, dig_side);
15720
15721     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15722                                         player->index_bit, dig_side);
15723
15724     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15725                                         player->index_bit, dig_side);
15726
15727     return MP_ACTION;
15728   }
15729   else
15730   {
15731     if (!PLAYER_SWITCHING(player, x, y))
15732     {
15733       player->is_switching = TRUE;
15734       player->switch_x = x;
15735       player->switch_y = y;
15736
15737       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15738                                  player->index_bit, dig_side);
15739       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15740                                           player->index_bit, dig_side);
15741
15742       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15743                                  player->index_bit, dig_side);
15744       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15745                                           player->index_bit, dig_side);
15746     }
15747
15748     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15749                                player->index_bit, dig_side);
15750     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15751                                         player->index_bit, dig_side);
15752
15753     return MP_NO_ACTION;
15754   }
15755
15756   player->push_delay = -1;
15757
15758   if (is_player)                /* function can also be called by EL_PENGUIN */
15759   {
15760     if (Feld[x][y] != element)          /* really digged/collected something */
15761     {
15762       player->is_collecting = !player->is_digging;
15763       player->is_active = TRUE;
15764     }
15765   }
15766
15767   return MP_MOVING;
15768 }
15769
15770 static boolean DigFieldByCE(int x, int y, int digging_element)
15771 {
15772   int element = Feld[x][y];
15773
15774   if (!IS_FREE(x, y))
15775   {
15776     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15777                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15778                   ACTION_BREAKING);
15779
15780     /* no element can dig solid indestructible elements */
15781     if (IS_INDESTRUCTIBLE(element) &&
15782         !IS_DIGGABLE(element) &&
15783         !IS_COLLECTIBLE(element))
15784       return FALSE;
15785
15786     if (AmoebaNr[x][y] &&
15787         (element == EL_AMOEBA_FULL ||
15788          element == EL_BD_AMOEBA ||
15789          element == EL_AMOEBA_GROWING))
15790     {
15791       AmoebaCnt[AmoebaNr[x][y]]--;
15792       AmoebaCnt2[AmoebaNr[x][y]]--;
15793     }
15794
15795     if (IS_MOVING(x, y))
15796       RemoveMovingField(x, y);
15797     else
15798     {
15799       RemoveField(x, y);
15800       TEST_DrawLevelField(x, y);
15801     }
15802
15803     /* if digged element was about to explode, prevent the explosion */
15804     ExplodeField[x][y] = EX_TYPE_NONE;
15805
15806     PlayLevelSoundAction(x, y, action);
15807   }
15808
15809   Store[x][y] = EL_EMPTY;
15810
15811 #if 1
15812   /* this makes it possible to leave the removed element again */
15813   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15814     Store[x][y] = element;
15815 #else
15816   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15817   {
15818     int move_leave_element = element_info[digging_element].move_leave_element;
15819
15820     /* this makes it possible to leave the removed element again */
15821     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15822                    element : move_leave_element);
15823   }
15824 #endif
15825
15826   return TRUE;
15827 }
15828
15829 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15830 {
15831   int jx = player->jx, jy = player->jy;
15832   int x = jx + dx, y = jy + dy;
15833   int snap_direction = (dx == -1 ? MV_LEFT  :
15834                         dx == +1 ? MV_RIGHT :
15835                         dy == -1 ? MV_UP    :
15836                         dy == +1 ? MV_DOWN  : MV_NONE);
15837   boolean can_continue_snapping = (level.continuous_snapping &&
15838                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15839
15840   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15841     return FALSE;
15842
15843   if (!player->active || !IN_LEV_FIELD(x, y))
15844     return FALSE;
15845
15846   if (dx && dy)
15847     return FALSE;
15848
15849   if (!dx && !dy)
15850   {
15851     if (player->MovPos == 0)
15852       player->is_pushing = FALSE;
15853
15854     player->is_snapping = FALSE;
15855
15856     if (player->MovPos == 0)
15857     {
15858       player->is_moving = FALSE;
15859       player->is_digging = FALSE;
15860       player->is_collecting = FALSE;
15861     }
15862
15863     return FALSE;
15864   }
15865
15866 #if USE_NEW_CONTINUOUS_SNAPPING
15867   /* prevent snapping with already pressed snap key when not allowed */
15868   if (player->is_snapping && !can_continue_snapping)
15869     return FALSE;
15870 #else
15871   if (player->is_snapping)
15872     return FALSE;
15873 #endif
15874
15875   player->MovDir = snap_direction;
15876
15877   if (player->MovPos == 0)
15878   {
15879     player->is_moving = FALSE;
15880     player->is_digging = FALSE;
15881     player->is_collecting = FALSE;
15882   }
15883
15884   player->is_dropping = FALSE;
15885   player->is_dropping_pressed = FALSE;
15886   player->drop_pressed_delay = 0;
15887
15888   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15889     return FALSE;
15890
15891   player->is_snapping = TRUE;
15892   player->is_active = TRUE;
15893
15894   if (player->MovPos == 0)
15895   {
15896     player->is_moving = FALSE;
15897     player->is_digging = FALSE;
15898     player->is_collecting = FALSE;
15899   }
15900
15901   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15902     TEST_DrawLevelField(player->last_jx, player->last_jy);
15903
15904   TEST_DrawLevelField(x, y);
15905
15906   return TRUE;
15907 }
15908
15909 static boolean DropElement(struct PlayerInfo *player)
15910 {
15911   int old_element, new_element;
15912   int dropx = player->jx, dropy = player->jy;
15913   int drop_direction = player->MovDir;
15914   int drop_side = drop_direction;
15915 #if 1
15916   int drop_element = get_next_dropped_element(player);
15917 #else
15918   int drop_element = (player->inventory_size > 0 ?
15919                       player->inventory_element[player->inventory_size - 1] :
15920                       player->inventory_infinite_element != EL_UNDEFINED ?
15921                       player->inventory_infinite_element :
15922                       player->dynabombs_left > 0 ?
15923                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15924                       EL_UNDEFINED);
15925 #endif
15926
15927   player->is_dropping_pressed = TRUE;
15928
15929   /* do not drop an element on top of another element; when holding drop key
15930      pressed without moving, dropped element must move away before the next
15931      element can be dropped (this is especially important if the next element
15932      is dynamite, which can be placed on background for historical reasons) */
15933   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15934     return MP_ACTION;
15935
15936   if (IS_THROWABLE(drop_element))
15937   {
15938     dropx += GET_DX_FROM_DIR(drop_direction);
15939     dropy += GET_DY_FROM_DIR(drop_direction);
15940
15941     if (!IN_LEV_FIELD(dropx, dropy))
15942       return FALSE;
15943   }
15944
15945   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15946   new_element = drop_element;           /* default: no change when dropping */
15947
15948   /* check if player is active, not moving and ready to drop */
15949   if (!player->active || player->MovPos || player->drop_delay > 0)
15950     return FALSE;
15951
15952   /* check if player has anything that can be dropped */
15953   if (new_element == EL_UNDEFINED)
15954     return FALSE;
15955
15956   /* check if drop key was pressed long enough for EM style dynamite */
15957   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15958     return FALSE;
15959
15960   /* check if anything can be dropped at the current position */
15961   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15962     return FALSE;
15963
15964   /* collected custom elements can only be dropped on empty fields */
15965   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15966     return FALSE;
15967
15968   if (old_element != EL_EMPTY)
15969     Back[dropx][dropy] = old_element;   /* store old element on this field */
15970
15971   ResetGfxAnimation(dropx, dropy);
15972   ResetRandomAnimationValue(dropx, dropy);
15973
15974   if (player->inventory_size > 0 ||
15975       player->inventory_infinite_element != EL_UNDEFINED)
15976   {
15977     if (player->inventory_size > 0)
15978     {
15979       player->inventory_size--;
15980
15981       DrawGameDoorValues();
15982
15983       if (new_element == EL_DYNAMITE)
15984         new_element = EL_DYNAMITE_ACTIVE;
15985       else if (new_element == EL_EM_DYNAMITE)
15986         new_element = EL_EM_DYNAMITE_ACTIVE;
15987       else if (new_element == EL_SP_DISK_RED)
15988         new_element = EL_SP_DISK_RED_ACTIVE;
15989     }
15990
15991     Feld[dropx][dropy] = new_element;
15992
15993     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15994       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15995                           el2img(Feld[dropx][dropy]), 0);
15996
15997     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15998
15999     /* needed if previous element just changed to "empty" in the last frame */
16000     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16001
16002     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16003                                player->index_bit, drop_side);
16004     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16005                                         CE_PLAYER_DROPS_X,
16006                                         player->index_bit, drop_side);
16007
16008     TestIfElementTouchesCustomElement(dropx, dropy);
16009   }
16010   else          /* player is dropping a dyna bomb */
16011   {
16012     player->dynabombs_left--;
16013
16014     Feld[dropx][dropy] = new_element;
16015
16016     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16017       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16018                           el2img(Feld[dropx][dropy]), 0);
16019
16020     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16021   }
16022
16023   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16024     InitField_WithBug1(dropx, dropy, FALSE);
16025
16026   new_element = Feld[dropx][dropy];     /* element might have changed */
16027
16028   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16029       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16030   {
16031     int move_direction, nextx, nexty;
16032
16033     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16034       MovDir[dropx][dropy] = drop_direction;
16035
16036     move_direction = MovDir[dropx][dropy];
16037     nextx = dropx + GET_DX_FROM_DIR(move_direction);
16038     nexty = dropy + GET_DY_FROM_DIR(move_direction);
16039
16040     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16041
16042 #if USE_FIX_IMPACT_COLLISION
16043     /* do not cause impact style collision by dropping elements that can fall */
16044     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16045 #else
16046     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16047 #endif
16048   }
16049
16050   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16051   player->is_dropping = TRUE;
16052
16053   player->drop_pressed_delay = 0;
16054   player->is_dropping_pressed = FALSE;
16055
16056   player->drop_x = dropx;
16057   player->drop_y = dropy;
16058
16059   return TRUE;
16060 }
16061
16062 /* ------------------------------------------------------------------------- */
16063 /* game sound playing functions                                              */
16064 /* ------------------------------------------------------------------------- */
16065
16066 static int *loop_sound_frame = NULL;
16067 static int *loop_sound_volume = NULL;
16068
16069 void InitPlayLevelSound()
16070 {
16071   int num_sounds = getSoundListSize();
16072
16073   checked_free(loop_sound_frame);
16074   checked_free(loop_sound_volume);
16075
16076   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16077   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16078 }
16079
16080 static void PlayLevelSound(int x, int y, int nr)
16081 {
16082   int sx = SCREENX(x), sy = SCREENY(y);
16083   int volume, stereo_position;
16084   int max_distance = 8;
16085   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16086
16087   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16088       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16089     return;
16090
16091   if (!IN_LEV_FIELD(x, y) ||
16092       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16093       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16094     return;
16095
16096   volume = SOUND_MAX_VOLUME;
16097
16098   if (!IN_SCR_FIELD(sx, sy))
16099   {
16100     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16101     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16102
16103     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16104   }
16105
16106   stereo_position = (SOUND_MAX_LEFT +
16107                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16108                      (SCR_FIELDX + 2 * max_distance));
16109
16110   if (IS_LOOP_SOUND(nr))
16111   {
16112     /* This assures that quieter loop sounds do not overwrite louder ones,
16113        while restarting sound volume comparison with each new game frame. */
16114
16115     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16116       return;
16117
16118     loop_sound_volume[nr] = volume;
16119     loop_sound_frame[nr] = FrameCounter;
16120   }
16121
16122   PlaySoundExt(nr, volume, stereo_position, type);
16123 }
16124
16125 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16126 {
16127   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16128                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16129                  y < LEVELY(BY1) ? LEVELY(BY1) :
16130                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16131                  sound_action);
16132 }
16133
16134 static void PlayLevelSoundAction(int x, int y, int action)
16135 {
16136   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16137 }
16138
16139 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16140 {
16141   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16142
16143   if (sound_effect != SND_UNDEFINED)
16144     PlayLevelSound(x, y, sound_effect);
16145 }
16146
16147 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16148                                               int action)
16149 {
16150   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16151
16152   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16153     PlayLevelSound(x, y, sound_effect);
16154 }
16155
16156 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16157 {
16158   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16159
16160   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16161     PlayLevelSound(x, y, sound_effect);
16162 }
16163
16164 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16165 {
16166   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16167
16168   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16169     StopSound(sound_effect);
16170 }
16171
16172 static void PlayLevelMusic()
16173 {
16174   if (levelset.music[level_nr] != MUS_UNDEFINED)
16175     PlayMusic(levelset.music[level_nr]);        /* from config file */
16176   else
16177     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16178 }
16179
16180 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16181 {
16182   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16183   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16184   int x = xx - 1 - offset;
16185   int y = yy - 1 - offset;
16186
16187   switch (sample)
16188   {
16189     case SAMPLE_blank:
16190       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16191       break;
16192
16193     case SAMPLE_roll:
16194       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16195       break;
16196
16197     case SAMPLE_stone:
16198       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16199       break;
16200
16201     case SAMPLE_nut:
16202       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16203       break;
16204
16205     case SAMPLE_crack:
16206       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16207       break;
16208
16209     case SAMPLE_bug:
16210       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16211       break;
16212
16213     case SAMPLE_tank:
16214       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16215       break;
16216
16217     case SAMPLE_android_clone:
16218       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16219       break;
16220
16221     case SAMPLE_android_move:
16222       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16223       break;
16224
16225     case SAMPLE_spring:
16226       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16227       break;
16228
16229     case SAMPLE_slurp:
16230       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16231       break;
16232
16233     case SAMPLE_eater:
16234       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16235       break;
16236
16237     case SAMPLE_eater_eat:
16238       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16239       break;
16240
16241     case SAMPLE_alien:
16242       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16243       break;
16244
16245     case SAMPLE_collect:
16246       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16247       break;
16248
16249     case SAMPLE_diamond:
16250       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16251       break;
16252
16253     case SAMPLE_squash:
16254       /* !!! CHECK THIS !!! */
16255 #if 1
16256       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16257 #else
16258       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16259 #endif
16260       break;
16261
16262     case SAMPLE_wonderfall:
16263       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16264       break;
16265
16266     case SAMPLE_drip:
16267       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16268       break;
16269
16270     case SAMPLE_push:
16271       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16272       break;
16273
16274     case SAMPLE_dirt:
16275       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16276       break;
16277
16278     case SAMPLE_acid:
16279       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16280       break;
16281
16282     case SAMPLE_ball:
16283       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16284       break;
16285
16286     case SAMPLE_grow:
16287       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16288       break;
16289
16290     case SAMPLE_wonder:
16291       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16292       break;
16293
16294     case SAMPLE_door:
16295       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16296       break;
16297
16298     case SAMPLE_exit_open:
16299       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16300       break;
16301
16302     case SAMPLE_exit_leave:
16303       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16304       break;
16305
16306     case SAMPLE_dynamite:
16307       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16308       break;
16309
16310     case SAMPLE_tick:
16311       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16312       break;
16313
16314     case SAMPLE_press:
16315       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16316       break;
16317
16318     case SAMPLE_wheel:
16319       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16320       break;
16321
16322     case SAMPLE_boom:
16323       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16324       break;
16325
16326     case SAMPLE_die:
16327       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16328       break;
16329
16330     case SAMPLE_time:
16331       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16332       break;
16333
16334     default:
16335       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16336       break;
16337   }
16338 }
16339
16340 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16341 {
16342   int element = map_element_SP_to_RND(element_sp);
16343   int action = map_action_SP_to_RND(action_sp);
16344   int offset = (setup.sp_show_border_elements ? 0 : 1);
16345   int x = xx - offset;
16346   int y = yy - offset;
16347
16348 #if 0
16349   printf("::: %d -> %d\n", element_sp, action_sp);
16350 #endif
16351
16352   PlayLevelSoundElementAction(x, y, element, action);
16353 }
16354
16355 #if 0
16356 void ChangeTime(int value)
16357 {
16358   int *time = (game.no_time_limit ? &TimePlayed : &TimeLeft);
16359
16360   *time += value;
16361
16362   /* EMC game engine uses value from time counter of RND game engine */
16363   level.native_em_level->lev->time = *time;
16364
16365   DrawGameValue_Time(*time);
16366 }
16367
16368 void RaiseScore(int value)
16369 {
16370   /* EMC game engine and RND game engine have separate score counters */
16371   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16372                 &level.native_em_level->lev->score : &local_player->score);
16373
16374   *score += value;
16375
16376   DrawGameValue_Score(*score);
16377 }
16378 #endif
16379
16380 void RaiseScore(int value)
16381 {
16382   local_player->score += value;
16383
16384 #if 1
16385   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16386
16387   DisplayGameControlValues();
16388 #else
16389   DrawGameValue_Score(local_player->score);
16390 #endif
16391 }
16392
16393 void RaiseScoreElement(int element)
16394 {
16395   switch (element)
16396   {
16397     case EL_EMERALD:
16398     case EL_BD_DIAMOND:
16399     case EL_EMERALD_YELLOW:
16400     case EL_EMERALD_RED:
16401     case EL_EMERALD_PURPLE:
16402     case EL_SP_INFOTRON:
16403       RaiseScore(level.score[SC_EMERALD]);
16404       break;
16405     case EL_DIAMOND:
16406       RaiseScore(level.score[SC_DIAMOND]);
16407       break;
16408     case EL_CRYSTAL:
16409       RaiseScore(level.score[SC_CRYSTAL]);
16410       break;
16411     case EL_PEARL:
16412       RaiseScore(level.score[SC_PEARL]);
16413       break;
16414     case EL_BUG:
16415     case EL_BD_BUTTERFLY:
16416     case EL_SP_ELECTRON:
16417       RaiseScore(level.score[SC_BUG]);
16418       break;
16419     case EL_SPACESHIP:
16420     case EL_BD_FIREFLY:
16421     case EL_SP_SNIKSNAK:
16422       RaiseScore(level.score[SC_SPACESHIP]);
16423       break;
16424     case EL_YAMYAM:
16425     case EL_DARK_YAMYAM:
16426       RaiseScore(level.score[SC_YAMYAM]);
16427       break;
16428     case EL_ROBOT:
16429       RaiseScore(level.score[SC_ROBOT]);
16430       break;
16431     case EL_PACMAN:
16432       RaiseScore(level.score[SC_PACMAN]);
16433       break;
16434     case EL_NUT:
16435       RaiseScore(level.score[SC_NUT]);
16436       break;
16437     case EL_DYNAMITE:
16438     case EL_EM_DYNAMITE:
16439     case EL_SP_DISK_RED:
16440     case EL_DYNABOMB_INCREASE_NUMBER:
16441     case EL_DYNABOMB_INCREASE_SIZE:
16442     case EL_DYNABOMB_INCREASE_POWER:
16443       RaiseScore(level.score[SC_DYNAMITE]);
16444       break;
16445     case EL_SHIELD_NORMAL:
16446     case EL_SHIELD_DEADLY:
16447       RaiseScore(level.score[SC_SHIELD]);
16448       break;
16449     case EL_EXTRA_TIME:
16450       RaiseScore(level.extra_time_score);
16451       break;
16452     case EL_KEY_1:
16453     case EL_KEY_2:
16454     case EL_KEY_3:
16455     case EL_KEY_4:
16456     case EL_EM_KEY_1:
16457     case EL_EM_KEY_2:
16458     case EL_EM_KEY_3:
16459     case EL_EM_KEY_4:
16460     case EL_EMC_KEY_5:
16461     case EL_EMC_KEY_6:
16462     case EL_EMC_KEY_7:
16463     case EL_EMC_KEY_8:
16464     case EL_DC_KEY_WHITE:
16465       RaiseScore(level.score[SC_KEY]);
16466       break;
16467     default:
16468       RaiseScore(element_info[element].collect_score);
16469       break;
16470   }
16471 }
16472
16473 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16474 {
16475   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16476   {
16477 #if defined(NETWORK_AVALIABLE)
16478     if (options.network)
16479       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16480     else
16481 #endif
16482     {
16483       if (quick_quit)
16484       {
16485 #if 1
16486
16487 #if 1
16488         FadeSkipNextFadeIn();
16489 #else
16490         fading = fading_none;
16491 #endif
16492
16493 #else
16494         OpenDoor(DOOR_CLOSE_1);
16495 #endif
16496
16497         game_status = GAME_MODE_MAIN;
16498
16499 #if 1
16500         DrawAndFadeInMainMenu(REDRAW_FIELD);
16501 #else
16502         DrawMainMenu();
16503 #endif
16504       }
16505       else
16506       {
16507 #if 0
16508         FadeOut(REDRAW_FIELD);
16509 #endif
16510
16511         game_status = GAME_MODE_MAIN;
16512
16513         DrawAndFadeInMainMenu(REDRAW_FIELD);
16514       }
16515     }
16516   }
16517   else          /* continue playing the game */
16518   {
16519     if (tape.playing && tape.deactivate_display)
16520       TapeDeactivateDisplayOff(TRUE);
16521
16522     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16523
16524     if (tape.playing && tape.deactivate_display)
16525       TapeDeactivateDisplayOn();
16526   }
16527 }
16528
16529 void RequestQuitGame(boolean ask_if_really_quit)
16530 {
16531   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16532   boolean skip_request = AllPlayersGone || quick_quit;
16533
16534   RequestQuitGameExt(skip_request, quick_quit,
16535                      "Do you really want to quit the game ?");
16536 }
16537
16538
16539 /* ------------------------------------------------------------------------- */
16540 /* random generator functions                                                */
16541 /* ------------------------------------------------------------------------- */
16542
16543 unsigned int InitEngineRandom_RND(int seed)
16544 {
16545   game.num_random_calls = 0;
16546
16547 #if 0
16548   unsigned int rnd_seed = InitEngineRandom(seed);
16549
16550   printf("::: START RND: %d\n", rnd_seed);
16551
16552   return rnd_seed;
16553 #else
16554
16555   return InitEngineRandom(seed);
16556
16557 #endif
16558
16559 }
16560
16561 unsigned int RND(int max)
16562 {
16563   if (max > 0)
16564   {
16565     game.num_random_calls++;
16566
16567     return GetEngineRandom(max);
16568   }
16569
16570   return 0;
16571 }
16572
16573
16574 /* ------------------------------------------------------------------------- */
16575 /* game engine snapshot handling functions                                   */
16576 /* ------------------------------------------------------------------------- */
16577
16578 struct EngineSnapshotInfo
16579 {
16580   /* runtime values for custom element collect score */
16581   int collect_score[NUM_CUSTOM_ELEMENTS];
16582
16583   /* runtime values for group element choice position */
16584   int choice_pos[NUM_GROUP_ELEMENTS];
16585
16586   /* runtime values for belt position animations */
16587   int belt_graphic[4][NUM_BELT_PARTS];
16588   int belt_anim_mode[4][NUM_BELT_PARTS];
16589 };
16590
16591 static struct EngineSnapshotInfo engine_snapshot_rnd;
16592 static char *snapshot_level_identifier = NULL;
16593 static int snapshot_level_nr = -1;
16594
16595 static void SaveEngineSnapshotValues_RND()
16596 {
16597   static int belt_base_active_element[4] =
16598   {
16599     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16600     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16601     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16602     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16603   };
16604   int i, j;
16605
16606   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16607   {
16608     int element = EL_CUSTOM_START + i;
16609
16610     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16611   }
16612
16613   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16614   {
16615     int element = EL_GROUP_START + i;
16616
16617     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16618   }
16619
16620   for (i = 0; i < 4; i++)
16621   {
16622     for (j = 0; j < NUM_BELT_PARTS; j++)
16623     {
16624       int element = belt_base_active_element[i] + j;
16625       int graphic = el2img(element);
16626       int anim_mode = graphic_info[graphic].anim_mode;
16627
16628       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16629       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16630     }
16631   }
16632 }
16633
16634 static void LoadEngineSnapshotValues_RND()
16635 {
16636   unsigned int num_random_calls = game.num_random_calls;
16637   int i, j;
16638
16639   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16640   {
16641     int element = EL_CUSTOM_START + i;
16642
16643     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16644   }
16645
16646   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16647   {
16648     int element = EL_GROUP_START + i;
16649
16650     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16651   }
16652
16653   for (i = 0; i < 4; i++)
16654   {
16655     for (j = 0; j < NUM_BELT_PARTS; j++)
16656     {
16657       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16658       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16659
16660       graphic_info[graphic].anim_mode = anim_mode;
16661     }
16662   }
16663
16664   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16665   {
16666     InitRND(tape.random_seed);
16667     for (i = 0; i < num_random_calls; i++)
16668       RND(1);
16669   }
16670
16671   if (game.num_random_calls != num_random_calls)
16672   {
16673     Error(ERR_INFO, "number of random calls out of sync");
16674     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16675     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16676     Error(ERR_EXIT, "this should not happen -- please debug");
16677   }
16678 }
16679
16680 void SaveEngineSnapshot()
16681 {
16682   /* do not save snapshots from editor */
16683   if (level_editor_test_game)
16684     return;
16685
16686   /* free previous snapshot buffers, if needed */
16687   FreeEngineSnapshotBuffers();
16688
16689   /* copy some special values to a structure better suited for the snapshot */
16690
16691   SaveEngineSnapshotValues_RND();
16692   SaveEngineSnapshotValues_EM();
16693   SaveEngineSnapshotValues_SP();
16694
16695   /* save values stored in special snapshot structure */
16696
16697   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16698   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16699   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16700
16701   /* save further RND engine values */
16702
16703   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16704   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16705   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16706
16707   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16708   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16709   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16710   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16711
16712   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16713   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16714   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16715   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16716   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16717
16718   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16719   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16720   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16721
16722   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16723
16724   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16725
16726   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16727   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16728
16729   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16730   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16731   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16732   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16733   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16734   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16735   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16736   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16737   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16738   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16739   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16740   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16741   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16742   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16743   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16744   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16745   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16746   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16747
16748   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16749   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16750
16751   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16752   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16753   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16754
16755   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16756   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16757
16758   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16759   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16760   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16761   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16762   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16763
16764   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16765   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16766
16767   /* save level identification information */
16768
16769   setString(&snapshot_level_identifier, leveldir_current->identifier);
16770   snapshot_level_nr = level_nr;
16771
16772 #if 0
16773   ListNode *node = engine_snapshot_list_rnd;
16774   int num_bytes = 0;
16775
16776   while (node != NULL)
16777   {
16778     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16779
16780     node = node->next;
16781   }
16782
16783   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16784 #endif
16785 }
16786
16787 void LoadEngineSnapshot()
16788 {
16789   /* restore generically stored snapshot buffers */
16790
16791   LoadEngineSnapshotBuffers();
16792
16793   /* restore special values from snapshot structure */
16794
16795   LoadEngineSnapshotValues_RND();
16796   LoadEngineSnapshotValues_EM();
16797   LoadEngineSnapshotValues_SP();
16798 }
16799
16800 boolean CheckEngineSnapshot()
16801 {
16802   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16803           snapshot_level_nr == level_nr);
16804 }
16805
16806
16807 /* ---------- new game button stuff ---------------------------------------- */
16808
16809 static struct
16810 {
16811   int graphic;
16812   struct Rect *pos;
16813   int gadget_id;
16814   char *infotext;
16815 } gamebutton_info[NUM_GAME_BUTTONS] =
16816 {
16817   {
16818     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16819     GAME_CTRL_ID_STOP,                  "stop game"
16820   },
16821   {
16822     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16823     GAME_CTRL_ID_PAUSE,                 "pause game"
16824   },
16825   {
16826     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16827     GAME_CTRL_ID_PLAY,                  "play game"
16828   },
16829   {
16830     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16831     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16832   },
16833   {
16834     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16835     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16836   },
16837   {
16838     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16839     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16840   }
16841 };
16842
16843 void CreateGameButtons()
16844 {
16845   int i;
16846
16847   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16848   {
16849     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16850     struct Rect *pos = gamebutton_info[i].pos;
16851     struct GadgetInfo *gi;
16852     int button_type;
16853     boolean checked;
16854     unsigned int event_mask;
16855     int gd_x   = gfx->src_x;
16856     int gd_y   = gfx->src_y;
16857     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16858     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16859     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16860     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16861     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16862     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16863     int id = i;
16864
16865     if (id == GAME_CTRL_ID_STOP ||
16866         id == GAME_CTRL_ID_PAUSE ||
16867         id == GAME_CTRL_ID_PLAY)
16868     {
16869       button_type = GD_TYPE_NORMAL_BUTTON;
16870       checked = FALSE;
16871       event_mask = GD_EVENT_RELEASED;
16872     }
16873     else
16874     {
16875       button_type = GD_TYPE_CHECK_BUTTON;
16876       checked =
16877         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16878          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16879          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16880       event_mask = GD_EVENT_PRESSED;
16881     }
16882
16883     gi = CreateGadget(GDI_CUSTOM_ID, id,
16884                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16885                       GDI_X, DX + pos->x,
16886                       GDI_Y, DY + pos->y,
16887                       GDI_WIDTH, gfx->width,
16888                       GDI_HEIGHT, gfx->height,
16889                       GDI_TYPE, button_type,
16890                       GDI_STATE, GD_BUTTON_UNPRESSED,
16891                       GDI_CHECKED, checked,
16892                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16893                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16894                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16895                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16896                       GDI_DIRECT_DRAW, FALSE,
16897                       GDI_EVENT_MASK, event_mask,
16898                       GDI_CALLBACK_ACTION, HandleGameButtons,
16899                       GDI_END);
16900
16901     if (gi == NULL)
16902       Error(ERR_EXIT, "cannot create gadget");
16903
16904     game_gadget[id] = gi;
16905   }
16906 }
16907
16908 void FreeGameButtons()
16909 {
16910   int i;
16911
16912   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16913     FreeGadget(game_gadget[i]);
16914 }
16915
16916 static void MapGameButtons()
16917 {
16918   int i;
16919
16920   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16921     MapGadget(game_gadget[i]);
16922 }
16923
16924 void UnmapGameButtons()
16925 {
16926   int i;
16927
16928   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16929     UnmapGadget(game_gadget[i]);
16930 }
16931
16932 void RedrawGameButtons()
16933 {
16934   int i;
16935
16936   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16937     RedrawGadget(game_gadget[i]);
16938 }
16939
16940 static void HandleGameButtonsExt(int id)
16941 {
16942   if (game_status != GAME_MODE_PLAYING)
16943     return;
16944
16945   switch (id)
16946   {
16947     case GAME_CTRL_ID_STOP:
16948       if (tape.playing)
16949         TapeStop();
16950       else
16951         RequestQuitGame(TRUE);
16952       break;
16953
16954     case GAME_CTRL_ID_PAUSE:
16955       if (options.network)
16956       {
16957 #if defined(NETWORK_AVALIABLE)
16958         if (tape.pausing)
16959           SendToServer_ContinuePlaying();
16960         else
16961           SendToServer_PausePlaying();
16962 #endif
16963       }
16964       else
16965         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16966       break;
16967
16968     case GAME_CTRL_ID_PLAY:
16969       if (tape.pausing)
16970       {
16971 #if defined(NETWORK_AVALIABLE)
16972         if (options.network)
16973           SendToServer_ContinuePlaying();
16974         else
16975 #endif
16976         {
16977           tape.pausing = FALSE;
16978           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16979         }
16980       }
16981       break;
16982
16983     case SOUND_CTRL_ID_MUSIC:
16984       if (setup.sound_music)
16985       { 
16986         setup.sound_music = FALSE;
16987
16988         FadeMusic();
16989       }
16990       else if (audio.music_available)
16991       { 
16992         setup.sound = setup.sound_music = TRUE;
16993
16994         SetAudioMode(setup.sound);
16995
16996         PlayLevelMusic();
16997       }
16998       break;
16999
17000     case SOUND_CTRL_ID_LOOPS:
17001       if (setup.sound_loops)
17002         setup.sound_loops = FALSE;
17003       else if (audio.loops_available)
17004       {
17005         setup.sound = setup.sound_loops = TRUE;
17006
17007         SetAudioMode(setup.sound);
17008       }
17009       break;
17010
17011     case SOUND_CTRL_ID_SIMPLE:
17012       if (setup.sound_simple)
17013         setup.sound_simple = FALSE;
17014       else if (audio.sound_available)
17015       {
17016         setup.sound = setup.sound_simple = TRUE;
17017
17018         SetAudioMode(setup.sound);
17019       }
17020       break;
17021
17022     default:
17023       break;
17024   }
17025 }
17026
17027 static void HandleGameButtons(struct GadgetInfo *gi)
17028 {
17029   HandleGameButtonsExt(gi->custom_id);
17030 }
17031
17032 void HandleSoundButtonKeys(Key key)
17033 {
17034 #if 1
17035   if (key == setup.shortcut.sound_simple)
17036     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17037   else if (key == setup.shortcut.sound_loops)
17038     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17039   else if (key == setup.shortcut.sound_music)
17040     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17041 #else
17042   if (key == setup.shortcut.sound_simple)
17043     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17044   else if (key == setup.shortcut.sound_loops)
17045     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17046   else if (key == setup.shortcut.sound_music)
17047     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17048 #endif
17049 }