rnd-20140315-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
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_NEW_STUFF                   (                         1)
34
35 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
36 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
37 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
38 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
39 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
40 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
41 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
42 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
43 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
44 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
45 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
46 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
47 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
48 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
49 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
50 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
51 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
52 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
53 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
54
55 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
56
57 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
58
59 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
60
61 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
62 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
63
64 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
65 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
66 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
67 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
68
69 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
70
71 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
72
73 #define USE_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
74
75 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
76
77 #if USE_DELAYED_GFX_REDRAW
78 #define TEST_DrawLevelField(x, y)                               \
79         GfxRedraw[x][y] |= GFX_REDRAW_TILE
80 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
81         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
82 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
83         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
84 #define TEST_DrawTwinkleOnField(x, y)                           \
85         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
86 #else
87 #define TEST_DrawLevelField(x, y)                               \
88              DrawLevelField(x, y)
89 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
90              DrawLevelFieldCrumbled(x, y)
91 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
92              DrawLevelFieldCrumbledNeighbours(x, y)
93 #define TEST_DrawTwinkleOnField(x, y)                           \
94              DrawTwinkleOnField(x, y)
95 #endif
96
97
98 /* for DigField() */
99 #define DF_NO_PUSH              0
100 #define DF_DIG                  1
101 #define DF_SNAP                 2
102
103 /* for MovePlayer() */
104 #define MP_NO_ACTION            0
105 #define MP_MOVING               1
106 #define MP_ACTION               2
107 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
108
109 /* for ScrollPlayer() */
110 #define SCROLL_INIT             0
111 #define SCROLL_GO_ON            1
112
113 /* for Bang()/Explode() */
114 #define EX_PHASE_START          0
115 #define EX_TYPE_NONE            0
116 #define EX_TYPE_NORMAL          (1 << 0)
117 #define EX_TYPE_CENTER          (1 << 1)
118 #define EX_TYPE_BORDER          (1 << 2)
119 #define EX_TYPE_CROSS           (1 << 3)
120 #define EX_TYPE_DYNA            (1 << 4)
121 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
122
123 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
124 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
125 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
126 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
127
128 /* game panel display and control definitions */
129 #define GAME_PANEL_LEVEL_NUMBER                 0
130 #define GAME_PANEL_GEMS                         1
131 #define GAME_PANEL_INVENTORY_COUNT              2
132 #define GAME_PANEL_INVENTORY_FIRST_1            3
133 #define GAME_PANEL_INVENTORY_FIRST_2            4
134 #define GAME_PANEL_INVENTORY_FIRST_3            5
135 #define GAME_PANEL_INVENTORY_FIRST_4            6
136 #define GAME_PANEL_INVENTORY_FIRST_5            7
137 #define GAME_PANEL_INVENTORY_FIRST_6            8
138 #define GAME_PANEL_INVENTORY_FIRST_7            9
139 #define GAME_PANEL_INVENTORY_FIRST_8            10
140 #define GAME_PANEL_INVENTORY_LAST_1             11
141 #define GAME_PANEL_INVENTORY_LAST_2             12
142 #define GAME_PANEL_INVENTORY_LAST_3             13
143 #define GAME_PANEL_INVENTORY_LAST_4             14
144 #define GAME_PANEL_INVENTORY_LAST_5             15
145 #define GAME_PANEL_INVENTORY_LAST_6             16
146 #define GAME_PANEL_INVENTORY_LAST_7             17
147 #define GAME_PANEL_INVENTORY_LAST_8             18
148 #define GAME_PANEL_KEY_1                        19
149 #define GAME_PANEL_KEY_2                        20
150 #define GAME_PANEL_KEY_3                        21
151 #define GAME_PANEL_KEY_4                        22
152 #define GAME_PANEL_KEY_5                        23
153 #define GAME_PANEL_KEY_6                        24
154 #define GAME_PANEL_KEY_7                        25
155 #define GAME_PANEL_KEY_8                        26
156 #define GAME_PANEL_KEY_WHITE                    27
157 #define GAME_PANEL_KEY_WHITE_COUNT              28
158 #define GAME_PANEL_SCORE                        29
159 #define GAME_PANEL_HIGHSCORE                    30
160 #define GAME_PANEL_TIME                         31
161 #define GAME_PANEL_TIME_HH                      32
162 #define GAME_PANEL_TIME_MM                      33
163 #define GAME_PANEL_TIME_SS                      34
164 #define GAME_PANEL_FRAME                        35
165 #define GAME_PANEL_SHIELD_NORMAL                36
166 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
167 #define GAME_PANEL_SHIELD_DEADLY                38
168 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
169 #define GAME_PANEL_EXIT                         40
170 #define GAME_PANEL_EMC_MAGIC_BALL               41
171 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
172 #define GAME_PANEL_LIGHT_SWITCH                 43
173 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
174 #define GAME_PANEL_TIMEGATE_SWITCH              45
175 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
176 #define GAME_PANEL_SWITCHGATE_SWITCH            47
177 #define GAME_PANEL_EMC_LENSES                   48
178 #define GAME_PANEL_EMC_LENSES_TIME              49
179 #define GAME_PANEL_EMC_MAGNIFIER                50
180 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
181 #define GAME_PANEL_BALLOON_SWITCH               52
182 #define GAME_PANEL_DYNABOMB_NUMBER              53
183 #define GAME_PANEL_DYNABOMB_SIZE                54
184 #define GAME_PANEL_DYNABOMB_POWER               55
185 #define GAME_PANEL_PENGUINS                     56
186 #define GAME_PANEL_SOKOBAN_OBJECTS              57
187 #define GAME_PANEL_SOKOBAN_FIELDS               58
188 #define GAME_PANEL_ROBOT_WHEEL                  59
189 #define GAME_PANEL_CONVEYOR_BELT_1              60
190 #define GAME_PANEL_CONVEYOR_BELT_2              61
191 #define GAME_PANEL_CONVEYOR_BELT_3              62
192 #define GAME_PANEL_CONVEYOR_BELT_4              63
193 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
194 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
195 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
196 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
197 #define GAME_PANEL_MAGIC_WALL                   68
198 #define GAME_PANEL_MAGIC_WALL_TIME              69
199 #define GAME_PANEL_GRAVITY_STATE                70
200 #define GAME_PANEL_GRAPHIC_1                    71
201 #define GAME_PANEL_GRAPHIC_2                    72
202 #define GAME_PANEL_GRAPHIC_3                    73
203 #define GAME_PANEL_GRAPHIC_4                    74
204 #define GAME_PANEL_GRAPHIC_5                    75
205 #define GAME_PANEL_GRAPHIC_6                    76
206 #define GAME_PANEL_GRAPHIC_7                    77
207 #define GAME_PANEL_GRAPHIC_8                    78
208 #define GAME_PANEL_ELEMENT_1                    79
209 #define GAME_PANEL_ELEMENT_2                    80
210 #define GAME_PANEL_ELEMENT_3                    81
211 #define GAME_PANEL_ELEMENT_4                    82
212 #define GAME_PANEL_ELEMENT_5                    83
213 #define GAME_PANEL_ELEMENT_6                    84
214 #define GAME_PANEL_ELEMENT_7                    85
215 #define GAME_PANEL_ELEMENT_8                    86
216 #define GAME_PANEL_ELEMENT_COUNT_1              87
217 #define GAME_PANEL_ELEMENT_COUNT_2              88
218 #define GAME_PANEL_ELEMENT_COUNT_3              89
219 #define GAME_PANEL_ELEMENT_COUNT_4              90
220 #define GAME_PANEL_ELEMENT_COUNT_5              91
221 #define GAME_PANEL_ELEMENT_COUNT_6              92
222 #define GAME_PANEL_ELEMENT_COUNT_7              93
223 #define GAME_PANEL_ELEMENT_COUNT_8              94
224 #define GAME_PANEL_CE_SCORE_1                   95
225 #define GAME_PANEL_CE_SCORE_2                   96
226 #define GAME_PANEL_CE_SCORE_3                   97
227 #define GAME_PANEL_CE_SCORE_4                   98
228 #define GAME_PANEL_CE_SCORE_5                   99
229 #define GAME_PANEL_CE_SCORE_6                   100
230 #define GAME_PANEL_CE_SCORE_7                   101
231 #define GAME_PANEL_CE_SCORE_8                   102
232 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
233 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
234 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
235 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
236 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
237 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
238 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
239 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
240 #define GAME_PANEL_PLAYER_NAME                  111
241 #define GAME_PANEL_LEVEL_NAME                   112
242 #define GAME_PANEL_LEVEL_AUTHOR                 113
243
244 #define NUM_GAME_PANEL_CONTROLS                 114
245
246 struct GamePanelOrderInfo
247 {
248   int nr;
249   int sort_priority;
250 };
251
252 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
253
254 struct GamePanelControlInfo
255 {
256   int nr;
257
258   struct TextPosInfo *pos;
259   int type;
260
261   int value, last_value;
262   int frame, last_frame;
263   int gfx_frame;
264   int gfx_random;
265 };
266
267 static struct GamePanelControlInfo game_panel_controls[] =
268 {
269   {
270     GAME_PANEL_LEVEL_NUMBER,
271     &game.panel.level_number,
272     TYPE_INTEGER,
273   },
274   {
275     GAME_PANEL_GEMS,
276     &game.panel.gems,
277     TYPE_INTEGER,
278   },
279   {
280     GAME_PANEL_INVENTORY_COUNT,
281     &game.panel.inventory_count,
282     TYPE_INTEGER,
283   },
284   {
285     GAME_PANEL_INVENTORY_FIRST_1,
286     &game.panel.inventory_first[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_FIRST_2,
291     &game.panel.inventory_first[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_FIRST_3,
296     &game.panel.inventory_first[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_FIRST_4,
301     &game.panel.inventory_first[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_FIRST_5,
306     &game.panel.inventory_first[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_FIRST_6,
311     &game.panel.inventory_first[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_FIRST_7,
316     &game.panel.inventory_first[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_FIRST_8,
321     &game.panel.inventory_first[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_INVENTORY_LAST_1,
326     &game.panel.inventory_last[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_INVENTORY_LAST_2,
331     &game.panel.inventory_last[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_INVENTORY_LAST_3,
336     &game.panel.inventory_last[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_INVENTORY_LAST_4,
341     &game.panel.inventory_last[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_INVENTORY_LAST_5,
346     &game.panel.inventory_last[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_INVENTORY_LAST_6,
351     &game.panel.inventory_last[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_INVENTORY_LAST_7,
356     &game.panel.inventory_last[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_INVENTORY_LAST_8,
361     &game.panel.inventory_last[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_1,
366     &game.panel.key[0],
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_2,
371     &game.panel.key[1],
372     TYPE_ELEMENT,
373   },
374   {
375     GAME_PANEL_KEY_3,
376     &game.panel.key[2],
377     TYPE_ELEMENT,
378   },
379   {
380     GAME_PANEL_KEY_4,
381     &game.panel.key[3],
382     TYPE_ELEMENT,
383   },
384   {
385     GAME_PANEL_KEY_5,
386     &game.panel.key[4],
387     TYPE_ELEMENT,
388   },
389   {
390     GAME_PANEL_KEY_6,
391     &game.panel.key[5],
392     TYPE_ELEMENT,
393   },
394   {
395     GAME_PANEL_KEY_7,
396     &game.panel.key[6],
397     TYPE_ELEMENT,
398   },
399   {
400     GAME_PANEL_KEY_8,
401     &game.panel.key[7],
402     TYPE_ELEMENT,
403   },
404   {
405     GAME_PANEL_KEY_WHITE,
406     &game.panel.key_white,
407     TYPE_ELEMENT,
408   },
409   {
410     GAME_PANEL_KEY_WHITE_COUNT,
411     &game.panel.key_white_count,
412     TYPE_INTEGER,
413   },
414   {
415     GAME_PANEL_SCORE,
416     &game.panel.score,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_HIGHSCORE,
421     &game.panel.highscore,
422     TYPE_INTEGER,
423   },
424   {
425     GAME_PANEL_TIME,
426     &game.panel.time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_TIME_HH,
431     &game.panel.time_hh,
432     TYPE_INTEGER,
433   },
434   {
435     GAME_PANEL_TIME_MM,
436     &game.panel.time_mm,
437     TYPE_INTEGER,
438   },
439   {
440     GAME_PANEL_TIME_SS,
441     &game.panel.time_ss,
442     TYPE_INTEGER,
443   },
444   {
445     GAME_PANEL_FRAME,
446     &game.panel.frame,
447     TYPE_INTEGER,
448   },
449   {
450     GAME_PANEL_SHIELD_NORMAL,
451     &game.panel.shield_normal,
452     TYPE_ELEMENT,
453   },
454   {
455     GAME_PANEL_SHIELD_NORMAL_TIME,
456     &game.panel.shield_normal_time,
457     TYPE_INTEGER,
458   },
459   {
460     GAME_PANEL_SHIELD_DEADLY,
461     &game.panel.shield_deadly,
462     TYPE_ELEMENT,
463   },
464   {
465     GAME_PANEL_SHIELD_DEADLY_TIME,
466     &game.panel.shield_deadly_time,
467     TYPE_INTEGER,
468   },
469   {
470     GAME_PANEL_EXIT,
471     &game.panel.exit,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_MAGIC_BALL,
476     &game.panel.emc_magic_ball,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
481     &game.panel.emc_magic_ball_switch,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_LIGHT_SWITCH,
486     &game.panel.light_switch,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_PANEL_LIGHT_SWITCH_TIME,
491     &game.panel.light_switch_time,
492     TYPE_INTEGER,
493   },
494   {
495     GAME_PANEL_TIMEGATE_SWITCH,
496     &game.panel.timegate_switch,
497     TYPE_ELEMENT,
498   },
499   {
500     GAME_PANEL_TIMEGATE_SWITCH_TIME,
501     &game.panel.timegate_switch_time,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_SWITCHGATE_SWITCH,
506     &game.panel.switchgate_switch,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_EMC_LENSES,
511     &game.panel.emc_lenses,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_PANEL_EMC_LENSES_TIME,
516     &game.panel.emc_lenses_time,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_EMC_MAGNIFIER,
521     &game.panel.emc_magnifier,
522     TYPE_ELEMENT,
523   },
524   {
525     GAME_PANEL_EMC_MAGNIFIER_TIME,
526     &game.panel.emc_magnifier_time,
527     TYPE_INTEGER,
528   },
529   {
530     GAME_PANEL_BALLOON_SWITCH,
531     &game.panel.balloon_switch,
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_DYNABOMB_NUMBER,
536     &game.panel.dynabomb_number,
537     TYPE_INTEGER,
538   },
539   {
540     GAME_PANEL_DYNABOMB_SIZE,
541     &game.panel.dynabomb_size,
542     TYPE_INTEGER,
543   },
544   {
545     GAME_PANEL_DYNABOMB_POWER,
546     &game.panel.dynabomb_power,
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_PENGUINS,
551     &game.panel.penguins,
552     TYPE_INTEGER,
553   },
554   {
555     GAME_PANEL_SOKOBAN_OBJECTS,
556     &game.panel.sokoban_objects,
557     TYPE_INTEGER,
558   },
559   {
560     GAME_PANEL_SOKOBAN_FIELDS,
561     &game.panel.sokoban_fields,
562     TYPE_INTEGER,
563   },
564   {
565     GAME_PANEL_ROBOT_WHEEL,
566     &game.panel.robot_wheel,
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_CONVEYOR_BELT_1,
571     &game.panel.conveyor_belt[0],
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_CONVEYOR_BELT_2,
576     &game.panel.conveyor_belt[1],
577     TYPE_ELEMENT,
578   },
579   {
580     GAME_PANEL_CONVEYOR_BELT_3,
581     &game.panel.conveyor_belt[2],
582     TYPE_ELEMENT,
583   },
584   {
585     GAME_PANEL_CONVEYOR_BELT_4,
586     &game.panel.conveyor_belt[3],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
591     &game.panel.conveyor_belt_switch[0],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
596     &game.panel.conveyor_belt_switch[1],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
601     &game.panel.conveyor_belt_switch[2],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
606     &game.panel.conveyor_belt_switch[3],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_MAGIC_WALL,
611     &game.panel.magic_wall,
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_MAGIC_WALL_TIME,
616     &game.panel.magic_wall_time,
617     TYPE_INTEGER,
618   },
619   {
620     GAME_PANEL_GRAVITY_STATE,
621     &game.panel.gravity_state,
622     TYPE_STRING,
623   },
624   {
625     GAME_PANEL_GRAPHIC_1,
626     &game.panel.graphic[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_GRAPHIC_2,
631     &game.panel.graphic[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_GRAPHIC_3,
636     &game.panel.graphic[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_GRAPHIC_4,
641     &game.panel.graphic[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_GRAPHIC_5,
646     &game.panel.graphic[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_GRAPHIC_6,
651     &game.panel.graphic[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_7,
656     &game.panel.graphic[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_GRAPHIC_8,
661     &game.panel.graphic[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_1,
666     &game.panel.element[0],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_ELEMENT_2,
671     &game.panel.element[1],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_ELEMENT_3,
676     &game.panel.element[2],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_ELEMENT_4,
681     &game.panel.element[3],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_ELEMENT_5,
686     &game.panel.element[4],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_6,
691     &game.panel.element[5],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_7,
696     &game.panel.element[6],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_8,
701     &game.panel.element[7],
702     TYPE_ELEMENT,
703   },
704   {
705     GAME_PANEL_ELEMENT_COUNT_1,
706     &game.panel.element_count[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_ELEMENT_COUNT_2,
711     &game.panel.element_count[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_ELEMENT_COUNT_3,
716     &game.panel.element_count[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_ELEMENT_COUNT_4,
721     &game.panel.element_count[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_ELEMENT_COUNT_5,
726     &game.panel.element_count[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_6,
731     &game.panel.element_count[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_7,
736     &game.panel.element_count[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_ELEMENT_COUNT_8,
741     &game.panel.element_count[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1,
746     &game.panel.ce_score[0],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2,
751     &game.panel.ce_score[1],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3,
756     &game.panel.ce_score[2],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4,
761     &game.panel.ce_score[3],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5,
766     &game.panel.ce_score[4],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6,
771     &game.panel.ce_score[5],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7,
776     &game.panel.ce_score[6],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8,
781     &game.panel.ce_score[7],
782     TYPE_INTEGER,
783   },
784   {
785     GAME_PANEL_CE_SCORE_1_ELEMENT,
786     &game.panel.ce_score_element[0],
787     TYPE_ELEMENT,
788   },
789   {
790     GAME_PANEL_CE_SCORE_2_ELEMENT,
791     &game.panel.ce_score_element[1],
792     TYPE_ELEMENT,
793   },
794   {
795     GAME_PANEL_CE_SCORE_3_ELEMENT,
796     &game.panel.ce_score_element[2],
797     TYPE_ELEMENT,
798   },
799   {
800     GAME_PANEL_CE_SCORE_4_ELEMENT,
801     &game.panel.ce_score_element[3],
802     TYPE_ELEMENT,
803   },
804   {
805     GAME_PANEL_CE_SCORE_5_ELEMENT,
806     &game.panel.ce_score_element[4],
807     TYPE_ELEMENT,
808   },
809   {
810     GAME_PANEL_CE_SCORE_6_ELEMENT,
811     &game.panel.ce_score_element[5],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_7_ELEMENT,
816     &game.panel.ce_score_element[6],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_CE_SCORE_8_ELEMENT,
821     &game.panel.ce_score_element[7],
822     TYPE_ELEMENT,
823   },
824   {
825     GAME_PANEL_PLAYER_NAME,
826     &game.panel.player_name,
827     TYPE_STRING,
828   },
829   {
830     GAME_PANEL_LEVEL_NAME,
831     &game.panel.level_name,
832     TYPE_STRING,
833   },
834   {
835     GAME_PANEL_LEVEL_AUTHOR,
836     &game.panel.level_author,
837     TYPE_STRING,
838   },
839
840   {
841     -1,
842     NULL,
843     -1,
844   }
845 };
846
847 /* values for delayed check of falling and moving elements and for collision */
848 #define CHECK_DELAY_MOVING      3
849 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
850 #define CHECK_DELAY_COLLISION   2
851 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
852
853 /* values for initial player move delay (initial delay counter value) */
854 #define INITIAL_MOVE_DELAY_OFF  -1
855 #define INITIAL_MOVE_DELAY_ON   0
856
857 /* values for player movement speed (which is in fact a delay value) */
858 #define MOVE_DELAY_MIN_SPEED    32
859 #define MOVE_DELAY_NORMAL_SPEED 8
860 #define MOVE_DELAY_HIGH_SPEED   4
861 #define MOVE_DELAY_MAX_SPEED    1
862
863 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
864 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
865
866 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
867 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
868
869 /* values for other actions */
870 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
871 #define MOVE_STEPSIZE_MIN       (1)
872 #define MOVE_STEPSIZE_MAX       (TILEX)
873
874 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
875 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
876
877 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
878
879 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
880                                  RND(element_info[e].push_delay_random))
881 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
882                                  RND(element_info[e].drop_delay_random))
883 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
884                                  RND(element_info[e].move_delay_random))
885 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
886                                     (element_info[e].move_delay_random))
887 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
888                                  RND(element_info[e].ce_value_random_initial))
889 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
890 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
891                                  RND((c)->delay_random * (c)->delay_frames))
892 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
893                                  RND((c)->delay_random))
894
895
896 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
897          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
898
899 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
900         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
901          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
902          (be) + (e) - EL_SELF)
903
904 #define GET_PLAYER_FROM_BITS(p)                                         \
905         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
906
907 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
908         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
909          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
910          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
911          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
912          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
913          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
914          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
915          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
916          (e))
917
918 #define CAN_GROW_INTO(e)                                                \
919         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
920
921 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
922                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
923                                         (condition)))
924
925 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
926                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
927                                         (CAN_MOVE_INTO_ACID(e) &&       \
928                                          Feld[x][y] == EL_ACID) ||      \
929                                         (condition)))
930
931 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
932                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
933                                         (CAN_MOVE_INTO_ACID(e) &&       \
934                                          Feld[x][y] == EL_ACID) ||      \
935                                         (condition)))
936
937 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
938                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
939                                         (condition) ||                  \
940                                         (CAN_MOVE_INTO_ACID(e) &&       \
941                                          Feld[x][y] == EL_ACID) ||      \
942                                         (DONT_COLLIDE_WITH(e) &&        \
943                                          IS_PLAYER(x, y) &&             \
944                                          !PLAYER_ENEMY_PROTECTED(x, y))))
945
946 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
947         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
948
949 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
950         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
951
952 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
953         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
954
955 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
956         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
957                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
958
959 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
960         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
961
962 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
963         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
964
965 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
966         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
967
968 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
969         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
970
971 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
972         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
973
974 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
975         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
976                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
977                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
978                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
979                                                  IS_FOOD_PENGUIN(Feld[x][y])))
980 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
981         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
982
983 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
984         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
985
986 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
987         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
988
989 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
990         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
991                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
992
993 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
994
995 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
996                 (!IS_PLAYER(x, y) &&                                    \
997                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
998
999 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1000         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1001
1002 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1003 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1004
1005 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1006 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1008 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1009
1010 /* game button identifiers */
1011 #define GAME_CTRL_ID_STOP               0
1012 #define GAME_CTRL_ID_PAUSE              1
1013 #define GAME_CTRL_ID_PLAY               2
1014 #define SOUND_CTRL_ID_MUSIC             3
1015 #define SOUND_CTRL_ID_LOOPS             4
1016 #define SOUND_CTRL_ID_SIMPLE            5
1017 #define GAME_CTRL_ID_SAVE               6
1018 #define GAME_CTRL_ID_LOAD               7
1019
1020 #define NUM_GAME_BUTTONS                8
1021
1022
1023 /* forward declaration for internal use */
1024
1025 static void CreateField(int, int, int);
1026
1027 static void ResetGfxAnimation(int, int);
1028
1029 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1030 static void AdvanceFrameAndPlayerCounters(int);
1031
1032 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1033 static boolean MovePlayer(struct PlayerInfo *, int, int);
1034 static void ScrollPlayer(struct PlayerInfo *, int);
1035 static void ScrollScreen(struct PlayerInfo *, int);
1036
1037 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1038 static boolean DigFieldByCE(int, int, int);
1039 static boolean SnapField(struct PlayerInfo *, int, int);
1040 static boolean DropElement(struct PlayerInfo *);
1041
1042 static void InitBeltMovement(void);
1043 static void CloseAllOpenTimegates(void);
1044 static void CheckGravityMovement(struct PlayerInfo *);
1045 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1046 static void KillPlayerUnlessEnemyProtected(int, int);
1047 static void KillPlayerUnlessExplosionProtected(int, int);
1048
1049 static void TestIfPlayerTouchesCustomElement(int, int);
1050 static void TestIfElementTouchesCustomElement(int, int);
1051 static void TestIfElementHitsCustomElement(int, int, int);
1052 #if 0
1053 static void TestIfElementSmashesCustomElement(int, int, int);
1054 #endif
1055
1056 static void HandleElementChange(int, int, int);
1057 static void ExecuteCustomElementAction(int, int, int, int);
1058 static boolean ChangeElement(int, int, int, int);
1059
1060 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1061 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1062         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1063 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1064         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1065 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1066         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1067 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1068         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1069
1070 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1071 #define CheckElementChange(x, y, e, te, ev)                             \
1072         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1073 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1074         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1075 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1076         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1077
1078 static void PlayLevelSound(int, int, int);
1079 static void PlayLevelSoundNearest(int, int, int);
1080 static void PlayLevelSoundAction(int, int, int);
1081 static void PlayLevelSoundElementAction(int, int, int, int);
1082 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1083 static void PlayLevelSoundActionIfLoop(int, int, int);
1084 static void StopLevelSoundActionIfLoop(int, int, int);
1085 static void PlayLevelMusic();
1086
1087 static void HandleGameButtons(struct GadgetInfo *);
1088
1089 int AmoebeNachbarNr(int, int);
1090 void AmoebeUmwandeln(int, int);
1091 void ContinueMoving(int, int);
1092 void Bang(int, int);
1093 void InitMovDir(int, int);
1094 void InitAmoebaNr(int, int);
1095 int NewHiScore(void);
1096
1097 void TestIfGoodThingHitsBadThing(int, int, int);
1098 void TestIfBadThingHitsGoodThing(int, int, int);
1099 void TestIfPlayerTouchesBadThing(int, int);
1100 void TestIfPlayerRunsIntoBadThing(int, int, int);
1101 void TestIfBadThingTouchesPlayer(int, int);
1102 void TestIfBadThingRunsIntoPlayer(int, int, int);
1103 void TestIfFriendTouchesBadThing(int, int);
1104 void TestIfBadThingTouchesFriend(int, int);
1105 void TestIfBadThingTouchesOtherBadThing(int, int);
1106 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1107
1108 void KillPlayer(struct PlayerInfo *);
1109 void BuryPlayer(struct PlayerInfo *);
1110 void RemovePlayer(struct PlayerInfo *);
1111
1112 static int getInvisibleActiveFromInvisibleElement(int);
1113 static int getInvisibleFromInvisibleActiveElement(int);
1114
1115 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1116
1117 /* for detection of endless loops, caused by custom element programming */
1118 /* (using maximal playfield width x 10 is just a rough approximation) */
1119 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1120
1121 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1122 {                                                                       \
1123   if (recursion_loop_detected)                                          \
1124     return (rc);                                                        \
1125                                                                         \
1126   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1127   {                                                                     \
1128     recursion_loop_detected = TRUE;                                     \
1129     recursion_loop_element = (e);                                       \
1130   }                                                                     \
1131                                                                         \
1132   recursion_loop_depth++;                                               \
1133 }
1134
1135 #define RECURSION_LOOP_DETECTION_END()                                  \
1136 {                                                                       \
1137   recursion_loop_depth--;                                               \
1138 }
1139
1140 static int recursion_loop_depth;
1141 static boolean recursion_loop_detected;
1142 static boolean recursion_loop_element;
1143
1144 static int map_player_action[MAX_PLAYERS];
1145
1146
1147 /* ------------------------------------------------------------------------- */
1148 /* definition of elements that automatically change to other elements after  */
1149 /* a specified time, eventually calling a function when changing             */
1150 /* ------------------------------------------------------------------------- */
1151
1152 /* forward declaration for changer functions */
1153 static void InitBuggyBase(int, int);
1154 static void WarnBuggyBase(int, int);
1155
1156 static void InitTrap(int, int);
1157 static void ActivateTrap(int, int);
1158 static void ChangeActiveTrap(int, int);
1159
1160 static void InitRobotWheel(int, int);
1161 static void RunRobotWheel(int, int);
1162 static void StopRobotWheel(int, int);
1163
1164 static void InitTimegateWheel(int, int);
1165 static void RunTimegateWheel(int, int);
1166
1167 static void InitMagicBallDelay(int, int);
1168 static void ActivateMagicBall(int, int);
1169
1170 struct ChangingElementInfo
1171 {
1172   int element;
1173   int target_element;
1174   int change_delay;
1175   void (*pre_change_function)(int x, int y);
1176   void (*change_function)(int x, int y);
1177   void (*post_change_function)(int x, int y);
1178 };
1179
1180 static struct ChangingElementInfo change_delay_list[] =
1181 {
1182   {
1183     EL_NUT_BREAKING,
1184     EL_EMERALD,
1185     6,
1186     NULL,
1187     NULL,
1188     NULL
1189   },
1190   {
1191     EL_PEARL_BREAKING,
1192     EL_EMPTY,
1193     8,
1194     NULL,
1195     NULL,
1196     NULL
1197   },
1198   {
1199     EL_EXIT_OPENING,
1200     EL_EXIT_OPEN,
1201     29,
1202     NULL,
1203     NULL,
1204     NULL
1205   },
1206   {
1207     EL_EXIT_CLOSING,
1208     EL_EXIT_CLOSED,
1209     29,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_STEEL_EXIT_OPENING,
1216     EL_STEEL_EXIT_OPEN,
1217     29,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_STEEL_EXIT_CLOSING,
1224     EL_STEEL_EXIT_CLOSED,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_EM_EXIT_OPENING,
1232     EL_EM_EXIT_OPEN,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_EM_EXIT_CLOSING,
1240 #if 1
1241     EL_EMPTY,
1242 #else
1243     EL_EM_EXIT_CLOSED,
1244 #endif
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_OPENING,
1252     EL_EM_STEEL_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_STEEL_EXIT_CLOSING,
1260 #if 1
1261     EL_STEELWALL,
1262 #else
1263     EL_EM_STEEL_EXIT_CLOSED,
1264 #endif
1265     29,
1266     NULL,
1267     NULL,
1268     NULL
1269   },
1270   {
1271     EL_SP_EXIT_OPENING,
1272     EL_SP_EXIT_OPEN,
1273     29,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_SP_EXIT_CLOSING,
1280     EL_SP_EXIT_CLOSED,
1281     29,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_SWITCHGATE_OPENING,
1288     EL_SWITCHGATE_OPEN,
1289     29,
1290     NULL,
1291     NULL,
1292     NULL
1293   },
1294   {
1295     EL_SWITCHGATE_CLOSING,
1296     EL_SWITCHGATE_CLOSED,
1297     29,
1298     NULL,
1299     NULL,
1300     NULL
1301   },
1302   {
1303     EL_TIMEGATE_OPENING,
1304     EL_TIMEGATE_OPEN,
1305     29,
1306     NULL,
1307     NULL,
1308     NULL
1309   },
1310   {
1311     EL_TIMEGATE_CLOSING,
1312     EL_TIMEGATE_CLOSED,
1313     29,
1314     NULL,
1315     NULL,
1316     NULL
1317   },
1318
1319   {
1320     EL_ACID_SPLASH_LEFT,
1321     EL_EMPTY,
1322     8,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327   {
1328     EL_ACID_SPLASH_RIGHT,
1329     EL_EMPTY,
1330     8,
1331     NULL,
1332     NULL,
1333     NULL
1334   },
1335   {
1336     EL_SP_BUGGY_BASE,
1337     EL_SP_BUGGY_BASE_ACTIVATING,
1338     0,
1339     InitBuggyBase,
1340     NULL,
1341     NULL
1342   },
1343   {
1344     EL_SP_BUGGY_BASE_ACTIVATING,
1345     EL_SP_BUGGY_BASE_ACTIVE,
1346     0,
1347     InitBuggyBase,
1348     NULL,
1349     NULL
1350   },
1351   {
1352     EL_SP_BUGGY_BASE_ACTIVE,
1353     EL_SP_BUGGY_BASE,
1354     0,
1355     InitBuggyBase,
1356     WarnBuggyBase,
1357     NULL
1358   },
1359   {
1360     EL_TRAP,
1361     EL_TRAP_ACTIVE,
1362     0,
1363     InitTrap,
1364     NULL,
1365     ActivateTrap
1366   },
1367   {
1368     EL_TRAP_ACTIVE,
1369     EL_TRAP,
1370     31,
1371     NULL,
1372     ChangeActiveTrap,
1373     NULL
1374   },
1375   {
1376     EL_ROBOT_WHEEL_ACTIVE,
1377     EL_ROBOT_WHEEL,
1378     0,
1379     InitRobotWheel,
1380     RunRobotWheel,
1381     StopRobotWheel
1382   },
1383   {
1384     EL_TIMEGATE_SWITCH_ACTIVE,
1385     EL_TIMEGATE_SWITCH,
1386     0,
1387     InitTimegateWheel,
1388     RunTimegateWheel,
1389     NULL
1390   },
1391   {
1392     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1393     EL_DC_TIMEGATE_SWITCH,
1394     0,
1395     InitTimegateWheel,
1396     RunTimegateWheel,
1397     NULL
1398   },
1399   {
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     EL_EMC_MAGIC_BALL_ACTIVE,
1402     0,
1403     InitMagicBallDelay,
1404     NULL,
1405     ActivateMagicBall
1406   },
1407   {
1408     EL_EMC_SPRING_BUMPER_ACTIVE,
1409     EL_EMC_SPRING_BUMPER,
1410     8,
1411     NULL,
1412     NULL,
1413     NULL
1414   },
1415   {
1416     EL_DIAGONAL_SHRINKING,
1417     EL_UNDEFINED,
1418     0,
1419     NULL,
1420     NULL,
1421     NULL
1422   },
1423   {
1424     EL_DIAGONAL_GROWING,
1425     EL_UNDEFINED,
1426     0,
1427     NULL,
1428     NULL,
1429     NULL,
1430   },
1431
1432   {
1433     EL_UNDEFINED,
1434     EL_UNDEFINED,
1435     -1,
1436     NULL,
1437     NULL,
1438     NULL
1439   }
1440 };
1441
1442 struct
1443 {
1444   int element;
1445   int push_delay_fixed, push_delay_random;
1446 }
1447 push_delay_list[] =
1448 {
1449   { EL_SPRING,                  0, 0 },
1450   { EL_BALLOON,                 0, 0 },
1451
1452   { EL_SOKOBAN_OBJECT,          2, 0 },
1453   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1454   { EL_SATELLITE,               2, 0 },
1455   { EL_SP_DISK_YELLOW,          2, 0 },
1456
1457   { EL_UNDEFINED,               0, 0 },
1458 };
1459
1460 struct
1461 {
1462   int element;
1463   int move_stepsize;
1464 }
1465 move_stepsize_list[] =
1466 {
1467   { EL_AMOEBA_DROP,             2 },
1468   { EL_AMOEBA_DROPPING,         2 },
1469   { EL_QUICKSAND_FILLING,       1 },
1470   { EL_QUICKSAND_EMPTYING,      1 },
1471   { EL_QUICKSAND_FAST_FILLING,  2 },
1472   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1473   { EL_MAGIC_WALL_FILLING,      2 },
1474   { EL_MAGIC_WALL_EMPTYING,     2 },
1475   { EL_BD_MAGIC_WALL_FILLING,   2 },
1476   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1477   { EL_DC_MAGIC_WALL_FILLING,   2 },
1478   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1479
1480   { EL_UNDEFINED,               0 },
1481 };
1482
1483 struct
1484 {
1485   int element;
1486   int count;
1487 }
1488 collect_count_list[] =
1489 {
1490   { EL_EMERALD,                 1 },
1491   { EL_BD_DIAMOND,              1 },
1492   { EL_EMERALD_YELLOW,          1 },
1493   { EL_EMERALD_RED,             1 },
1494   { EL_EMERALD_PURPLE,          1 },
1495   { EL_DIAMOND,                 3 },
1496   { EL_SP_INFOTRON,             1 },
1497   { EL_PEARL,                   5 },
1498   { EL_CRYSTAL,                 8 },
1499
1500   { EL_UNDEFINED,               0 },
1501 };
1502
1503 struct
1504 {
1505   int element;
1506   int direction;
1507 }
1508 access_direction_list[] =
1509 {
1510   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1513   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1514   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1515   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1516   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1517   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1518   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1519   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1520   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1521
1522   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1523   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1524   { EL_SP_PORT_UP,                                                   MV_DOWN },
1525   { EL_SP_PORT_DOWN,                                         MV_UP           },
1526   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1527   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1528   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1530   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1531   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1532   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1533   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1534   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1535   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1536   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1537   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1538   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1539   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1540   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1541
1542   { EL_UNDEFINED,                       MV_NONE                              }
1543 };
1544
1545 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1546
1547 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1548 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1549 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1550                                  IS_JUST_CHANGING(x, y))
1551
1552 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1553
1554 /* static variables for playfield scan mode (scanning forward or backward) */
1555 static int playfield_scan_start_x = 0;
1556 static int playfield_scan_start_y = 0;
1557 static int playfield_scan_delta_x = 1;
1558 static int playfield_scan_delta_y = 1;
1559
1560 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1561                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1562                                      (y) += playfield_scan_delta_y)     \
1563                                 for ((x) = playfield_scan_start_x;      \
1564                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1565                                      (x) += playfield_scan_delta_x)
1566
1567 #ifdef DEBUG
1568 void DEBUG_SetMaximumDynamite()
1569 {
1570   int i;
1571
1572   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1573     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1574       local_player->inventory_element[local_player->inventory_size++] =
1575         EL_DYNAMITE;
1576 }
1577 #endif
1578
1579 static void InitPlayfieldScanModeVars()
1580 {
1581   if (game.use_reverse_scan_direction)
1582   {
1583     playfield_scan_start_x = lev_fieldx - 1;
1584     playfield_scan_start_y = lev_fieldy - 1;
1585
1586     playfield_scan_delta_x = -1;
1587     playfield_scan_delta_y = -1;
1588   }
1589   else
1590   {
1591     playfield_scan_start_x = 0;
1592     playfield_scan_start_y = 0;
1593
1594     playfield_scan_delta_x = 1;
1595     playfield_scan_delta_y = 1;
1596   }
1597 }
1598
1599 static void InitPlayfieldScanMode(int mode)
1600 {
1601   game.use_reverse_scan_direction =
1602     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1603
1604   InitPlayfieldScanModeVars();
1605 }
1606
1607 static int get_move_delay_from_stepsize(int move_stepsize)
1608 {
1609   move_stepsize =
1610     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1611
1612   /* make sure that stepsize value is always a power of 2 */
1613   move_stepsize = (1 << log_2(move_stepsize));
1614
1615   return TILEX / move_stepsize;
1616 }
1617
1618 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1619                                boolean init_game)
1620 {
1621   int player_nr = player->index_nr;
1622   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1623   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1624
1625   /* do no immediately change move delay -- the player might just be moving */
1626   player->move_delay_value_next = move_delay;
1627
1628   /* information if player can move must be set separately */
1629   player->cannot_move = cannot_move;
1630
1631   if (init_game)
1632   {
1633     player->move_delay       = game.initial_move_delay[player_nr];
1634     player->move_delay_value = game.initial_move_delay_value[player_nr];
1635
1636     player->move_delay_value_next = -1;
1637
1638     player->move_delay_reset_counter = 0;
1639   }
1640 }
1641
1642 void GetPlayerConfig()
1643 {
1644   GameFrameDelay = setup.game_frame_delay;
1645
1646   if (!audio.sound_available)
1647     setup.sound_simple = FALSE;
1648
1649   if (!audio.loops_available)
1650     setup.sound_loops = FALSE;
1651
1652   if (!audio.music_available)
1653     setup.sound_music = FALSE;
1654
1655   if (!video.fullscreen_available)
1656     setup.fullscreen = FALSE;
1657
1658   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1659
1660   SetAudioMode(setup.sound);
1661   InitJoysticks();
1662 }
1663
1664 int GetElementFromGroupElement(int element)
1665 {
1666   if (IS_GROUP_ELEMENT(element))
1667   {
1668     struct ElementGroupInfo *group = element_info[element].group;
1669     int last_anim_random_frame = gfx.anim_random_frame;
1670     int element_pos;
1671
1672     if (group->choice_mode == ANIM_RANDOM)
1673       gfx.anim_random_frame = RND(group->num_elements_resolved);
1674
1675     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1676                                     group->choice_mode, 0,
1677                                     group->choice_pos);
1678
1679     if (group->choice_mode == ANIM_RANDOM)
1680       gfx.anim_random_frame = last_anim_random_frame;
1681
1682     group->choice_pos++;
1683
1684     element = group->element_resolved[element_pos];
1685   }
1686
1687   return element;
1688 }
1689
1690 static void InitPlayerField(int x, int y, int element, boolean init_game)
1691 {
1692   if (element == EL_SP_MURPHY)
1693   {
1694     if (init_game)
1695     {
1696       if (stored_player[0].present)
1697       {
1698         Feld[x][y] = EL_SP_MURPHY_CLONE;
1699
1700         return;
1701       }
1702       else
1703       {
1704         stored_player[0].initial_element = element;
1705         stored_player[0].use_murphy = TRUE;
1706
1707         if (!level.use_artwork_element[0])
1708           stored_player[0].artwork_element = EL_SP_MURPHY;
1709       }
1710
1711       Feld[x][y] = EL_PLAYER_1;
1712     }
1713   }
1714
1715   if (init_game)
1716   {
1717     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1718     int jx = player->jx, jy = player->jy;
1719
1720     player->present = TRUE;
1721
1722     player->block_last_field = (element == EL_SP_MURPHY ?
1723                                 level.sp_block_last_field :
1724                                 level.block_last_field);
1725
1726     /* ---------- initialize player's last field block delay --------------- */
1727
1728     /* always start with reliable default value (no adjustment needed) */
1729     player->block_delay_adjustment = 0;
1730
1731     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1732     if (player->block_last_field && element == EL_SP_MURPHY)
1733       player->block_delay_adjustment = 1;
1734
1735     /* special case 2: in game engines before 3.1.1, blocking was different */
1736     if (game.use_block_last_field_bug)
1737       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1738
1739     if (!options.network || player->connected)
1740     {
1741       player->active = TRUE;
1742
1743       /* remove potentially duplicate players */
1744       if (StorePlayer[jx][jy] == Feld[x][y])
1745         StorePlayer[jx][jy] = 0;
1746
1747       StorePlayer[x][y] = Feld[x][y];
1748
1749 #if DEBUG_INIT_PLAYER
1750       if (options.debug)
1751       {
1752         printf("- player element %d activated", player->element_nr);
1753         printf(" (local player is %d and currently %s)\n",
1754                local_player->element_nr,
1755                local_player->active ? "active" : "not active");
1756       }
1757     }
1758 #endif
1759
1760     Feld[x][y] = EL_EMPTY;
1761
1762     player->jx = player->last_jx = x;
1763     player->jy = player->last_jy = y;
1764   }
1765
1766 #if USE_PLAYER_REANIMATION
1767   if (!init_game)
1768   {
1769     int player_nr = GET_PLAYER_NR(element);
1770     struct PlayerInfo *player = &stored_player[player_nr];
1771
1772     if (player->active && player->killed)
1773       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1774   }
1775 #endif
1776 }
1777
1778 static void InitField(int x, int y, boolean init_game)
1779 {
1780   int element = Feld[x][y];
1781
1782   switch (element)
1783   {
1784     case EL_SP_MURPHY:
1785     case EL_PLAYER_1:
1786     case EL_PLAYER_2:
1787     case EL_PLAYER_3:
1788     case EL_PLAYER_4:
1789       InitPlayerField(x, y, element, init_game);
1790       break;
1791
1792     case EL_SOKOBAN_FIELD_PLAYER:
1793       element = Feld[x][y] = EL_PLAYER_1;
1794       InitField(x, y, init_game);
1795
1796       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1797       InitField(x, y, init_game);
1798       break;
1799
1800     case EL_SOKOBAN_FIELD_EMPTY:
1801       local_player->sokobanfields_still_needed++;
1802       break;
1803
1804     case EL_STONEBLOCK:
1805       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1807       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1808         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1809       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1810         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1811       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1812         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1813       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1814         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1815       break;
1816
1817     case EL_BUG:
1818     case EL_BUG_RIGHT:
1819     case EL_BUG_UP:
1820     case EL_BUG_LEFT:
1821     case EL_BUG_DOWN:
1822     case EL_SPACESHIP:
1823     case EL_SPACESHIP_RIGHT:
1824     case EL_SPACESHIP_UP:
1825     case EL_SPACESHIP_LEFT:
1826     case EL_SPACESHIP_DOWN:
1827     case EL_BD_BUTTERFLY:
1828     case EL_BD_BUTTERFLY_RIGHT:
1829     case EL_BD_BUTTERFLY_UP:
1830     case EL_BD_BUTTERFLY_LEFT:
1831     case EL_BD_BUTTERFLY_DOWN:
1832     case EL_BD_FIREFLY:
1833     case EL_BD_FIREFLY_RIGHT:
1834     case EL_BD_FIREFLY_UP:
1835     case EL_BD_FIREFLY_LEFT:
1836     case EL_BD_FIREFLY_DOWN:
1837     case EL_PACMAN_RIGHT:
1838     case EL_PACMAN_UP:
1839     case EL_PACMAN_LEFT:
1840     case EL_PACMAN_DOWN:
1841     case EL_YAMYAM:
1842     case EL_YAMYAM_LEFT:
1843     case EL_YAMYAM_RIGHT:
1844     case EL_YAMYAM_UP:
1845     case EL_YAMYAM_DOWN:
1846     case EL_DARK_YAMYAM:
1847     case EL_ROBOT:
1848     case EL_PACMAN:
1849     case EL_SP_SNIKSNAK:
1850     case EL_SP_ELECTRON:
1851     case EL_MOLE:
1852     case EL_MOLE_LEFT:
1853     case EL_MOLE_RIGHT:
1854     case EL_MOLE_UP:
1855     case EL_MOLE_DOWN:
1856       InitMovDir(x, y);
1857       break;
1858
1859     case EL_AMOEBA_FULL:
1860     case EL_BD_AMOEBA:
1861       InitAmoebaNr(x, y);
1862       break;
1863
1864     case EL_AMOEBA_DROP:
1865       if (y == lev_fieldy - 1)
1866       {
1867         Feld[x][y] = EL_AMOEBA_GROWING;
1868         Store[x][y] = EL_AMOEBA_WET;
1869       }
1870       break;
1871
1872     case EL_DYNAMITE_ACTIVE:
1873     case EL_SP_DISK_RED_ACTIVE:
1874     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1875     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1876     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1877     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1878       MovDelay[x][y] = 96;
1879       break;
1880
1881     case EL_EM_DYNAMITE_ACTIVE:
1882       MovDelay[x][y] = 32;
1883       break;
1884
1885     case EL_LAMP:
1886       local_player->lights_still_needed++;
1887       break;
1888
1889     case EL_PENGUIN:
1890       local_player->friends_still_needed++;
1891       break;
1892
1893     case EL_PIG:
1894     case EL_DRAGON:
1895       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1896       break;
1897
1898     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1904     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1905     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1906     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1907     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1908     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1909     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1910       if (init_game)
1911       {
1912         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1913         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1914         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1915
1916         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1917         {
1918           game.belt_dir[belt_nr] = belt_dir;
1919           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1920         }
1921         else    /* more than one switch -- set it like the first switch */
1922         {
1923           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1924         }
1925       }
1926       break;
1927
1928 #if !USE_BOTH_SWITCHGATE_SWITCHES
1929     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1930       if (init_game)
1931         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1932       break;
1933
1934     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1935       if (init_game)
1936         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1937       break;
1938 #endif
1939
1940     case EL_LIGHT_SWITCH_ACTIVE:
1941       if (init_game)
1942         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1943       break;
1944
1945     case EL_INVISIBLE_STEELWALL:
1946     case EL_INVISIBLE_WALL:
1947     case EL_INVISIBLE_SAND:
1948       if (game.light_time_left > 0 ||
1949           game.lenses_time_left > 0)
1950         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1951       break;
1952
1953     case EL_EMC_MAGIC_BALL:
1954       if (game.ball_state)
1955         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1956       break;
1957
1958     case EL_EMC_MAGIC_BALL_SWITCH:
1959       if (game.ball_state)
1960         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1961       break;
1962
1963     case EL_TRIGGER_PLAYER:
1964     case EL_TRIGGER_ELEMENT:
1965     case EL_TRIGGER_CE_VALUE:
1966     case EL_TRIGGER_CE_SCORE:
1967     case EL_SELF:
1968     case EL_ANY_ELEMENT:
1969     case EL_CURRENT_CE_VALUE:
1970     case EL_CURRENT_CE_SCORE:
1971     case EL_PREV_CE_1:
1972     case EL_PREV_CE_2:
1973     case EL_PREV_CE_3:
1974     case EL_PREV_CE_4:
1975     case EL_PREV_CE_5:
1976     case EL_PREV_CE_6:
1977     case EL_PREV_CE_7:
1978     case EL_PREV_CE_8:
1979     case EL_NEXT_CE_1:
1980     case EL_NEXT_CE_2:
1981     case EL_NEXT_CE_3:
1982     case EL_NEXT_CE_4:
1983     case EL_NEXT_CE_5:
1984     case EL_NEXT_CE_6:
1985     case EL_NEXT_CE_7:
1986     case EL_NEXT_CE_8:
1987       /* reference elements should not be used on the playfield */
1988       Feld[x][y] = EL_EMPTY;
1989       break;
1990
1991     default:
1992       if (IS_CUSTOM_ELEMENT(element))
1993       {
1994         if (CAN_MOVE(element))
1995           InitMovDir(x, y);
1996
1997 #if USE_NEW_CUSTOM_VALUE
1998         if (!element_info[element].use_last_ce_value || init_game)
1999           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2000 #endif
2001       }
2002       else if (IS_GROUP_ELEMENT(element))
2003       {
2004         Feld[x][y] = GetElementFromGroupElement(element);
2005
2006         InitField(x, y, init_game);
2007       }
2008
2009       break;
2010   }
2011
2012   if (!init_game)
2013     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2014 }
2015
2016 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2017 {
2018   InitField(x, y, init_game);
2019
2020   /* not needed to call InitMovDir() -- already done by InitField()! */
2021   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2022       CAN_MOVE(Feld[x][y]))
2023     InitMovDir(x, y);
2024 }
2025
2026 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2027 {
2028   int old_element = Feld[x][y];
2029
2030   InitField(x, y, init_game);
2031
2032   /* not needed to call InitMovDir() -- already done by InitField()! */
2033   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034       CAN_MOVE(old_element) &&
2035       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2036     InitMovDir(x, y);
2037
2038   /* this case is in fact a combination of not less than three bugs:
2039      first, it calls InitMovDir() for elements that can move, although this is
2040      already done by InitField(); then, it checks the element that was at this
2041      field _before_ the call to InitField() (which can change it); lastly, it
2042      was not called for "mole with direction" elements, which were treated as
2043      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2044   */
2045 }
2046
2047 static int get_key_element_from_nr(int key_nr)
2048 {
2049   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2050                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2051                           EL_EM_KEY_1 : EL_KEY_1);
2052
2053   return key_base_element + key_nr;
2054 }
2055
2056 static int get_next_dropped_element(struct PlayerInfo *player)
2057 {
2058   return (player->inventory_size > 0 ?
2059           player->inventory_element[player->inventory_size - 1] :
2060           player->inventory_infinite_element != EL_UNDEFINED ?
2061           player->inventory_infinite_element :
2062           player->dynabombs_left > 0 ?
2063           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2064           EL_UNDEFINED);
2065 }
2066
2067 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2068 {
2069   /* pos >= 0: get element from bottom of the stack;
2070      pos <  0: get element from top of the stack */
2071
2072   if (pos < 0)
2073   {
2074     int min_inventory_size = -pos;
2075     int inventory_pos = player->inventory_size - min_inventory_size;
2076     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2077
2078     return (player->inventory_size >= min_inventory_size ?
2079             player->inventory_element[inventory_pos] :
2080             player->inventory_infinite_element != EL_UNDEFINED ?
2081             player->inventory_infinite_element :
2082             player->dynabombs_left >= min_dynabombs_left ?
2083             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2084             EL_UNDEFINED);
2085   }
2086   else
2087   {
2088     int min_dynabombs_left = pos + 1;
2089     int min_inventory_size = pos + 1 - player->dynabombs_left;
2090     int inventory_pos = pos - player->dynabombs_left;
2091
2092     return (player->inventory_infinite_element != EL_UNDEFINED ?
2093             player->inventory_infinite_element :
2094             player->dynabombs_left >= min_dynabombs_left ?
2095             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096             player->inventory_size >= min_inventory_size ?
2097             player->inventory_element[inventory_pos] :
2098             EL_UNDEFINED);
2099   }
2100 }
2101
2102 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2103 {
2104   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2105   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2106   int compare_result;
2107
2108   if (gpo1->sort_priority != gpo2->sort_priority)
2109     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2110   else
2111     compare_result = gpo1->nr - gpo2->nr;
2112
2113   return compare_result;
2114 }
2115
2116 void InitGameControlValues()
2117 {
2118   int i;
2119
2120   for (i = 0; game_panel_controls[i].nr != -1; i++)
2121   {
2122     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2123     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2124     struct TextPosInfo *pos = gpc->pos;
2125     int nr = gpc->nr;
2126     int type = gpc->type;
2127
2128     if (nr != i)
2129     {
2130       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2131       Error(ERR_EXIT, "this should not happen -- please debug");
2132     }
2133
2134     /* force update of game controls after initialization */
2135     gpc->value = gpc->last_value = -1;
2136     gpc->frame = gpc->last_frame = -1;
2137     gpc->gfx_frame = -1;
2138
2139     /* determine panel value width for later calculation of alignment */
2140     if (type == TYPE_INTEGER || type == TYPE_STRING)
2141     {
2142       pos->width = pos->size * getFontWidth(pos->font);
2143       pos->height = getFontHeight(pos->font);
2144     }
2145     else if (type == TYPE_ELEMENT)
2146     {
2147       pos->width = pos->size;
2148       pos->height = pos->size;
2149     }
2150
2151     /* fill structure for game panel draw order */
2152     gpo->nr = gpc->nr;
2153     gpo->sort_priority = pos->sort_priority;
2154   }
2155
2156   /* sort game panel controls according to sort_priority and control number */
2157   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2158         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2159 }
2160
2161 void UpdatePlayfieldElementCount()
2162 {
2163   boolean use_element_count = FALSE;
2164   int i, j, x, y;
2165
2166   /* first check if it is needed at all to calculate playfield element count */
2167   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2168     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2169       use_element_count = TRUE;
2170
2171   if (!use_element_count)
2172     return;
2173
2174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2175     element_info[i].element_count = 0;
2176
2177   SCAN_PLAYFIELD(x, y)
2178   {
2179     element_info[Feld[x][y]].element_count++;
2180   }
2181
2182   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2183     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2184       if (IS_IN_GROUP(j, i))
2185         element_info[EL_GROUP_START + i].element_count +=
2186           element_info[j].element_count;
2187 }
2188
2189 void UpdateGameControlValues()
2190 {
2191   int i, k;
2192   int time = (local_player->LevelSolved ?
2193               local_player->LevelSolved_CountingTime :
2194               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2195               level.native_em_level->lev->time :
2196               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2197               level.native_sp_level->game_sp->time_played :
2198               game.no_time_limit ? TimePlayed : TimeLeft);
2199   int score = (local_player->LevelSolved ?
2200                local_player->LevelSolved_CountingScore :
2201                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2202                level.native_em_level->lev->score :
2203                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2204                level.native_sp_level->game_sp->score :
2205                local_player->score);
2206   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207               level.native_em_level->lev->required :
2208               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209               level.native_sp_level->game_sp->infotrons_still_needed :
2210               local_player->gems_still_needed);
2211   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2212                      level.native_em_level->lev->required > 0 :
2213                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2214                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2215                      local_player->gems_still_needed > 0 ||
2216                      local_player->sokobanfields_still_needed > 0 ||
2217                      local_player->lights_still_needed > 0);
2218
2219   UpdatePlayfieldElementCount();
2220
2221   /* update game panel control values */
2222
2223   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2224   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2225
2226   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2227   for (i = 0; i < MAX_NUM_KEYS; i++)
2228     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2229   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2230   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2231
2232   if (game.centered_player_nr == -1)
2233   {
2234     for (i = 0; i < MAX_PLAYERS; i++)
2235     {
2236       /* only one player in Supaplex game engine */
2237       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2238         break;
2239
2240       for (k = 0; k < MAX_NUM_KEYS; k++)
2241       {
2242         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2243         {
2244           if (level.native_em_level->ply[i]->keys & (1 << k))
2245             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2246               get_key_element_from_nr(k);
2247         }
2248         else if (stored_player[i].key[k])
2249           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2250             get_key_element_from_nr(k);
2251       }
2252
2253       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2254         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2255           level.native_em_level->ply[i]->dynamite;
2256       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2257         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2258           level.native_sp_level->game_sp->red_disk_count;
2259       else
2260         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2261           stored_player[i].inventory_size;
2262
2263       if (stored_player[i].num_white_keys > 0)
2264         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2265           EL_DC_KEY_WHITE;
2266
2267       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2268         stored_player[i].num_white_keys;
2269     }
2270   }
2271   else
2272   {
2273     int player_nr = game.centered_player_nr;
2274
2275     for (k = 0; k < MAX_NUM_KEYS; k++)
2276     {
2277       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2278       {
2279         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2280           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281             get_key_element_from_nr(k);
2282       }
2283       else if (stored_player[player_nr].key[k])
2284         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2285           get_key_element_from_nr(k);
2286     }
2287
2288     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2289       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290         level.native_em_level->ply[player_nr]->dynamite;
2291     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2292       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2293         level.native_sp_level->game_sp->red_disk_count;
2294     else
2295       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2296         stored_player[player_nr].inventory_size;
2297
2298     if (stored_player[player_nr].num_white_keys > 0)
2299       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2300
2301     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302       stored_player[player_nr].num_white_keys;
2303   }
2304
2305   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2306   {
2307     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2308       get_inventory_element_from_pos(local_player, i);
2309     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2310       get_inventory_element_from_pos(local_player, -i - 1);
2311   }
2312
2313   game_panel_controls[GAME_PANEL_SCORE].value = score;
2314   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2315
2316   game_panel_controls[GAME_PANEL_TIME].value = time;
2317
2318   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2319   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2320   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2321
2322   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2323
2324   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2325     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2326      EL_EMPTY);
2327   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2328     local_player->shield_normal_time_left;
2329   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2330     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2331      EL_EMPTY);
2332   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2333     local_player->shield_deadly_time_left;
2334
2335   game_panel_controls[GAME_PANEL_EXIT].value =
2336     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2337
2338   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2339     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2340   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2341     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2342      EL_EMC_MAGIC_BALL_SWITCH);
2343
2344   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2345     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2346   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2347     game.light_time_left;
2348
2349   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2350     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2351   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2352     game.timegate_time_left;
2353
2354   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2355     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2356
2357   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2358     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2359   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2360     game.lenses_time_left;
2361
2362   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2363     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2364   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2365     game.magnify_time_left;
2366
2367   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2368     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2369      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2370      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2371      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2372      EL_BALLOON_SWITCH_NONE);
2373
2374   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2375     local_player->dynabomb_count;
2376   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2377     local_player->dynabomb_size;
2378   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2379     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2380
2381   game_panel_controls[GAME_PANEL_PENGUINS].value =
2382     local_player->friends_still_needed;
2383
2384   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2385     local_player->sokobanfields_still_needed;
2386   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2387     local_player->sokobanfields_still_needed;
2388
2389   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2390     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2391
2392   for (i = 0; i < NUM_BELTS; i++)
2393   {
2394     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2395       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2396        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2397     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2398       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2399   }
2400
2401   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2402     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2403   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2404     game.magic_wall_time_left;
2405
2406 #if USE_PLAYER_GRAVITY
2407   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2408     local_player->gravity;
2409 #else
2410   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2411 #endif
2412
2413   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2414     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2415
2416   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2418       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2419        game.panel.element[i].id : EL_UNDEFINED);
2420
2421   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2422     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2423       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2424        element_info[game.panel.element_count[i].id].element_count : 0);
2425
2426   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2428       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2429        element_info[game.panel.ce_score[i].id].collect_score : 0);
2430
2431   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2432     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2433       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2434        element_info[game.panel.ce_score_element[i].id].collect_score :
2435        EL_UNDEFINED);
2436
2437   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2438   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2439   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2440
2441   /* update game panel control frames */
2442
2443   for (i = 0; game_panel_controls[i].nr != -1; i++)
2444   {
2445     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2446
2447     if (gpc->type == TYPE_ELEMENT)
2448     {
2449       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2450       {
2451         int last_anim_random_frame = gfx.anim_random_frame;
2452         int element = gpc->value;
2453         int graphic = el2panelimg(element);
2454
2455         if (gpc->value != gpc->last_value)
2456         {
2457           gpc->gfx_frame = 0;
2458           gpc->gfx_random = INIT_GFX_RANDOM();
2459         }
2460         else
2461         {
2462           gpc->gfx_frame++;
2463
2464           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2465               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2466             gpc->gfx_random = INIT_GFX_RANDOM();
2467         }
2468
2469         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2470           gfx.anim_random_frame = gpc->gfx_random;
2471
2472         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2473           gpc->gfx_frame = element_info[element].collect_score;
2474
2475         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2476                                               gpc->gfx_frame);
2477
2478         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2479           gfx.anim_random_frame = last_anim_random_frame;
2480       }
2481     }
2482   }
2483 }
2484
2485 void DisplayGameControlValues()
2486 {
2487   boolean redraw_panel = FALSE;
2488   int i;
2489
2490   for (i = 0; game_panel_controls[i].nr != -1; i++)
2491   {
2492     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2493
2494     if (PANEL_DEACTIVATED(gpc->pos))
2495       continue;
2496
2497     if (gpc->value == gpc->last_value &&
2498         gpc->frame == gpc->last_frame)
2499       continue;
2500
2501     redraw_panel = TRUE;
2502   }
2503
2504   if (!redraw_panel)
2505     return;
2506
2507   /* copy default game door content to main double buffer */
2508 #if 1
2509   /* !!! CHECK AGAIN !!! */
2510   SetPanelBackground();
2511   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2512   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2513 #else
2514   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2515              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2516 #endif
2517
2518   /* redraw game control buttons */
2519 #if 1
2520   RedrawGameButtons();
2521 #else
2522   UnmapGameButtons();
2523   MapGameButtons();
2524 #endif
2525
2526   game_status = GAME_MODE_PSEUDO_PANEL;
2527
2528 #if 1
2529   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2530 #else
2531   for (i = 0; game_panel_controls[i].nr != -1; i++)
2532 #endif
2533   {
2534 #if 1
2535     int nr = game_panel_order[i].nr;
2536     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2537 #else
2538     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2539     int nr = gpc->nr;
2540 #endif
2541     struct TextPosInfo *pos = gpc->pos;
2542     int type = gpc->type;
2543     int value = gpc->value;
2544     int frame = gpc->frame;
2545 #if 0
2546     int last_value = gpc->last_value;
2547     int last_frame = gpc->last_frame;
2548 #endif
2549     int size = pos->size;
2550     int font = pos->font;
2551     boolean draw_masked = pos->draw_masked;
2552     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2553
2554     if (PANEL_DEACTIVATED(pos))
2555       continue;
2556
2557 #if 0
2558     if (value == last_value && frame == last_frame)
2559       continue;
2560 #endif
2561
2562     gpc->last_value = value;
2563     gpc->last_frame = frame;
2564
2565 #if 0
2566     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2567 #endif
2568
2569     if (type == TYPE_INTEGER)
2570     {
2571       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2572           nr == GAME_PANEL_TIME)
2573       {
2574         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2575
2576         if (use_dynamic_size)           /* use dynamic number of digits */
2577         {
2578           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2579           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2580           int size2 = size1 + 1;
2581           int font1 = pos->font;
2582           int font2 = pos->font_alt;
2583
2584           size = (value < value_change ? size1 : size2);
2585           font = (value < value_change ? font1 : font2);
2586
2587 #if 0
2588           /* clear background if value just changed its size (dynamic digits) */
2589           if ((last_value < value_change) != (value < value_change))
2590           {
2591             int width1 = size1 * getFontWidth(font1);
2592             int width2 = size2 * getFontWidth(font2);
2593             int max_width = MAX(width1, width2);
2594             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2595
2596             pos->width = max_width;
2597
2598             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2599                                        max_width, max_height);
2600           }
2601 #endif
2602         }
2603       }
2604
2605 #if 1
2606       /* correct text size if "digits" is zero or less */
2607       if (size <= 0)
2608         size = strlen(int2str(value, size));
2609
2610       /* dynamically correct text alignment */
2611       pos->width = size * getFontWidth(font);
2612 #endif
2613
2614       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2615                   int2str(value, size), font, mask_mode);
2616     }
2617     else if (type == TYPE_ELEMENT)
2618     {
2619       int element, graphic;
2620       Bitmap *src_bitmap;
2621       int src_x, src_y;
2622       int width, height;
2623       int dst_x = PANEL_XPOS(pos);
2624       int dst_y = PANEL_YPOS(pos);
2625
2626 #if 1
2627       if (value != EL_UNDEFINED && value != EL_EMPTY)
2628       {
2629         element = value;
2630         graphic = el2panelimg(value);
2631
2632         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2633
2634 #if 1
2635         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2636           size = TILESIZE;
2637 #endif
2638
2639         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2640                               &src_x, &src_y);
2641
2642         width  = graphic_info[graphic].width  * size / TILESIZE;
2643         height = graphic_info[graphic].height * size / TILESIZE;
2644
2645         if (draw_masked)
2646         {
2647           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2648                         dst_x - src_x, dst_y - src_y);
2649           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2650                            dst_x, dst_y);
2651         }
2652         else
2653         {
2654           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2655                      dst_x, dst_y);
2656         }
2657       }
2658 #else
2659       if (value == EL_UNDEFINED || value == EL_EMPTY)
2660       {
2661         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2662         graphic = el2panelimg(element);
2663
2664         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2665         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2666         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2667       }
2668       else
2669       {
2670         element = value;
2671         graphic = el2panelimg(value);
2672
2673         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2674       }
2675
2676       width  = graphic_info[graphic].width  * size / TILESIZE;
2677       height = graphic_info[graphic].height * size / TILESIZE;
2678
2679       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2680 #endif
2681     }
2682     else if (type == TYPE_STRING)
2683     {
2684       boolean active = (value != 0);
2685       char *state_normal = "off";
2686       char *state_active = "on";
2687       char *state = (active ? state_active : state_normal);
2688       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2689                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2690                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2691                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2692
2693       if (nr == GAME_PANEL_GRAVITY_STATE)
2694       {
2695         int font1 = pos->font;          /* (used for normal state) */
2696         int font2 = pos->font_alt;      /* (used for active state) */
2697 #if 0
2698         int size1 = strlen(state_normal);
2699         int size2 = strlen(state_active);
2700         int width1 = size1 * getFontWidth(font1);
2701         int width2 = size2 * getFontWidth(font2);
2702         int max_width = MAX(width1, width2);
2703         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2704
2705         pos->width = max_width;
2706
2707         /* clear background for values that may have changed its size */
2708         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2709                                    max_width, max_height);
2710 #endif
2711
2712         font = (active ? font2 : font1);
2713       }
2714
2715       if (s != NULL)
2716       {
2717         char *s_cut;
2718
2719 #if 1
2720         if (size <= 0)
2721         {
2722           /* don't truncate output if "chars" is zero or less */
2723           size = strlen(s);
2724
2725           /* dynamically correct text alignment */
2726           pos->width = size * getFontWidth(font);
2727         }
2728 #endif
2729
2730         s_cut = getStringCopyN(s, size);
2731
2732         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2733                     s_cut, font, mask_mode);
2734
2735         free(s_cut);
2736       }
2737     }
2738
2739     redraw_mask |= REDRAW_DOOR_1;
2740   }
2741
2742   game_status = GAME_MODE_PLAYING;
2743 }
2744
2745 void UpdateAndDisplayGameControlValues()
2746 {
2747   if (tape.warp_forward)
2748     return;
2749
2750   UpdateGameControlValues();
2751   DisplayGameControlValues();
2752 }
2753
2754 void DrawGameValue_Emeralds(int value)
2755 {
2756   struct TextPosInfo *pos = &game.panel.gems;
2757   int font_nr = pos->font;
2758   int font_width = getFontWidth(font_nr);
2759   int chars = pos->size;
2760
2761 #if 1
2762   return;       /* !!! USE NEW STUFF !!! */
2763 #endif
2764
2765   if (PANEL_DEACTIVATED(pos))
2766     return;
2767
2768   pos->width = chars * font_width;
2769
2770   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2771 }
2772
2773 void DrawGameValue_Dynamite(int value)
2774 {
2775   struct TextPosInfo *pos = &game.panel.inventory_count;
2776   int font_nr = pos->font;
2777   int font_width = getFontWidth(font_nr);
2778   int chars = pos->size;
2779
2780 #if 1
2781   return;       /* !!! USE NEW STUFF !!! */
2782 #endif
2783
2784   if (PANEL_DEACTIVATED(pos))
2785     return;
2786
2787   pos->width = chars * font_width;
2788
2789   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2790 }
2791
2792 void DrawGameValue_Score(int value)
2793 {
2794   struct TextPosInfo *pos = &game.panel.score;
2795   int font_nr = pos->font;
2796   int font_width = getFontWidth(font_nr);
2797   int chars = pos->size;
2798
2799 #if 1
2800   return;       /* !!! USE NEW STUFF !!! */
2801 #endif
2802
2803   if (PANEL_DEACTIVATED(pos))
2804     return;
2805
2806   pos->width = chars * font_width;
2807
2808   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2809 }
2810
2811 void DrawGameValue_Time(int value)
2812 {
2813   struct TextPosInfo *pos = &game.panel.time;
2814   static int last_value = -1;
2815   int chars1 = 3;
2816   int chars2 = 4;
2817   int chars = pos->size;
2818   int font1_nr = pos->font;
2819   int font2_nr = pos->font_alt;
2820   int font_nr = font1_nr;
2821   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2822
2823 #if 1
2824   return;       /* !!! USE NEW STUFF !!! */
2825 #endif
2826
2827   if (PANEL_DEACTIVATED(pos))
2828     return;
2829
2830   if (use_dynamic_chars)                /* use dynamic number of chars */
2831   {
2832     chars   = (value < 1000 ? chars1   : chars2);
2833     font_nr = (value < 1000 ? font1_nr : font2_nr);
2834   }
2835
2836   /* clear background if value just changed its size (dynamic chars only) */
2837   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2838   {
2839     int width1 = chars1 * getFontWidth(font1_nr);
2840     int width2 = chars2 * getFontWidth(font2_nr);
2841     int max_width = MAX(width1, width2);
2842     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2843
2844     pos->width = max_width;
2845
2846     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2847                                max_width, max_height);
2848   }
2849
2850   pos->width = chars * getFontWidth(font_nr);
2851
2852   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2853
2854   last_value = value;
2855 }
2856
2857 void DrawGameValue_Level(int value)
2858 {
2859   struct TextPosInfo *pos = &game.panel.level_number;
2860   int chars1 = 2;
2861   int chars2 = 3;
2862   int chars = pos->size;
2863   int font1_nr = pos->font;
2864   int font2_nr = pos->font_alt;
2865   int font_nr = font1_nr;
2866   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2867
2868 #if 1
2869   return;       /* !!! USE NEW STUFF !!! */
2870 #endif
2871
2872   if (PANEL_DEACTIVATED(pos))
2873     return;
2874
2875   if (use_dynamic_chars)                /* use dynamic number of chars */
2876   {
2877     chars   = (level_nr < 100 ? chars1   : chars2);
2878     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2879   }
2880
2881   pos->width = chars * getFontWidth(font_nr);
2882
2883   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2884 }
2885
2886 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2887 {
2888   int i;
2889
2890 #if 1
2891   return;       /* !!! USE NEW STUFF !!! */
2892 #endif
2893
2894   for (i = 0; i < MAX_NUM_KEYS; i++)
2895   {
2896     struct TextPosInfo *pos = &game.panel.key[i];
2897     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2898     int src_y = DOOR_GFX_PAGEY1 + 123;
2899     int dst_x = PANEL_XPOS(pos);
2900     int dst_y = PANEL_YPOS(pos);
2901
2902     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2903                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2904                    EL_KEY_1) + i;
2905     int graphic = el2edimg(element);
2906
2907     if (PANEL_DEACTIVATED(pos))
2908       continue;
2909
2910 #if 0
2911     /* masked blit with tiles from half-size scaled bitmap does not work yet
2912        (no mask bitmap created for these sizes after loading and scaling) --
2913        solution: load without creating mask, scale, then create final mask */
2914
2915     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2916                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2917
2918     if (key[i])
2919     {
2920       Bitmap *src_bitmap;
2921       int src_x, src_y;
2922
2923       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2924
2925       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2926                     dst_x - src_x, dst_y - src_y);
2927       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2928                        dst_x, dst_y);
2929     }
2930 #else
2931     if (key[i])
2932       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2933     else
2934       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2935                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2936 #endif
2937   }
2938 }
2939
2940 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2941                        int key_bits)
2942 {
2943   int key[MAX_NUM_KEYS];
2944   int i;
2945
2946   /* prevent EM engine from updating time/score values parallel to GameWon() */
2947   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2948       local_player->LevelSolved)
2949     return;
2950
2951   for (i = 0; i < MAX_NUM_KEYS; i++)
2952     key[i] = key_bits & (1 << i);
2953
2954   DrawGameValue_Level(level_nr);
2955
2956   DrawGameValue_Emeralds(emeralds);
2957   DrawGameValue_Dynamite(dynamite);
2958   DrawGameValue_Score(score);
2959   DrawGameValue_Time(time);
2960
2961   DrawGameValue_Keys(key);
2962 }
2963
2964 void UpdateGameDoorValues()
2965 {
2966   UpdateGameControlValues();
2967 }
2968
2969 void DrawGameDoorValues()
2970 {
2971   DisplayGameControlValues();
2972 }
2973
2974 void DrawGameDoorValues_OLD()
2975 {
2976   int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2977   int dynamite_value = 0;
2978   int score_value = (local_player->LevelSolved ? local_player->score_final :
2979                      local_player->score);
2980   int gems_value = local_player->gems_still_needed;
2981   int key_bits = 0;
2982   int i, j;
2983
2984   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2985   {
2986     DrawGameDoorValues_EM();
2987
2988     return;
2989   }
2990
2991   if (game.centered_player_nr == -1)
2992   {
2993     for (i = 0; i < MAX_PLAYERS; i++)
2994     {
2995       for (j = 0; j < MAX_NUM_KEYS; j++)
2996         if (stored_player[i].key[j])
2997           key_bits |= (1 << j);
2998
2999       dynamite_value += stored_player[i].inventory_size;
3000     }
3001   }
3002   else
3003   {
3004     int player_nr = game.centered_player_nr;
3005
3006     for (i = 0; i < MAX_NUM_KEYS; i++)
3007       if (stored_player[player_nr].key[i])
3008         key_bits |= (1 << i);
3009
3010     dynamite_value = stored_player[player_nr].inventory_size;
3011   }
3012
3013   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3014                     key_bits);
3015 }
3016
3017
3018 /*
3019   =============================================================================
3020   InitGameEngine()
3021   -----------------------------------------------------------------------------
3022   initialize game engine due to level / tape version number
3023   =============================================================================
3024 */
3025
3026 static void InitGameEngine()
3027 {
3028   int i, j, k, l, x, y;
3029
3030   /* set game engine from tape file when re-playing, else from level file */
3031   game.engine_version = (tape.playing ? tape.engine_version :
3032                          level.game_version);
3033
3034   /* set single or multi-player game mode (needed for re-playing tapes) */
3035   game.team_mode = setup.team_mode;
3036
3037   if (tape.playing)
3038   {
3039     int num_players = 0;
3040
3041     for (i = 0; i < MAX_PLAYERS; i++)
3042       if (tape.player_participates[i])
3043         num_players++;
3044
3045     /* multi-player tapes contain input data for more than one player */
3046     game.team_mode = (num_players > 1);
3047   }
3048
3049   /* ---------------------------------------------------------------------- */
3050   /* set flags for bugs and changes according to active game engine version */
3051   /* ---------------------------------------------------------------------- */
3052
3053   /*
3054     Summary of bugfix/change:
3055     Fixed handling for custom elements that change when pushed by the player.
3056
3057     Fixed/changed in version:
3058     3.1.0
3059
3060     Description:
3061     Before 3.1.0, custom elements that "change when pushing" changed directly
3062     after the player started pushing them (until then handled in "DigField()").
3063     Since 3.1.0, these custom elements are not changed until the "pushing"
3064     move of the element is finished (now handled in "ContinueMoving()").
3065
3066     Affected levels/tapes:
3067     The first condition is generally needed for all levels/tapes before version
3068     3.1.0, which might use the old behaviour before it was changed; known tapes
3069     that are affected are some tapes from the level set "Walpurgis Gardens" by
3070     Jamie Cullen.
3071     The second condition is an exception from the above case and is needed for
3072     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3073     above (including some development versions of 3.1.0), but before it was
3074     known that this change would break tapes like the above and was fixed in
3075     3.1.1, so that the changed behaviour was active although the engine version
3076     while recording maybe was before 3.1.0. There is at least one tape that is
3077     affected by this exception, which is the tape for the one-level set "Bug
3078     Machine" by Juergen Bonhagen.
3079   */
3080
3081   game.use_change_when_pushing_bug =
3082     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3083      !(tape.playing &&
3084        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3085        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3086
3087   /*
3088     Summary of bugfix/change:
3089     Fixed handling for blocking the field the player leaves when moving.
3090
3091     Fixed/changed in version:
3092     3.1.1
3093
3094     Description:
3095     Before 3.1.1, when "block last field when moving" was enabled, the field
3096     the player is leaving when moving was blocked for the time of the move,
3097     and was directly unblocked afterwards. This resulted in the last field
3098     being blocked for exactly one less than the number of frames of one player
3099     move. Additionally, even when blocking was disabled, the last field was
3100     blocked for exactly one frame.
3101     Since 3.1.1, due to changes in player movement handling, the last field
3102     is not blocked at all when blocking is disabled. When blocking is enabled,
3103     the last field is blocked for exactly the number of frames of one player
3104     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3105     last field is blocked for exactly one more than the number of frames of
3106     one player move.
3107
3108     Affected levels/tapes:
3109     (!!! yet to be determined -- probably many !!!)
3110   */
3111
3112   game.use_block_last_field_bug =
3113     (game.engine_version < VERSION_IDENT(3,1,1,0));
3114
3115   /*
3116     Summary of bugfix/change:
3117     Changed behaviour of CE changes with multiple changes per single frame.
3118
3119     Fixed/changed in version:
3120     3.2.0-6
3121
3122     Description:
3123     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3124     This resulted in race conditions where CEs seem to behave strange in some
3125     situations (where triggered CE changes were just skipped because there was
3126     already a CE change on that tile in the playfield in that engine frame).
3127     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3128     (The number of changes per frame must be limited in any case, because else
3129     it is easily possible to define CE changes that would result in an infinite
3130     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3131     should be set large enough so that it would only be reached in cases where
3132     the corresponding CE change conditions run into a loop. Therefore, it seems
3133     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3134     maximal number of change pages for custom elements.)
3135
3136     Affected levels/tapes:
3137     Probably many.
3138   */
3139
3140 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3141   game.max_num_changes_per_frame = 1;
3142 #else
3143   game.max_num_changes_per_frame =
3144     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3145 #endif
3146
3147   /* ---------------------------------------------------------------------- */
3148
3149   /* default scan direction: scan playfield from top/left to bottom/right */
3150   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3151
3152   /* dynamically adjust element properties according to game engine version */
3153   InitElementPropertiesEngine(game.engine_version);
3154
3155 #if 0
3156   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3157   printf("          tape version == %06d [%s] [file: %06d]\n",
3158          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3159          tape.file_version);
3160   printf("       => game.engine_version == %06d\n", game.engine_version);
3161 #endif
3162
3163   /* ---------- initialize player's initial move delay --------------------- */
3164
3165   /* dynamically adjust player properties according to level information */
3166   for (i = 0; i < MAX_PLAYERS; i++)
3167     game.initial_move_delay_value[i] =
3168       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3169
3170   /* dynamically adjust player properties according to game engine version */
3171   for (i = 0; i < MAX_PLAYERS; i++)
3172     game.initial_move_delay[i] =
3173       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3174        game.initial_move_delay_value[i] : 0);
3175
3176   /* ---------- initialize player's initial push delay --------------------- */
3177
3178   /* dynamically adjust player properties according to game engine version */
3179   game.initial_push_delay_value =
3180     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3181
3182   /* ---------- initialize changing elements ------------------------------- */
3183
3184   /* initialize changing elements information */
3185   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3186   {
3187     struct ElementInfo *ei = &element_info[i];
3188
3189     /* this pointer might have been changed in the level editor */
3190     ei->change = &ei->change_page[0];
3191
3192     if (!IS_CUSTOM_ELEMENT(i))
3193     {
3194       ei->change->target_element = EL_EMPTY_SPACE;
3195       ei->change->delay_fixed = 0;
3196       ei->change->delay_random = 0;
3197       ei->change->delay_frames = 1;
3198     }
3199
3200     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3201     {
3202       ei->has_change_event[j] = FALSE;
3203
3204       ei->event_page_nr[j] = 0;
3205       ei->event_page[j] = &ei->change_page[0];
3206     }
3207   }
3208
3209   /* add changing elements from pre-defined list */
3210   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3211   {
3212     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3213     struct ElementInfo *ei = &element_info[ch_delay->element];
3214
3215     ei->change->target_element       = ch_delay->target_element;
3216     ei->change->delay_fixed          = ch_delay->change_delay;
3217
3218     ei->change->pre_change_function  = ch_delay->pre_change_function;
3219     ei->change->change_function      = ch_delay->change_function;
3220     ei->change->post_change_function = ch_delay->post_change_function;
3221
3222     ei->change->can_change = TRUE;
3223     ei->change->can_change_or_has_action = TRUE;
3224
3225     ei->has_change_event[CE_DELAY] = TRUE;
3226
3227     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3228     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3229   }
3230
3231   /* ---------- initialize internal run-time variables --------------------- */
3232
3233   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3234   {
3235     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3236
3237     for (j = 0; j < ei->num_change_pages; j++)
3238     {
3239       ei->change_page[j].can_change_or_has_action =
3240         (ei->change_page[j].can_change |
3241          ei->change_page[j].has_action);
3242     }
3243   }
3244
3245   /* add change events from custom element configuration */
3246   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3247   {
3248     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3249
3250     for (j = 0; j < ei->num_change_pages; j++)
3251     {
3252       if (!ei->change_page[j].can_change_or_has_action)
3253         continue;
3254
3255       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3256       {
3257         /* only add event page for the first page found with this event */
3258         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3259         {
3260           ei->has_change_event[k] = TRUE;
3261
3262           ei->event_page_nr[k] = j;
3263           ei->event_page[k] = &ei->change_page[j];
3264         }
3265       }
3266     }
3267   }
3268
3269 #if 1
3270   /* ---------- initialize reference elements in change conditions --------- */
3271
3272   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3273   {
3274     int element = EL_CUSTOM_START + i;
3275     struct ElementInfo *ei = &element_info[element];
3276
3277     for (j = 0; j < ei->num_change_pages; j++)
3278     {
3279       int trigger_element = ei->change_page[j].initial_trigger_element;
3280
3281       if (trigger_element >= EL_PREV_CE_8 &&
3282           trigger_element <= EL_NEXT_CE_8)
3283         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3284
3285       ei->change_page[j].trigger_element = trigger_element;
3286     }
3287   }
3288 #endif
3289
3290   /* ---------- initialize run-time trigger player and element ------------- */
3291
3292   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3293   {
3294     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3295
3296     for (j = 0; j < ei->num_change_pages; j++)
3297     {
3298       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3299       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3300       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3301       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3302       ei->change_page[j].actual_trigger_ce_value = 0;
3303       ei->change_page[j].actual_trigger_ce_score = 0;
3304     }
3305   }
3306
3307   /* ---------- initialize trigger events ---------------------------------- */
3308
3309   /* initialize trigger events information */
3310   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3311     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3312       trigger_events[i][j] = FALSE;
3313
3314   /* add trigger events from element change event properties */
3315   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3316   {
3317     struct ElementInfo *ei = &element_info[i];
3318
3319     for (j = 0; j < ei->num_change_pages; j++)
3320     {
3321       if (!ei->change_page[j].can_change_or_has_action)
3322         continue;
3323
3324       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3325       {
3326         int trigger_element = ei->change_page[j].trigger_element;
3327
3328         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3329         {
3330           if (ei->change_page[j].has_event[k])
3331           {
3332             if (IS_GROUP_ELEMENT(trigger_element))
3333             {
3334               struct ElementGroupInfo *group =
3335                 element_info[trigger_element].group;
3336
3337               for (l = 0; l < group->num_elements_resolved; l++)
3338                 trigger_events[group->element_resolved[l]][k] = TRUE;
3339             }
3340             else if (trigger_element == EL_ANY_ELEMENT)
3341               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3342                 trigger_events[l][k] = TRUE;
3343             else
3344               trigger_events[trigger_element][k] = TRUE;
3345           }
3346         }
3347       }
3348     }
3349   }
3350
3351   /* ---------- initialize push delay -------------------------------------- */
3352
3353   /* initialize push delay values to default */
3354   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3355   {
3356     if (!IS_CUSTOM_ELEMENT(i))
3357     {
3358       /* set default push delay values (corrected since version 3.0.7-1) */
3359       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3360       {
3361         element_info[i].push_delay_fixed = 2;
3362         element_info[i].push_delay_random = 8;
3363       }
3364       else
3365       {
3366         element_info[i].push_delay_fixed = 8;
3367         element_info[i].push_delay_random = 8;
3368       }
3369     }
3370   }
3371
3372   /* set push delay value for certain elements from pre-defined list */
3373   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3374   {
3375     int e = push_delay_list[i].element;
3376
3377     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3378     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3379   }
3380
3381   /* set push delay value for Supaplex elements for newer engine versions */
3382   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3383   {
3384     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3385     {
3386       if (IS_SP_ELEMENT(i))
3387       {
3388         /* set SP push delay to just enough to push under a falling zonk */
3389         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3390
3391         element_info[i].push_delay_fixed  = delay;
3392         element_info[i].push_delay_random = 0;
3393       }
3394     }
3395   }
3396
3397   /* ---------- initialize move stepsize ----------------------------------- */
3398
3399   /* initialize move stepsize values to default */
3400   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3401     if (!IS_CUSTOM_ELEMENT(i))
3402       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3403
3404   /* set move stepsize value for certain elements from pre-defined list */
3405   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3406   {
3407     int e = move_stepsize_list[i].element;
3408
3409     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3410   }
3411
3412   /* ---------- initialize collect score ----------------------------------- */
3413
3414   /* initialize collect score values for custom elements from initial value */
3415   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3416     if (IS_CUSTOM_ELEMENT(i))
3417       element_info[i].collect_score = element_info[i].collect_score_initial;
3418
3419   /* ---------- initialize collect count ----------------------------------- */
3420
3421   /* initialize collect count values for non-custom elements */
3422   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3423     if (!IS_CUSTOM_ELEMENT(i))
3424       element_info[i].collect_count_initial = 0;
3425
3426   /* add collect count values for all elements from pre-defined list */
3427   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3428     element_info[collect_count_list[i].element].collect_count_initial =
3429       collect_count_list[i].count;
3430
3431   /* ---------- initialize access direction -------------------------------- */
3432
3433   /* initialize access direction values to default (access from every side) */
3434   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435     if (!IS_CUSTOM_ELEMENT(i))
3436       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3437
3438   /* set access direction value for certain elements from pre-defined list */
3439   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3440     element_info[access_direction_list[i].element].access_direction =
3441       access_direction_list[i].direction;
3442
3443   /* ---------- initialize explosion content ------------------------------- */
3444   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3445   {
3446     if (IS_CUSTOM_ELEMENT(i))
3447       continue;
3448
3449     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3450     {
3451       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3452
3453       element_info[i].content.e[x][y] =
3454         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3455          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3456          i == EL_PLAYER_3 ? EL_EMERALD :
3457          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3458          i == EL_MOLE ? EL_EMERALD_RED :
3459          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3460          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3461          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3462          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3463          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3464          i == EL_WALL_EMERALD ? EL_EMERALD :
3465          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3466          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3467          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3468          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3469          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3470          i == EL_WALL_PEARL ? EL_PEARL :
3471          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3472          EL_EMPTY);
3473     }
3474   }
3475
3476   /* ---------- initialize recursion detection ------------------------------ */
3477   recursion_loop_depth = 0;
3478   recursion_loop_detected = FALSE;
3479   recursion_loop_element = EL_UNDEFINED;
3480
3481   /* ---------- initialize graphics engine ---------------------------------- */
3482   game.scroll_delay_value =
3483     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3484      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3485   game.scroll_delay_value =
3486     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3487 }
3488
3489 int get_num_special_action(int element, int action_first, int action_last)
3490 {
3491   int num_special_action = 0;
3492   int i, j;
3493
3494   for (i = action_first; i <= action_last; i++)
3495   {
3496     boolean found = FALSE;
3497
3498     for (j = 0; j < NUM_DIRECTIONS; j++)
3499       if (el_act_dir2img(element, i, j) !=
3500           el_act_dir2img(element, ACTION_DEFAULT, j))
3501         found = TRUE;
3502
3503     if (found)
3504       num_special_action++;
3505     else
3506       break;
3507   }
3508
3509   return num_special_action;
3510 }
3511
3512
3513 /*
3514   =============================================================================
3515   InitGame()
3516   -----------------------------------------------------------------------------
3517   initialize and start new game
3518   =============================================================================
3519 */
3520
3521 void InitGame()
3522 {
3523   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3524   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3525   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3526 #if 0
3527   boolean do_fading = (game_status == GAME_MODE_MAIN);
3528 #endif
3529 #if 1
3530   int initial_move_dir = MV_DOWN;
3531 #else
3532   int initial_move_dir = MV_NONE;
3533 #endif
3534   int i, j, x, y;
3535
3536 #if 1
3537
3538   StopAnimation();
3539
3540   if (!game.restart_level)
3541     CloseDoor(DOOR_CLOSE_1);
3542
3543 #if 1
3544   if (level_editor_test_game)
3545     FadeSkipNextFadeIn();
3546   else
3547     FadeSetEnterScreen();
3548 #else
3549   if (level_editor_test_game)
3550     fading = fading_none;
3551   else
3552     fading = menu.destination;
3553 #endif
3554
3555 #if 1
3556   FadeOut(REDRAW_FIELD);
3557 #else
3558   if (do_fading)
3559     FadeOut(REDRAW_FIELD);
3560 #endif
3561
3562 #endif
3563
3564   game_status = GAME_MODE_PLAYING;
3565
3566 #if 1
3567   /* needed if different viewport properties defined for playing */
3568   ChangeViewportPropertiesIfNeeded();
3569 #endif
3570
3571 #if 1
3572   DrawCompleteVideoDisplay();
3573 #endif
3574
3575   InitGameEngine();
3576   InitGameControlValues();
3577
3578   /* don't play tapes over network */
3579   network_playing = (options.network && !tape.playing);
3580
3581   for (i = 0; i < MAX_PLAYERS; i++)
3582   {
3583     struct PlayerInfo *player = &stored_player[i];
3584
3585     player->index_nr = i;
3586     player->index_bit = (1 << i);
3587     player->element_nr = EL_PLAYER_1 + i;
3588
3589     player->present = FALSE;
3590     player->active = FALSE;
3591     player->mapped = FALSE;
3592
3593     player->killed = FALSE;
3594     player->reanimated = FALSE;
3595
3596     player->action = 0;
3597     player->effective_action = 0;
3598     player->programmed_action = 0;
3599
3600     player->score = 0;
3601     player->score_final = 0;
3602
3603     player->gems_still_needed = level.gems_needed;
3604     player->sokobanfields_still_needed = 0;
3605     player->lights_still_needed = 0;
3606     player->friends_still_needed = 0;
3607
3608     for (j = 0; j < MAX_NUM_KEYS; j++)
3609       player->key[j] = FALSE;
3610
3611     player->num_white_keys = 0;
3612
3613     player->dynabomb_count = 0;
3614     player->dynabomb_size = 1;
3615     player->dynabombs_left = 0;
3616     player->dynabomb_xl = FALSE;
3617
3618     player->MovDir = initial_move_dir;
3619     player->MovPos = 0;
3620     player->GfxPos = 0;
3621     player->GfxDir = initial_move_dir;
3622     player->GfxAction = ACTION_DEFAULT;
3623     player->Frame = 0;
3624     player->StepFrame = 0;
3625
3626     player->initial_element = player->element_nr;
3627     player->artwork_element =
3628       (level.use_artwork_element[i] ? level.artwork_element[i] :
3629        player->element_nr);
3630     player->use_murphy = FALSE;
3631
3632     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3633     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3634
3635     player->gravity = level.initial_player_gravity[i];
3636
3637     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3638
3639     player->actual_frame_counter = 0;
3640
3641     player->step_counter = 0;
3642
3643     player->last_move_dir = initial_move_dir;
3644
3645     player->is_active = FALSE;
3646
3647     player->is_waiting = FALSE;
3648     player->is_moving = FALSE;
3649     player->is_auto_moving = FALSE;
3650     player->is_digging = FALSE;
3651     player->is_snapping = FALSE;
3652     player->is_collecting = FALSE;
3653     player->is_pushing = FALSE;
3654     player->is_switching = FALSE;
3655     player->is_dropping = FALSE;
3656     player->is_dropping_pressed = FALSE;
3657
3658     player->is_bored = FALSE;
3659     player->is_sleeping = FALSE;
3660
3661     player->frame_counter_bored = -1;
3662     player->frame_counter_sleeping = -1;
3663
3664     player->anim_delay_counter = 0;
3665     player->post_delay_counter = 0;
3666
3667     player->dir_waiting = initial_move_dir;
3668     player->action_waiting = ACTION_DEFAULT;
3669     player->last_action_waiting = ACTION_DEFAULT;
3670     player->special_action_bored = ACTION_DEFAULT;
3671     player->special_action_sleeping = ACTION_DEFAULT;
3672
3673     player->switch_x = -1;
3674     player->switch_y = -1;
3675
3676     player->drop_x = -1;
3677     player->drop_y = -1;
3678
3679     player->show_envelope = 0;
3680
3681     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3682
3683     player->push_delay       = -1;      /* initialized when pushing starts */
3684     player->push_delay_value = game.initial_push_delay_value;
3685
3686     player->drop_delay = 0;
3687     player->drop_pressed_delay = 0;
3688
3689     player->last_jx = -1;
3690     player->last_jy = -1;
3691     player->jx = -1;
3692     player->jy = -1;
3693
3694     player->shield_normal_time_left = 0;
3695     player->shield_deadly_time_left = 0;
3696
3697     player->inventory_infinite_element = EL_UNDEFINED;
3698     player->inventory_size = 0;
3699
3700     if (level.use_initial_inventory[i])
3701     {
3702       for (j = 0; j < level.initial_inventory_size[i]; j++)
3703       {
3704         int element = level.initial_inventory_content[i][j];
3705         int collect_count = element_info[element].collect_count_initial;
3706         int k;
3707
3708         if (!IS_CUSTOM_ELEMENT(element))
3709           collect_count = 1;
3710
3711         if (collect_count == 0)
3712           player->inventory_infinite_element = element;
3713         else
3714           for (k = 0; k < collect_count; k++)
3715             if (player->inventory_size < MAX_INVENTORY_SIZE)
3716               player->inventory_element[player->inventory_size++] = element;
3717       }
3718     }
3719
3720     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3721     SnapField(player, 0, 0);
3722
3723     player->LevelSolved = FALSE;
3724     player->GameOver = FALSE;
3725
3726     player->LevelSolved_GameWon = FALSE;
3727     player->LevelSolved_GameEnd = FALSE;
3728     player->LevelSolved_PanelOff = FALSE;
3729     player->LevelSolved_SaveTape = FALSE;
3730     player->LevelSolved_SaveScore = FALSE;
3731     player->LevelSolved_CountingTime = 0;
3732     player->LevelSolved_CountingScore = 0;
3733
3734     map_player_action[i] = i;
3735   }
3736
3737   network_player_action_received = FALSE;
3738
3739 #if defined(NETWORK_AVALIABLE)
3740   /* initial null action */
3741   if (network_playing)
3742     SendToServer_MovePlayer(MV_NONE);
3743 #endif
3744
3745   ZX = ZY = -1;
3746   ExitX = ExitY = -1;
3747
3748   FrameCounter = 0;
3749   TimeFrames = 0;
3750   TimePlayed = 0;
3751   TimeLeft = level.time;
3752   TapeTime = 0;
3753
3754   ScreenMovDir = MV_NONE;
3755   ScreenMovPos = 0;
3756   ScreenGfxPos = 0;
3757
3758   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3759
3760   AllPlayersGone = FALSE;
3761
3762   game.no_time_limit = (level.time == 0);
3763
3764   game.yamyam_content_nr = 0;
3765   game.robot_wheel_active = FALSE;
3766   game.magic_wall_active = FALSE;
3767   game.magic_wall_time_left = 0;
3768   game.light_time_left = 0;
3769   game.timegate_time_left = 0;
3770   game.switchgate_pos = 0;
3771   game.wind_direction = level.wind_direction_initial;
3772
3773 #if !USE_PLAYER_GRAVITY
3774   game.gravity = FALSE;
3775   game.explosions_delayed = TRUE;
3776 #endif
3777
3778   game.lenses_time_left = 0;
3779   game.magnify_time_left = 0;
3780
3781   game.ball_state = level.ball_state_initial;
3782   game.ball_content_nr = 0;
3783
3784   game.envelope_active = FALSE;
3785
3786   /* set focus to local player for network games, else to all players */
3787   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3788   game.centered_player_nr_next = game.centered_player_nr;
3789   game.set_centered_player = FALSE;
3790
3791   if (network_playing && tape.recording)
3792   {
3793     /* store client dependent player focus when recording network games */
3794     tape.centered_player_nr_next = game.centered_player_nr_next;
3795     tape.set_centered_player = TRUE;
3796   }
3797
3798   for (i = 0; i < NUM_BELTS; i++)
3799   {
3800     game.belt_dir[i] = MV_NONE;
3801     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3802   }
3803
3804   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3805     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3806
3807 #if DEBUG_INIT_PLAYER
3808   if (options.debug)
3809   {
3810     printf("Player status at level initialization:\n");
3811   }
3812 #endif
3813
3814   SCAN_PLAYFIELD(x, y)
3815   {
3816     Feld[x][y] = level.field[x][y];
3817     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3818     ChangeDelay[x][y] = 0;
3819     ChangePage[x][y] = -1;
3820 #if USE_NEW_CUSTOM_VALUE
3821     CustomValue[x][y] = 0;              /* initialized in InitField() */
3822 #endif
3823     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3824     AmoebaNr[x][y] = 0;
3825     WasJustMoving[x][y] = 0;
3826     WasJustFalling[x][y] = 0;
3827     CheckCollision[x][y] = 0;
3828     CheckImpact[x][y] = 0;
3829     Stop[x][y] = FALSE;
3830     Pushed[x][y] = FALSE;
3831
3832     ChangeCount[x][y] = 0;
3833     ChangeEvent[x][y] = -1;
3834
3835     ExplodePhase[x][y] = 0;
3836     ExplodeDelay[x][y] = 0;
3837     ExplodeField[x][y] = EX_TYPE_NONE;
3838
3839     RunnerVisit[x][y] = 0;
3840     PlayerVisit[x][y] = 0;
3841
3842     GfxFrame[x][y] = 0;
3843     GfxRandom[x][y] = INIT_GFX_RANDOM();
3844     GfxElement[x][y] = EL_UNDEFINED;
3845     GfxAction[x][y] = ACTION_DEFAULT;
3846     GfxDir[x][y] = MV_NONE;
3847     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3848   }
3849
3850   SCAN_PLAYFIELD(x, y)
3851   {
3852     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3853       emulate_bd = FALSE;
3854     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3855       emulate_sb = FALSE;
3856     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3857       emulate_sp = FALSE;
3858
3859     InitField(x, y, TRUE);
3860
3861     ResetGfxAnimation(x, y);
3862   }
3863
3864   InitBeltMovement();
3865
3866   for (i = 0; i < MAX_PLAYERS; i++)
3867   {
3868     struct PlayerInfo *player = &stored_player[i];
3869
3870     /* set number of special actions for bored and sleeping animation */
3871     player->num_special_action_bored =
3872       get_num_special_action(player->artwork_element,
3873                              ACTION_BORING_1, ACTION_BORING_LAST);
3874     player->num_special_action_sleeping =
3875       get_num_special_action(player->artwork_element,
3876                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3877   }
3878
3879   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3880                     emulate_sb ? EMU_SOKOBAN :
3881                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3882
3883 #if USE_NEW_ALL_SLIPPERY
3884   /* initialize type of slippery elements */
3885   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3886   {
3887     if (!IS_CUSTOM_ELEMENT(i))
3888     {
3889       /* default: elements slip down either to the left or right randomly */
3890       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3891
3892       /* SP style elements prefer to slip down on the left side */
3893       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3894         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3895
3896       /* BD style elements prefer to slip down on the left side */
3897       if (game.emulation == EMU_BOULDERDASH)
3898         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3899     }
3900   }
3901 #endif
3902
3903   /* initialize explosion and ignition delay */
3904   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3905   {
3906     if (!IS_CUSTOM_ELEMENT(i))
3907     {
3908       int num_phase = 8;
3909       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3910                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3911                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3912       int last_phase = (num_phase + 1) * delay;
3913       int half_phase = (num_phase / 2) * delay;
3914
3915       element_info[i].explosion_delay = last_phase - 1;
3916       element_info[i].ignition_delay = half_phase;
3917
3918       if (i == EL_BLACK_ORB)
3919         element_info[i].ignition_delay = 1;
3920     }
3921
3922 #if 0
3923     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3924       element_info[i].explosion_delay = 1;
3925
3926     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3927       element_info[i].ignition_delay = 1;
3928 #endif
3929   }
3930
3931   /* correct non-moving belts to start moving left */
3932   for (i = 0; i < NUM_BELTS; i++)
3933     if (game.belt_dir[i] == MV_NONE)
3934       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3935
3936 #if USE_NEW_PLAYER_ASSIGNMENTS
3937   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3938   /* choose default local player */
3939   local_player = &stored_player[0];
3940
3941   for (i = 0; i < MAX_PLAYERS; i++)
3942     stored_player[i].connected = FALSE;
3943
3944   local_player->connected = TRUE;
3945   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3946
3947 #if 0
3948   printf("::: TEAM MODE: %d\n", game.team_mode);
3949 #endif
3950
3951   if (tape.playing)
3952   {
3953 #if 1
3954     for (i = 0; i < MAX_PLAYERS; i++)
3955       stored_player[i].connected = tape.player_participates[i];
3956 #else
3957     /* try to guess locally connected team mode players (needed for correct
3958        assignment of player figures from level to locally playing players) */
3959
3960     for (i = 0; i < MAX_PLAYERS; i++)
3961       if (tape.player_participates[i])
3962         stored_player[i].connected = TRUE;
3963 #endif
3964   }
3965   else if (game.team_mode && !options.network)
3966   {
3967     /* try to guess locally connected team mode players (needed for correct
3968        assignment of player figures from level to locally playing players) */
3969
3970     for (i = 0; i < MAX_PLAYERS; i++)
3971       if (setup.input[i].use_joystick ||
3972           setup.input[i].key.left != KSYM_UNDEFINED)
3973         stored_player[i].connected = TRUE;
3974   }
3975
3976 #if DEBUG_INIT_PLAYER
3977   if (options.debug)
3978   {
3979     printf("Player status after level initialization:\n");
3980
3981     for (i = 0; i < MAX_PLAYERS; i++)
3982     {
3983       struct PlayerInfo *player = &stored_player[i];
3984
3985       printf("- player %d: present == %d, connected == %d, active == %d",
3986              i + 1,
3987              player->present,
3988              player->connected,
3989              player->active);
3990
3991       if (local_player == player)
3992         printf(" (local player)");
3993
3994       printf("\n");
3995     }
3996   }
3997 #endif
3998
3999 #if DEBUG_INIT_PLAYER
4000   if (options.debug)
4001     printf("Reassigning players ...\n");
4002 #endif
4003
4004   /* check if any connected player was not found in playfield */
4005   for (i = 0; i < MAX_PLAYERS; i++)
4006   {
4007     struct PlayerInfo *player = &stored_player[i];
4008
4009     if (player->connected && !player->present)
4010     {
4011       struct PlayerInfo *field_player = NULL;
4012
4013 #if DEBUG_INIT_PLAYER
4014       if (options.debug)
4015         printf("- looking for field player for player %d ...\n", i + 1);
4016 #endif
4017
4018       /* assign first free player found that is present in the playfield */
4019
4020 #if 1
4021       /* first try: look for unmapped playfield player that is not connected */
4022       for (j = 0; j < MAX_PLAYERS; j++)
4023         if (field_player == NULL &&
4024             stored_player[j].present &&
4025             !stored_player[j].mapped &&
4026             !stored_player[j].connected)
4027           field_player = &stored_player[j];
4028
4029       /* second try: look for *any* unmapped playfield player */
4030       for (j = 0; j < MAX_PLAYERS; j++)
4031         if (field_player == NULL &&
4032             stored_player[j].present &&
4033             !stored_player[j].mapped)
4034           field_player = &stored_player[j];
4035 #else
4036       /* first try: look for unmapped playfield player that is not connected */
4037       if (field_player == NULL)
4038         for (j = 0; j < MAX_PLAYERS; j++)
4039           if (stored_player[j].present &&
4040               !stored_player[j].mapped &&
4041               !stored_player[j].connected)
4042             field_player = &stored_player[j];
4043
4044       /* second try: look for *any* unmapped playfield player */
4045       if (field_player == NULL)
4046         for (j = 0; j < MAX_PLAYERS; j++)
4047           if (stored_player[j].present &&
4048               !stored_player[j].mapped)
4049             field_player = &stored_player[j];
4050 #endif
4051
4052       if (field_player != NULL)
4053       {
4054         int jx = field_player->jx, jy = field_player->jy;
4055
4056 #if DEBUG_INIT_PLAYER
4057         if (options.debug)
4058           printf("- found player %d\n", field_player->index_nr + 1);
4059 #endif
4060
4061         player->present = FALSE;
4062         player->active = FALSE;
4063
4064         field_player->present = TRUE;
4065         field_player->active = TRUE;
4066
4067         /*
4068         player->initial_element = field_player->initial_element;
4069         player->artwork_element = field_player->artwork_element;
4070
4071         player->block_last_field       = field_player->block_last_field;
4072         player->block_delay_adjustment = field_player->block_delay_adjustment;
4073         */
4074
4075         StorePlayer[jx][jy] = field_player->element_nr;
4076
4077         field_player->jx = field_player->last_jx = jx;
4078         field_player->jy = field_player->last_jy = jy;
4079
4080         if (local_player == player)
4081           local_player = field_player;
4082
4083         map_player_action[field_player->index_nr] = i;
4084
4085         field_player->mapped = TRUE;
4086
4087 #if DEBUG_INIT_PLAYER
4088         if (options.debug)
4089           printf("- map_player_action[%d] == %d\n",
4090                  field_player->index_nr + 1, i + 1);
4091 #endif
4092       }
4093     }
4094
4095     if (player->connected && player->present)
4096       player->mapped = TRUE;
4097   }
4098
4099 #if DEBUG_INIT_PLAYER
4100   if (options.debug)
4101   {
4102     printf("Player status after player assignment (first stage):\n");
4103
4104     for (i = 0; i < MAX_PLAYERS; i++)
4105     {
4106       struct PlayerInfo *player = &stored_player[i];
4107
4108       printf("- player %d: present == %d, connected == %d, active == %d",
4109              i + 1,
4110              player->present,
4111              player->connected,
4112              player->active);
4113
4114       if (local_player == player)
4115         printf(" (local player)");
4116
4117       printf("\n");
4118     }
4119   }
4120 #endif
4121
4122 #else
4123
4124   /* check if any connected player was not found in playfield */
4125   for (i = 0; i < MAX_PLAYERS; i++)
4126   {
4127     struct PlayerInfo *player = &stored_player[i];
4128
4129     if (player->connected && !player->present)
4130     {
4131       for (j = 0; j < MAX_PLAYERS; j++)
4132       {
4133         struct PlayerInfo *field_player = &stored_player[j];
4134         int jx = field_player->jx, jy = field_player->jy;
4135
4136         /* assign first free player found that is present in the playfield */
4137         if (field_player->present && !field_player->connected)
4138         {
4139           player->present = TRUE;
4140           player->active = TRUE;
4141
4142           field_player->present = FALSE;
4143           field_player->active = FALSE;
4144
4145           player->initial_element = field_player->initial_element;
4146           player->artwork_element = field_player->artwork_element;
4147
4148           player->block_last_field       = field_player->block_last_field;
4149           player->block_delay_adjustment = field_player->block_delay_adjustment;
4150
4151           StorePlayer[jx][jy] = player->element_nr;
4152
4153           player->jx = player->last_jx = jx;
4154           player->jy = player->last_jy = jy;
4155
4156           break;
4157         }
4158       }
4159     }
4160   }
4161 #endif
4162
4163 #if 0
4164   printf("::: local_player->present == %d\n", local_player->present);
4165 #endif
4166
4167   if (tape.playing)
4168   {
4169     /* when playing a tape, eliminate all players who do not participate */
4170
4171 #if USE_NEW_PLAYER_ASSIGNMENTS
4172
4173 #if 1
4174     if (!game.team_mode)
4175 #endif
4176
4177     for (i = 0; i < MAX_PLAYERS; i++)
4178     {
4179       if (stored_player[i].active &&
4180           !tape.player_participates[map_player_action[i]])
4181       {
4182         struct PlayerInfo *player = &stored_player[i];
4183         int jx = player->jx, jy = player->jy;
4184
4185 #if DEBUG_INIT_PLAYER
4186         if (options.debug)
4187           printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4188 #endif
4189
4190         player->active = FALSE;
4191         StorePlayer[jx][jy] = 0;
4192         Feld[jx][jy] = EL_EMPTY;
4193       }
4194     }
4195
4196 #else
4197
4198     for (i = 0; i < MAX_PLAYERS; i++)
4199     {
4200       if (stored_player[i].active &&
4201           !tape.player_participates[i])
4202       {
4203         struct PlayerInfo *player = &stored_player[i];
4204         int jx = player->jx, jy = player->jy;
4205
4206         player->active = FALSE;
4207         StorePlayer[jx][jy] = 0;
4208         Feld[jx][jy] = EL_EMPTY;
4209       }
4210     }
4211 #endif
4212   }
4213   else if (!options.network && !game.team_mode)         /* && !tape.playing */
4214   {
4215     /* when in single player mode, eliminate all but the first active player */
4216
4217     for (i = 0; i < MAX_PLAYERS; i++)
4218     {
4219       if (stored_player[i].active)
4220       {
4221         for (j = i + 1; j < MAX_PLAYERS; j++)
4222         {
4223           if (stored_player[j].active)
4224           {
4225             struct PlayerInfo *player = &stored_player[j];
4226             int jx = player->jx, jy = player->jy;
4227
4228             player->active = FALSE;
4229             player->present = FALSE;
4230
4231             StorePlayer[jx][jy] = 0;
4232             Feld[jx][jy] = EL_EMPTY;
4233           }
4234         }
4235       }
4236     }
4237   }
4238
4239   /* when recording the game, store which players take part in the game */
4240   if (tape.recording)
4241   {
4242 #if USE_NEW_PLAYER_ASSIGNMENTS
4243     for (i = 0; i < MAX_PLAYERS; i++)
4244       if (stored_player[i].connected)
4245         tape.player_participates[i] = TRUE;
4246 #else
4247     for (i = 0; i < MAX_PLAYERS; i++)
4248       if (stored_player[i].active)
4249         tape.player_participates[i] = TRUE;
4250 #endif
4251   }
4252
4253 #if DEBUG_INIT_PLAYER
4254   if (options.debug)
4255   {
4256     printf("Player status after player assignment (final stage):\n");
4257
4258     for (i = 0; i < MAX_PLAYERS; i++)
4259     {
4260       struct PlayerInfo *player = &stored_player[i];
4261
4262       printf("- player %d: present == %d, connected == %d, active == %d",
4263              i + 1,
4264              player->present,
4265              player->connected,
4266              player->active);
4267
4268       if (local_player == player)
4269         printf(" (local player)");
4270
4271       printf("\n");
4272     }
4273   }
4274 #endif
4275
4276   if (BorderElement == EL_EMPTY)
4277   {
4278     SBX_Left = 0;
4279     SBX_Right = lev_fieldx - SCR_FIELDX;
4280     SBY_Upper = 0;
4281     SBY_Lower = lev_fieldy - SCR_FIELDY;
4282   }
4283   else
4284   {
4285     SBX_Left = -1;
4286     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4287     SBY_Upper = -1;
4288     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4289   }
4290
4291 #if NEW_TILESIZE
4292
4293   if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4294     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4295
4296   if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4297     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4298
4299   if (EVEN(SCR_FIELDX))
4300     SBX_Left--;
4301   if (EVEN(SCR_FIELDY))
4302     SBY_Upper--;
4303
4304 #else
4305
4306   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4307     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4308
4309   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4310     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4311 #endif
4312
4313   /* if local player not found, look for custom element that might create
4314      the player (make some assumptions about the right custom element) */
4315   if (!local_player->present)
4316   {
4317     int start_x = 0, start_y = 0;
4318     int found_rating = 0;
4319     int found_element = EL_UNDEFINED;
4320     int player_nr = local_player->index_nr;
4321
4322     SCAN_PLAYFIELD(x, y)
4323     {
4324       int element = Feld[x][y];
4325       int content;
4326       int xx, yy;
4327       boolean is_player;
4328
4329       if (level.use_start_element[player_nr] &&
4330           level.start_element[player_nr] == element &&
4331           found_rating < 4)
4332       {
4333         start_x = x;
4334         start_y = y;
4335
4336         found_rating = 4;
4337         found_element = element;
4338       }
4339
4340       if (!IS_CUSTOM_ELEMENT(element))
4341         continue;
4342
4343       if (CAN_CHANGE(element))
4344       {
4345         for (i = 0; i < element_info[element].num_change_pages; i++)
4346         {
4347           /* check for player created from custom element as single target */
4348           content = element_info[element].change_page[i].target_element;
4349           is_player = ELEM_IS_PLAYER(content);
4350
4351           if (is_player && (found_rating < 3 ||
4352                             (found_rating == 3 && element < found_element)))
4353           {
4354             start_x = x;
4355             start_y = y;
4356
4357             found_rating = 3;
4358             found_element = element;
4359           }
4360         }
4361       }
4362
4363       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4364       {
4365         /* check for player created from custom element as explosion content */
4366         content = element_info[element].content.e[xx][yy];
4367         is_player = ELEM_IS_PLAYER(content);
4368
4369         if (is_player && (found_rating < 2 ||
4370                           (found_rating == 2 && element < found_element)))
4371         {
4372           start_x = x + xx - 1;
4373           start_y = y + yy - 1;
4374
4375           found_rating = 2;
4376           found_element = element;
4377         }
4378
4379         if (!CAN_CHANGE(element))
4380           continue;
4381
4382         for (i = 0; i < element_info[element].num_change_pages; i++)
4383         {
4384           /* check for player created from custom element as extended target */
4385           content =
4386             element_info[element].change_page[i].target_content.e[xx][yy];
4387
4388           is_player = ELEM_IS_PLAYER(content);
4389
4390           if (is_player && (found_rating < 1 ||
4391                             (found_rating == 1 && element < found_element)))
4392           {
4393             start_x = x + xx - 1;
4394             start_y = y + yy - 1;
4395
4396             found_rating = 1;
4397             found_element = element;
4398           }
4399         }
4400       }
4401     }
4402
4403     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4404                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4405                 start_x - MIDPOSX);
4406
4407     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4408                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4409                 start_y - MIDPOSY);
4410   }
4411   else
4412   {
4413     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4414                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4415                 local_player->jx - MIDPOSX);
4416
4417     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4418                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4419                 local_player->jy - MIDPOSY);
4420   }
4421
4422 #if 0
4423   printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4424 #endif
4425
4426 #if 0
4427   /* do not use PLAYING mask for fading out from main screen */
4428   game_status = GAME_MODE_MAIN;
4429 #endif
4430
4431 #if 0
4432
4433   StopAnimation();
4434
4435   if (!game.restart_level)
4436     CloseDoor(DOOR_CLOSE_1);
4437
4438 #if 1
4439   if (level_editor_test_game)
4440     FadeSkipNextFadeIn();
4441   else
4442     FadeSetEnterScreen();
4443 #else
4444   if (level_editor_test_game)
4445     fading = fading_none;
4446   else
4447     fading = menu.destination;
4448 #endif
4449
4450 #if 1
4451   FadeOut(REDRAW_FIELD);
4452 #else
4453   if (do_fading)
4454     FadeOut(REDRAW_FIELD);
4455 #endif
4456
4457 #endif
4458
4459 #if 0
4460   game_status = GAME_MODE_PLAYING;
4461 #endif
4462
4463   /* !!! FIX THIS (START) !!! */
4464   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4465   {
4466     InitGameEngine_EM();
4467
4468     /* blit playfield from scroll buffer to normal back buffer for fading in */
4469     BlitScreenToBitmap_EM(backbuffer);
4470   }
4471   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4472   {
4473     InitGameEngine_SP();
4474
4475     /* blit playfield from scroll buffer to normal back buffer for fading in */
4476     BlitScreenToBitmap_SP(backbuffer);
4477   }
4478   else
4479   {
4480     DrawLevel();
4481     DrawAllPlayers();
4482
4483     /* after drawing the level, correct some elements */
4484     if (game.timegate_time_left == 0)
4485       CloseAllOpenTimegates();
4486
4487 #if NEW_TILESIZE
4488     BlitScreenToBitmap(backbuffer);
4489 #else
4490     /* blit playfield from scroll buffer to normal back buffer for fading in */
4491     if (setup.soft_scrolling)
4492       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4493 #endif
4494
4495     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4496   }
4497   /* !!! FIX THIS (END) !!! */
4498
4499 #if 1
4500   FadeIn(REDRAW_FIELD);
4501 #else
4502   if (do_fading)
4503     FadeIn(REDRAW_FIELD);
4504
4505   BackToFront();
4506 #endif
4507
4508   if (!game.restart_level)
4509   {
4510     /* copy default game door content to main double buffer */
4511 #if 1
4512 #if 1
4513     /* !!! CHECK AGAIN !!! */
4514     SetPanelBackground();
4515     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4516     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4517 #else
4518     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4519
4520     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4521     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4522     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4523                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4524 #endif
4525 #else
4526     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4527                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4528 #endif
4529   }
4530
4531   SetPanelBackground();
4532   SetDrawBackgroundMask(REDRAW_DOOR_1);
4533
4534 #if 1
4535   UpdateAndDisplayGameControlValues();
4536 #else
4537   UpdateGameDoorValues();
4538   DrawGameDoorValues();
4539 #endif
4540
4541   if (!game.restart_level)
4542   {
4543     UnmapGameButtons();
4544     UnmapTapeButtons();
4545     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4546     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4547     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4548     MapGameButtons();
4549     MapTapeButtons();
4550
4551     /* copy actual game door content to door double buffer for OpenDoor() */
4552 #if 1
4553     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4554 #else
4555     BlitBitmap(drawto, bitmap_db_door,
4556                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4557 #endif
4558
4559     OpenDoor(DOOR_OPEN_ALL);
4560
4561     PlaySound(SND_GAME_STARTING);
4562
4563     if (setup.sound_music)
4564       PlayLevelMusic();
4565
4566     KeyboardAutoRepeatOffUnlessAutoplay();
4567
4568 #if DEBUG_INIT_PLAYER
4569     if (options.debug)
4570     {
4571       printf("Player status (final):\n");
4572
4573       for (i = 0; i < MAX_PLAYERS; i++)
4574       {
4575         struct PlayerInfo *player = &stored_player[i];
4576
4577         printf("- player %d: present == %d, connected == %d, active == %d",
4578                i + 1,
4579                player->present,
4580                player->connected,
4581                player->active);
4582
4583         if (local_player == player)
4584           printf(" (local player)");
4585
4586         printf("\n");
4587       }
4588     }
4589 #endif
4590   }
4591
4592 #if 1
4593   UnmapAllGadgets();
4594
4595   MapGameButtons();
4596   MapTapeButtons();
4597 #endif
4598
4599   if (!game.restart_level && !tape.playing)
4600   {
4601     LevelStats_incPlayed(level_nr);
4602
4603     SaveLevelSetup_SeriesInfo();
4604
4605 #if 0
4606     printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4607 #endif
4608   }
4609
4610   game.restart_level = FALSE;
4611 }
4612
4613 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4614 {
4615   /* this is used for non-R'n'D game engines to update certain engine values */
4616
4617   /* needed to determine if sounds are played within the visible screen area */
4618   scroll_x = actual_scroll_x;
4619   scroll_y = actual_scroll_y;
4620 }
4621
4622 void InitMovDir(int x, int y)
4623 {
4624   int i, element = Feld[x][y];
4625   static int xy[4][2] =
4626   {
4627     {  0, +1 },
4628     { +1,  0 },
4629     {  0, -1 },
4630     { -1,  0 }
4631   };
4632   static int direction[3][4] =
4633   {
4634     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4635     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4636     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4637   };
4638
4639   switch (element)
4640   {
4641     case EL_BUG_RIGHT:
4642     case EL_BUG_UP:
4643     case EL_BUG_LEFT:
4644     case EL_BUG_DOWN:
4645       Feld[x][y] = EL_BUG;
4646       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4647       break;
4648
4649     case EL_SPACESHIP_RIGHT:
4650     case EL_SPACESHIP_UP:
4651     case EL_SPACESHIP_LEFT:
4652     case EL_SPACESHIP_DOWN:
4653       Feld[x][y] = EL_SPACESHIP;
4654       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4655       break;
4656
4657     case EL_BD_BUTTERFLY_RIGHT:
4658     case EL_BD_BUTTERFLY_UP:
4659     case EL_BD_BUTTERFLY_LEFT:
4660     case EL_BD_BUTTERFLY_DOWN:
4661       Feld[x][y] = EL_BD_BUTTERFLY;
4662       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4663       break;
4664
4665     case EL_BD_FIREFLY_RIGHT:
4666     case EL_BD_FIREFLY_UP:
4667     case EL_BD_FIREFLY_LEFT:
4668     case EL_BD_FIREFLY_DOWN:
4669       Feld[x][y] = EL_BD_FIREFLY;
4670       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4671       break;
4672
4673     case EL_PACMAN_RIGHT:
4674     case EL_PACMAN_UP:
4675     case EL_PACMAN_LEFT:
4676     case EL_PACMAN_DOWN:
4677       Feld[x][y] = EL_PACMAN;
4678       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4679       break;
4680
4681     case EL_YAMYAM_LEFT:
4682     case EL_YAMYAM_RIGHT:
4683     case EL_YAMYAM_UP:
4684     case EL_YAMYAM_DOWN:
4685       Feld[x][y] = EL_YAMYAM;
4686       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4687       break;
4688
4689     case EL_SP_SNIKSNAK:
4690       MovDir[x][y] = MV_UP;
4691       break;
4692
4693     case EL_SP_ELECTRON:
4694       MovDir[x][y] = MV_LEFT;
4695       break;
4696
4697     case EL_MOLE_LEFT:
4698     case EL_MOLE_RIGHT:
4699     case EL_MOLE_UP:
4700     case EL_MOLE_DOWN:
4701       Feld[x][y] = EL_MOLE;
4702       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4703       break;
4704
4705     default:
4706       if (IS_CUSTOM_ELEMENT(element))
4707       {
4708         struct ElementInfo *ei = &element_info[element];
4709         int move_direction_initial = ei->move_direction_initial;
4710         int move_pattern = ei->move_pattern;
4711
4712         if (move_direction_initial == MV_START_PREVIOUS)
4713         {
4714           if (MovDir[x][y] != MV_NONE)
4715             return;
4716
4717           move_direction_initial = MV_START_AUTOMATIC;
4718         }
4719
4720         if (move_direction_initial == MV_START_RANDOM)
4721           MovDir[x][y] = 1 << RND(4);
4722         else if (move_direction_initial & MV_ANY_DIRECTION)
4723           MovDir[x][y] = move_direction_initial;
4724         else if (move_pattern == MV_ALL_DIRECTIONS ||
4725                  move_pattern == MV_TURNING_LEFT ||
4726                  move_pattern == MV_TURNING_RIGHT ||
4727                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4728                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4729                  move_pattern == MV_TURNING_RANDOM)
4730           MovDir[x][y] = 1 << RND(4);
4731         else if (move_pattern == MV_HORIZONTAL)
4732           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4733         else if (move_pattern == MV_VERTICAL)
4734           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4735         else if (move_pattern & MV_ANY_DIRECTION)
4736           MovDir[x][y] = element_info[element].move_pattern;
4737         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4738                  move_pattern == MV_ALONG_RIGHT_SIDE)
4739         {
4740           /* use random direction as default start direction */
4741           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4742             MovDir[x][y] = 1 << RND(4);
4743
4744           for (i = 0; i < NUM_DIRECTIONS; i++)
4745           {
4746             int x1 = x + xy[i][0];
4747             int y1 = y + xy[i][1];
4748
4749             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4750             {
4751               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4752                 MovDir[x][y] = direction[0][i];
4753               else
4754                 MovDir[x][y] = direction[1][i];
4755
4756               break;
4757             }
4758           }
4759         }                
4760       }
4761       else
4762       {
4763         MovDir[x][y] = 1 << RND(4);
4764
4765         if (element != EL_BUG &&
4766             element != EL_SPACESHIP &&
4767             element != EL_BD_BUTTERFLY &&
4768             element != EL_BD_FIREFLY)
4769           break;
4770
4771         for (i = 0; i < NUM_DIRECTIONS; i++)
4772         {
4773           int x1 = x + xy[i][0];
4774           int y1 = y + xy[i][1];
4775
4776           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4777           {
4778             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4779             {
4780               MovDir[x][y] = direction[0][i];
4781               break;
4782             }
4783             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4784                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4785             {
4786               MovDir[x][y] = direction[1][i];
4787               break;
4788             }
4789           }
4790         }
4791       }
4792       break;
4793   }
4794
4795   GfxDir[x][y] = MovDir[x][y];
4796 }
4797
4798 void InitAmoebaNr(int x, int y)
4799 {
4800   int i;
4801   int group_nr = AmoebeNachbarNr(x, y);
4802
4803   if (group_nr == 0)
4804   {
4805     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4806     {
4807       if (AmoebaCnt[i] == 0)
4808       {
4809         group_nr = i;
4810         break;
4811       }
4812     }
4813   }
4814
4815   AmoebaNr[x][y] = group_nr;
4816   AmoebaCnt[group_nr]++;
4817   AmoebaCnt2[group_nr]++;
4818 }
4819
4820 static void PlayerWins(struct PlayerInfo *player)
4821 {
4822   player->LevelSolved = TRUE;
4823   player->GameOver = TRUE;
4824
4825   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4826                          level.native_em_level->lev->score : player->score);
4827
4828   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4829                                       TimeLeft);
4830   player->LevelSolved_CountingScore = player->score_final;
4831 }
4832
4833 void GameWon()
4834 {
4835   static int time, time_final;
4836   static int score, score_final;
4837   static int game_over_delay_1 = 0;
4838   static int game_over_delay_2 = 0;
4839   int game_over_delay_value_1 = 50;
4840   int game_over_delay_value_2 = 50;
4841
4842   if (!local_player->LevelSolved_GameWon)
4843   {
4844     int i;
4845
4846     /* do not start end game actions before the player stops moving (to exit) */
4847     if (local_player->MovPos)
4848       return;
4849
4850     local_player->LevelSolved_GameWon = TRUE;
4851     local_player->LevelSolved_SaveTape = tape.recording;
4852     local_player->LevelSolved_SaveScore = !tape.playing;
4853
4854     if (!tape.playing)
4855     {
4856       LevelStats_incSolved(level_nr);
4857
4858       SaveLevelSetup_SeriesInfo();
4859
4860 #if 0
4861       printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4862 #endif
4863     }
4864
4865     if (tape.auto_play)         /* tape might already be stopped here */
4866       tape.auto_play_level_solved = TRUE;
4867
4868 #if 1
4869     TapeStop();
4870 #endif
4871
4872     game_over_delay_1 = game_over_delay_value_1;
4873     game_over_delay_2 = game_over_delay_value_2;
4874
4875     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4876     score = score_final = local_player->score_final;
4877
4878     if (TimeLeft > 0)
4879     {
4880       time_final = 0;
4881       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4882     }
4883     else if (game.no_time_limit && TimePlayed < 999)
4884     {
4885       time_final = 999;
4886       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4887     }
4888
4889     local_player->score_final = score_final;
4890
4891     if (level_editor_test_game)
4892     {
4893       time = time_final;
4894       score = score_final;
4895
4896 #if 1
4897       local_player->LevelSolved_CountingTime = time;
4898       local_player->LevelSolved_CountingScore = score;
4899
4900       game_panel_controls[GAME_PANEL_TIME].value = time;
4901       game_panel_controls[GAME_PANEL_SCORE].value = score;
4902
4903       DisplayGameControlValues();
4904 #else
4905       DrawGameValue_Time(time);
4906       DrawGameValue_Score(score);
4907 #endif
4908     }
4909
4910     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4911     {
4912       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4913       {
4914         /* close exit door after last player */
4915         if ((AllPlayersGone &&
4916              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4917               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4918               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4919             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4920             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4921         {
4922           int element = Feld[ExitX][ExitY];
4923
4924 #if 0
4925           if (element == EL_EM_EXIT_OPEN ||
4926               element == EL_EM_STEEL_EXIT_OPEN)
4927           {
4928             Bang(ExitX, ExitY);
4929           }
4930           else
4931 #endif
4932           {
4933             Feld[ExitX][ExitY] =
4934               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4935                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4936                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4937                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4938                EL_EM_STEEL_EXIT_CLOSING);
4939
4940             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4941           }
4942         }
4943
4944         /* player disappears */
4945         DrawLevelField(ExitX, ExitY);
4946       }
4947
4948       for (i = 0; i < MAX_PLAYERS; i++)
4949       {
4950         struct PlayerInfo *player = &stored_player[i];
4951
4952         if (player->present)
4953         {
4954           RemovePlayer(player);
4955
4956           /* player disappears */
4957           DrawLevelField(player->jx, player->jy);
4958         }
4959       }
4960     }
4961
4962     PlaySound(SND_GAME_WINNING);
4963   }
4964
4965   if (game_over_delay_1 > 0)
4966   {
4967     game_over_delay_1--;
4968
4969     return;
4970   }
4971
4972   if (time != time_final)
4973   {
4974     int time_to_go = ABS(time_final - time);
4975     int time_count_dir = (time < time_final ? +1 : -1);
4976     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4977
4978     time  += time_count_steps * time_count_dir;
4979     score += time_count_steps * level.score[SC_TIME_BONUS];
4980
4981 #if 1
4982     local_player->LevelSolved_CountingTime = time;
4983     local_player->LevelSolved_CountingScore = score;
4984
4985     game_panel_controls[GAME_PANEL_TIME].value = time;
4986     game_panel_controls[GAME_PANEL_SCORE].value = score;
4987
4988     DisplayGameControlValues();
4989 #else
4990     DrawGameValue_Time(time);
4991     DrawGameValue_Score(score);
4992 #endif
4993
4994     if (time == time_final)
4995       StopSound(SND_GAME_LEVELTIME_BONUS);
4996     else if (setup.sound_loops)
4997       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4998     else
4999       PlaySound(SND_GAME_LEVELTIME_BONUS);
5000
5001     return;
5002   }
5003
5004   local_player->LevelSolved_PanelOff = TRUE;
5005
5006   if (game_over_delay_2 > 0)
5007   {
5008     game_over_delay_2--;
5009
5010     return;
5011   }
5012
5013 #if 1
5014   GameEnd();
5015 #endif
5016 }
5017
5018 void GameEnd()
5019 {
5020   int hi_pos;
5021   boolean raise_level = FALSE;
5022
5023   local_player->LevelSolved_GameEnd = TRUE;
5024
5025   CloseDoor(DOOR_CLOSE_1);
5026
5027   if (local_player->LevelSolved_SaveTape)
5028   {
5029 #if 0
5030     TapeStop();
5031 #endif
5032
5033 #if 1
5034     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5035 #else
5036     SaveTape(tape.level_nr);            /* ask to save tape */
5037 #endif
5038   }
5039
5040   if (level_editor_test_game)
5041   {
5042     game_status = GAME_MODE_MAIN;
5043
5044 #if 1
5045     DrawAndFadeInMainMenu(REDRAW_FIELD);
5046 #else
5047     DrawMainMenu();
5048 #endif
5049
5050     return;
5051   }
5052
5053   if (!local_player->LevelSolved_SaveScore)
5054   {
5055 #if 1
5056     FadeOut(REDRAW_FIELD);
5057 #endif
5058
5059     game_status = GAME_MODE_MAIN;
5060
5061     DrawAndFadeInMainMenu(REDRAW_FIELD);
5062
5063     return;
5064   }
5065
5066   if (level_nr == leveldir_current->handicap_level)
5067   {
5068     leveldir_current->handicap_level++;
5069
5070     SaveLevelSetup_SeriesInfo();
5071   }
5072
5073   if (level_nr < leveldir_current->last_level)
5074     raise_level = TRUE;                 /* advance to next level */
5075
5076   if ((hi_pos = NewHiScore()) >= 0) 
5077   {
5078     game_status = GAME_MODE_SCORES;
5079
5080     DrawHallOfFame(hi_pos);
5081
5082     if (raise_level)
5083     {
5084       level_nr++;
5085       TapeErase();
5086     }
5087   }
5088   else
5089   {
5090 #if 1
5091     FadeOut(REDRAW_FIELD);
5092 #endif
5093
5094     game_status = GAME_MODE_MAIN;
5095
5096     if (raise_level)
5097     {
5098       level_nr++;
5099       TapeErase();
5100     }
5101
5102     DrawAndFadeInMainMenu(REDRAW_FIELD);
5103   }
5104 }
5105
5106 int NewHiScore()
5107 {
5108   int k, l;
5109   int position = -1;
5110
5111   LoadScore(level_nr);
5112
5113   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5114       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5115     return -1;
5116
5117   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5118   {
5119     if (local_player->score_final > highscore[k].Score)
5120     {
5121       /* player has made it to the hall of fame */
5122
5123       if (k < MAX_SCORE_ENTRIES - 1)
5124       {
5125         int m = MAX_SCORE_ENTRIES - 1;
5126
5127 #ifdef ONE_PER_NAME
5128         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5129           if (strEqual(setup.player_name, highscore[l].Name))
5130             m = l;
5131         if (m == k)     /* player's new highscore overwrites his old one */
5132           goto put_into_list;
5133 #endif
5134
5135         for (l = m; l > k; l--)
5136         {
5137           strcpy(highscore[l].Name, highscore[l - 1].Name);
5138           highscore[l].Score = highscore[l - 1].Score;
5139         }
5140       }
5141
5142 #ifdef ONE_PER_NAME
5143       put_into_list:
5144 #endif
5145       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5146       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5147       highscore[k].Score = local_player->score_final; 
5148       position = k;
5149       break;
5150     }
5151
5152 #ifdef ONE_PER_NAME
5153     else if (!strncmp(setup.player_name, highscore[k].Name,
5154                       MAX_PLAYER_NAME_LEN))
5155       break;    /* player already there with a higher score */
5156 #endif
5157
5158   }
5159
5160   if (position >= 0) 
5161     SaveScore(level_nr);
5162
5163   return position;
5164 }
5165
5166 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5167 {
5168   int element = Feld[x][y];
5169   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5170   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5171   int horiz_move = (dx != 0);
5172   int sign = (horiz_move ? dx : dy);
5173   int step = sign * element_info[element].move_stepsize;
5174
5175   /* special values for move stepsize for spring and things on conveyor belt */
5176   if (horiz_move)
5177   {
5178     if (CAN_FALL(element) &&
5179         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5180       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5181     else if (element == EL_SPRING)
5182       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5183   }
5184
5185   return step;
5186 }
5187
5188 inline static int getElementMoveStepsize(int x, int y)
5189 {
5190   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5191 }
5192
5193 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5194 {
5195   if (player->GfxAction != action || player->GfxDir != dir)
5196   {
5197 #if 0
5198     printf("Player frame reset! (%d => %d, %d => %d)\n",
5199            player->GfxAction, action, player->GfxDir, dir);
5200 #endif
5201
5202     player->GfxAction = action;
5203     player->GfxDir = dir;
5204     player->Frame = 0;
5205     player->StepFrame = 0;
5206   }
5207 }
5208
5209 #if USE_GFX_RESET_GFX_ANIMATION
5210 static void ResetGfxFrame(int x, int y, boolean redraw)
5211 {
5212   int element = Feld[x][y];
5213   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5214   int last_gfx_frame = GfxFrame[x][y];
5215
5216   if (graphic_info[graphic].anim_global_sync)
5217     GfxFrame[x][y] = FrameCounter;
5218   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5219     GfxFrame[x][y] = CustomValue[x][y];
5220   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5221     GfxFrame[x][y] = element_info[element].collect_score;
5222   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5223     GfxFrame[x][y] = ChangeDelay[x][y];
5224
5225   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5226     DrawLevelGraphicAnimation(x, y, graphic);
5227 }
5228 #endif
5229
5230 static void ResetGfxAnimation(int x, int y)
5231 {
5232   GfxAction[x][y] = ACTION_DEFAULT;
5233   GfxDir[x][y] = MovDir[x][y];
5234   GfxFrame[x][y] = 0;
5235
5236 #if USE_GFX_RESET_GFX_ANIMATION
5237   ResetGfxFrame(x, y, FALSE);
5238 #endif
5239 }
5240
5241 static void ResetRandomAnimationValue(int x, int y)
5242 {
5243   GfxRandom[x][y] = INIT_GFX_RANDOM();
5244 }
5245
5246 void InitMovingField(int x, int y, int direction)
5247 {
5248   int element = Feld[x][y];
5249   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5250   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5251   int newx = x + dx;
5252   int newy = y + dy;
5253   boolean is_moving_before, is_moving_after;
5254 #if 0
5255   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5256 #endif
5257
5258   /* check if element was/is moving or being moved before/after mode change */
5259 #if 1
5260 #if 1
5261   is_moving_before = (WasJustMoving[x][y] != 0);
5262 #else
5263   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5264   is_moving_before = WasJustMoving[x][y];
5265 #endif
5266 #else
5267   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5268 #endif
5269   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5270
5271   /* reset animation only for moving elements which change direction of moving
5272      or which just started or stopped moving
5273      (else CEs with property "can move" / "not moving" are reset each frame) */
5274 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5275 #if 1
5276   if (is_moving_before != is_moving_after ||
5277       direction != MovDir[x][y])
5278     ResetGfxAnimation(x, y);
5279 #else
5280   if ((is_moving_before || is_moving_after) && !continues_moving)
5281     ResetGfxAnimation(x, y);
5282 #endif
5283 #else
5284   if (!continues_moving)
5285     ResetGfxAnimation(x, y);
5286 #endif
5287
5288   MovDir[x][y] = direction;
5289   GfxDir[x][y] = direction;
5290
5291 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5292   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5293                      direction == MV_DOWN && CAN_FALL(element) ?
5294                      ACTION_FALLING : ACTION_MOVING);
5295 #else
5296   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5297                      ACTION_FALLING : ACTION_MOVING);
5298 #endif
5299
5300   /* this is needed for CEs with property "can move" / "not moving" */
5301
5302   if (is_moving_after)
5303   {
5304     if (Feld[newx][newy] == EL_EMPTY)
5305       Feld[newx][newy] = EL_BLOCKED;
5306
5307     MovDir[newx][newy] = MovDir[x][y];
5308
5309 #if USE_NEW_CUSTOM_VALUE
5310     CustomValue[newx][newy] = CustomValue[x][y];
5311 #endif
5312
5313     GfxFrame[newx][newy] = GfxFrame[x][y];
5314     GfxRandom[newx][newy] = GfxRandom[x][y];
5315     GfxAction[newx][newy] = GfxAction[x][y];
5316     GfxDir[newx][newy] = GfxDir[x][y];
5317   }
5318 }
5319
5320 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5321 {
5322   int direction = MovDir[x][y];
5323   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5324   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5325
5326   *goes_to_x = newx;
5327   *goes_to_y = newy;
5328 }
5329
5330 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5331 {
5332   int oldx = x, oldy = y;
5333   int direction = MovDir[x][y];
5334
5335   if (direction == MV_LEFT)
5336     oldx++;
5337   else if (direction == MV_RIGHT)
5338     oldx--;
5339   else if (direction == MV_UP)
5340     oldy++;
5341   else if (direction == MV_DOWN)
5342     oldy--;
5343
5344   *comes_from_x = oldx;
5345   *comes_from_y = oldy;
5346 }
5347
5348 int MovingOrBlocked2Element(int x, int y)
5349 {
5350   int element = Feld[x][y];
5351
5352   if (element == EL_BLOCKED)
5353   {
5354     int oldx, oldy;
5355
5356     Blocked2Moving(x, y, &oldx, &oldy);
5357     return Feld[oldx][oldy];
5358   }
5359   else
5360     return element;
5361 }
5362
5363 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5364 {
5365   /* like MovingOrBlocked2Element(), but if element is moving
5366      and (x,y) is the field the moving element is just leaving,
5367      return EL_BLOCKED instead of the element value */
5368   int element = Feld[x][y];
5369
5370   if (IS_MOVING(x, y))
5371   {
5372     if (element == EL_BLOCKED)
5373     {
5374       int oldx, oldy;
5375
5376       Blocked2Moving(x, y, &oldx, &oldy);
5377       return Feld[oldx][oldy];
5378     }
5379     else
5380       return EL_BLOCKED;
5381   }
5382   else
5383     return element;
5384 }
5385
5386 static void RemoveField(int x, int y)
5387 {
5388   Feld[x][y] = EL_EMPTY;
5389
5390   MovPos[x][y] = 0;
5391   MovDir[x][y] = 0;
5392   MovDelay[x][y] = 0;
5393
5394 #if USE_NEW_CUSTOM_VALUE
5395   CustomValue[x][y] = 0;
5396 #endif
5397
5398   AmoebaNr[x][y] = 0;
5399   ChangeDelay[x][y] = 0;
5400   ChangePage[x][y] = -1;
5401   Pushed[x][y] = FALSE;
5402
5403 #if 0
5404   ExplodeField[x][y] = EX_TYPE_NONE;
5405 #endif
5406
5407   GfxElement[x][y] = EL_UNDEFINED;
5408   GfxAction[x][y] = ACTION_DEFAULT;
5409   GfxDir[x][y] = MV_NONE;
5410 #if 0
5411   /* !!! this would prevent the removed tile from being redrawn !!! */
5412   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5413 #endif
5414 }
5415
5416 void RemoveMovingField(int x, int y)
5417 {
5418   int oldx = x, oldy = y, newx = x, newy = y;
5419   int element = Feld[x][y];
5420   int next_element = EL_UNDEFINED;
5421
5422   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5423     return;
5424
5425   if (IS_MOVING(x, y))
5426   {
5427     Moving2Blocked(x, y, &newx, &newy);
5428
5429     if (Feld[newx][newy] != EL_BLOCKED)
5430     {
5431       /* element is moving, but target field is not free (blocked), but
5432          already occupied by something different (example: acid pool);
5433          in this case, only remove the moving field, but not the target */
5434
5435       RemoveField(oldx, oldy);
5436
5437       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5438
5439       TEST_DrawLevelField(oldx, oldy);
5440
5441       return;
5442     }
5443   }
5444   else if (element == EL_BLOCKED)
5445   {
5446     Blocked2Moving(x, y, &oldx, &oldy);
5447     if (!IS_MOVING(oldx, oldy))
5448       return;
5449   }
5450
5451   if (element == EL_BLOCKED &&
5452       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5453        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5454        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5455        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5456        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5457        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5458     next_element = get_next_element(Feld[oldx][oldy]);
5459
5460   RemoveField(oldx, oldy);
5461   RemoveField(newx, newy);
5462
5463   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5464
5465   if (next_element != EL_UNDEFINED)
5466     Feld[oldx][oldy] = next_element;
5467
5468   TEST_DrawLevelField(oldx, oldy);
5469   TEST_DrawLevelField(newx, newy);
5470 }
5471
5472 void DrawDynamite(int x, int y)
5473 {
5474   int sx = SCREENX(x), sy = SCREENY(y);
5475   int graphic = el2img(Feld[x][y]);
5476   int frame;
5477
5478   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5479     return;
5480
5481   if (IS_WALKABLE_INSIDE(Back[x][y]))
5482     return;
5483
5484   if (Back[x][y])
5485     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5486   else if (Store[x][y])
5487     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5488
5489   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5490
5491   if (Back[x][y] || Store[x][y])
5492     DrawGraphicThruMask(sx, sy, graphic, frame);
5493   else
5494     DrawGraphic(sx, sy, graphic, frame);
5495 }
5496
5497 void CheckDynamite(int x, int y)
5498 {
5499   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5500   {
5501     MovDelay[x][y]--;
5502
5503     if (MovDelay[x][y] != 0)
5504     {
5505       DrawDynamite(x, y);
5506       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5507
5508       return;
5509     }
5510   }
5511
5512   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5513
5514   Bang(x, y);
5515 }
5516
5517 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5518 {
5519   boolean num_checked_players = 0;
5520   int i;
5521
5522   for (i = 0; i < MAX_PLAYERS; i++)
5523   {
5524     if (stored_player[i].active)
5525     {
5526       int sx = stored_player[i].jx;
5527       int sy = stored_player[i].jy;
5528
5529       if (num_checked_players == 0)
5530       {
5531         *sx1 = *sx2 = sx;
5532         *sy1 = *sy2 = sy;
5533       }
5534       else
5535       {
5536         *sx1 = MIN(*sx1, sx);
5537         *sy1 = MIN(*sy1, sy);
5538         *sx2 = MAX(*sx2, sx);
5539         *sy2 = MAX(*sy2, sy);
5540       }
5541
5542       num_checked_players++;
5543     }
5544   }
5545 }
5546
5547 static boolean checkIfAllPlayersFitToScreen_RND()
5548 {
5549   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5550
5551   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5552
5553   return (sx2 - sx1 < SCR_FIELDX &&
5554           sy2 - sy1 < SCR_FIELDY);
5555 }
5556
5557 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5558 {
5559   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5560
5561   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5562
5563   *sx = (sx1 + sx2) / 2;
5564   *sy = (sy1 + sy2) / 2;
5565 }
5566
5567 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5568                         boolean center_screen, boolean quick_relocation)
5569 {
5570   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5571   boolean no_delay = (tape.warp_forward);
5572   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5573   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5574
5575   if (quick_relocation)
5576   {
5577     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5578     {
5579       if (!level.shifted_relocation || center_screen)
5580       {
5581         /* quick relocation (without scrolling), with centering of screen */
5582
5583         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5584                     x > SBX_Right + MIDPOSX ? SBX_Right :
5585                     x - MIDPOSX);
5586
5587         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5588                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5589                     y - MIDPOSY);
5590       }
5591       else
5592       {
5593         /* quick relocation (without scrolling), but do not center screen */
5594
5595         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5596                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5597                                old_x - MIDPOSX);
5598
5599         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5600                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5601                                old_y - MIDPOSY);
5602
5603         int offset_x = x + (scroll_x - center_scroll_x);
5604         int offset_y = y + (scroll_y - center_scroll_y);
5605
5606         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5607                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5608                     offset_x - MIDPOSX);
5609
5610         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5611                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5612                     offset_y - MIDPOSY);
5613       }
5614     }
5615     else
5616     {
5617 #if 1
5618       if (!level.shifted_relocation || center_screen)
5619       {
5620         /* quick relocation (without scrolling), with centering of screen */
5621
5622         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5623                     x > SBX_Right + MIDPOSX ? SBX_Right :
5624                     x - MIDPOSX);
5625
5626         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5627                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5628                     y - MIDPOSY);
5629       }
5630       else
5631       {
5632         /* quick relocation (without scrolling), but do not center screen */
5633
5634         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5635                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5636                                old_x - MIDPOSX);
5637
5638         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5639                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5640                                old_y - MIDPOSY);
5641
5642         int offset_x = x + (scroll_x - center_scroll_x);
5643         int offset_y = y + (scroll_y - center_scroll_y);
5644
5645         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5646                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5647                     offset_x - MIDPOSX);
5648
5649         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5650                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5651                     offset_y - MIDPOSY);
5652       }
5653 #else
5654       /* quick relocation (without scrolling), inside visible screen area */
5655
5656       int offset = game.scroll_delay_value;
5657
5658       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5659           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5660         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5661
5662       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5663           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5664         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5665
5666       /* don't scroll over playfield boundaries */
5667       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5668         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5669
5670       /* don't scroll over playfield boundaries */
5671       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5672         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5673 #endif
5674     }
5675
5676     RedrawPlayfield(TRUE, 0,0,0,0);
5677   }
5678   else
5679   {
5680 #if 1
5681     int scroll_xx, scroll_yy;
5682
5683     if (!level.shifted_relocation || center_screen)
5684     {
5685       /* visible relocation (with scrolling), with centering of screen */
5686
5687       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5688                    x > SBX_Right + MIDPOSX ? SBX_Right :
5689                    x - MIDPOSX);
5690
5691       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5692                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5693                    y - MIDPOSY);
5694     }
5695     else
5696     {
5697       /* visible relocation (with scrolling), but do not center screen */
5698
5699       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5700                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5701                              old_x - MIDPOSX);
5702
5703       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5704                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5705                              old_y - MIDPOSY);
5706
5707       int offset_x = x + (scroll_x - center_scroll_x);
5708       int offset_y = y + (scroll_y - center_scroll_y);
5709
5710       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5711                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5712                    offset_x - MIDPOSX);
5713
5714       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5715                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5716                    offset_y - MIDPOSY);
5717     }
5718
5719 #else
5720
5721     /* visible relocation (with scrolling), with centering of screen */
5722
5723     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5724                      x > SBX_Right + MIDPOSX ? SBX_Right :
5725                      x - MIDPOSX);
5726
5727     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5728                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5729                      y - MIDPOSY);
5730 #endif
5731
5732     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5733
5734     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5735     {
5736       int dx = 0, dy = 0;
5737       int fx = FX, fy = FY;
5738
5739       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5740       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5741
5742       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5743         break;
5744
5745       scroll_x -= dx;
5746       scroll_y -= dy;
5747
5748       fx += dx * TILEX / 2;
5749       fy += dy * TILEY / 2;
5750
5751       ScrollLevel(dx, dy);
5752       DrawAllPlayers();
5753
5754       /* scroll in two steps of half tile size to make things smoother */
5755       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5756       FlushDisplay();
5757       Delay(wait_delay_value);
5758
5759       /* scroll second step to align at full tile size */
5760       BackToFront();
5761       Delay(wait_delay_value);
5762     }
5763
5764     DrawAllPlayers();
5765     BackToFront();
5766     Delay(wait_delay_value);
5767   }
5768 }
5769
5770 void RelocatePlayer(int jx, int jy, int el_player_raw)
5771 {
5772   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5773   int player_nr = GET_PLAYER_NR(el_player);
5774   struct PlayerInfo *player = &stored_player[player_nr];
5775   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5776   boolean no_delay = (tape.warp_forward);
5777   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5778   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5779   int old_jx = player->jx;
5780   int old_jy = player->jy;
5781   int old_element = Feld[old_jx][old_jy];
5782   int element = Feld[jx][jy];
5783   boolean player_relocated = (old_jx != jx || old_jy != jy);
5784
5785   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5786   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5787   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5788   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5789   int leave_side_horiz = move_dir_horiz;
5790   int leave_side_vert  = move_dir_vert;
5791   int enter_side = enter_side_horiz | enter_side_vert;
5792   int leave_side = leave_side_horiz | leave_side_vert;
5793
5794   if (player->GameOver)         /* do not reanimate dead player */
5795     return;
5796
5797   if (!player_relocated)        /* no need to relocate the player */
5798     return;
5799
5800   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5801   {
5802     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5803     DrawLevelField(jx, jy);
5804   }
5805
5806   if (player->present)
5807   {
5808     while (player->MovPos)
5809     {
5810       ScrollPlayer(player, SCROLL_GO_ON);
5811       ScrollScreen(NULL, SCROLL_GO_ON);
5812
5813       AdvanceFrameAndPlayerCounters(player->index_nr);
5814
5815       DrawPlayer(player);
5816
5817       BackToFront();
5818       Delay(wait_delay_value);
5819     }
5820
5821     DrawPlayer(player);         /* needed here only to cleanup last field */
5822     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5823
5824     player->is_moving = FALSE;
5825   }
5826
5827   if (IS_CUSTOM_ELEMENT(old_element))
5828     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5829                                CE_LEFT_BY_PLAYER,
5830                                player->index_bit, leave_side);
5831
5832   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5833                                       CE_PLAYER_LEAVES_X,
5834                                       player->index_bit, leave_side);
5835
5836   Feld[jx][jy] = el_player;
5837   InitPlayerField(jx, jy, el_player, TRUE);
5838
5839   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5840      possible that the relocation target field did not contain a player element,
5841      but a walkable element, to which the new player was relocated -- in this
5842      case, restore that (already initialized!) element on the player field */
5843   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5844   {
5845     Feld[jx][jy] = element;     /* restore previously existing element */
5846 #if 0
5847     /* !!! do not initialize already initialized element a second time !!! */
5848     /* (this causes at least problems with "element creation" CE trigger for
5849        already existing elements, and existing Sokoban fields counted twice) */
5850     InitField(jx, jy, FALSE);
5851 #endif
5852   }
5853
5854   /* only visually relocate centered player */
5855   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5856                      FALSE, level.instant_relocation);
5857
5858   TestIfPlayerTouchesBadThing(jx, jy);
5859   TestIfPlayerTouchesCustomElement(jx, jy);
5860
5861   if (IS_CUSTOM_ELEMENT(element))
5862     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5863                                player->index_bit, enter_side);
5864
5865   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5866                                       player->index_bit, enter_side);
5867
5868 #if 1
5869   if (player->is_switching)
5870   {
5871     /* ensure that relocation while still switching an element does not cause
5872        a new element to be treated as also switched directly after relocation
5873        (this is important for teleporter switches that teleport the player to
5874        a place where another teleporter switch is in the same direction, which
5875        would then incorrectly be treated as immediately switched before the
5876        direction key that caused the switch was released) */
5877
5878     player->switch_x += jx - old_jx;
5879     player->switch_y += jy - old_jy;
5880   }
5881 #endif
5882 }
5883
5884 void Explode(int ex, int ey, int phase, int mode)
5885 {
5886   int x, y;
5887   int last_phase;
5888   int border_element;
5889
5890   /* !!! eliminate this variable !!! */
5891   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5892
5893   if (game.explosions_delayed)
5894   {
5895     ExplodeField[ex][ey] = mode;
5896     return;
5897   }
5898
5899   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5900   {
5901     int center_element = Feld[ex][ey];
5902     int artwork_element, explosion_element;     /* set these values later */
5903
5904 #if 0
5905     /* --- This is only really needed (and now handled) in "Impact()". --- */
5906     /* do not explode moving elements that left the explode field in time */
5907     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5908         center_element == EL_EMPTY &&
5909         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5910       return;
5911 #endif
5912
5913 #if 0
5914     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5915     if (mode == EX_TYPE_NORMAL ||
5916         mode == EX_TYPE_CENTER ||
5917         mode == EX_TYPE_CROSS)
5918       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5919 #endif
5920
5921     /* remove things displayed in background while burning dynamite */
5922     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5923       Back[ex][ey] = 0;
5924
5925     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5926     {
5927       /* put moving element to center field (and let it explode there) */
5928       center_element = MovingOrBlocked2Element(ex, ey);
5929       RemoveMovingField(ex, ey);
5930       Feld[ex][ey] = center_element;
5931     }
5932
5933     /* now "center_element" is finally determined -- set related values now */
5934     artwork_element = center_element;           /* for custom player artwork */
5935     explosion_element = center_element;         /* for custom player artwork */
5936
5937     if (IS_PLAYER(ex, ey))
5938     {
5939       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5940
5941       artwork_element = stored_player[player_nr].artwork_element;
5942
5943       if (level.use_explosion_element[player_nr])
5944       {
5945         explosion_element = level.explosion_element[player_nr];
5946         artwork_element = explosion_element;
5947       }
5948     }
5949
5950 #if 1
5951     if (mode == EX_TYPE_NORMAL ||
5952         mode == EX_TYPE_CENTER ||
5953         mode == EX_TYPE_CROSS)
5954       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5955 #endif
5956
5957     last_phase = element_info[explosion_element].explosion_delay + 1;
5958
5959     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5960     {
5961       int xx = x - ex + 1;
5962       int yy = y - ey + 1;
5963       int element;
5964
5965       if (!IN_LEV_FIELD(x, y) ||
5966           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5967           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5968         continue;
5969
5970       element = Feld[x][y];
5971
5972       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5973       {
5974         element = MovingOrBlocked2Element(x, y);
5975
5976         if (!IS_EXPLOSION_PROOF(element))
5977           RemoveMovingField(x, y);
5978       }
5979
5980       /* indestructible elements can only explode in center (but not flames) */
5981       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5982                                            mode == EX_TYPE_BORDER)) ||
5983           element == EL_FLAMES)
5984         continue;
5985
5986       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5987          behaviour, for example when touching a yamyam that explodes to rocks
5988          with active deadly shield, a rock is created under the player !!! */
5989       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5990 #if 0
5991       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5992           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5993            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5994 #else
5995       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5996 #endif
5997       {
5998         if (IS_ACTIVE_BOMB(element))
5999         {
6000           /* re-activate things under the bomb like gate or penguin */
6001           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6002           Back[x][y] = 0;
6003         }
6004
6005         continue;
6006       }
6007
6008       /* save walkable background elements while explosion on same tile */
6009       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6010           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6011         Back[x][y] = element;
6012
6013       /* ignite explodable elements reached by other explosion */
6014       if (element == EL_EXPLOSION)
6015         element = Store2[x][y];
6016
6017       if (AmoebaNr[x][y] &&
6018           (element == EL_AMOEBA_FULL ||
6019            element == EL_BD_AMOEBA ||
6020            element == EL_AMOEBA_GROWING))
6021       {
6022         AmoebaCnt[AmoebaNr[x][y]]--;
6023         AmoebaCnt2[AmoebaNr[x][y]]--;
6024       }
6025
6026       RemoveField(x, y);
6027
6028       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6029       {
6030         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6031
6032         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6033
6034         if (PLAYERINFO(ex, ey)->use_murphy)
6035           Store[x][y] = EL_EMPTY;
6036       }
6037
6038       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6039          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6040       else if (ELEM_IS_PLAYER(center_element))
6041         Store[x][y] = EL_EMPTY;
6042       else if (center_element == EL_YAMYAM)
6043         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6044       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6045         Store[x][y] = element_info[center_element].content.e[xx][yy];
6046 #if 1
6047       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6048          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6049          otherwise) -- FIX THIS !!! */
6050       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6051         Store[x][y] = element_info[element].content.e[1][1];
6052 #else
6053       else if (!CAN_EXPLODE(element))
6054         Store[x][y] = element_info[element].content.e[1][1];
6055 #endif
6056       else
6057         Store[x][y] = EL_EMPTY;
6058
6059       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6060           center_element == EL_AMOEBA_TO_DIAMOND)
6061         Store2[x][y] = element;
6062
6063       Feld[x][y] = EL_EXPLOSION;
6064       GfxElement[x][y] = artwork_element;
6065
6066       ExplodePhase[x][y] = 1;
6067       ExplodeDelay[x][y] = last_phase;
6068
6069       Stop[x][y] = TRUE;
6070     }
6071
6072     if (center_element == EL_YAMYAM)
6073       game.yamyam_content_nr =
6074         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6075
6076     return;
6077   }
6078
6079   if (Stop[ex][ey])
6080     return;
6081
6082   x = ex;
6083   y = ey;
6084
6085   if (phase == 1)
6086     GfxFrame[x][y] = 0;         /* restart explosion animation */
6087
6088   last_phase = ExplodeDelay[x][y];
6089
6090   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6091
6092 #ifdef DEBUG
6093
6094   /* activate this even in non-DEBUG version until cause for crash in
6095      getGraphicAnimationFrame() (see below) is found and eliminated */
6096
6097 #endif
6098 #if 1
6099
6100 #if 1
6101   /* this can happen if the player leaves an explosion just in time */
6102   if (GfxElement[x][y] == EL_UNDEFINED)
6103     GfxElement[x][y] = EL_EMPTY;
6104 #else
6105   if (GfxElement[x][y] == EL_UNDEFINED)
6106   {
6107     printf("\n\n");
6108     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6109     printf("Explode(): This should never happen!\n");
6110     printf("\n\n");
6111
6112     GfxElement[x][y] = EL_EMPTY;
6113   }
6114 #endif
6115
6116 #endif
6117
6118   border_element = Store2[x][y];
6119   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6120     border_element = StorePlayer[x][y];
6121
6122   if (phase == element_info[border_element].ignition_delay ||
6123       phase == last_phase)
6124   {
6125     boolean border_explosion = FALSE;
6126
6127     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6128         !PLAYER_EXPLOSION_PROTECTED(x, y))
6129     {
6130       KillPlayerUnlessExplosionProtected(x, y);
6131       border_explosion = TRUE;
6132     }
6133     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6134     {
6135       Feld[x][y] = Store2[x][y];
6136       Store2[x][y] = 0;
6137       Bang(x, y);
6138       border_explosion = TRUE;
6139     }
6140     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6141     {
6142       AmoebeUmwandeln(x, y);
6143       Store2[x][y] = 0;
6144       border_explosion = TRUE;
6145     }
6146
6147     /* if an element just explodes due to another explosion (chain-reaction),
6148        do not immediately end the new explosion when it was the last frame of
6149        the explosion (as it would be done in the following "if"-statement!) */
6150     if (border_explosion && phase == last_phase)
6151       return;
6152   }
6153
6154   if (phase == last_phase)
6155   {
6156     int element;
6157
6158     element = Feld[x][y] = Store[x][y];
6159     Store[x][y] = Store2[x][y] = 0;
6160     GfxElement[x][y] = EL_UNDEFINED;
6161
6162     /* player can escape from explosions and might therefore be still alive */
6163     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6164         element <= EL_PLAYER_IS_EXPLODING_4)
6165     {
6166       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6167       int explosion_element = EL_PLAYER_1 + player_nr;
6168       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6169       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6170
6171       if (level.use_explosion_element[player_nr])
6172         explosion_element = level.explosion_element[player_nr];
6173
6174       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6175                     element_info[explosion_element].content.e[xx][yy]);
6176     }
6177
6178     /* restore probably existing indestructible background element */
6179     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6180       element = Feld[x][y] = Back[x][y];
6181     Back[x][y] = 0;
6182
6183     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6184     GfxDir[x][y] = MV_NONE;
6185     ChangeDelay[x][y] = 0;
6186     ChangePage[x][y] = -1;
6187
6188 #if USE_NEW_CUSTOM_VALUE
6189     CustomValue[x][y] = 0;
6190 #endif
6191
6192     InitField_WithBug2(x, y, FALSE);
6193
6194     TEST_DrawLevelField(x, y);
6195
6196     TestIfElementTouchesCustomElement(x, y);
6197
6198     if (GFX_CRUMBLED(element))
6199       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6200
6201     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6202       StorePlayer[x][y] = 0;
6203
6204     if (ELEM_IS_PLAYER(element))
6205       RelocatePlayer(x, y, element);
6206   }
6207   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6208   {
6209     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6210     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6211
6212     if (phase == delay)
6213       TEST_DrawLevelFieldCrumbled(x, y);
6214
6215     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6216     {
6217       DrawLevelElement(x, y, Back[x][y]);
6218       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6219     }
6220     else if (IS_WALKABLE_UNDER(Back[x][y]))
6221     {
6222       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6223       DrawLevelElementThruMask(x, y, Back[x][y]);
6224     }
6225     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6226       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6227   }
6228 }
6229
6230 void DynaExplode(int ex, int ey)
6231 {
6232   int i, j;
6233   int dynabomb_element = Feld[ex][ey];
6234   int dynabomb_size = 1;
6235   boolean dynabomb_xl = FALSE;
6236   struct PlayerInfo *player;
6237   static int xy[4][2] =
6238   {
6239     { 0, -1 },
6240     { -1, 0 },
6241     { +1, 0 },
6242     { 0, +1 }
6243   };
6244
6245   if (IS_ACTIVE_BOMB(dynabomb_element))
6246   {
6247     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6248     dynabomb_size = player->dynabomb_size;
6249     dynabomb_xl = player->dynabomb_xl;
6250     player->dynabombs_left++;
6251   }
6252
6253   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6254
6255   for (i = 0; i < NUM_DIRECTIONS; i++)
6256   {
6257     for (j = 1; j <= dynabomb_size; j++)
6258     {
6259       int x = ex + j * xy[i][0];
6260       int y = ey + j * xy[i][1];
6261       int element;
6262
6263       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6264         break;
6265
6266       element = Feld[x][y];
6267
6268       /* do not restart explosions of fields with active bombs */
6269       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6270         continue;
6271
6272       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6273
6274       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6275           !IS_DIGGABLE(element) && !dynabomb_xl)
6276         break;
6277     }
6278   }
6279 }
6280
6281 void Bang(int x, int y)
6282 {
6283   int element = MovingOrBlocked2Element(x, y);
6284   int explosion_type = EX_TYPE_NORMAL;
6285
6286   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6287   {
6288     struct PlayerInfo *player = PLAYERINFO(x, y);
6289
6290 #if USE_FIX_CE_ACTION_WITH_PLAYER
6291     element = Feld[x][y] = player->initial_element;
6292 #else
6293     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6294                             player->element_nr);
6295 #endif
6296
6297     if (level.use_explosion_element[player->index_nr])
6298     {
6299       int explosion_element = level.explosion_element[player->index_nr];
6300
6301       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6302         explosion_type = EX_TYPE_CROSS;
6303       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6304         explosion_type = EX_TYPE_CENTER;
6305     }
6306   }
6307
6308   switch (element)
6309   {
6310     case EL_BUG:
6311     case EL_SPACESHIP:
6312     case EL_BD_BUTTERFLY:
6313     case EL_BD_FIREFLY:
6314     case EL_YAMYAM:
6315     case EL_DARK_YAMYAM:
6316     case EL_ROBOT:
6317     case EL_PACMAN:
6318     case EL_MOLE:
6319       RaiseScoreElement(element);
6320       break;
6321
6322     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6323     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6324     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6325     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6326     case EL_DYNABOMB_INCREASE_NUMBER:
6327     case EL_DYNABOMB_INCREASE_SIZE:
6328     case EL_DYNABOMB_INCREASE_POWER:
6329       explosion_type = EX_TYPE_DYNA;
6330       break;
6331
6332     case EL_DC_LANDMINE:
6333 #if 0
6334     case EL_EM_EXIT_OPEN:
6335     case EL_EM_STEEL_EXIT_OPEN:
6336 #endif
6337       explosion_type = EX_TYPE_CENTER;
6338       break;
6339
6340     case EL_PENGUIN:
6341     case EL_LAMP:
6342     case EL_LAMP_ACTIVE:
6343     case EL_AMOEBA_TO_DIAMOND:
6344       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6345         explosion_type = EX_TYPE_CENTER;
6346       break;
6347
6348     default:
6349       if (element_info[element].explosion_type == EXPLODES_CROSS)
6350         explosion_type = EX_TYPE_CROSS;
6351       else if (element_info[element].explosion_type == EXPLODES_1X1)
6352         explosion_type = EX_TYPE_CENTER;
6353       break;
6354   }
6355
6356   if (explosion_type == EX_TYPE_DYNA)
6357     DynaExplode(x, y);
6358   else
6359     Explode(x, y, EX_PHASE_START, explosion_type);
6360
6361   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6362 }
6363
6364 void SplashAcid(int x, int y)
6365 {
6366   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6367       (!IN_LEV_FIELD(x - 1, y - 2) ||
6368        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6369     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6370
6371   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6372       (!IN_LEV_FIELD(x + 1, y - 2) ||
6373        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6374     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6375
6376   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6377 }
6378
6379 static void InitBeltMovement()
6380 {
6381   static int belt_base_element[4] =
6382   {
6383     EL_CONVEYOR_BELT_1_LEFT,
6384     EL_CONVEYOR_BELT_2_LEFT,
6385     EL_CONVEYOR_BELT_3_LEFT,
6386     EL_CONVEYOR_BELT_4_LEFT
6387   };
6388   static int belt_base_active_element[4] =
6389   {
6390     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6391     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6392     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6393     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6394   };
6395
6396   int x, y, i, j;
6397
6398   /* set frame order for belt animation graphic according to belt direction */
6399   for (i = 0; i < NUM_BELTS; i++)
6400   {
6401     int belt_nr = i;
6402
6403     for (j = 0; j < NUM_BELT_PARTS; j++)
6404     {
6405       int element = belt_base_active_element[belt_nr] + j;
6406       int graphic_1 = el2img(element);
6407       int graphic_2 = el2panelimg(element);
6408
6409       if (game.belt_dir[i] == MV_LEFT)
6410       {
6411         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6412         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6413       }
6414       else
6415       {
6416         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6417         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6418       }
6419     }
6420   }
6421
6422   SCAN_PLAYFIELD(x, y)
6423   {
6424     int element = Feld[x][y];
6425
6426     for (i = 0; i < NUM_BELTS; i++)
6427     {
6428       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6429       {
6430         int e_belt_nr = getBeltNrFromBeltElement(element);
6431         int belt_nr = i;
6432
6433         if (e_belt_nr == belt_nr)
6434         {
6435           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6436
6437           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6438         }
6439       }
6440     }
6441   }
6442 }
6443
6444 static void ToggleBeltSwitch(int x, int y)
6445 {
6446   static int belt_base_element[4] =
6447   {
6448     EL_CONVEYOR_BELT_1_LEFT,
6449     EL_CONVEYOR_BELT_2_LEFT,
6450     EL_CONVEYOR_BELT_3_LEFT,
6451     EL_CONVEYOR_BELT_4_LEFT
6452   };
6453   static int belt_base_active_element[4] =
6454   {
6455     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6456     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6457     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6458     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6459   };
6460   static int belt_base_switch_element[4] =
6461   {
6462     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6463     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6464     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6465     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6466   };
6467   static int belt_move_dir[4] =
6468   {
6469     MV_LEFT,
6470     MV_NONE,
6471     MV_RIGHT,
6472     MV_NONE,
6473   };
6474
6475   int element = Feld[x][y];
6476   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6477   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6478   int belt_dir = belt_move_dir[belt_dir_nr];
6479   int xx, yy, i;
6480
6481   if (!IS_BELT_SWITCH(element))
6482     return;
6483
6484   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6485   game.belt_dir[belt_nr] = belt_dir;
6486
6487   if (belt_dir_nr == 3)
6488     belt_dir_nr = 1;
6489
6490   /* set frame order for belt animation graphic according to belt direction */
6491   for (i = 0; i < NUM_BELT_PARTS; i++)
6492   {
6493     int element = belt_base_active_element[belt_nr] + i;
6494     int graphic_1 = el2img(element);
6495     int graphic_2 = el2panelimg(element);
6496
6497     if (belt_dir == MV_LEFT)
6498     {
6499       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6500       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6501     }
6502     else
6503     {
6504       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6505       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6506     }
6507   }
6508
6509   SCAN_PLAYFIELD(xx, yy)
6510   {
6511     int element = Feld[xx][yy];
6512
6513     if (IS_BELT_SWITCH(element))
6514     {
6515       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6516
6517       if (e_belt_nr == belt_nr)
6518       {
6519         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6520         TEST_DrawLevelField(xx, yy);
6521       }
6522     }
6523     else if (IS_BELT(element) && belt_dir != MV_NONE)
6524     {
6525       int e_belt_nr = getBeltNrFromBeltElement(element);
6526
6527       if (e_belt_nr == belt_nr)
6528       {
6529         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6530
6531         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6532         TEST_DrawLevelField(xx, yy);
6533       }
6534     }
6535     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6536     {
6537       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6538
6539       if (e_belt_nr == belt_nr)
6540       {
6541         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6542
6543         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6544         TEST_DrawLevelField(xx, yy);
6545       }
6546     }
6547   }
6548 }
6549
6550 static void ToggleSwitchgateSwitch(int x, int y)
6551 {
6552   int xx, yy;
6553
6554   game.switchgate_pos = !game.switchgate_pos;
6555
6556   SCAN_PLAYFIELD(xx, yy)
6557   {
6558     int element = Feld[xx][yy];
6559
6560 #if !USE_BOTH_SWITCHGATE_SWITCHES
6561     if (element == EL_SWITCHGATE_SWITCH_UP ||
6562         element == EL_SWITCHGATE_SWITCH_DOWN)
6563     {
6564       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6565       TEST_DrawLevelField(xx, yy);
6566     }
6567     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6568              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6569     {
6570       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6571       TEST_DrawLevelField(xx, yy);
6572     }
6573 #else
6574     if (element == EL_SWITCHGATE_SWITCH_UP)
6575     {
6576       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6577       TEST_DrawLevelField(xx, yy);
6578     }
6579     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6580     {
6581       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6582       TEST_DrawLevelField(xx, yy);
6583     }
6584     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6585     {
6586       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6587       TEST_DrawLevelField(xx, yy);
6588     }
6589     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6590     {
6591       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6592       TEST_DrawLevelField(xx, yy);
6593     }
6594 #endif
6595     else if (element == EL_SWITCHGATE_OPEN ||
6596              element == EL_SWITCHGATE_OPENING)
6597     {
6598       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6599
6600       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6601     }
6602     else if (element == EL_SWITCHGATE_CLOSED ||
6603              element == EL_SWITCHGATE_CLOSING)
6604     {
6605       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6606
6607       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6608     }
6609   }
6610 }
6611
6612 static int getInvisibleActiveFromInvisibleElement(int element)
6613 {
6614   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6615           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6616           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6617           element);
6618 }
6619
6620 static int getInvisibleFromInvisibleActiveElement(int element)
6621 {
6622   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6623           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6624           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6625           element);
6626 }
6627
6628 static void RedrawAllLightSwitchesAndInvisibleElements()
6629 {
6630   int x, y;
6631
6632   SCAN_PLAYFIELD(x, y)
6633   {
6634     int element = Feld[x][y];
6635
6636     if (element == EL_LIGHT_SWITCH &&
6637         game.light_time_left > 0)
6638     {
6639       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6640       TEST_DrawLevelField(x, y);
6641     }
6642     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6643              game.light_time_left == 0)
6644     {
6645       Feld[x][y] = EL_LIGHT_SWITCH;
6646       TEST_DrawLevelField(x, y);
6647     }
6648     else if (element == EL_EMC_DRIPPER &&
6649              game.light_time_left > 0)
6650     {
6651       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6652       TEST_DrawLevelField(x, y);
6653     }
6654     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6655              game.light_time_left == 0)
6656     {
6657       Feld[x][y] = EL_EMC_DRIPPER;
6658       TEST_DrawLevelField(x, y);
6659     }
6660     else if (element == EL_INVISIBLE_STEELWALL ||
6661              element == EL_INVISIBLE_WALL ||
6662              element == EL_INVISIBLE_SAND)
6663     {
6664       if (game.light_time_left > 0)
6665         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6666
6667       TEST_DrawLevelField(x, y);
6668
6669       /* uncrumble neighbour fields, if needed */
6670       if (element == EL_INVISIBLE_SAND)
6671         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6672     }
6673     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6674              element == EL_INVISIBLE_WALL_ACTIVE ||
6675              element == EL_INVISIBLE_SAND_ACTIVE)
6676     {
6677       if (game.light_time_left == 0)
6678         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6679
6680       TEST_DrawLevelField(x, y);
6681
6682       /* re-crumble neighbour fields, if needed */
6683       if (element == EL_INVISIBLE_SAND)
6684         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6685     }
6686   }
6687 }
6688
6689 static void RedrawAllInvisibleElementsForLenses()
6690 {
6691   int x, y;
6692
6693   SCAN_PLAYFIELD(x, y)
6694   {
6695     int element = Feld[x][y];
6696
6697     if (element == EL_EMC_DRIPPER &&
6698         game.lenses_time_left > 0)
6699     {
6700       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6701       TEST_DrawLevelField(x, y);
6702     }
6703     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6704              game.lenses_time_left == 0)
6705     {
6706       Feld[x][y] = EL_EMC_DRIPPER;
6707       TEST_DrawLevelField(x, y);
6708     }
6709     else if (element == EL_INVISIBLE_STEELWALL ||
6710              element == EL_INVISIBLE_WALL ||
6711              element == EL_INVISIBLE_SAND)
6712     {
6713       if (game.lenses_time_left > 0)
6714         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6715
6716       TEST_DrawLevelField(x, y);
6717
6718       /* uncrumble neighbour fields, if needed */
6719       if (element == EL_INVISIBLE_SAND)
6720         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6721     }
6722     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6723              element == EL_INVISIBLE_WALL_ACTIVE ||
6724              element == EL_INVISIBLE_SAND_ACTIVE)
6725     {
6726       if (game.lenses_time_left == 0)
6727         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6728
6729       TEST_DrawLevelField(x, y);
6730
6731       /* re-crumble neighbour fields, if needed */
6732       if (element == EL_INVISIBLE_SAND)
6733         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6734     }
6735   }
6736 }
6737
6738 static void RedrawAllInvisibleElementsForMagnifier()
6739 {
6740   int x, y;
6741
6742   SCAN_PLAYFIELD(x, y)
6743   {
6744     int element = Feld[x][y];
6745
6746     if (element == EL_EMC_FAKE_GRASS &&
6747         game.magnify_time_left > 0)
6748     {
6749       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6750       TEST_DrawLevelField(x, y);
6751     }
6752     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6753              game.magnify_time_left == 0)
6754     {
6755       Feld[x][y] = EL_EMC_FAKE_GRASS;
6756       TEST_DrawLevelField(x, y);
6757     }
6758     else if (IS_GATE_GRAY(element) &&
6759              game.magnify_time_left > 0)
6760     {
6761       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6762                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6763                     IS_EM_GATE_GRAY(element) ?
6764                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6765                     IS_EMC_GATE_GRAY(element) ?
6766                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6767                     IS_DC_GATE_GRAY(element) ?
6768                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6769                     element);
6770       TEST_DrawLevelField(x, y);
6771     }
6772     else if (IS_GATE_GRAY_ACTIVE(element) &&
6773              game.magnify_time_left == 0)
6774     {
6775       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6776                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6777                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6778                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6779                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6780                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6781                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6782                     EL_DC_GATE_WHITE_GRAY :
6783                     element);
6784       TEST_DrawLevelField(x, y);
6785     }
6786   }
6787 }
6788
6789 static void ToggleLightSwitch(int x, int y)
6790 {
6791   int element = Feld[x][y];
6792
6793   game.light_time_left =
6794     (element == EL_LIGHT_SWITCH ?
6795      level.time_light * FRAMES_PER_SECOND : 0);
6796
6797   RedrawAllLightSwitchesAndInvisibleElements();
6798 }
6799
6800 static void ActivateTimegateSwitch(int x, int y)
6801 {
6802   int xx, yy;
6803
6804   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6805
6806   SCAN_PLAYFIELD(xx, yy)
6807   {
6808     int element = Feld[xx][yy];
6809
6810     if (element == EL_TIMEGATE_CLOSED ||
6811         element == EL_TIMEGATE_CLOSING)
6812     {
6813       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6814       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6815     }
6816
6817     /*
6818     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6819     {
6820       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6821       TEST_DrawLevelField(xx, yy);
6822     }
6823     */
6824
6825   }
6826
6827 #if 1
6828   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6829                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6830 #else
6831   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6832 #endif
6833 }
6834
6835 void Impact(int x, int y)
6836 {
6837   boolean last_line = (y == lev_fieldy - 1);
6838   boolean object_hit = FALSE;
6839   boolean impact = (last_line || object_hit);
6840   int element = Feld[x][y];
6841   int smashed = EL_STEELWALL;
6842
6843   if (!last_line)       /* check if element below was hit */
6844   {
6845     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6846       return;
6847
6848     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6849                                          MovDir[x][y + 1] != MV_DOWN ||
6850                                          MovPos[x][y + 1] <= TILEY / 2));
6851
6852     /* do not smash moving elements that left the smashed field in time */
6853     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6854         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6855       object_hit = FALSE;
6856
6857 #if USE_QUICKSAND_IMPACT_BUGFIX
6858     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6859     {
6860       RemoveMovingField(x, y + 1);
6861       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6862       Feld[x][y + 2] = EL_ROCK;
6863       TEST_DrawLevelField(x, y + 2);
6864
6865       object_hit = TRUE;
6866     }
6867
6868     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6869     {
6870       RemoveMovingField(x, y + 1);
6871       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6872       Feld[x][y + 2] = EL_ROCK;
6873       TEST_DrawLevelField(x, y + 2);
6874
6875       object_hit = TRUE;
6876     }
6877 #endif
6878
6879     if (object_hit)
6880       smashed = MovingOrBlocked2Element(x, y + 1);
6881
6882     impact = (last_line || object_hit);
6883   }
6884
6885   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6886   {
6887     SplashAcid(x, y + 1);
6888     return;
6889   }
6890
6891   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6892   /* only reset graphic animation if graphic really changes after impact */
6893   if (impact &&
6894       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6895   {
6896     ResetGfxAnimation(x, y);
6897     TEST_DrawLevelField(x, y);
6898   }
6899
6900   if (impact && CAN_EXPLODE_IMPACT(element))
6901   {
6902     Bang(x, y);
6903     return;
6904   }
6905   else if (impact && element == EL_PEARL &&
6906            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6907   {
6908     ResetGfxAnimation(x, y);
6909
6910     Feld[x][y] = EL_PEARL_BREAKING;
6911     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6912     return;
6913   }
6914   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6915   {
6916     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6917
6918     return;
6919   }
6920
6921   if (impact && element == EL_AMOEBA_DROP)
6922   {
6923     if (object_hit && IS_PLAYER(x, y + 1))
6924       KillPlayerUnlessEnemyProtected(x, y + 1);
6925     else if (object_hit && smashed == EL_PENGUIN)
6926       Bang(x, y + 1);
6927     else
6928     {
6929       Feld[x][y] = EL_AMOEBA_GROWING;
6930       Store[x][y] = EL_AMOEBA_WET;
6931
6932       ResetRandomAnimationValue(x, y);
6933     }
6934     return;
6935   }
6936
6937   if (object_hit)               /* check which object was hit */
6938   {
6939     if ((CAN_PASS_MAGIC_WALL(element) && 
6940          (smashed == EL_MAGIC_WALL ||
6941           smashed == EL_BD_MAGIC_WALL)) ||
6942         (CAN_PASS_DC_MAGIC_WALL(element) &&
6943          smashed == EL_DC_MAGIC_WALL))
6944     {
6945       int xx, yy;
6946       int activated_magic_wall =
6947         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6948          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6949          EL_DC_MAGIC_WALL_ACTIVE);
6950
6951       /* activate magic wall / mill */
6952       SCAN_PLAYFIELD(xx, yy)
6953       {
6954         if (Feld[xx][yy] == smashed)
6955           Feld[xx][yy] = activated_magic_wall;
6956       }
6957
6958       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6959       game.magic_wall_active = TRUE;
6960
6961       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6962                             SND_MAGIC_WALL_ACTIVATING :
6963                             smashed == EL_BD_MAGIC_WALL ?
6964                             SND_BD_MAGIC_WALL_ACTIVATING :
6965                             SND_DC_MAGIC_WALL_ACTIVATING));
6966     }
6967
6968     if (IS_PLAYER(x, y + 1))
6969     {
6970       if (CAN_SMASH_PLAYER(element))
6971       {
6972         KillPlayerUnlessEnemyProtected(x, y + 1);
6973         return;
6974       }
6975     }
6976     else if (smashed == EL_PENGUIN)
6977     {
6978       if (CAN_SMASH_PLAYER(element))
6979       {
6980         Bang(x, y + 1);
6981         return;
6982       }
6983     }
6984     else if (element == EL_BD_DIAMOND)
6985     {
6986       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6987       {
6988         Bang(x, y + 1);
6989         return;
6990       }
6991     }
6992     else if (((element == EL_SP_INFOTRON ||
6993                element == EL_SP_ZONK) &&
6994               (smashed == EL_SP_SNIKSNAK ||
6995                smashed == EL_SP_ELECTRON ||
6996                smashed == EL_SP_DISK_ORANGE)) ||
6997              (element == EL_SP_INFOTRON &&
6998               smashed == EL_SP_DISK_YELLOW))
6999     {
7000       Bang(x, y + 1);
7001       return;
7002     }
7003     else if (CAN_SMASH_EVERYTHING(element))
7004     {
7005       if (IS_CLASSIC_ENEMY(smashed) ||
7006           CAN_EXPLODE_SMASHED(smashed))
7007       {
7008         Bang(x, y + 1);
7009         return;
7010       }
7011       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7012       {
7013         if (smashed == EL_LAMP ||
7014             smashed == EL_LAMP_ACTIVE)
7015         {
7016           Bang(x, y + 1);
7017           return;
7018         }
7019         else if (smashed == EL_NUT)
7020         {
7021           Feld[x][y + 1] = EL_NUT_BREAKING;
7022           PlayLevelSound(x, y, SND_NUT_BREAKING);
7023           RaiseScoreElement(EL_NUT);
7024           return;
7025         }
7026         else if (smashed == EL_PEARL)
7027         {
7028           ResetGfxAnimation(x, y);
7029
7030           Feld[x][y + 1] = EL_PEARL_BREAKING;
7031           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7032           return;
7033         }
7034         else if (smashed == EL_DIAMOND)
7035         {
7036           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7037           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7038           return;
7039         }
7040         else if (IS_BELT_SWITCH(smashed))
7041         {
7042           ToggleBeltSwitch(x, y + 1);
7043         }
7044         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7045                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7046                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7047                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7048         {
7049           ToggleSwitchgateSwitch(x, y + 1);
7050         }
7051         else if (smashed == EL_LIGHT_SWITCH ||
7052                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7053         {
7054           ToggleLightSwitch(x, y + 1);
7055         }
7056         else
7057         {
7058 #if 0
7059           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7060 #endif
7061
7062           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7063
7064           CheckElementChangeBySide(x, y + 1, smashed, element,
7065                                    CE_SWITCHED, CH_SIDE_TOP);
7066           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7067                                             CH_SIDE_TOP);
7068         }
7069       }
7070       else
7071       {
7072         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7073       }
7074     }
7075   }
7076
7077   /* play sound of magic wall / mill */
7078   if (!last_line &&
7079       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7080        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7081        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7082   {
7083     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7084       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7085     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7086       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7087     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7088       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7089
7090     return;
7091   }
7092
7093   /* play sound of object that hits the ground */
7094   if (last_line || object_hit)
7095     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7096 }
7097
7098 inline static void TurnRoundExt(int x, int y)
7099 {
7100   static struct
7101   {
7102     int dx, dy;
7103   } move_xy[] =
7104   {
7105     {  0,  0 },
7106     { -1,  0 },
7107     { +1,  0 },
7108     {  0,  0 },
7109     {  0, -1 },
7110     {  0,  0 }, { 0, 0 }, { 0, 0 },
7111     {  0, +1 }
7112   };
7113   static struct
7114   {
7115     int left, right, back;
7116   } turn[] =
7117   {
7118     { 0,        0,              0        },
7119     { MV_DOWN,  MV_UP,          MV_RIGHT },
7120     { MV_UP,    MV_DOWN,        MV_LEFT  },
7121     { 0,        0,              0        },
7122     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7123     { 0,        0,              0        },
7124     { 0,        0,              0        },
7125     { 0,        0,              0        },
7126     { MV_RIGHT, MV_LEFT,        MV_UP    }
7127   };
7128
7129   int element = Feld[x][y];
7130   int move_pattern = element_info[element].move_pattern;
7131
7132   int old_move_dir = MovDir[x][y];
7133   int left_dir  = turn[old_move_dir].left;
7134   int right_dir = turn[old_move_dir].right;
7135   int back_dir  = turn[old_move_dir].back;
7136
7137   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7138   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7139   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7140   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7141
7142   int left_x  = x + left_dx,  left_y  = y + left_dy;
7143   int right_x = x + right_dx, right_y = y + right_dy;
7144   int move_x  = x + move_dx,  move_y  = y + move_dy;
7145
7146   int xx, yy;
7147
7148   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7149   {
7150     TestIfBadThingTouchesOtherBadThing(x, y);
7151
7152     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7153       MovDir[x][y] = right_dir;
7154     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7155       MovDir[x][y] = left_dir;
7156
7157     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7158       MovDelay[x][y] = 9;
7159     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7160       MovDelay[x][y] = 1;
7161   }
7162   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7163   {
7164     TestIfBadThingTouchesOtherBadThing(x, y);
7165
7166     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7167       MovDir[x][y] = left_dir;
7168     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7169       MovDir[x][y] = right_dir;
7170
7171     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7172       MovDelay[x][y] = 9;
7173     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7174       MovDelay[x][y] = 1;
7175   }
7176   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7177   {
7178     TestIfBadThingTouchesOtherBadThing(x, y);
7179
7180     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7181       MovDir[x][y] = left_dir;
7182     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7183       MovDir[x][y] = right_dir;
7184
7185     if (MovDir[x][y] != old_move_dir)
7186       MovDelay[x][y] = 9;
7187   }
7188   else if (element == EL_YAMYAM)
7189   {
7190     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7191     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7192
7193     if (can_turn_left && can_turn_right)
7194       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7195     else if (can_turn_left)
7196       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7197     else if (can_turn_right)
7198       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7199     else
7200       MovDir[x][y] = back_dir;
7201
7202     MovDelay[x][y] = 16 + 16 * RND(3);
7203   }
7204   else if (element == EL_DARK_YAMYAM)
7205   {
7206     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7207                                                          left_x, left_y);
7208     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7209                                                          right_x, right_y);
7210
7211     if (can_turn_left && can_turn_right)
7212       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7213     else if (can_turn_left)
7214       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7215     else if (can_turn_right)
7216       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7217     else
7218       MovDir[x][y] = back_dir;
7219
7220     MovDelay[x][y] = 16 + 16 * RND(3);
7221   }
7222   else if (element == EL_PACMAN)
7223   {
7224     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7225     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7226
7227     if (can_turn_left && can_turn_right)
7228       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7229     else if (can_turn_left)
7230       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7231     else if (can_turn_right)
7232       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7233     else
7234       MovDir[x][y] = back_dir;
7235
7236     MovDelay[x][y] = 6 + RND(40);
7237   }
7238   else if (element == EL_PIG)
7239   {
7240     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7241     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7242     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7243     boolean should_turn_left, should_turn_right, should_move_on;
7244     int rnd_value = 24;
7245     int rnd = RND(rnd_value);
7246
7247     should_turn_left = (can_turn_left &&
7248                         (!can_move_on ||
7249                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7250                                                    y + back_dy + left_dy)));
7251     should_turn_right = (can_turn_right &&
7252                          (!can_move_on ||
7253                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7254                                                     y + back_dy + right_dy)));
7255     should_move_on = (can_move_on &&
7256                       (!can_turn_left ||
7257                        !can_turn_right ||
7258                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7259                                                  y + move_dy + left_dy) ||
7260                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7261                                                  y + move_dy + right_dy)));
7262
7263     if (should_turn_left || should_turn_right || should_move_on)
7264     {
7265       if (should_turn_left && should_turn_right && should_move_on)
7266         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7267                         rnd < 2 * rnd_value / 3 ? right_dir :
7268                         old_move_dir);
7269       else if (should_turn_left && should_turn_right)
7270         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7271       else if (should_turn_left && should_move_on)
7272         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7273       else if (should_turn_right && should_move_on)
7274         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7275       else if (should_turn_left)
7276         MovDir[x][y] = left_dir;
7277       else if (should_turn_right)
7278         MovDir[x][y] = right_dir;
7279       else if (should_move_on)
7280         MovDir[x][y] = old_move_dir;
7281     }
7282     else if (can_move_on && rnd > rnd_value / 8)
7283       MovDir[x][y] = old_move_dir;
7284     else if (can_turn_left && can_turn_right)
7285       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7286     else if (can_turn_left && rnd > rnd_value / 8)
7287       MovDir[x][y] = left_dir;
7288     else if (can_turn_right && rnd > rnd_value/8)
7289       MovDir[x][y] = right_dir;
7290     else
7291       MovDir[x][y] = back_dir;
7292
7293     xx = x + move_xy[MovDir[x][y]].dx;
7294     yy = y + move_xy[MovDir[x][y]].dy;
7295
7296     if (!IN_LEV_FIELD(xx, yy) ||
7297         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7298       MovDir[x][y] = old_move_dir;
7299
7300     MovDelay[x][y] = 0;
7301   }
7302   else if (element == EL_DRAGON)
7303   {
7304     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7305     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7306     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7307     int rnd_value = 24;
7308     int rnd = RND(rnd_value);
7309
7310     if (can_move_on && rnd > rnd_value / 8)
7311       MovDir[x][y] = old_move_dir;
7312     else if (can_turn_left && can_turn_right)
7313       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7314     else if (can_turn_left && rnd > rnd_value / 8)
7315       MovDir[x][y] = left_dir;
7316     else if (can_turn_right && rnd > rnd_value / 8)
7317       MovDir[x][y] = right_dir;
7318     else
7319       MovDir[x][y] = back_dir;
7320
7321     xx = x + move_xy[MovDir[x][y]].dx;
7322     yy = y + move_xy[MovDir[x][y]].dy;
7323
7324     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7325       MovDir[x][y] = old_move_dir;
7326
7327     MovDelay[x][y] = 0;
7328   }
7329   else if (element == EL_MOLE)
7330   {
7331     boolean can_move_on =
7332       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7333                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7334                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7335     if (!can_move_on)
7336     {
7337       boolean can_turn_left =
7338         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7339                               IS_AMOEBOID(Feld[left_x][left_y])));
7340
7341       boolean can_turn_right =
7342         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7343                               IS_AMOEBOID(Feld[right_x][right_y])));
7344
7345       if (can_turn_left && can_turn_right)
7346         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7347       else if (can_turn_left)
7348         MovDir[x][y] = left_dir;
7349       else
7350         MovDir[x][y] = right_dir;
7351     }
7352
7353     if (MovDir[x][y] != old_move_dir)
7354       MovDelay[x][y] = 9;
7355   }
7356   else if (element == EL_BALLOON)
7357   {
7358     MovDir[x][y] = game.wind_direction;
7359     MovDelay[x][y] = 0;
7360   }
7361   else if (element == EL_SPRING)
7362   {
7363 #if USE_NEW_SPRING_BUMPER
7364     if (MovDir[x][y] & MV_HORIZONTAL)
7365     {
7366       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7367           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7368       {
7369         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7370         ResetGfxAnimation(move_x, move_y);
7371         TEST_DrawLevelField(move_x, move_y);
7372
7373         MovDir[x][y] = back_dir;
7374       }
7375       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7376                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7377         MovDir[x][y] = MV_NONE;
7378     }
7379 #else
7380     if (MovDir[x][y] & MV_HORIZONTAL &&
7381         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7382          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7383       MovDir[x][y] = MV_NONE;
7384 #endif
7385
7386     MovDelay[x][y] = 0;
7387   }
7388   else if (element == EL_ROBOT ||
7389            element == EL_SATELLITE ||
7390            element == EL_PENGUIN ||
7391            element == EL_EMC_ANDROID)
7392   {
7393     int attr_x = -1, attr_y = -1;
7394
7395     if (AllPlayersGone)
7396     {
7397       attr_x = ExitX;
7398       attr_y = ExitY;
7399     }
7400     else
7401     {
7402       int i;
7403
7404       for (i = 0; i < MAX_PLAYERS; i++)
7405       {
7406         struct PlayerInfo *player = &stored_player[i];
7407         int jx = player->jx, jy = player->jy;
7408
7409         if (!player->active)
7410           continue;
7411
7412         if (attr_x == -1 ||
7413             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7414         {
7415           attr_x = jx;
7416           attr_y = jy;
7417         }
7418       }
7419     }
7420
7421     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7422         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7423          game.engine_version < VERSION_IDENT(3,1,0,0)))
7424     {
7425       attr_x = ZX;
7426       attr_y = ZY;
7427     }
7428
7429     if (element == EL_PENGUIN)
7430     {
7431       int i;
7432       static int xy[4][2] =
7433       {
7434         { 0, -1 },
7435         { -1, 0 },
7436         { +1, 0 },
7437         { 0, +1 }
7438       };
7439
7440       for (i = 0; i < NUM_DIRECTIONS; i++)
7441       {
7442         int ex = x + xy[i][0];
7443         int ey = y + xy[i][1];
7444
7445         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7446                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7447                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7448                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7449         {
7450           attr_x = ex;
7451           attr_y = ey;
7452           break;
7453         }
7454       }
7455     }
7456
7457     MovDir[x][y] = MV_NONE;
7458     if (attr_x < x)
7459       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7460     else if (attr_x > x)
7461       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7462     if (attr_y < y)
7463       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7464     else if (attr_y > y)
7465       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7466
7467     if (element == EL_ROBOT)
7468     {
7469       int newx, newy;
7470
7471       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7472         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7473       Moving2Blocked(x, y, &newx, &newy);
7474
7475       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7476         MovDelay[x][y] = 8 + 8 * !RND(3);
7477       else
7478         MovDelay[x][y] = 16;
7479     }
7480     else if (element == EL_PENGUIN)
7481     {
7482       int newx, newy;
7483
7484       MovDelay[x][y] = 1;
7485
7486       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7487       {
7488         boolean first_horiz = RND(2);
7489         int new_move_dir = MovDir[x][y];
7490
7491         MovDir[x][y] =
7492           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7493         Moving2Blocked(x, y, &newx, &newy);
7494
7495         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7496           return;
7497
7498         MovDir[x][y] =
7499           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7500         Moving2Blocked(x, y, &newx, &newy);
7501
7502         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7503           return;
7504
7505         MovDir[x][y] = old_move_dir;
7506         return;
7507       }
7508     }
7509     else if (element == EL_SATELLITE)
7510     {
7511       int newx, newy;
7512
7513       MovDelay[x][y] = 1;
7514
7515       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7516       {
7517         boolean first_horiz = RND(2);
7518         int new_move_dir = MovDir[x][y];
7519
7520         MovDir[x][y] =
7521           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7522         Moving2Blocked(x, y, &newx, &newy);
7523
7524         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7525           return;
7526
7527         MovDir[x][y] =
7528           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7529         Moving2Blocked(x, y, &newx, &newy);
7530
7531         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7532           return;
7533
7534         MovDir[x][y] = old_move_dir;
7535         return;
7536       }
7537     }
7538     else if (element == EL_EMC_ANDROID)
7539     {
7540       static int check_pos[16] =
7541       {
7542         -1,             /*  0 => (invalid)          */
7543         7,              /*  1 => MV_LEFT            */
7544         3,              /*  2 => MV_RIGHT           */
7545         -1,             /*  3 => (invalid)          */
7546         1,              /*  4 =>            MV_UP   */
7547         0,              /*  5 => MV_LEFT  | MV_UP   */
7548         2,              /*  6 => MV_RIGHT | MV_UP   */
7549         -1,             /*  7 => (invalid)          */
7550         5,              /*  8 =>            MV_DOWN */
7551         6,              /*  9 => MV_LEFT  | MV_DOWN */
7552         4,              /* 10 => MV_RIGHT | MV_DOWN */
7553         -1,             /* 11 => (invalid)          */
7554         -1,             /* 12 => (invalid)          */
7555         -1,             /* 13 => (invalid)          */
7556         -1,             /* 14 => (invalid)          */
7557         -1,             /* 15 => (invalid)          */
7558       };
7559       static struct
7560       {
7561         int dx, dy;
7562         int dir;
7563       } check_xy[8] =
7564       {
7565         { -1, -1,       MV_LEFT  | MV_UP   },
7566         {  0, -1,                  MV_UP   },
7567         { +1, -1,       MV_RIGHT | MV_UP   },
7568         { +1,  0,       MV_RIGHT           },
7569         { +1, +1,       MV_RIGHT | MV_DOWN },
7570         {  0, +1,                  MV_DOWN },
7571         { -1, +1,       MV_LEFT  | MV_DOWN },
7572         { -1,  0,       MV_LEFT            },
7573       };
7574       int start_pos, check_order;
7575       boolean can_clone = FALSE;
7576       int i;
7577
7578       /* check if there is any free field around current position */
7579       for (i = 0; i < 8; i++)
7580       {
7581         int newx = x + check_xy[i].dx;
7582         int newy = y + check_xy[i].dy;
7583
7584         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7585         {
7586           can_clone = TRUE;
7587
7588           break;
7589         }
7590       }
7591
7592       if (can_clone)            /* randomly find an element to clone */
7593       {
7594         can_clone = FALSE;
7595
7596         start_pos = check_pos[RND(8)];
7597         check_order = (RND(2) ? -1 : +1);
7598
7599         for (i = 0; i < 8; i++)
7600         {
7601           int pos_raw = start_pos + i * check_order;
7602           int pos = (pos_raw + 8) % 8;
7603           int newx = x + check_xy[pos].dx;
7604           int newy = y + check_xy[pos].dy;
7605
7606           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7607           {
7608             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7609             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7610
7611             Store[x][y] = Feld[newx][newy];
7612
7613             can_clone = TRUE;
7614
7615             break;
7616           }
7617         }
7618       }
7619
7620       if (can_clone)            /* randomly find a direction to move */
7621       {
7622         can_clone = FALSE;
7623
7624         start_pos = check_pos[RND(8)];
7625         check_order = (RND(2) ? -1 : +1);
7626
7627         for (i = 0; i < 8; i++)
7628         {
7629           int pos_raw = start_pos + i * check_order;
7630           int pos = (pos_raw + 8) % 8;
7631           int newx = x + check_xy[pos].dx;
7632           int newy = y + check_xy[pos].dy;
7633           int new_move_dir = check_xy[pos].dir;
7634
7635           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7636           {
7637             MovDir[x][y] = new_move_dir;
7638             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7639
7640             can_clone = TRUE;
7641
7642             break;
7643           }
7644         }
7645       }
7646
7647       if (can_clone)            /* cloning and moving successful */
7648         return;
7649
7650       /* cannot clone -- try to move towards player */
7651
7652       start_pos = check_pos[MovDir[x][y] & 0x0f];
7653       check_order = (RND(2) ? -1 : +1);
7654
7655       for (i = 0; i < 3; i++)
7656       {
7657         /* first check start_pos, then previous/next or (next/previous) pos */
7658         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7659         int pos = (pos_raw + 8) % 8;
7660         int newx = x + check_xy[pos].dx;
7661         int newy = y + check_xy[pos].dy;
7662         int new_move_dir = check_xy[pos].dir;
7663
7664         if (IS_PLAYER(newx, newy))
7665           break;
7666
7667         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7668         {
7669           MovDir[x][y] = new_move_dir;
7670           MovDelay[x][y] = level.android_move_time * 8 + 1;
7671
7672           break;
7673         }
7674       }
7675     }
7676   }
7677   else if (move_pattern == MV_TURNING_LEFT ||
7678            move_pattern == MV_TURNING_RIGHT ||
7679            move_pattern == MV_TURNING_LEFT_RIGHT ||
7680            move_pattern == MV_TURNING_RIGHT_LEFT ||
7681            move_pattern == MV_TURNING_RANDOM ||
7682            move_pattern == MV_ALL_DIRECTIONS)
7683   {
7684     boolean can_turn_left =
7685       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7686     boolean can_turn_right =
7687       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7688
7689     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7690       return;
7691
7692     if (move_pattern == MV_TURNING_LEFT)
7693       MovDir[x][y] = left_dir;
7694     else if (move_pattern == MV_TURNING_RIGHT)
7695       MovDir[x][y] = right_dir;
7696     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7697       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7698     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7699       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7700     else if (move_pattern == MV_TURNING_RANDOM)
7701       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7702                       can_turn_right && !can_turn_left ? right_dir :
7703                       RND(2) ? left_dir : right_dir);
7704     else if (can_turn_left && can_turn_right)
7705       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7706     else if (can_turn_left)
7707       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7708     else if (can_turn_right)
7709       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7710     else
7711       MovDir[x][y] = back_dir;
7712
7713     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7714   }
7715   else if (move_pattern == MV_HORIZONTAL ||
7716            move_pattern == MV_VERTICAL)
7717   {
7718     if (move_pattern & old_move_dir)
7719       MovDir[x][y] = back_dir;
7720     else if (move_pattern == MV_HORIZONTAL)
7721       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7722     else if (move_pattern == MV_VERTICAL)
7723       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7724
7725     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7726   }
7727   else if (move_pattern & MV_ANY_DIRECTION)
7728   {
7729     MovDir[x][y] = move_pattern;
7730     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7731   }
7732   else if (move_pattern & MV_WIND_DIRECTION)
7733   {
7734     MovDir[x][y] = game.wind_direction;
7735     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7736   }
7737   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7738   {
7739     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7740       MovDir[x][y] = left_dir;
7741     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7742       MovDir[x][y] = right_dir;
7743
7744     if (MovDir[x][y] != old_move_dir)
7745       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7746   }
7747   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7748   {
7749     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7750       MovDir[x][y] = right_dir;
7751     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7752       MovDir[x][y] = left_dir;
7753
7754     if (MovDir[x][y] != old_move_dir)
7755       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7756   }
7757   else if (move_pattern == MV_TOWARDS_PLAYER ||
7758            move_pattern == MV_AWAY_FROM_PLAYER)
7759   {
7760     int attr_x = -1, attr_y = -1;
7761     int newx, newy;
7762     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7763
7764     if (AllPlayersGone)
7765     {
7766       attr_x = ExitX;
7767       attr_y = ExitY;
7768     }
7769     else
7770     {
7771       int i;
7772
7773       for (i = 0; i < MAX_PLAYERS; i++)
7774       {
7775         struct PlayerInfo *player = &stored_player[i];
7776         int jx = player->jx, jy = player->jy;
7777
7778         if (!player->active)
7779           continue;
7780
7781         if (attr_x == -1 ||
7782             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7783         {
7784           attr_x = jx;
7785           attr_y = jy;
7786         }
7787       }
7788     }
7789
7790     MovDir[x][y] = MV_NONE;
7791     if (attr_x < x)
7792       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7793     else if (attr_x > x)
7794       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7795     if (attr_y < y)
7796       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7797     else if (attr_y > y)
7798       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7799
7800     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7801
7802     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7803     {
7804       boolean first_horiz = RND(2);
7805       int new_move_dir = MovDir[x][y];
7806
7807       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7808       {
7809         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7810         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7811
7812         return;
7813       }
7814
7815       MovDir[x][y] =
7816         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7817       Moving2Blocked(x, y, &newx, &newy);
7818
7819       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7820         return;
7821
7822       MovDir[x][y] =
7823         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7824       Moving2Blocked(x, y, &newx, &newy);
7825
7826       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7827         return;
7828
7829       MovDir[x][y] = old_move_dir;
7830     }
7831   }
7832   else if (move_pattern == MV_WHEN_PUSHED ||
7833            move_pattern == MV_WHEN_DROPPED)
7834   {
7835     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7836       MovDir[x][y] = MV_NONE;
7837
7838     MovDelay[x][y] = 0;
7839   }
7840   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7841   {
7842     static int test_xy[7][2] =
7843     {
7844       { 0, -1 },
7845       { -1, 0 },
7846       { +1, 0 },
7847       { 0, +1 },
7848       { 0, -1 },
7849       { -1, 0 },
7850       { +1, 0 },
7851     };
7852     static int test_dir[7] =
7853     {
7854       MV_UP,
7855       MV_LEFT,
7856       MV_RIGHT,
7857       MV_DOWN,
7858       MV_UP,
7859       MV_LEFT,
7860       MV_RIGHT,
7861     };
7862     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7863     int move_preference = -1000000;     /* start with very low preference */
7864     int new_move_dir = MV_NONE;
7865     int start_test = RND(4);
7866     int i;
7867
7868     for (i = 0; i < NUM_DIRECTIONS; i++)
7869     {
7870       int move_dir = test_dir[start_test + i];
7871       int move_dir_preference;
7872
7873       xx = x + test_xy[start_test + i][0];
7874       yy = y + test_xy[start_test + i][1];
7875
7876       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7877           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7878       {
7879         new_move_dir = move_dir;
7880
7881         break;
7882       }
7883
7884       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7885         continue;
7886
7887       move_dir_preference = -1 * RunnerVisit[xx][yy];
7888       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7889         move_dir_preference = PlayerVisit[xx][yy];
7890
7891       if (move_dir_preference > move_preference)
7892       {
7893         /* prefer field that has not been visited for the longest time */
7894         move_preference = move_dir_preference;
7895         new_move_dir = move_dir;
7896       }
7897       else if (move_dir_preference == move_preference &&
7898                move_dir == old_move_dir)
7899       {
7900         /* prefer last direction when all directions are preferred equally */
7901         move_preference = move_dir_preference;
7902         new_move_dir = move_dir;
7903       }
7904     }
7905
7906     MovDir[x][y] = new_move_dir;
7907     if (old_move_dir != new_move_dir)
7908       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7909   }
7910 }
7911
7912 static void TurnRound(int x, int y)
7913 {
7914   int direction = MovDir[x][y];
7915
7916   TurnRoundExt(x, y);
7917
7918   GfxDir[x][y] = MovDir[x][y];
7919
7920   if (direction != MovDir[x][y])
7921     GfxFrame[x][y] = 0;
7922
7923   if (MovDelay[x][y])
7924     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7925
7926   ResetGfxFrame(x, y, FALSE);
7927 }
7928
7929 static boolean JustBeingPushed(int x, int y)
7930 {
7931   int i;
7932
7933   for (i = 0; i < MAX_PLAYERS; i++)
7934   {
7935     struct PlayerInfo *player = &stored_player[i];
7936
7937     if (player->active && player->is_pushing && player->MovPos)
7938     {
7939       int next_jx = player->jx + (player->jx - player->last_jx);
7940       int next_jy = player->jy + (player->jy - player->last_jy);
7941
7942       if (x == next_jx && y == next_jy)
7943         return TRUE;
7944     }
7945   }
7946
7947   return FALSE;
7948 }
7949
7950 void StartMoving(int x, int y)
7951 {
7952   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7953   int element = Feld[x][y];
7954
7955   if (Stop[x][y])
7956     return;
7957
7958   if (MovDelay[x][y] == 0)
7959     GfxAction[x][y] = ACTION_DEFAULT;
7960
7961   if (CAN_FALL(element) && y < lev_fieldy - 1)
7962   {
7963     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7964         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7965       if (JustBeingPushed(x, y))
7966         return;
7967
7968     if (element == EL_QUICKSAND_FULL)
7969     {
7970       if (IS_FREE(x, y + 1))
7971       {
7972         InitMovingField(x, y, MV_DOWN);
7973         started_moving = TRUE;
7974
7975         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7976 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7977         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7978           Store[x][y] = EL_ROCK;
7979 #else
7980         Store[x][y] = EL_ROCK;
7981 #endif
7982
7983         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7984       }
7985       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7986       {
7987         if (!MovDelay[x][y])
7988         {
7989           MovDelay[x][y] = TILEY + 1;
7990
7991           ResetGfxAnimation(x, y);
7992           ResetGfxAnimation(x, y + 1);
7993         }
7994
7995         if (MovDelay[x][y])
7996         {
7997           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7998           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7999
8000           MovDelay[x][y]--;
8001           if (MovDelay[x][y])
8002             return;
8003         }
8004
8005         Feld[x][y] = EL_QUICKSAND_EMPTY;
8006         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8007         Store[x][y + 1] = Store[x][y];
8008         Store[x][y] = 0;
8009
8010         PlayLevelSoundAction(x, y, ACTION_FILLING);
8011       }
8012       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8013       {
8014         if (!MovDelay[x][y])
8015         {
8016           MovDelay[x][y] = TILEY + 1;
8017
8018           ResetGfxAnimation(x, y);
8019           ResetGfxAnimation(x, y + 1);
8020         }
8021
8022         if (MovDelay[x][y])
8023         {
8024           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8025           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8026
8027           MovDelay[x][y]--;
8028           if (MovDelay[x][y])
8029             return;
8030         }
8031
8032         Feld[x][y] = EL_QUICKSAND_EMPTY;
8033         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8034         Store[x][y + 1] = Store[x][y];
8035         Store[x][y] = 0;
8036
8037         PlayLevelSoundAction(x, y, ACTION_FILLING);
8038       }
8039     }
8040     else if (element == EL_QUICKSAND_FAST_FULL)
8041     {
8042       if (IS_FREE(x, y + 1))
8043       {
8044         InitMovingField(x, y, MV_DOWN);
8045         started_moving = TRUE;
8046
8047         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8048 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8049         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8050           Store[x][y] = EL_ROCK;
8051 #else
8052         Store[x][y] = EL_ROCK;
8053 #endif
8054
8055         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8056       }
8057       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8058       {
8059         if (!MovDelay[x][y])
8060         {
8061           MovDelay[x][y] = TILEY + 1;
8062
8063           ResetGfxAnimation(x, y);
8064           ResetGfxAnimation(x, y + 1);
8065         }
8066
8067         if (MovDelay[x][y])
8068         {
8069           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8070           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8071
8072           MovDelay[x][y]--;
8073           if (MovDelay[x][y])
8074             return;
8075         }
8076
8077         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8078         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8079         Store[x][y + 1] = Store[x][y];
8080         Store[x][y] = 0;
8081
8082         PlayLevelSoundAction(x, y, ACTION_FILLING);
8083       }
8084       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8085       {
8086         if (!MovDelay[x][y])
8087         {
8088           MovDelay[x][y] = TILEY + 1;
8089
8090           ResetGfxAnimation(x, y);
8091           ResetGfxAnimation(x, y + 1);
8092         }
8093
8094         if (MovDelay[x][y])
8095         {
8096           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8097           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8098
8099           MovDelay[x][y]--;
8100           if (MovDelay[x][y])
8101             return;
8102         }
8103
8104         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8105         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8106         Store[x][y + 1] = Store[x][y];
8107         Store[x][y] = 0;
8108
8109         PlayLevelSoundAction(x, y, ACTION_FILLING);
8110       }
8111     }
8112     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8113              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8114     {
8115       InitMovingField(x, y, MV_DOWN);
8116       started_moving = TRUE;
8117
8118       Feld[x][y] = EL_QUICKSAND_FILLING;
8119       Store[x][y] = element;
8120
8121       PlayLevelSoundAction(x, y, ACTION_FILLING);
8122     }
8123     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8124              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8125     {
8126       InitMovingField(x, y, MV_DOWN);
8127       started_moving = TRUE;
8128
8129       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8130       Store[x][y] = element;
8131
8132       PlayLevelSoundAction(x, y, ACTION_FILLING);
8133     }
8134     else if (element == EL_MAGIC_WALL_FULL)
8135     {
8136       if (IS_FREE(x, y + 1))
8137       {
8138         InitMovingField(x, y, MV_DOWN);
8139         started_moving = TRUE;
8140
8141         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8142         Store[x][y] = EL_CHANGED(Store[x][y]);
8143       }
8144       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8145       {
8146         if (!MovDelay[x][y])
8147           MovDelay[x][y] = TILEY / 4 + 1;
8148
8149         if (MovDelay[x][y])
8150         {
8151           MovDelay[x][y]--;
8152           if (MovDelay[x][y])
8153             return;
8154         }
8155
8156         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8157         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8158         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8159         Store[x][y] = 0;
8160       }
8161     }
8162     else if (element == EL_BD_MAGIC_WALL_FULL)
8163     {
8164       if (IS_FREE(x, y + 1))
8165       {
8166         InitMovingField(x, y, MV_DOWN);
8167         started_moving = TRUE;
8168
8169         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8170         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8171       }
8172       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8173       {
8174         if (!MovDelay[x][y])
8175           MovDelay[x][y] = TILEY / 4 + 1;
8176
8177         if (MovDelay[x][y])
8178         {
8179           MovDelay[x][y]--;
8180           if (MovDelay[x][y])
8181             return;
8182         }
8183
8184         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8185         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8186         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8187         Store[x][y] = 0;
8188       }
8189     }
8190     else if (element == EL_DC_MAGIC_WALL_FULL)
8191     {
8192       if (IS_FREE(x, y + 1))
8193       {
8194         InitMovingField(x, y, MV_DOWN);
8195         started_moving = TRUE;
8196
8197         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8198         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8199       }
8200       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8201       {
8202         if (!MovDelay[x][y])
8203           MovDelay[x][y] = TILEY / 4 + 1;
8204
8205         if (MovDelay[x][y])
8206         {
8207           MovDelay[x][y]--;
8208           if (MovDelay[x][y])
8209             return;
8210         }
8211
8212         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8213         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8214         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8215         Store[x][y] = 0;
8216       }
8217     }
8218     else if ((CAN_PASS_MAGIC_WALL(element) &&
8219               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8220                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8221              (CAN_PASS_DC_MAGIC_WALL(element) &&
8222               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8223
8224     {
8225       InitMovingField(x, y, MV_DOWN);
8226       started_moving = TRUE;
8227
8228       Feld[x][y] =
8229         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8230          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8231          EL_DC_MAGIC_WALL_FILLING);
8232       Store[x][y] = element;
8233     }
8234     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8235     {
8236       SplashAcid(x, y + 1);
8237
8238       InitMovingField(x, y, MV_DOWN);
8239       started_moving = TRUE;
8240
8241       Store[x][y] = EL_ACID;
8242     }
8243     else if (
8244 #if USE_FIX_IMPACT_COLLISION
8245              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8246               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8247 #else
8248              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8249               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8250 #endif
8251              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8252               CAN_FALL(element) && WasJustFalling[x][y] &&
8253               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8254
8255              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8256               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8257               (Feld[x][y + 1] == EL_BLOCKED)))
8258     {
8259       /* this is needed for a special case not covered by calling "Impact()"
8260          from "ContinueMoving()": if an element moves to a tile directly below
8261          another element which was just falling on that tile (which was empty
8262          in the previous frame), the falling element above would just stop
8263          instead of smashing the element below (in previous version, the above
8264          element was just checked for "moving" instead of "falling", resulting
8265          in incorrect smashes caused by horizontal movement of the above
8266          element; also, the case of the player being the element to smash was
8267          simply not covered here... :-/ ) */
8268
8269       CheckCollision[x][y] = 0;
8270       CheckImpact[x][y] = 0;
8271
8272       Impact(x, y);
8273     }
8274     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8275     {
8276       if (MovDir[x][y] == MV_NONE)
8277       {
8278         InitMovingField(x, y, MV_DOWN);
8279         started_moving = TRUE;
8280       }
8281     }
8282     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8283     {
8284       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8285         MovDir[x][y] = MV_DOWN;
8286
8287       InitMovingField(x, y, MV_DOWN);
8288       started_moving = TRUE;
8289     }
8290     else if (element == EL_AMOEBA_DROP)
8291     {
8292       Feld[x][y] = EL_AMOEBA_GROWING;
8293       Store[x][y] = EL_AMOEBA_WET;
8294     }
8295     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8296               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8297              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8298              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8299     {
8300       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8301                                 (IS_FREE(x - 1, y + 1) ||
8302                                  Feld[x - 1][y + 1] == EL_ACID));
8303       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8304                                 (IS_FREE(x + 1, y + 1) ||
8305                                  Feld[x + 1][y + 1] == EL_ACID));
8306       boolean can_fall_any  = (can_fall_left || can_fall_right);
8307       boolean can_fall_both = (can_fall_left && can_fall_right);
8308       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8309
8310 #if USE_NEW_ALL_SLIPPERY
8311       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8312       {
8313         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8314           can_fall_right = FALSE;
8315         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8316           can_fall_left = FALSE;
8317         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8318           can_fall_right = FALSE;
8319         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8320           can_fall_left = FALSE;
8321
8322         can_fall_any  = (can_fall_left || can_fall_right);
8323         can_fall_both = FALSE;
8324       }
8325 #else
8326       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8327       {
8328         if (slippery_type == SLIPPERY_ONLY_LEFT)
8329           can_fall_right = FALSE;
8330         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8331           can_fall_left = FALSE;
8332         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8333           can_fall_right = FALSE;
8334         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8335           can_fall_left = FALSE;
8336
8337         can_fall_any  = (can_fall_left || can_fall_right);
8338         can_fall_both = (can_fall_left && can_fall_right);
8339       }
8340 #endif
8341
8342 #if USE_NEW_ALL_SLIPPERY
8343 #else
8344 #if USE_NEW_SP_SLIPPERY
8345       /* !!! better use the same properties as for custom elements here !!! */
8346       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8347                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8348       {
8349         can_fall_right = FALSE;         /* slip down on left side */
8350         can_fall_both = FALSE;
8351       }
8352 #endif
8353 #endif
8354
8355 #if USE_NEW_ALL_SLIPPERY
8356       if (can_fall_both)
8357       {
8358         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8359           can_fall_right = FALSE;       /* slip down on left side */
8360         else
8361           can_fall_left = !(can_fall_right = RND(2));
8362
8363         can_fall_both = FALSE;
8364       }
8365 #else
8366       if (can_fall_both)
8367       {
8368         if (game.emulation == EMU_BOULDERDASH ||
8369             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8370           can_fall_right = FALSE;       /* slip down on left side */
8371         else
8372           can_fall_left = !(can_fall_right = RND(2));
8373
8374         can_fall_both = FALSE;
8375       }
8376 #endif
8377
8378       if (can_fall_any)
8379       {
8380         /* if not determined otherwise, prefer left side for slipping down */
8381         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8382         started_moving = TRUE;
8383       }
8384     }
8385 #if 0
8386     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8387 #else
8388     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8389 #endif
8390     {
8391       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8392       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8393       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8394       int belt_dir = game.belt_dir[belt_nr];
8395
8396       if ((belt_dir == MV_LEFT  && left_is_free) ||
8397           (belt_dir == MV_RIGHT && right_is_free))
8398       {
8399         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8400
8401         InitMovingField(x, y, belt_dir);
8402         started_moving = TRUE;
8403
8404         Pushed[x][y] = TRUE;
8405         Pushed[nextx][y] = TRUE;
8406
8407         GfxAction[x][y] = ACTION_DEFAULT;
8408       }
8409       else
8410       {
8411         MovDir[x][y] = 0;       /* if element was moving, stop it */
8412       }
8413     }
8414   }
8415
8416   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8417 #if 0
8418   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8419 #else
8420   if (CAN_MOVE(element) && !started_moving)
8421 #endif
8422   {
8423     int move_pattern = element_info[element].move_pattern;
8424     int newx, newy;
8425
8426 #if 0
8427 #if DEBUG
8428     if (MovDir[x][y] == MV_NONE)
8429     {
8430       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8431              x, y, element, element_info[element].token_name);
8432       printf("StartMoving(): This should never happen!\n");
8433     }
8434 #endif
8435 #endif
8436
8437     Moving2Blocked(x, y, &newx, &newy);
8438
8439     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8440       return;
8441
8442     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8443         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8444     {
8445       WasJustMoving[x][y] = 0;
8446       CheckCollision[x][y] = 0;
8447
8448       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8449
8450       if (Feld[x][y] != element)        /* element has changed */
8451         return;
8452     }
8453
8454     if (!MovDelay[x][y])        /* start new movement phase */
8455     {
8456       /* all objects that can change their move direction after each step
8457          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8458
8459       if (element != EL_YAMYAM &&
8460           element != EL_DARK_YAMYAM &&
8461           element != EL_PACMAN &&
8462           !(move_pattern & MV_ANY_DIRECTION) &&
8463           move_pattern != MV_TURNING_LEFT &&
8464           move_pattern != MV_TURNING_RIGHT &&
8465           move_pattern != MV_TURNING_LEFT_RIGHT &&
8466           move_pattern != MV_TURNING_RIGHT_LEFT &&
8467           move_pattern != MV_TURNING_RANDOM)
8468       {
8469         TurnRound(x, y);
8470
8471         if (MovDelay[x][y] && (element == EL_BUG ||
8472                                element == EL_SPACESHIP ||
8473                                element == EL_SP_SNIKSNAK ||
8474                                element == EL_SP_ELECTRON ||
8475                                element == EL_MOLE))
8476           TEST_DrawLevelField(x, y);
8477       }
8478     }
8479
8480     if (MovDelay[x][y])         /* wait some time before next movement */
8481     {
8482       MovDelay[x][y]--;
8483
8484       if (element == EL_ROBOT ||
8485           element == EL_YAMYAM ||
8486           element == EL_DARK_YAMYAM)
8487       {
8488         DrawLevelElementAnimationIfNeeded(x, y, element);
8489         PlayLevelSoundAction(x, y, ACTION_WAITING);
8490       }
8491       else if (element == EL_SP_ELECTRON)
8492         DrawLevelElementAnimationIfNeeded(x, y, element);
8493       else if (element == EL_DRAGON)
8494       {
8495         int i;
8496         int dir = MovDir[x][y];
8497         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8498         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8499         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8500                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8501                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8502                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8503         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8504
8505         GfxAction[x][y] = ACTION_ATTACKING;
8506
8507         if (IS_PLAYER(x, y))
8508           DrawPlayerField(x, y);
8509         else
8510           TEST_DrawLevelField(x, y);
8511
8512         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8513
8514         for (i = 1; i <= 3; i++)
8515         {
8516           int xx = x + i * dx;
8517           int yy = y + i * dy;
8518           int sx = SCREENX(xx);
8519           int sy = SCREENY(yy);
8520           int flame_graphic = graphic + (i - 1);
8521
8522           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8523             break;
8524
8525           if (MovDelay[x][y])
8526           {
8527             int flamed = MovingOrBlocked2Element(xx, yy);
8528
8529             /* !!! */
8530 #if 0
8531             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8532               Bang(xx, yy);
8533             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8534               RemoveMovingField(xx, yy);
8535             else
8536               RemoveField(xx, yy);
8537 #else
8538             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8539               Bang(xx, yy);
8540             else
8541               RemoveMovingField(xx, yy);
8542 #endif
8543
8544             ChangeDelay[xx][yy] = 0;
8545
8546             Feld[xx][yy] = EL_FLAMES;
8547
8548             if (IN_SCR_FIELD(sx, sy))
8549             {
8550               TEST_DrawLevelFieldCrumbled(xx, yy);
8551               DrawGraphic(sx, sy, flame_graphic, frame);
8552             }
8553           }
8554           else
8555           {
8556             if (Feld[xx][yy] == EL_FLAMES)
8557               Feld[xx][yy] = EL_EMPTY;
8558             TEST_DrawLevelField(xx, yy);
8559           }
8560         }
8561       }
8562
8563       if (MovDelay[x][y])       /* element still has to wait some time */
8564       {
8565         PlayLevelSoundAction(x, y, ACTION_WAITING);
8566
8567         return;
8568       }
8569     }
8570
8571     /* now make next step */
8572
8573     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8574
8575     if (DONT_COLLIDE_WITH(element) &&
8576         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8577         !PLAYER_ENEMY_PROTECTED(newx, newy))
8578     {
8579       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8580
8581       return;
8582     }
8583
8584     else if (CAN_MOVE_INTO_ACID(element) &&
8585              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8586              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8587              (MovDir[x][y] == MV_DOWN ||
8588               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8589     {
8590       SplashAcid(newx, newy);
8591       Store[x][y] = EL_ACID;
8592     }
8593     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8594     {
8595       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8596           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8597           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8598           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8599       {
8600         RemoveField(x, y);
8601         TEST_DrawLevelField(x, y);
8602
8603         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8604         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8605           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8606
8607         local_player->friends_still_needed--;
8608         if (!local_player->friends_still_needed &&
8609             !local_player->GameOver && AllPlayersGone)
8610           PlayerWins(local_player);
8611
8612         return;
8613       }
8614       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8615       {
8616         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8617           TEST_DrawLevelField(newx, newy);
8618         else
8619           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8620       }
8621       else if (!IS_FREE(newx, newy))
8622       {
8623         GfxAction[x][y] = ACTION_WAITING;
8624
8625         if (IS_PLAYER(x, y))
8626           DrawPlayerField(x, y);
8627         else
8628           TEST_DrawLevelField(x, y);
8629
8630         return;
8631       }
8632     }
8633     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8634     {
8635       if (IS_FOOD_PIG(Feld[newx][newy]))
8636       {
8637         if (IS_MOVING(newx, newy))
8638           RemoveMovingField(newx, newy);
8639         else
8640         {
8641           Feld[newx][newy] = EL_EMPTY;
8642           TEST_DrawLevelField(newx, newy);
8643         }
8644
8645         PlayLevelSound(x, y, SND_PIG_DIGGING);
8646       }
8647       else if (!IS_FREE(newx, newy))
8648       {
8649         if (IS_PLAYER(x, y))
8650           DrawPlayerField(x, y);
8651         else
8652           TEST_DrawLevelField(x, y);
8653
8654         return;
8655       }
8656     }
8657     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8658     {
8659       if (Store[x][y] != EL_EMPTY)
8660       {
8661         boolean can_clone = FALSE;
8662         int xx, yy;
8663
8664         /* check if element to clone is still there */
8665         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8666         {
8667           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8668           {
8669             can_clone = TRUE;
8670
8671             break;
8672           }
8673         }
8674
8675         /* cannot clone or target field not free anymore -- do not clone */
8676         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8677           Store[x][y] = EL_EMPTY;
8678       }
8679
8680       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8681       {
8682         if (IS_MV_DIAGONAL(MovDir[x][y]))
8683         {
8684           int diagonal_move_dir = MovDir[x][y];
8685           int stored = Store[x][y];
8686           int change_delay = 8;
8687           int graphic;
8688
8689           /* android is moving diagonally */
8690
8691           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8692
8693           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8694           GfxElement[x][y] = EL_EMC_ANDROID;
8695           GfxAction[x][y] = ACTION_SHRINKING;
8696           GfxDir[x][y] = diagonal_move_dir;
8697           ChangeDelay[x][y] = change_delay;
8698
8699           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8700                                    GfxDir[x][y]);
8701
8702           DrawLevelGraphicAnimation(x, y, graphic);
8703           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8704
8705           if (Feld[newx][newy] == EL_ACID)
8706           {
8707             SplashAcid(newx, newy);
8708
8709             return;
8710           }
8711
8712           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8713
8714           Store[newx][newy] = EL_EMC_ANDROID;
8715           GfxElement[newx][newy] = EL_EMC_ANDROID;
8716           GfxAction[newx][newy] = ACTION_GROWING;
8717           GfxDir[newx][newy] = diagonal_move_dir;
8718           ChangeDelay[newx][newy] = change_delay;
8719
8720           graphic = el_act_dir2img(GfxElement[newx][newy],
8721                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8722
8723           DrawLevelGraphicAnimation(newx, newy, graphic);
8724           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8725
8726           return;
8727         }
8728         else
8729         {
8730           Feld[newx][newy] = EL_EMPTY;
8731           TEST_DrawLevelField(newx, newy);
8732
8733           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8734         }
8735       }
8736       else if (!IS_FREE(newx, newy))
8737       {
8738 #if 0
8739         if (IS_PLAYER(x, y))
8740           DrawPlayerField(x, y);
8741         else
8742           TEST_DrawLevelField(x, y);
8743 #endif
8744
8745         return;
8746       }
8747     }
8748     else if (IS_CUSTOM_ELEMENT(element) &&
8749              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8750     {
8751 #if 1
8752       if (!DigFieldByCE(newx, newy, element))
8753         return;
8754 #else
8755       int new_element = Feld[newx][newy];
8756
8757       if (!IS_FREE(newx, newy))
8758       {
8759         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8760                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8761                       ACTION_BREAKING);
8762
8763         /* no element can dig solid indestructible elements */
8764         if (IS_INDESTRUCTIBLE(new_element) &&
8765             !IS_DIGGABLE(new_element) &&
8766             !IS_COLLECTIBLE(new_element))
8767           return;
8768
8769         if (AmoebaNr[newx][newy] &&
8770             (new_element == EL_AMOEBA_FULL ||
8771              new_element == EL_BD_AMOEBA ||
8772              new_element == EL_AMOEBA_GROWING))
8773         {
8774           AmoebaCnt[AmoebaNr[newx][newy]]--;
8775           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8776         }
8777
8778         if (IS_MOVING(newx, newy))
8779           RemoveMovingField(newx, newy);
8780         else
8781         {
8782           RemoveField(newx, newy);
8783           TEST_DrawLevelField(newx, newy);
8784         }
8785
8786         /* if digged element was about to explode, prevent the explosion */
8787         ExplodeField[newx][newy] = EX_TYPE_NONE;
8788
8789         PlayLevelSoundAction(x, y, action);
8790       }
8791
8792       Store[newx][newy] = EL_EMPTY;
8793
8794 #if 1
8795       /* this makes it possible to leave the removed element again */
8796       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8797         Store[newx][newy] = new_element;
8798 #else
8799       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8800       {
8801         int move_leave_element = element_info[element].move_leave_element;
8802
8803         /* this makes it possible to leave the removed element again */
8804         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8805                              new_element : move_leave_element);
8806       }
8807 #endif
8808
8809 #endif
8810
8811       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8812       {
8813         RunnerVisit[x][y] = FrameCounter;
8814         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8815       }
8816     }
8817     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8818     {
8819       if (!IS_FREE(newx, newy))
8820       {
8821         if (IS_PLAYER(x, y))
8822           DrawPlayerField(x, y);
8823         else
8824           TEST_DrawLevelField(x, y);
8825
8826         return;
8827       }
8828       else
8829       {
8830         boolean wanna_flame = !RND(10);
8831         int dx = newx - x, dy = newy - y;
8832         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8833         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8834         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8835                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8836         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8837                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8838
8839         if ((wanna_flame ||
8840              IS_CLASSIC_ENEMY(element1) ||
8841              IS_CLASSIC_ENEMY(element2)) &&
8842             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8843             element1 != EL_FLAMES && element2 != EL_FLAMES)
8844         {
8845           ResetGfxAnimation(x, y);
8846           GfxAction[x][y] = ACTION_ATTACKING;
8847
8848           if (IS_PLAYER(x, y))
8849             DrawPlayerField(x, y);
8850           else
8851             TEST_DrawLevelField(x, y);
8852
8853           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8854
8855           MovDelay[x][y] = 50;
8856
8857           /* !!! */
8858 #if 0
8859           RemoveField(newx, newy);
8860 #endif
8861           Feld[newx][newy] = EL_FLAMES;
8862           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8863           {
8864 #if 0
8865             RemoveField(newx1, newy1);
8866 #endif
8867             Feld[newx1][newy1] = EL_FLAMES;
8868           }
8869           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8870           {
8871 #if 0
8872             RemoveField(newx2, newy2);
8873 #endif
8874             Feld[newx2][newy2] = EL_FLAMES;
8875           }
8876
8877           return;
8878         }
8879       }
8880     }
8881     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8882              Feld[newx][newy] == EL_DIAMOND)
8883     {
8884       if (IS_MOVING(newx, newy))
8885         RemoveMovingField(newx, newy);
8886       else
8887       {
8888         Feld[newx][newy] = EL_EMPTY;
8889         TEST_DrawLevelField(newx, newy);
8890       }
8891
8892       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8893     }
8894     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8895              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8896     {
8897       if (AmoebaNr[newx][newy])
8898       {
8899         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8900         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8901             Feld[newx][newy] == EL_BD_AMOEBA)
8902           AmoebaCnt[AmoebaNr[newx][newy]]--;
8903       }
8904
8905 #if 0
8906       /* !!! test !!! */
8907       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8908       {
8909         RemoveMovingField(newx, newy);
8910       }
8911 #else
8912       if (IS_MOVING(newx, newy))
8913       {
8914         RemoveMovingField(newx, newy);
8915       }
8916 #endif
8917       else
8918       {
8919         Feld[newx][newy] = EL_EMPTY;
8920         TEST_DrawLevelField(newx, newy);
8921       }
8922
8923       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8924     }
8925     else if ((element == EL_PACMAN || element == EL_MOLE)
8926              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8927     {
8928       if (AmoebaNr[newx][newy])
8929       {
8930         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8931         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8932             Feld[newx][newy] == EL_BD_AMOEBA)
8933           AmoebaCnt[AmoebaNr[newx][newy]]--;
8934       }
8935
8936       if (element == EL_MOLE)
8937       {
8938         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8939         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8940
8941         ResetGfxAnimation(x, y);
8942         GfxAction[x][y] = ACTION_DIGGING;
8943         TEST_DrawLevelField(x, y);
8944
8945         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8946
8947         return;                         /* wait for shrinking amoeba */
8948       }
8949       else      /* element == EL_PACMAN */
8950       {
8951         Feld[newx][newy] = EL_EMPTY;
8952         TEST_DrawLevelField(newx, newy);
8953         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8954       }
8955     }
8956     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8957              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8958               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8959     {
8960       /* wait for shrinking amoeba to completely disappear */
8961       return;
8962     }
8963     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8964     {
8965       /* object was running against a wall */
8966
8967       TurnRound(x, y);
8968
8969 #if 0
8970       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8971       if (move_pattern & MV_ANY_DIRECTION &&
8972           move_pattern == MovDir[x][y])
8973       {
8974         int blocking_element =
8975           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8976
8977         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8978                                  MovDir[x][y]);
8979
8980         element = Feld[x][y];   /* element might have changed */
8981       }
8982 #endif
8983
8984       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8985         DrawLevelElementAnimation(x, y, element);
8986
8987       if (DONT_TOUCH(element))
8988         TestIfBadThingTouchesPlayer(x, y);
8989
8990       return;
8991     }
8992
8993     InitMovingField(x, y, MovDir[x][y]);
8994
8995     PlayLevelSoundAction(x, y, ACTION_MOVING);
8996   }
8997
8998   if (MovDir[x][y])
8999     ContinueMoving(x, y);
9000 }
9001
9002 void ContinueMoving(int x, int y)
9003 {
9004   int element = Feld[x][y];
9005   struct ElementInfo *ei = &element_info[element];
9006   int direction = MovDir[x][y];
9007   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9008   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9009   int newx = x + dx, newy = y + dy;
9010   int stored = Store[x][y];
9011   int stored_new = Store[newx][newy];
9012   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
9013   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9014   boolean last_line = (newy == lev_fieldy - 1);
9015
9016   MovPos[x][y] += getElementMoveStepsize(x, y);
9017
9018   if (pushed_by_player) /* special case: moving object pushed by player */
9019     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9020
9021   if (ABS(MovPos[x][y]) < TILEX)
9022   {
9023 #if 0
9024     int ee = Feld[x][y];
9025     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9026     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9027
9028     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9029            x, y, ABS(MovPos[x][y]),
9030            ee, gg, ff,
9031            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9032 #endif
9033
9034     TEST_DrawLevelField(x, y);
9035
9036     return;     /* element is still moving */
9037   }
9038
9039   /* element reached destination field */
9040
9041   Feld[x][y] = EL_EMPTY;
9042   Feld[newx][newy] = element;
9043   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9044
9045   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9046   {
9047     element = Feld[newx][newy] = EL_ACID;
9048   }
9049   else if (element == EL_MOLE)
9050   {
9051     Feld[x][y] = EL_SAND;
9052
9053     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9054   }
9055   else if (element == EL_QUICKSAND_FILLING)
9056   {
9057     element = Feld[newx][newy] = get_next_element(element);
9058     Store[newx][newy] = Store[x][y];
9059   }
9060   else if (element == EL_QUICKSAND_EMPTYING)
9061   {
9062     Feld[x][y] = get_next_element(element);
9063     element = Feld[newx][newy] = Store[x][y];
9064   }
9065   else if (element == EL_QUICKSAND_FAST_FILLING)
9066   {
9067     element = Feld[newx][newy] = get_next_element(element);
9068     Store[newx][newy] = Store[x][y];
9069   }
9070   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9071   {
9072     Feld[x][y] = get_next_element(element);
9073     element = Feld[newx][newy] = Store[x][y];
9074   }
9075   else if (element == EL_MAGIC_WALL_FILLING)
9076   {
9077     element = Feld[newx][newy] = get_next_element(element);
9078     if (!game.magic_wall_active)
9079       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9080     Store[newx][newy] = Store[x][y];
9081   }
9082   else if (element == EL_MAGIC_WALL_EMPTYING)
9083   {
9084     Feld[x][y] = get_next_element(element);
9085     if (!game.magic_wall_active)
9086       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9087     element = Feld[newx][newy] = Store[x][y];
9088
9089 #if USE_NEW_CUSTOM_VALUE
9090     InitField(newx, newy, FALSE);
9091 #endif
9092   }
9093   else if (element == EL_BD_MAGIC_WALL_FILLING)
9094   {
9095     element = Feld[newx][newy] = get_next_element(element);
9096     if (!game.magic_wall_active)
9097       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9098     Store[newx][newy] = Store[x][y];
9099   }
9100   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9101   {
9102     Feld[x][y] = get_next_element(element);
9103     if (!game.magic_wall_active)
9104       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9105     element = Feld[newx][newy] = Store[x][y];
9106
9107 #if USE_NEW_CUSTOM_VALUE
9108     InitField(newx, newy, FALSE);
9109 #endif
9110   }
9111   else if (element == EL_DC_MAGIC_WALL_FILLING)
9112   {
9113     element = Feld[newx][newy] = get_next_element(element);
9114     if (!game.magic_wall_active)
9115       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9116     Store[newx][newy] = Store[x][y];
9117   }
9118   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9119   {
9120     Feld[x][y] = get_next_element(element);
9121     if (!game.magic_wall_active)
9122       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9123     element = Feld[newx][newy] = Store[x][y];
9124
9125 #if USE_NEW_CUSTOM_VALUE
9126     InitField(newx, newy, FALSE);
9127 #endif
9128   }
9129   else if (element == EL_AMOEBA_DROPPING)
9130   {
9131     Feld[x][y] = get_next_element(element);
9132     element = Feld[newx][newy] = Store[x][y];
9133   }
9134   else if (element == EL_SOKOBAN_OBJECT)
9135   {
9136     if (Back[x][y])
9137       Feld[x][y] = Back[x][y];
9138
9139     if (Back[newx][newy])
9140       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9141
9142     Back[x][y] = Back[newx][newy] = 0;
9143   }
9144
9145   Store[x][y] = EL_EMPTY;
9146   MovPos[x][y] = 0;
9147   MovDir[x][y] = 0;
9148   MovDelay[x][y] = 0;
9149
9150   MovDelay[newx][newy] = 0;
9151
9152   if (CAN_CHANGE_OR_HAS_ACTION(element))
9153   {
9154     /* copy element change control values to new field */
9155     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9156     ChangePage[newx][newy]  = ChangePage[x][y];
9157     ChangeCount[newx][newy] = ChangeCount[x][y];
9158     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9159   }
9160
9161 #if USE_NEW_CUSTOM_VALUE
9162   CustomValue[newx][newy] = CustomValue[x][y];
9163 #endif
9164
9165   ChangeDelay[x][y] = 0;
9166   ChangePage[x][y] = -1;
9167   ChangeCount[x][y] = 0;
9168   ChangeEvent[x][y] = -1;
9169
9170 #if USE_NEW_CUSTOM_VALUE
9171   CustomValue[x][y] = 0;
9172 #endif
9173
9174   /* copy animation control values to new field */
9175   GfxFrame[newx][newy]  = GfxFrame[x][y];
9176   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9177   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9178   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9179
9180   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9181
9182   /* some elements can leave other elements behind after moving */
9183 #if 1
9184   if (ei->move_leave_element != EL_EMPTY &&
9185       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9186       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9187 #else
9188   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9189       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9190       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9191 #endif
9192   {
9193     int move_leave_element = ei->move_leave_element;
9194
9195 #if 1
9196 #if 1
9197     /* this makes it possible to leave the removed element again */
9198     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9199       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9200 #else
9201     /* this makes it possible to leave the removed element again */
9202     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9203       move_leave_element = stored;
9204 #endif
9205 #else
9206     /* this makes it possible to leave the removed element again */
9207     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9208         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9209       move_leave_element = stored;
9210 #endif
9211
9212     Feld[x][y] = move_leave_element;
9213
9214     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9215       MovDir[x][y] = direction;
9216
9217     InitField(x, y, FALSE);
9218
9219     if (GFX_CRUMBLED(Feld[x][y]))
9220       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9221
9222     if (ELEM_IS_PLAYER(move_leave_element))
9223       RelocatePlayer(x, y, move_leave_element);
9224   }
9225
9226   /* do this after checking for left-behind element */
9227   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9228
9229   if (!CAN_MOVE(element) ||
9230       (CAN_FALL(element) && direction == MV_DOWN &&
9231        (element == EL_SPRING ||
9232         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9233         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9234     GfxDir[x][y] = MovDir[newx][newy] = 0;
9235
9236   TEST_DrawLevelField(x, y);
9237   TEST_DrawLevelField(newx, newy);
9238
9239   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9240
9241   /* prevent pushed element from moving on in pushed direction */
9242   if (pushed_by_player && CAN_MOVE(element) &&
9243       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9244       !(element_info[element].move_pattern & direction))
9245     TurnRound(newx, newy);
9246
9247   /* prevent elements on conveyor belt from moving on in last direction */
9248   if (pushed_by_conveyor && CAN_FALL(element) &&
9249       direction & MV_HORIZONTAL)
9250     MovDir[newx][newy] = 0;
9251
9252   if (!pushed_by_player)
9253   {
9254     int nextx = newx + dx, nexty = newy + dy;
9255     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9256
9257     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9258
9259     if (CAN_FALL(element) && direction == MV_DOWN)
9260       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9261
9262     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9263       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9264
9265 #if USE_FIX_IMPACT_COLLISION
9266     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9267       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9268 #endif
9269   }
9270
9271   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9272   {
9273     TestIfBadThingTouchesPlayer(newx, newy);
9274     TestIfBadThingTouchesFriend(newx, newy);
9275
9276     if (!IS_CUSTOM_ELEMENT(element))
9277       TestIfBadThingTouchesOtherBadThing(newx, newy);
9278   }
9279   else if (element == EL_PENGUIN)
9280     TestIfFriendTouchesBadThing(newx, newy);
9281
9282   if (DONT_GET_HIT_BY(element))
9283   {
9284     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9285   }
9286
9287   /* give the player one last chance (one more frame) to move away */
9288   if (CAN_FALL(element) && direction == MV_DOWN &&
9289       (last_line || (!IS_FREE(x, newy + 1) &&
9290                      (!IS_PLAYER(x, newy + 1) ||
9291                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9292     Impact(x, newy);
9293
9294   if (pushed_by_player && !game.use_change_when_pushing_bug)
9295   {
9296     int push_side = MV_DIR_OPPOSITE(direction);
9297     struct PlayerInfo *player = PLAYERINFO(x, y);
9298
9299     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9300                                player->index_bit, push_side);
9301     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9302                                         player->index_bit, push_side);
9303   }
9304
9305   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9306     MovDelay[newx][newy] = 1;
9307
9308   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9309
9310   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9311
9312 #if 0
9313   if (ChangePage[newx][newy] != -1)             /* delayed change */
9314   {
9315     int page = ChangePage[newx][newy];
9316     struct ElementChangeInfo *change = &ei->change_page[page];
9317
9318     ChangePage[newx][newy] = -1;
9319
9320     if (change->can_change)
9321     {
9322       if (ChangeElement(newx, newy, element, page))
9323       {
9324         if (change->post_change_function)
9325           change->post_change_function(newx, newy);
9326       }
9327     }
9328
9329     if (change->has_action)
9330       ExecuteCustomElementAction(newx, newy, element, page);
9331   }
9332 #endif
9333
9334   TestIfElementHitsCustomElement(newx, newy, direction);
9335   TestIfPlayerTouchesCustomElement(newx, newy);
9336   TestIfElementTouchesCustomElement(newx, newy);
9337
9338   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9339       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9340     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9341                              MV_DIR_OPPOSITE(direction));
9342 }
9343
9344 int AmoebeNachbarNr(int ax, int ay)
9345 {
9346   int i;
9347   int element = Feld[ax][ay];
9348   int group_nr = 0;
9349   static int xy[4][2] =
9350   {
9351     { 0, -1 },
9352     { -1, 0 },
9353     { +1, 0 },
9354     { 0, +1 }
9355   };
9356
9357   for (i = 0; i < NUM_DIRECTIONS; i++)
9358   {
9359     int x = ax + xy[i][0];
9360     int y = ay + xy[i][1];
9361
9362     if (!IN_LEV_FIELD(x, y))
9363       continue;
9364
9365     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9366       group_nr = AmoebaNr[x][y];
9367   }
9368
9369   return group_nr;
9370 }
9371
9372 void AmoebenVereinigen(int ax, int ay)
9373 {
9374   int i, x, y, xx, yy;
9375   int new_group_nr = AmoebaNr[ax][ay];
9376   static int xy[4][2] =
9377   {
9378     { 0, -1 },
9379     { -1, 0 },
9380     { +1, 0 },
9381     { 0, +1 }
9382   };
9383
9384   if (new_group_nr == 0)
9385     return;
9386
9387   for (i = 0; i < NUM_DIRECTIONS; i++)
9388   {
9389     x = ax + xy[i][0];
9390     y = ay + xy[i][1];
9391
9392     if (!IN_LEV_FIELD(x, y))
9393       continue;
9394
9395     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9396          Feld[x][y] == EL_BD_AMOEBA ||
9397          Feld[x][y] == EL_AMOEBA_DEAD) &&
9398         AmoebaNr[x][y] != new_group_nr)
9399     {
9400       int old_group_nr = AmoebaNr[x][y];
9401
9402       if (old_group_nr == 0)
9403         return;
9404
9405       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9406       AmoebaCnt[old_group_nr] = 0;
9407       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9408       AmoebaCnt2[old_group_nr] = 0;
9409
9410       SCAN_PLAYFIELD(xx, yy)
9411       {
9412         if (AmoebaNr[xx][yy] == old_group_nr)
9413           AmoebaNr[xx][yy] = new_group_nr;
9414       }
9415     }
9416   }
9417 }
9418
9419 void AmoebeUmwandeln(int ax, int ay)
9420 {
9421   int i, x, y;
9422
9423   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9424   {
9425     int group_nr = AmoebaNr[ax][ay];
9426
9427 #ifdef DEBUG
9428     if (group_nr == 0)
9429     {
9430       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9431       printf("AmoebeUmwandeln(): This should never happen!\n");
9432       return;
9433     }
9434 #endif
9435
9436     SCAN_PLAYFIELD(x, y)
9437     {
9438       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9439       {
9440         AmoebaNr[x][y] = 0;
9441         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9442       }
9443     }
9444
9445     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9446                             SND_AMOEBA_TURNING_TO_GEM :
9447                             SND_AMOEBA_TURNING_TO_ROCK));
9448     Bang(ax, ay);
9449   }
9450   else
9451   {
9452     static int xy[4][2] =
9453     {
9454       { 0, -1 },
9455       { -1, 0 },
9456       { +1, 0 },
9457       { 0, +1 }
9458     };
9459
9460     for (i = 0; i < NUM_DIRECTIONS; i++)
9461     {
9462       x = ax + xy[i][0];
9463       y = ay + xy[i][1];
9464
9465       if (!IN_LEV_FIELD(x, y))
9466         continue;
9467
9468       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9469       {
9470         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9471                               SND_AMOEBA_TURNING_TO_GEM :
9472                               SND_AMOEBA_TURNING_TO_ROCK));
9473         Bang(x, y);
9474       }
9475     }
9476   }
9477 }
9478
9479 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9480 {
9481   int x, y;
9482   int group_nr = AmoebaNr[ax][ay];
9483   boolean done = FALSE;
9484
9485 #ifdef DEBUG
9486   if (group_nr == 0)
9487   {
9488     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9489     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9490     return;
9491   }
9492 #endif
9493
9494   SCAN_PLAYFIELD(x, y)
9495   {
9496     if (AmoebaNr[x][y] == group_nr &&
9497         (Feld[x][y] == EL_AMOEBA_DEAD ||
9498          Feld[x][y] == EL_BD_AMOEBA ||
9499          Feld[x][y] == EL_AMOEBA_GROWING))
9500     {
9501       AmoebaNr[x][y] = 0;
9502       Feld[x][y] = new_element;
9503       InitField(x, y, FALSE);
9504       TEST_DrawLevelField(x, y);
9505       done = TRUE;
9506     }
9507   }
9508
9509   if (done)
9510     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9511                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9512                             SND_BD_AMOEBA_TURNING_TO_GEM));
9513 }
9514
9515 void AmoebeWaechst(int x, int y)
9516 {
9517   static unsigned int sound_delay = 0;
9518   static unsigned int sound_delay_value = 0;
9519
9520   if (!MovDelay[x][y])          /* start new growing cycle */
9521   {
9522     MovDelay[x][y] = 7;
9523
9524     if (DelayReached(&sound_delay, sound_delay_value))
9525     {
9526       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9527       sound_delay_value = 30;
9528     }
9529   }
9530
9531   if (MovDelay[x][y])           /* wait some time before growing bigger */
9532   {
9533     MovDelay[x][y]--;
9534     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9535     {
9536       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9537                                            6 - MovDelay[x][y]);
9538
9539       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9540     }
9541
9542     if (!MovDelay[x][y])
9543     {
9544       Feld[x][y] = Store[x][y];
9545       Store[x][y] = 0;
9546       TEST_DrawLevelField(x, y);
9547     }
9548   }
9549 }
9550
9551 void AmoebaDisappearing(int x, int y)
9552 {
9553   static unsigned int sound_delay = 0;
9554   static unsigned int sound_delay_value = 0;
9555
9556   if (!MovDelay[x][y])          /* start new shrinking cycle */
9557   {
9558     MovDelay[x][y] = 7;
9559
9560     if (DelayReached(&sound_delay, sound_delay_value))
9561       sound_delay_value = 30;
9562   }
9563
9564   if (MovDelay[x][y])           /* wait some time before shrinking */
9565   {
9566     MovDelay[x][y]--;
9567     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9568     {
9569       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9570                                            6 - MovDelay[x][y]);
9571
9572       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9573     }
9574
9575     if (!MovDelay[x][y])
9576     {
9577       Feld[x][y] = EL_EMPTY;
9578       TEST_DrawLevelField(x, y);
9579
9580       /* don't let mole enter this field in this cycle;
9581          (give priority to objects falling to this field from above) */
9582       Stop[x][y] = TRUE;
9583     }
9584   }
9585 }
9586
9587 void AmoebeAbleger(int ax, int ay)
9588 {
9589   int i;
9590   int element = Feld[ax][ay];
9591   int graphic = el2img(element);
9592   int newax = ax, neway = ay;
9593   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9594   static int xy[4][2] =
9595   {
9596     { 0, -1 },
9597     { -1, 0 },
9598     { +1, 0 },
9599     { 0, +1 }
9600   };
9601
9602   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9603   {
9604     Feld[ax][ay] = EL_AMOEBA_DEAD;
9605     TEST_DrawLevelField(ax, ay);
9606     return;
9607   }
9608
9609   if (IS_ANIMATED(graphic))
9610     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9611
9612   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9613     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9614
9615   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9616   {
9617     MovDelay[ax][ay]--;
9618     if (MovDelay[ax][ay])
9619       return;
9620   }
9621
9622   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9623   {
9624     int start = RND(4);
9625     int x = ax + xy[start][0];
9626     int y = ay + xy[start][1];
9627
9628     if (!IN_LEV_FIELD(x, y))
9629       return;
9630
9631     if (IS_FREE(x, y) ||
9632         CAN_GROW_INTO(Feld[x][y]) ||
9633         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9634         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9635     {
9636       newax = x;
9637       neway = y;
9638     }
9639
9640     if (newax == ax && neway == ay)
9641       return;
9642   }
9643   else                          /* normal or "filled" (BD style) amoeba */
9644   {
9645     int start = RND(4);
9646     boolean waiting_for_player = FALSE;
9647
9648     for (i = 0; i < NUM_DIRECTIONS; i++)
9649     {
9650       int j = (start + i) % 4;
9651       int x = ax + xy[j][0];
9652       int y = ay + xy[j][1];
9653
9654       if (!IN_LEV_FIELD(x, y))
9655         continue;
9656
9657       if (IS_FREE(x, y) ||
9658           CAN_GROW_INTO(Feld[x][y]) ||
9659           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9660           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9661       {
9662         newax = x;
9663         neway = y;
9664         break;
9665       }
9666       else if (IS_PLAYER(x, y))
9667         waiting_for_player = TRUE;
9668     }
9669
9670     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9671     {
9672       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9673       {
9674         Feld[ax][ay] = EL_AMOEBA_DEAD;
9675         TEST_DrawLevelField(ax, ay);
9676         AmoebaCnt[AmoebaNr[ax][ay]]--;
9677
9678         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9679         {
9680           if (element == EL_AMOEBA_FULL)
9681             AmoebeUmwandeln(ax, ay);
9682           else if (element == EL_BD_AMOEBA)
9683             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9684         }
9685       }
9686       return;
9687     }
9688     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9689     {
9690       /* amoeba gets larger by growing in some direction */
9691
9692       int new_group_nr = AmoebaNr[ax][ay];
9693
9694 #ifdef DEBUG
9695   if (new_group_nr == 0)
9696   {
9697     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9698     printf("AmoebeAbleger(): This should never happen!\n");
9699     return;
9700   }
9701 #endif
9702
9703       AmoebaNr[newax][neway] = new_group_nr;
9704       AmoebaCnt[new_group_nr]++;
9705       AmoebaCnt2[new_group_nr]++;
9706
9707       /* if amoeba touches other amoeba(s) after growing, unify them */
9708       AmoebenVereinigen(newax, neway);
9709
9710       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9711       {
9712         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9713         return;
9714       }
9715     }
9716   }
9717
9718   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9719       (neway == lev_fieldy - 1 && newax != ax))
9720   {
9721     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9722     Store[newax][neway] = element;
9723   }
9724   else if (neway == ay || element == EL_EMC_DRIPPER)
9725   {
9726     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9727
9728     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9729   }
9730   else
9731   {
9732     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9733     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9734     Store[ax][ay] = EL_AMOEBA_DROP;
9735     ContinueMoving(ax, ay);
9736     return;
9737   }
9738
9739   TEST_DrawLevelField(newax, neway);
9740 }
9741
9742 void Life(int ax, int ay)
9743 {
9744   int x1, y1, x2, y2;
9745   int life_time = 40;
9746   int element = Feld[ax][ay];
9747   int graphic = el2img(element);
9748   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9749                          level.biomaze);
9750   boolean changed = FALSE;
9751
9752   if (IS_ANIMATED(graphic))
9753     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9754
9755   if (Stop[ax][ay])
9756     return;
9757
9758   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9759     MovDelay[ax][ay] = life_time;
9760
9761   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9762   {
9763     MovDelay[ax][ay]--;
9764     if (MovDelay[ax][ay])
9765       return;
9766   }
9767
9768   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9769   {
9770     int xx = ax+x1, yy = ay+y1;
9771     int nachbarn = 0;
9772
9773     if (!IN_LEV_FIELD(xx, yy))
9774       continue;
9775
9776     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9777     {
9778       int x = xx+x2, y = yy+y2;
9779
9780       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9781         continue;
9782
9783       if (((Feld[x][y] == element ||
9784             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9785            !Stop[x][y]) ||
9786           (IS_FREE(x, y) && Stop[x][y]))
9787         nachbarn++;
9788     }
9789
9790     if (xx == ax && yy == ay)           /* field in the middle */
9791     {
9792       if (nachbarn < life_parameter[0] ||
9793           nachbarn > life_parameter[1])
9794       {
9795         Feld[xx][yy] = EL_EMPTY;
9796         if (!Stop[xx][yy])
9797           TEST_DrawLevelField(xx, yy);
9798         Stop[xx][yy] = TRUE;
9799         changed = TRUE;
9800       }
9801     }
9802     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9803     {                                   /* free border field */
9804       if (nachbarn >= life_parameter[2] &&
9805           nachbarn <= life_parameter[3])
9806       {
9807         Feld[xx][yy] = element;
9808         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9809         if (!Stop[xx][yy])
9810           TEST_DrawLevelField(xx, yy);
9811         Stop[xx][yy] = TRUE;
9812         changed = TRUE;
9813       }
9814     }
9815   }
9816
9817   if (changed)
9818     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9819                    SND_GAME_OF_LIFE_GROWING);
9820 }
9821
9822 static void InitRobotWheel(int x, int y)
9823 {
9824   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9825 }
9826
9827 static void RunRobotWheel(int x, int y)
9828 {
9829   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9830 }
9831
9832 static void StopRobotWheel(int x, int y)
9833 {
9834   if (ZX == x && ZY == y)
9835   {
9836     ZX = ZY = -1;
9837
9838     game.robot_wheel_active = FALSE;
9839   }
9840 }
9841
9842 static void InitTimegateWheel(int x, int y)
9843 {
9844   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9845 }
9846
9847 static void RunTimegateWheel(int x, int y)
9848 {
9849   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9850 }
9851
9852 static void InitMagicBallDelay(int x, int y)
9853 {
9854 #if 1
9855   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9856 #else
9857   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9858 #endif
9859 }
9860
9861 static void ActivateMagicBall(int bx, int by)
9862 {
9863   int x, y;
9864
9865   if (level.ball_random)
9866   {
9867     int pos_border = RND(8);    /* select one of the eight border elements */
9868     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9869     int xx = pos_content % 3;
9870     int yy = pos_content / 3;
9871
9872     x = bx - 1 + xx;
9873     y = by - 1 + yy;
9874
9875     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9876       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9877   }
9878   else
9879   {
9880     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9881     {
9882       int xx = x - bx + 1;
9883       int yy = y - by + 1;
9884
9885       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9886         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9887     }
9888   }
9889
9890   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9891 }
9892
9893 void CheckExit(int x, int y)
9894 {
9895   if (local_player->gems_still_needed > 0 ||
9896       local_player->sokobanfields_still_needed > 0 ||
9897       local_player->lights_still_needed > 0)
9898   {
9899     int element = Feld[x][y];
9900     int graphic = el2img(element);
9901
9902     if (IS_ANIMATED(graphic))
9903       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9904
9905     return;
9906   }
9907
9908   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9909     return;
9910
9911   Feld[x][y] = EL_EXIT_OPENING;
9912
9913   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9914 }
9915
9916 void CheckExitEM(int x, int y)
9917 {
9918   if (local_player->gems_still_needed > 0 ||
9919       local_player->sokobanfields_still_needed > 0 ||
9920       local_player->lights_still_needed > 0)
9921   {
9922     int element = Feld[x][y];
9923     int graphic = el2img(element);
9924
9925     if (IS_ANIMATED(graphic))
9926       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9927
9928     return;
9929   }
9930
9931   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9932     return;
9933
9934   Feld[x][y] = EL_EM_EXIT_OPENING;
9935
9936   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9937 }
9938
9939 void CheckExitSteel(int x, int y)
9940 {
9941   if (local_player->gems_still_needed > 0 ||
9942       local_player->sokobanfields_still_needed > 0 ||
9943       local_player->lights_still_needed > 0)
9944   {
9945     int element = Feld[x][y];
9946     int graphic = el2img(element);
9947
9948     if (IS_ANIMATED(graphic))
9949       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9950
9951     return;
9952   }
9953
9954   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9955     return;
9956
9957   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9958
9959   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9960 }
9961
9962 void CheckExitSteelEM(int x, int y)
9963 {
9964   if (local_player->gems_still_needed > 0 ||
9965       local_player->sokobanfields_still_needed > 0 ||
9966       local_player->lights_still_needed > 0)
9967   {
9968     int element = Feld[x][y];
9969     int graphic = el2img(element);
9970
9971     if (IS_ANIMATED(graphic))
9972       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9973
9974     return;
9975   }
9976
9977   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9978     return;
9979
9980   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9981
9982   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9983 }
9984
9985 void CheckExitSP(int x, int y)
9986 {
9987   if (local_player->gems_still_needed > 0)
9988   {
9989     int element = Feld[x][y];
9990     int graphic = el2img(element);
9991
9992     if (IS_ANIMATED(graphic))
9993       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9994
9995     return;
9996   }
9997
9998   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9999     return;
10000
10001   Feld[x][y] = EL_SP_EXIT_OPENING;
10002
10003   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10004 }
10005
10006 static void CloseAllOpenTimegates()
10007 {
10008   int x, y;
10009
10010   SCAN_PLAYFIELD(x, y)
10011   {
10012     int element = Feld[x][y];
10013
10014     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10015     {
10016       Feld[x][y] = EL_TIMEGATE_CLOSING;
10017
10018       PlayLevelSoundAction(x, y, ACTION_CLOSING);
10019     }
10020   }
10021 }
10022
10023 void DrawTwinkleOnField(int x, int y)
10024 {
10025   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10026     return;
10027
10028   if (Feld[x][y] == EL_BD_DIAMOND)
10029     return;
10030
10031   if (MovDelay[x][y] == 0)      /* next animation frame */
10032     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10033
10034   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10035   {
10036     MovDelay[x][y]--;
10037
10038     DrawLevelElementAnimation(x, y, Feld[x][y]);
10039
10040     if (MovDelay[x][y] != 0)
10041     {
10042       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10043                                            10 - MovDelay[x][y]);
10044
10045       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10046     }
10047   }
10048 }
10049
10050 void MauerWaechst(int x, int y)
10051 {
10052   int delay = 6;
10053
10054   if (!MovDelay[x][y])          /* next animation frame */
10055     MovDelay[x][y] = 3 * delay;
10056
10057   if (MovDelay[x][y])           /* wait some time before next frame */
10058   {
10059     MovDelay[x][y]--;
10060
10061     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10062     {
10063       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10064       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10065
10066       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10067     }
10068
10069     if (!MovDelay[x][y])
10070     {
10071       if (MovDir[x][y] == MV_LEFT)
10072       {
10073         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10074           TEST_DrawLevelField(x - 1, y);
10075       }
10076       else if (MovDir[x][y] == MV_RIGHT)
10077       {
10078         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10079           TEST_DrawLevelField(x + 1, y);
10080       }
10081       else if (MovDir[x][y] == MV_UP)
10082       {
10083         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10084           TEST_DrawLevelField(x, y - 1);
10085       }
10086       else
10087       {
10088         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10089           TEST_DrawLevelField(x, y + 1);
10090       }
10091
10092       Feld[x][y] = Store[x][y];
10093       Store[x][y] = 0;
10094       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10095       TEST_DrawLevelField(x, y);
10096     }
10097   }
10098 }
10099
10100 void MauerAbleger(int ax, int ay)
10101 {
10102   int element = Feld[ax][ay];
10103   int graphic = el2img(element);
10104   boolean oben_frei = FALSE, unten_frei = FALSE;
10105   boolean links_frei = FALSE, rechts_frei = FALSE;
10106   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10107   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10108   boolean new_wall = FALSE;
10109
10110   if (IS_ANIMATED(graphic))
10111     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10112
10113   if (!MovDelay[ax][ay])        /* start building new wall */
10114     MovDelay[ax][ay] = 6;
10115
10116   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10117   {
10118     MovDelay[ax][ay]--;
10119     if (MovDelay[ax][ay])
10120       return;
10121   }
10122
10123   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10124     oben_frei = TRUE;
10125   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10126     unten_frei = TRUE;
10127   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10128     links_frei = TRUE;
10129   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10130     rechts_frei = TRUE;
10131
10132   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10133       element == EL_EXPANDABLE_WALL_ANY)
10134   {
10135     if (oben_frei)
10136     {
10137       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10138       Store[ax][ay-1] = element;
10139       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10140       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10141         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10142                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10143       new_wall = TRUE;
10144     }
10145     if (unten_frei)
10146     {
10147       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10148       Store[ax][ay+1] = element;
10149       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10150       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10151         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10152                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10153       new_wall = TRUE;
10154     }
10155   }
10156
10157   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10158       element == EL_EXPANDABLE_WALL_ANY ||
10159       element == EL_EXPANDABLE_WALL ||
10160       element == EL_BD_EXPANDABLE_WALL)
10161   {
10162     if (links_frei)
10163     {
10164       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10165       Store[ax-1][ay] = element;
10166       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10167       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10168         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10169                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10170       new_wall = TRUE;
10171     }
10172
10173     if (rechts_frei)
10174     {
10175       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10176       Store[ax+1][ay] = element;
10177       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10178       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10179         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10180                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10181       new_wall = TRUE;
10182     }
10183   }
10184
10185   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10186     TEST_DrawLevelField(ax, ay);
10187
10188   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10189     oben_massiv = TRUE;
10190   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10191     unten_massiv = TRUE;
10192   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10193     links_massiv = TRUE;
10194   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10195     rechts_massiv = TRUE;
10196
10197   if (((oben_massiv && unten_massiv) ||
10198        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10199        element == EL_EXPANDABLE_WALL) &&
10200       ((links_massiv && rechts_massiv) ||
10201        element == EL_EXPANDABLE_WALL_VERTICAL))
10202     Feld[ax][ay] = EL_WALL;
10203
10204   if (new_wall)
10205     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10206 }
10207
10208 void MauerAblegerStahl(int ax, int ay)
10209 {
10210   int element = Feld[ax][ay];
10211   int graphic = el2img(element);
10212   boolean oben_frei = FALSE, unten_frei = FALSE;
10213   boolean links_frei = FALSE, rechts_frei = FALSE;
10214   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10215   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10216   boolean new_wall = FALSE;
10217
10218   if (IS_ANIMATED(graphic))
10219     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10220
10221   if (!MovDelay[ax][ay])        /* start building new wall */
10222     MovDelay[ax][ay] = 6;
10223
10224   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10225   {
10226     MovDelay[ax][ay]--;
10227     if (MovDelay[ax][ay])
10228       return;
10229   }
10230
10231   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10232     oben_frei = TRUE;
10233   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10234     unten_frei = TRUE;
10235   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10236     links_frei = TRUE;
10237   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10238     rechts_frei = TRUE;
10239
10240   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10241       element == EL_EXPANDABLE_STEELWALL_ANY)
10242   {
10243     if (oben_frei)
10244     {
10245       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10246       Store[ax][ay-1] = element;
10247       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10248       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10249         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10250                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10251       new_wall = TRUE;
10252     }
10253     if (unten_frei)
10254     {
10255       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10256       Store[ax][ay+1] = element;
10257       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10258       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10259         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10260                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10261       new_wall = TRUE;
10262     }
10263   }
10264
10265   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10266       element == EL_EXPANDABLE_STEELWALL_ANY)
10267   {
10268     if (links_frei)
10269     {
10270       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10271       Store[ax-1][ay] = element;
10272       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10273       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10274         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10275                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10276       new_wall = TRUE;
10277     }
10278
10279     if (rechts_frei)
10280     {
10281       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10282       Store[ax+1][ay] = element;
10283       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10284       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10285         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10286                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10287       new_wall = TRUE;
10288     }
10289   }
10290
10291   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10292     oben_massiv = TRUE;
10293   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10294     unten_massiv = TRUE;
10295   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10296     links_massiv = TRUE;
10297   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10298     rechts_massiv = TRUE;
10299
10300   if (((oben_massiv && unten_massiv) ||
10301        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10302       ((links_massiv && rechts_massiv) ||
10303        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10304     Feld[ax][ay] = EL_STEELWALL;
10305
10306   if (new_wall)
10307     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10308 }
10309
10310 void CheckForDragon(int x, int y)
10311 {
10312   int i, j;
10313   boolean dragon_found = FALSE;
10314   static int xy[4][2] =
10315   {
10316     { 0, -1 },
10317     { -1, 0 },
10318     { +1, 0 },
10319     { 0, +1 }
10320   };
10321
10322   for (i = 0; i < NUM_DIRECTIONS; i++)
10323   {
10324     for (j = 0; j < 4; j++)
10325     {
10326       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10327
10328       if (IN_LEV_FIELD(xx, yy) &&
10329           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10330       {
10331         if (Feld[xx][yy] == EL_DRAGON)
10332           dragon_found = TRUE;
10333       }
10334       else
10335         break;
10336     }
10337   }
10338
10339   if (!dragon_found)
10340   {
10341     for (i = 0; i < NUM_DIRECTIONS; i++)
10342     {
10343       for (j = 0; j < 3; j++)
10344       {
10345         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10346   
10347         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10348         {
10349           Feld[xx][yy] = EL_EMPTY;
10350           TEST_DrawLevelField(xx, yy);
10351         }
10352         else
10353           break;
10354       }
10355     }
10356   }
10357 }
10358
10359 static void InitBuggyBase(int x, int y)
10360 {
10361   int element = Feld[x][y];
10362   int activating_delay = FRAMES_PER_SECOND / 4;
10363
10364   ChangeDelay[x][y] =
10365     (element == EL_SP_BUGGY_BASE ?
10366      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10367      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10368      activating_delay :
10369      element == EL_SP_BUGGY_BASE_ACTIVE ?
10370      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10371 }
10372
10373 static void WarnBuggyBase(int x, int y)
10374 {
10375   int i;
10376   static int xy[4][2] =
10377   {
10378     { 0, -1 },
10379     { -1, 0 },
10380     { +1, 0 },
10381     { 0, +1 }
10382   };
10383
10384   for (i = 0; i < NUM_DIRECTIONS; i++)
10385   {
10386     int xx = x + xy[i][0];
10387     int yy = y + xy[i][1];
10388
10389     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10390     {
10391       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10392
10393       break;
10394     }
10395   }
10396 }
10397
10398 static void InitTrap(int x, int y)
10399 {
10400   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10401 }
10402
10403 static void ActivateTrap(int x, int y)
10404 {
10405   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10406 }
10407
10408 static void ChangeActiveTrap(int x, int y)
10409 {
10410   int graphic = IMG_TRAP_ACTIVE;
10411
10412   /* if new animation frame was drawn, correct crumbled sand border */
10413   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10414     TEST_DrawLevelFieldCrumbled(x, y);
10415 }
10416
10417 static int getSpecialActionElement(int element, int number, int base_element)
10418 {
10419   return (element != EL_EMPTY ? element :
10420           number != -1 ? base_element + number - 1 :
10421           EL_EMPTY);
10422 }
10423
10424 static int getModifiedActionNumber(int value_old, int operator, int operand,
10425                                    int value_min, int value_max)
10426 {
10427   int value_new = (operator == CA_MODE_SET      ? operand :
10428                    operator == CA_MODE_ADD      ? value_old + operand :
10429                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10430                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10431                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10432                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10433                    value_old);
10434
10435   return (value_new < value_min ? value_min :
10436           value_new > value_max ? value_max :
10437           value_new);
10438 }
10439
10440 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10441 {
10442   struct ElementInfo *ei = &element_info[element];
10443   struct ElementChangeInfo *change = &ei->change_page[page];
10444   int target_element = change->target_element;
10445   int action_type = change->action_type;
10446   int action_mode = change->action_mode;
10447   int action_arg = change->action_arg;
10448   int action_element = change->action_element;
10449   int i;
10450
10451   if (!change->has_action)
10452     return;
10453
10454   /* ---------- determine action paramater values -------------------------- */
10455
10456   int level_time_value =
10457     (level.time > 0 ? TimeLeft :
10458      TimePlayed);
10459
10460   int action_arg_element_raw =
10461     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10462      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10463      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10464      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10465      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10466      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10467      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10468      EL_EMPTY);
10469   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10470
10471 #if 0
10472   if (action_arg_element_raw == EL_GROUP_START)
10473     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10474 #endif
10475
10476   int action_arg_direction =
10477     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10478      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10479      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10480      change->actual_trigger_side :
10481      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10482      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10483      MV_NONE);
10484
10485   int action_arg_number_min =
10486     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10487      CA_ARG_MIN);
10488
10489   int action_arg_number_max =
10490     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10491      action_type == CA_SET_LEVEL_GEMS ? 999 :
10492      action_type == CA_SET_LEVEL_TIME ? 9999 :
10493      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10494      action_type == CA_SET_CE_VALUE ? 9999 :
10495      action_type == CA_SET_CE_SCORE ? 9999 :
10496      CA_ARG_MAX);
10497
10498   int action_arg_number_reset =
10499     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10500      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10501      action_type == CA_SET_LEVEL_TIME ? level.time :
10502      action_type == CA_SET_LEVEL_SCORE ? 0 :
10503 #if USE_NEW_CUSTOM_VALUE
10504      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10505 #else
10506      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10507 #endif
10508      action_type == CA_SET_CE_SCORE ? 0 :
10509      0);
10510
10511   int action_arg_number =
10512     (action_arg <= CA_ARG_MAX ? action_arg :
10513      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10514      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10515      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10516      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10517      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10518      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10519 #if USE_NEW_CUSTOM_VALUE
10520      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10521 #else
10522      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10523 #endif
10524      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10525      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10526      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10527      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10528      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10529      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10530      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10531      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10532      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10533      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10534      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10535      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10536      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10537      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10538      -1);
10539
10540   int action_arg_number_old =
10541     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10542      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10543      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10544      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10545      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10546      0);
10547
10548   int action_arg_number_new =
10549     getModifiedActionNumber(action_arg_number_old,
10550                             action_mode, action_arg_number,
10551                             action_arg_number_min, action_arg_number_max);
10552
10553 #if 1
10554   int trigger_player_bits =
10555     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10556      change->actual_trigger_player_bits : change->trigger_player);
10557 #else
10558   int trigger_player_bits =
10559     (change->actual_trigger_player >= EL_PLAYER_1 &&
10560      change->actual_trigger_player <= EL_PLAYER_4 ?
10561      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10562      PLAYER_BITS_ANY);
10563 #endif
10564
10565   int action_arg_player_bits =
10566     (action_arg >= CA_ARG_PLAYER_1 &&
10567      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10568      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10569      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10570      PLAYER_BITS_ANY);
10571
10572   /* ---------- execute action  -------------------------------------------- */
10573
10574   switch (action_type)
10575   {
10576     case CA_NO_ACTION:
10577     {
10578       return;
10579     }
10580
10581     /* ---------- level actions  ------------------------------------------- */
10582
10583     case CA_RESTART_LEVEL:
10584     {
10585       game.restart_level = TRUE;
10586
10587       break;
10588     }
10589
10590     case CA_SHOW_ENVELOPE:
10591     {
10592       int element = getSpecialActionElement(action_arg_element,
10593                                             action_arg_number, EL_ENVELOPE_1);
10594
10595       if (IS_ENVELOPE(element))
10596         local_player->show_envelope = element;
10597
10598       break;
10599     }
10600
10601     case CA_SET_LEVEL_TIME:
10602     {
10603       if (level.time > 0)       /* only modify limited time value */
10604       {
10605         TimeLeft = action_arg_number_new;
10606
10607 #if 1
10608         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10609
10610         DisplayGameControlValues();
10611 #else
10612         DrawGameValue_Time(TimeLeft);
10613 #endif
10614
10615         if (!TimeLeft && setup.time_limit)
10616           for (i = 0; i < MAX_PLAYERS; i++)
10617             KillPlayer(&stored_player[i]);
10618       }
10619
10620       break;
10621     }
10622
10623     case CA_SET_LEVEL_SCORE:
10624     {
10625       local_player->score = action_arg_number_new;
10626
10627 #if 1
10628       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10629
10630       DisplayGameControlValues();
10631 #else
10632       DrawGameValue_Score(local_player->score);
10633 #endif
10634
10635       break;
10636     }
10637
10638     case CA_SET_LEVEL_GEMS:
10639     {
10640       local_player->gems_still_needed = action_arg_number_new;
10641
10642 #if 1
10643       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10644
10645       DisplayGameControlValues();
10646 #else
10647       DrawGameValue_Emeralds(local_player->gems_still_needed);
10648 #endif
10649
10650       break;
10651     }
10652
10653 #if !USE_PLAYER_GRAVITY
10654     case CA_SET_LEVEL_GRAVITY:
10655     {
10656       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10657                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10658                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10659                       game.gravity);
10660       break;
10661     }
10662 #endif
10663
10664     case CA_SET_LEVEL_WIND:
10665     {
10666       game.wind_direction = action_arg_direction;
10667
10668       break;
10669     }
10670
10671     case CA_SET_LEVEL_RANDOM_SEED:
10672     {
10673 #if 1
10674       /* ensure that setting a new random seed while playing is predictable */
10675       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10676 #else
10677       InitRND(action_arg_number_new);
10678 #endif
10679
10680 #if 0
10681       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10682 #endif
10683
10684 #if 0
10685       {
10686         int i;
10687
10688         printf("::: ");
10689         for (i = 0; i < 9; i++)
10690           printf("%d, ", RND(2));
10691         printf("\n");
10692       }
10693 #endif
10694
10695       break;
10696     }
10697
10698     /* ---------- player actions  ------------------------------------------ */
10699
10700     case CA_MOVE_PLAYER:
10701     {
10702       /* automatically move to the next field in specified direction */
10703       for (i = 0; i < MAX_PLAYERS; i++)
10704         if (trigger_player_bits & (1 << i))
10705           stored_player[i].programmed_action = action_arg_direction;
10706
10707       break;
10708     }
10709
10710     case CA_EXIT_PLAYER:
10711     {
10712       for (i = 0; i < MAX_PLAYERS; i++)
10713         if (action_arg_player_bits & (1 << i))
10714           PlayerWins(&stored_player[i]);
10715
10716       break;
10717     }
10718
10719     case CA_KILL_PLAYER:
10720     {
10721       for (i = 0; i < MAX_PLAYERS; i++)
10722         if (action_arg_player_bits & (1 << i))
10723           KillPlayer(&stored_player[i]);
10724
10725       break;
10726     }
10727
10728     case CA_SET_PLAYER_KEYS:
10729     {
10730       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10731       int element = getSpecialActionElement(action_arg_element,
10732                                             action_arg_number, EL_KEY_1);
10733
10734       if (IS_KEY(element))
10735       {
10736         for (i = 0; i < MAX_PLAYERS; i++)
10737         {
10738           if (trigger_player_bits & (1 << i))
10739           {
10740             stored_player[i].key[KEY_NR(element)] = key_state;
10741
10742             DrawGameDoorValues();
10743           }
10744         }
10745       }
10746
10747       break;
10748     }
10749
10750     case CA_SET_PLAYER_SPEED:
10751     {
10752 #if 0
10753       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10754 #endif
10755
10756       for (i = 0; i < MAX_PLAYERS; i++)
10757       {
10758         if (trigger_player_bits & (1 << i))
10759         {
10760           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10761
10762           if (action_arg == CA_ARG_SPEED_FASTER &&
10763               stored_player[i].cannot_move)
10764           {
10765             action_arg_number = STEPSIZE_VERY_SLOW;
10766           }
10767           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10768                    action_arg == CA_ARG_SPEED_FASTER)
10769           {
10770             action_arg_number = 2;
10771             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10772                            CA_MODE_MULTIPLY);
10773           }
10774           else if (action_arg == CA_ARG_NUMBER_RESET)
10775           {
10776             action_arg_number = level.initial_player_stepsize[i];
10777           }
10778
10779           move_stepsize =
10780             getModifiedActionNumber(move_stepsize,
10781                                     action_mode,
10782                                     action_arg_number,
10783                                     action_arg_number_min,
10784                                     action_arg_number_max);
10785
10786           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10787         }
10788       }
10789
10790       break;
10791     }
10792
10793     case CA_SET_PLAYER_SHIELD:
10794     {
10795       for (i = 0; i < MAX_PLAYERS; i++)
10796       {
10797         if (trigger_player_bits & (1 << i))
10798         {
10799           if (action_arg == CA_ARG_SHIELD_OFF)
10800           {
10801             stored_player[i].shield_normal_time_left = 0;
10802             stored_player[i].shield_deadly_time_left = 0;
10803           }
10804           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10805           {
10806             stored_player[i].shield_normal_time_left = 999999;
10807           }
10808           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10809           {
10810             stored_player[i].shield_normal_time_left = 999999;
10811             stored_player[i].shield_deadly_time_left = 999999;
10812           }
10813         }
10814       }
10815
10816       break;
10817     }
10818
10819 #if USE_PLAYER_GRAVITY
10820     case CA_SET_PLAYER_GRAVITY:
10821     {
10822       for (i = 0; i < MAX_PLAYERS; i++)
10823       {
10824         if (trigger_player_bits & (1 << i))
10825         {
10826           stored_player[i].gravity =
10827             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10828              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10829              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10830              stored_player[i].gravity);
10831         }
10832       }
10833
10834       break;
10835     }
10836 #endif
10837
10838     case CA_SET_PLAYER_ARTWORK:
10839     {
10840       for (i = 0; i < MAX_PLAYERS; i++)
10841       {
10842         if (trigger_player_bits & (1 << i))
10843         {
10844           int artwork_element = action_arg_element;
10845
10846           if (action_arg == CA_ARG_ELEMENT_RESET)
10847             artwork_element =
10848               (level.use_artwork_element[i] ? level.artwork_element[i] :
10849                stored_player[i].element_nr);
10850
10851 #if USE_GFX_RESET_PLAYER_ARTWORK
10852           if (stored_player[i].artwork_element != artwork_element)
10853             stored_player[i].Frame = 0;
10854 #endif
10855
10856           stored_player[i].artwork_element = artwork_element;
10857
10858           SetPlayerWaiting(&stored_player[i], FALSE);
10859
10860           /* set number of special actions for bored and sleeping animation */
10861           stored_player[i].num_special_action_bored =
10862             get_num_special_action(artwork_element,
10863                                    ACTION_BORING_1, ACTION_BORING_LAST);
10864           stored_player[i].num_special_action_sleeping =
10865             get_num_special_action(artwork_element,
10866                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10867         }
10868       }
10869
10870       break;
10871     }
10872
10873     case CA_SET_PLAYER_INVENTORY:
10874     {
10875       for (i = 0; i < MAX_PLAYERS; i++)
10876       {
10877         struct PlayerInfo *player = &stored_player[i];
10878         int j, k;
10879
10880         if (trigger_player_bits & (1 << i))
10881         {
10882           int inventory_element = action_arg_element;
10883
10884           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10885               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10886               action_arg == CA_ARG_ELEMENT_ACTION)
10887           {
10888             int element = inventory_element;
10889             int collect_count = element_info[element].collect_count_initial;
10890
10891             if (!IS_CUSTOM_ELEMENT(element))
10892               collect_count = 1;
10893
10894             if (collect_count == 0)
10895               player->inventory_infinite_element = element;
10896             else
10897               for (k = 0; k < collect_count; k++)
10898                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10899                   player->inventory_element[player->inventory_size++] =
10900                     element;
10901           }
10902           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10903                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10904                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10905           {
10906             if (player->inventory_infinite_element != EL_UNDEFINED &&
10907                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10908                                      action_arg_element_raw))
10909               player->inventory_infinite_element = EL_UNDEFINED;
10910
10911             for (k = 0, j = 0; j < player->inventory_size; j++)
10912             {
10913               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10914                                         action_arg_element_raw))
10915                 player->inventory_element[k++] = player->inventory_element[j];
10916             }
10917
10918             player->inventory_size = k;
10919           }
10920           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10921           {
10922             if (player->inventory_size > 0)
10923             {
10924               for (j = 0; j < player->inventory_size - 1; j++)
10925                 player->inventory_element[j] = player->inventory_element[j + 1];
10926
10927               player->inventory_size--;
10928             }
10929           }
10930           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10931           {
10932             if (player->inventory_size > 0)
10933               player->inventory_size--;
10934           }
10935           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10936           {
10937             player->inventory_infinite_element = EL_UNDEFINED;
10938             player->inventory_size = 0;
10939           }
10940           else if (action_arg == CA_ARG_INVENTORY_RESET)
10941           {
10942             player->inventory_infinite_element = EL_UNDEFINED;
10943             player->inventory_size = 0;
10944
10945             if (level.use_initial_inventory[i])
10946             {
10947               for (j = 0; j < level.initial_inventory_size[i]; j++)
10948               {
10949                 int element = level.initial_inventory_content[i][j];
10950                 int collect_count = element_info[element].collect_count_initial;
10951
10952                 if (!IS_CUSTOM_ELEMENT(element))
10953                   collect_count = 1;
10954
10955                 if (collect_count == 0)
10956                   player->inventory_infinite_element = element;
10957                 else
10958                   for (k = 0; k < collect_count; k++)
10959                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10960                       player->inventory_element[player->inventory_size++] =
10961                         element;
10962               }
10963             }
10964           }
10965         }
10966       }
10967
10968       break;
10969     }
10970
10971     /* ---------- CE actions  ---------------------------------------------- */
10972
10973     case CA_SET_CE_VALUE:
10974     {
10975 #if USE_NEW_CUSTOM_VALUE
10976       int last_ce_value = CustomValue[x][y];
10977
10978       CustomValue[x][y] = action_arg_number_new;
10979
10980       if (CustomValue[x][y] != last_ce_value)
10981       {
10982         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10983         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10984
10985         if (CustomValue[x][y] == 0)
10986         {
10987           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10988           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10989         }
10990       }
10991 #endif
10992
10993       break;
10994     }
10995
10996     case CA_SET_CE_SCORE:
10997     {
10998 #if USE_NEW_CUSTOM_VALUE
10999       int last_ce_score = ei->collect_score;
11000
11001       ei->collect_score = action_arg_number_new;
11002
11003       if (ei->collect_score != last_ce_score)
11004       {
11005         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11006         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11007
11008         if (ei->collect_score == 0)
11009         {
11010           int xx, yy;
11011
11012           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11013           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11014
11015           /*
11016             This is a very special case that seems to be a mixture between
11017             CheckElementChange() and CheckTriggeredElementChange(): while
11018             the first one only affects single elements that are triggered
11019             directly, the second one affects multiple elements in the playfield
11020             that are triggered indirectly by another element. This is a third
11021             case: Changing the CE score always affects multiple identical CEs,
11022             so every affected CE must be checked, not only the single CE for
11023             which the CE score was changed in the first place (as every instance
11024             of that CE shares the same CE score, and therefore also can change)!
11025           */
11026           SCAN_PLAYFIELD(xx, yy)
11027           {
11028             if (Feld[xx][yy] == element)
11029               CheckElementChange(xx, yy, element, EL_UNDEFINED,
11030                                  CE_SCORE_GETS_ZERO);
11031           }
11032         }
11033       }
11034 #endif
11035
11036       break;
11037     }
11038
11039     case CA_SET_CE_ARTWORK:
11040     {
11041       int artwork_element = action_arg_element;
11042       boolean reset_frame = FALSE;
11043       int xx, yy;
11044
11045       if (action_arg == CA_ARG_ELEMENT_RESET)
11046         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11047                            element);
11048
11049       if (ei->gfx_element != artwork_element)
11050         reset_frame = TRUE;
11051
11052       ei->gfx_element = artwork_element;
11053
11054       SCAN_PLAYFIELD(xx, yy)
11055       {
11056         if (Feld[xx][yy] == element)
11057         {
11058           if (reset_frame)
11059           {
11060             ResetGfxAnimation(xx, yy);
11061             ResetRandomAnimationValue(xx, yy);
11062           }
11063
11064           TEST_DrawLevelField(xx, yy);
11065         }
11066       }
11067
11068       break;
11069     }
11070
11071     /* ---------- engine actions  ------------------------------------------ */
11072
11073     case CA_SET_ENGINE_SCAN_MODE:
11074     {
11075       InitPlayfieldScanMode(action_arg);
11076
11077       break;
11078     }
11079
11080     default:
11081       break;
11082   }
11083 }
11084
11085 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11086 {
11087   int old_element = Feld[x][y];
11088   int new_element = GetElementFromGroupElement(element);
11089   int previous_move_direction = MovDir[x][y];
11090 #if USE_NEW_CUSTOM_VALUE
11091   int last_ce_value = CustomValue[x][y];
11092 #endif
11093   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11094   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11095   boolean add_player_onto_element = (new_element_is_player &&
11096 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11097                                      /* this breaks SnakeBite when a snake is
11098                                         halfway through a door that closes */
11099                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11100                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11101 #endif
11102                                      IS_WALKABLE(old_element));
11103
11104 #if 0
11105   /* check if element under the player changes from accessible to unaccessible
11106      (needed for special case of dropping element which then changes) */
11107   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11108       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11109   {
11110     Bang(x, y);
11111
11112     return;
11113   }
11114 #endif
11115
11116   if (!add_player_onto_element)
11117   {
11118     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11119       RemoveMovingField(x, y);
11120     else
11121       RemoveField(x, y);
11122
11123     Feld[x][y] = new_element;
11124
11125 #if !USE_GFX_RESET_GFX_ANIMATION
11126     ResetGfxAnimation(x, y);
11127     ResetRandomAnimationValue(x, y);
11128 #endif
11129
11130     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11131       MovDir[x][y] = previous_move_direction;
11132
11133 #if USE_NEW_CUSTOM_VALUE
11134     if (element_info[new_element].use_last_ce_value)
11135       CustomValue[x][y] = last_ce_value;
11136 #endif
11137
11138     InitField_WithBug1(x, y, FALSE);
11139
11140     new_element = Feld[x][y];   /* element may have changed */
11141
11142 #if USE_GFX_RESET_GFX_ANIMATION
11143     ResetGfxAnimation(x, y);
11144     ResetRandomAnimationValue(x, y);
11145 #endif
11146
11147     TEST_DrawLevelField(x, y);
11148
11149     if (GFX_CRUMBLED(new_element))
11150       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11151   }
11152
11153 #if 1
11154   /* check if element under the player changes from accessible to unaccessible
11155      (needed for special case of dropping element which then changes) */
11156   /* (must be checked after creating new element for walkable group elements) */
11157 #if USE_FIX_KILLED_BY_NON_WALKABLE
11158   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11159       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11160   {
11161     Bang(x, y);
11162
11163     return;
11164   }
11165 #else
11166   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11167       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11168   {
11169     Bang(x, y);
11170
11171     return;
11172   }
11173 #endif
11174 #endif
11175
11176   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11177   if (new_element_is_player)
11178     RelocatePlayer(x, y, new_element);
11179
11180   if (is_change)
11181     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11182
11183   TestIfBadThingTouchesPlayer(x, y);
11184   TestIfPlayerTouchesCustomElement(x, y);
11185   TestIfElementTouchesCustomElement(x, y);
11186 }
11187
11188 static void CreateField(int x, int y, int element)
11189 {
11190   CreateFieldExt(x, y, element, FALSE);
11191 }
11192
11193 static void CreateElementFromChange(int x, int y, int element)
11194 {
11195   element = GET_VALID_RUNTIME_ELEMENT(element);
11196
11197 #if USE_STOP_CHANGED_ELEMENTS
11198   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11199   {
11200     int old_element = Feld[x][y];
11201
11202     /* prevent changed element from moving in same engine frame
11203        unless both old and new element can either fall or move */
11204     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11205         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11206       Stop[x][y] = TRUE;
11207   }
11208 #endif
11209
11210   CreateFieldExt(x, y, element, TRUE);
11211 }
11212
11213 static boolean ChangeElement(int x, int y, int element, int page)
11214 {
11215   struct ElementInfo *ei = &element_info[element];
11216   struct ElementChangeInfo *change = &ei->change_page[page];
11217   int ce_value = CustomValue[x][y];
11218   int ce_score = ei->collect_score;
11219   int target_element;
11220   int old_element = Feld[x][y];
11221
11222   /* always use default change event to prevent running into a loop */
11223   if (ChangeEvent[x][y] == -1)
11224     ChangeEvent[x][y] = CE_DELAY;
11225
11226   if (ChangeEvent[x][y] == CE_DELAY)
11227   {
11228     /* reset actual trigger element, trigger player and action element */
11229     change->actual_trigger_element = EL_EMPTY;
11230     change->actual_trigger_player = EL_EMPTY;
11231     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11232     change->actual_trigger_side = CH_SIDE_NONE;
11233     change->actual_trigger_ce_value = 0;
11234     change->actual_trigger_ce_score = 0;
11235   }
11236
11237   /* do not change elements more than a specified maximum number of changes */
11238   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11239     return FALSE;
11240
11241   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11242
11243   if (change->explode)
11244   {
11245     Bang(x, y);
11246
11247     return TRUE;
11248   }
11249
11250   if (change->use_target_content)
11251   {
11252     boolean complete_replace = TRUE;
11253     boolean can_replace[3][3];
11254     int xx, yy;
11255
11256     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11257     {
11258       boolean is_empty;
11259       boolean is_walkable;
11260       boolean is_diggable;
11261       boolean is_collectible;
11262       boolean is_removable;
11263       boolean is_destructible;
11264       int ex = x + xx - 1;
11265       int ey = y + yy - 1;
11266       int content_element = change->target_content.e[xx][yy];
11267       int e;
11268
11269       can_replace[xx][yy] = TRUE;
11270
11271       if (ex == x && ey == y)   /* do not check changing element itself */
11272         continue;
11273
11274       if (content_element == EL_EMPTY_SPACE)
11275       {
11276         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11277
11278         continue;
11279       }
11280
11281       if (!IN_LEV_FIELD(ex, ey))
11282       {
11283         can_replace[xx][yy] = FALSE;
11284         complete_replace = FALSE;
11285
11286         continue;
11287       }
11288
11289       e = Feld[ex][ey];
11290
11291       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11292         e = MovingOrBlocked2Element(ex, ey);
11293
11294       is_empty = (IS_FREE(ex, ey) ||
11295                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11296
11297       is_walkable     = (is_empty || IS_WALKABLE(e));
11298       is_diggable     = (is_empty || IS_DIGGABLE(e));
11299       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11300       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11301       is_removable    = (is_diggable || is_collectible);
11302
11303       can_replace[xx][yy] =
11304         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11305           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11306           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11307           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11308           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11309           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11310          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11311
11312       if (!can_replace[xx][yy])
11313         complete_replace = FALSE;
11314     }
11315
11316     if (!change->only_if_complete || complete_replace)
11317     {
11318       boolean something_has_changed = FALSE;
11319
11320       if (change->only_if_complete && change->use_random_replace &&
11321           RND(100) < change->random_percentage)
11322         return FALSE;
11323
11324       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11325       {
11326         int ex = x + xx - 1;
11327         int ey = y + yy - 1;
11328         int content_element;
11329
11330         if (can_replace[xx][yy] && (!change->use_random_replace ||
11331                                     RND(100) < change->random_percentage))
11332         {
11333           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11334             RemoveMovingField(ex, ey);
11335
11336           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11337
11338           content_element = change->target_content.e[xx][yy];
11339           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11340                                               ce_value, ce_score);
11341
11342           CreateElementFromChange(ex, ey, target_element);
11343
11344           something_has_changed = TRUE;
11345
11346           /* for symmetry reasons, freeze newly created border elements */
11347           if (ex != x || ey != y)
11348             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11349         }
11350       }
11351
11352       if (something_has_changed)
11353       {
11354         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11355         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11356       }
11357     }
11358   }
11359   else
11360   {
11361     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11362                                         ce_value, ce_score);
11363
11364     if (element == EL_DIAGONAL_GROWING ||
11365         element == EL_DIAGONAL_SHRINKING)
11366     {
11367       target_element = Store[x][y];
11368
11369       Store[x][y] = EL_EMPTY;
11370     }
11371
11372     CreateElementFromChange(x, y, target_element);
11373
11374     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11375     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11376   }
11377
11378   /* this uses direct change before indirect change */
11379   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11380
11381   return TRUE;
11382 }
11383
11384 #if USE_NEW_DELAYED_ACTION
11385
11386 static void HandleElementChange(int x, int y, int page)
11387 {
11388   int element = MovingOrBlocked2Element(x, y);
11389   struct ElementInfo *ei = &element_info[element];
11390   struct ElementChangeInfo *change = &ei->change_page[page];
11391   boolean handle_action_before_change = FALSE;
11392
11393 #ifdef DEBUG
11394   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11395       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11396   {
11397     printf("\n\n");
11398     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11399            x, y, element, element_info[element].token_name);
11400     printf("HandleElementChange(): This should never happen!\n");
11401     printf("\n\n");
11402   }
11403 #endif
11404
11405   /* this can happen with classic bombs on walkable, changing elements */
11406   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11407   {
11408 #if 0
11409     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11410       ChangeDelay[x][y] = 0;
11411 #endif
11412
11413     return;
11414   }
11415
11416   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11417   {
11418     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11419
11420     if (change->can_change)
11421     {
11422 #if 1
11423       /* !!! not clear why graphic animation should be reset at all here !!! */
11424       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11425 #if USE_GFX_RESET_WHEN_NOT_MOVING
11426       /* when a custom element is about to change (for example by change delay),
11427          do not reset graphic animation when the custom element is moving */
11428       if (!IS_MOVING(x, y))
11429 #endif
11430       {
11431         ResetGfxAnimation(x, y);
11432         ResetRandomAnimationValue(x, y);
11433       }
11434 #endif
11435
11436       if (change->pre_change_function)
11437         change->pre_change_function(x, y);
11438     }
11439   }
11440
11441   ChangeDelay[x][y]--;
11442
11443   if (ChangeDelay[x][y] != 0)           /* continue element change */
11444   {
11445     if (change->can_change)
11446     {
11447       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11448
11449       if (IS_ANIMATED(graphic))
11450         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11451
11452       if (change->change_function)
11453         change->change_function(x, y);
11454     }
11455   }
11456   else                                  /* finish element change */
11457   {
11458     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11459     {
11460       page = ChangePage[x][y];
11461       ChangePage[x][y] = -1;
11462
11463       change = &ei->change_page[page];
11464     }
11465
11466     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11467     {
11468       ChangeDelay[x][y] = 1;            /* try change after next move step */
11469       ChangePage[x][y] = page;          /* remember page to use for change */
11470
11471       return;
11472     }
11473
11474 #if 1
11475     /* special case: set new level random seed before changing element */
11476     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11477       handle_action_before_change = TRUE;
11478
11479     if (change->has_action && handle_action_before_change)
11480       ExecuteCustomElementAction(x, y, element, page);
11481 #endif
11482
11483     if (change->can_change)
11484     {
11485       if (ChangeElement(x, y, element, page))
11486       {
11487         if (change->post_change_function)
11488           change->post_change_function(x, y);
11489       }
11490     }
11491
11492     if (change->has_action && !handle_action_before_change)
11493       ExecuteCustomElementAction(x, y, element, page);
11494   }
11495 }
11496
11497 #else
11498
11499 static void HandleElementChange(int x, int y, int page)
11500 {
11501   int element = MovingOrBlocked2Element(x, y);
11502   struct ElementInfo *ei = &element_info[element];
11503   struct ElementChangeInfo *change = &ei->change_page[page];
11504
11505 #ifdef DEBUG
11506   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11507   {
11508     printf("\n\n");
11509     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11510            x, y, element, element_info[element].token_name);
11511     printf("HandleElementChange(): This should never happen!\n");
11512     printf("\n\n");
11513   }
11514 #endif
11515
11516   /* this can happen with classic bombs on walkable, changing elements */
11517   if (!CAN_CHANGE(element))
11518   {
11519 #if 0
11520     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11521       ChangeDelay[x][y] = 0;
11522 #endif
11523
11524     return;
11525   }
11526
11527   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11528   {
11529     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11530
11531     ResetGfxAnimation(x, y);
11532     ResetRandomAnimationValue(x, y);
11533
11534     if (change->pre_change_function)
11535       change->pre_change_function(x, y);
11536   }
11537
11538   ChangeDelay[x][y]--;
11539
11540   if (ChangeDelay[x][y] != 0)           /* continue element change */
11541   {
11542     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11543
11544     if (IS_ANIMATED(graphic))
11545       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11546
11547     if (change->change_function)
11548       change->change_function(x, y);
11549   }
11550   else                                  /* finish element change */
11551   {
11552     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11553     {
11554       page = ChangePage[x][y];
11555       ChangePage[x][y] = -1;
11556
11557       change = &ei->change_page[page];
11558     }
11559
11560     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11561     {
11562       ChangeDelay[x][y] = 1;            /* try change after next move step */
11563       ChangePage[x][y] = page;          /* remember page to use for change */
11564
11565       return;
11566     }
11567
11568     if (ChangeElement(x, y, element, page))
11569     {
11570       if (change->post_change_function)
11571         change->post_change_function(x, y);
11572     }
11573   }
11574 }
11575
11576 #endif
11577
11578 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11579                                               int trigger_element,
11580                                               int trigger_event,
11581                                               int trigger_player,
11582                                               int trigger_side,
11583                                               int trigger_page)
11584 {
11585   boolean change_done_any = FALSE;
11586   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11587   int i;
11588
11589   if (!(trigger_events[trigger_element][trigger_event]))
11590     return FALSE;
11591
11592 #if 0
11593   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11594          trigger_event, recursion_loop_depth, recursion_loop_detected,
11595          recursion_loop_element, EL_NAME(recursion_loop_element));
11596 #endif
11597
11598   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11599
11600   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11601   {
11602     int element = EL_CUSTOM_START + i;
11603     boolean change_done = FALSE;
11604     int p;
11605
11606     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11607         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11608       continue;
11609
11610     for (p = 0; p < element_info[element].num_change_pages; p++)
11611     {
11612       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11613
11614       if (change->can_change_or_has_action &&
11615           change->has_event[trigger_event] &&
11616           change->trigger_side & trigger_side &&
11617           change->trigger_player & trigger_player &&
11618           change->trigger_page & trigger_page_bits &&
11619           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11620       {
11621         change->actual_trigger_element = trigger_element;
11622         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11623         change->actual_trigger_player_bits = trigger_player;
11624         change->actual_trigger_side = trigger_side;
11625         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11626         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11627
11628 #if 0
11629         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11630                element, EL_NAME(element), p);
11631 #endif
11632
11633         if ((change->can_change && !change_done) || change->has_action)
11634         {
11635           int x, y;
11636
11637           SCAN_PLAYFIELD(x, y)
11638           {
11639             if (Feld[x][y] == element)
11640             {
11641               if (change->can_change && !change_done)
11642               {
11643 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11644                 /* if element already changed in this frame, not only prevent
11645                    another element change (checked in ChangeElement()), but
11646                    also prevent additional element actions for this element */
11647
11648                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11649                     !level.use_action_after_change_bug)
11650                   continue;
11651 #endif
11652
11653 #if 0
11654                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11655                        element, EL_NAME(element), p);
11656 #endif
11657
11658                 ChangeDelay[x][y] = 1;
11659                 ChangeEvent[x][y] = trigger_event;
11660
11661                 HandleElementChange(x, y, p);
11662               }
11663 #if USE_NEW_DELAYED_ACTION
11664               else if (change->has_action)
11665               {
11666 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11667                 /* if element already changed in this frame, not only prevent
11668                    another element change (checked in ChangeElement()), but
11669                    also prevent additional element actions for this element */
11670
11671                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11672                     !level.use_action_after_change_bug)
11673                   continue;
11674 #endif
11675
11676
11677 #if 0
11678                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11679                        element, EL_NAME(element), p);
11680 #endif
11681
11682                 ExecuteCustomElementAction(x, y, element, p);
11683                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11684               }
11685 #else
11686               if (change->has_action)
11687               {
11688                 ExecuteCustomElementAction(x, y, element, p);
11689                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11690               }
11691 #endif
11692             }
11693           }
11694
11695           if (change->can_change)
11696           {
11697             change_done = TRUE;
11698             change_done_any = TRUE;
11699
11700 #if 0
11701             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11702                    element, EL_NAME(element), p);
11703 #endif
11704
11705           }
11706         }
11707       }
11708     }
11709   }
11710
11711   RECURSION_LOOP_DETECTION_END();
11712
11713   return change_done_any;
11714 }
11715
11716 static boolean CheckElementChangeExt(int x, int y,
11717                                      int element,
11718                                      int trigger_element,
11719                                      int trigger_event,
11720                                      int trigger_player,
11721                                      int trigger_side)
11722 {
11723   boolean change_done = FALSE;
11724   int p;
11725
11726   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11727       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11728     return FALSE;
11729
11730   if (Feld[x][y] == EL_BLOCKED)
11731   {
11732     Blocked2Moving(x, y, &x, &y);
11733     element = Feld[x][y];
11734   }
11735
11736 #if 0
11737   /* check if element has already changed */
11738   if (Feld[x][y] != element)
11739     return FALSE;
11740 #else
11741   /* check if element has already changed or is about to change after moving */
11742   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11743        Feld[x][y] != element) ||
11744
11745       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11746        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11747         ChangePage[x][y] != -1)))
11748     return FALSE;
11749 #endif
11750
11751 #if 0
11752   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11753          trigger_event, recursion_loop_depth, recursion_loop_detected,
11754          recursion_loop_element, EL_NAME(recursion_loop_element));
11755 #endif
11756
11757   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11758
11759 #if 0
11760   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11761 #endif
11762
11763   for (p = 0; p < element_info[element].num_change_pages; p++)
11764   {
11765     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11766
11767     /* check trigger element for all events where the element that is checked
11768        for changing interacts with a directly adjacent element -- this is
11769        different to element changes that affect other elements to change on the
11770        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11771     boolean check_trigger_element =
11772       (trigger_event == CE_TOUCHING_X ||
11773        trigger_event == CE_HITTING_X ||
11774        trigger_event == CE_HIT_BY_X ||
11775 #if 1
11776        /* this one was forgotten until 3.2.3 */
11777        trigger_event == CE_DIGGING_X);
11778 #endif
11779
11780     if (change->can_change_or_has_action &&
11781         change->has_event[trigger_event] &&
11782         change->trigger_side & trigger_side &&
11783         change->trigger_player & trigger_player &&
11784         (!check_trigger_element ||
11785          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11786     {
11787       change->actual_trigger_element = trigger_element;
11788       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11789       change->actual_trigger_player_bits = trigger_player;
11790       change->actual_trigger_side = trigger_side;
11791       change->actual_trigger_ce_value = CustomValue[x][y];
11792       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11793
11794       /* special case: trigger element not at (x,y) position for some events */
11795       if (check_trigger_element)
11796       {
11797         static struct
11798         {
11799           int dx, dy;
11800         } move_xy[] =
11801           {
11802             {  0,  0 },
11803             { -1,  0 },
11804             { +1,  0 },
11805             {  0,  0 },
11806             {  0, -1 },
11807             {  0,  0 }, { 0, 0 }, { 0, 0 },
11808             {  0, +1 }
11809           };
11810
11811         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11812         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11813
11814         change->actual_trigger_ce_value = CustomValue[xx][yy];
11815         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11816       }
11817
11818       if (change->can_change && !change_done)
11819       {
11820         ChangeDelay[x][y] = 1;
11821         ChangeEvent[x][y] = trigger_event;
11822
11823         HandleElementChange(x, y, p);
11824
11825         change_done = TRUE;
11826       }
11827 #if USE_NEW_DELAYED_ACTION
11828       else if (change->has_action)
11829       {
11830         ExecuteCustomElementAction(x, y, element, p);
11831         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11832       }
11833 #else
11834       if (change->has_action)
11835       {
11836         ExecuteCustomElementAction(x, y, element, p);
11837         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11838       }
11839 #endif
11840     }
11841   }
11842
11843   RECURSION_LOOP_DETECTION_END();
11844
11845   return change_done;
11846 }
11847
11848 static void PlayPlayerSound(struct PlayerInfo *player)
11849 {
11850   int jx = player->jx, jy = player->jy;
11851   int sound_element = player->artwork_element;
11852   int last_action = player->last_action_waiting;
11853   int action = player->action_waiting;
11854
11855   if (player->is_waiting)
11856   {
11857     if (action != last_action)
11858       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11859     else
11860       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11861   }
11862   else
11863   {
11864     if (action != last_action)
11865       StopSound(element_info[sound_element].sound[last_action]);
11866
11867     if (last_action == ACTION_SLEEPING)
11868       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11869   }
11870 }
11871
11872 static void PlayAllPlayersSound()
11873 {
11874   int i;
11875
11876   for (i = 0; i < MAX_PLAYERS; i++)
11877     if (stored_player[i].active)
11878       PlayPlayerSound(&stored_player[i]);
11879 }
11880
11881 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11882 {
11883   boolean last_waiting = player->is_waiting;
11884   int move_dir = player->MovDir;
11885
11886   player->dir_waiting = move_dir;
11887   player->last_action_waiting = player->action_waiting;
11888
11889   if (is_waiting)
11890   {
11891     if (!last_waiting)          /* not waiting -> waiting */
11892     {
11893       player->is_waiting = TRUE;
11894
11895       player->frame_counter_bored =
11896         FrameCounter +
11897         game.player_boring_delay_fixed +
11898         GetSimpleRandom(game.player_boring_delay_random);
11899       player->frame_counter_sleeping =
11900         FrameCounter +
11901         game.player_sleeping_delay_fixed +
11902         GetSimpleRandom(game.player_sleeping_delay_random);
11903
11904       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11905     }
11906
11907     if (game.player_sleeping_delay_fixed +
11908         game.player_sleeping_delay_random > 0 &&
11909         player->anim_delay_counter == 0 &&
11910         player->post_delay_counter == 0 &&
11911         FrameCounter >= player->frame_counter_sleeping)
11912       player->is_sleeping = TRUE;
11913     else if (game.player_boring_delay_fixed +
11914              game.player_boring_delay_random > 0 &&
11915              FrameCounter >= player->frame_counter_bored)
11916       player->is_bored = TRUE;
11917
11918     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11919                               player->is_bored ? ACTION_BORING :
11920                               ACTION_WAITING);
11921
11922     if (player->is_sleeping && player->use_murphy)
11923     {
11924       /* special case for sleeping Murphy when leaning against non-free tile */
11925
11926       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11927           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11928            !IS_MOVING(player->jx - 1, player->jy)))
11929         move_dir = MV_LEFT;
11930       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11931                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11932                 !IS_MOVING(player->jx + 1, player->jy)))
11933         move_dir = MV_RIGHT;
11934       else
11935         player->is_sleeping = FALSE;
11936
11937       player->dir_waiting = move_dir;
11938     }
11939
11940     if (player->is_sleeping)
11941     {
11942       if (player->num_special_action_sleeping > 0)
11943       {
11944         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11945         {
11946           int last_special_action = player->special_action_sleeping;
11947           int num_special_action = player->num_special_action_sleeping;
11948           int special_action =
11949             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11950              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11951              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11952              last_special_action + 1 : ACTION_SLEEPING);
11953           int special_graphic =
11954             el_act_dir2img(player->artwork_element, special_action, move_dir);
11955
11956           player->anim_delay_counter =
11957             graphic_info[special_graphic].anim_delay_fixed +
11958             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11959           player->post_delay_counter =
11960             graphic_info[special_graphic].post_delay_fixed +
11961             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11962
11963           player->special_action_sleeping = special_action;
11964         }
11965
11966         if (player->anim_delay_counter > 0)
11967         {
11968           player->action_waiting = player->special_action_sleeping;
11969           player->anim_delay_counter--;
11970         }
11971         else if (player->post_delay_counter > 0)
11972         {
11973           player->post_delay_counter--;
11974         }
11975       }
11976     }
11977     else if (player->is_bored)
11978     {
11979       if (player->num_special_action_bored > 0)
11980       {
11981         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11982         {
11983           int special_action =
11984             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11985           int special_graphic =
11986             el_act_dir2img(player->artwork_element, special_action, move_dir);
11987
11988           player->anim_delay_counter =
11989             graphic_info[special_graphic].anim_delay_fixed +
11990             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11991           player->post_delay_counter =
11992             graphic_info[special_graphic].post_delay_fixed +
11993             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11994
11995           player->special_action_bored = special_action;
11996         }
11997
11998         if (player->anim_delay_counter > 0)
11999         {
12000           player->action_waiting = player->special_action_bored;
12001           player->anim_delay_counter--;
12002         }
12003         else if (player->post_delay_counter > 0)
12004         {
12005           player->post_delay_counter--;
12006         }
12007       }
12008     }
12009   }
12010   else if (last_waiting)        /* waiting -> not waiting */
12011   {
12012     player->is_waiting = FALSE;
12013     player->is_bored = FALSE;
12014     player->is_sleeping = FALSE;
12015
12016     player->frame_counter_bored = -1;
12017     player->frame_counter_sleeping = -1;
12018
12019     player->anim_delay_counter = 0;
12020     player->post_delay_counter = 0;
12021
12022     player->dir_waiting = player->MovDir;
12023     player->action_waiting = ACTION_DEFAULT;
12024
12025     player->special_action_bored = ACTION_DEFAULT;
12026     player->special_action_sleeping = ACTION_DEFAULT;
12027   }
12028 }
12029
12030 static void CheckSingleStepMode(struct PlayerInfo *player)
12031 {
12032   if (tape.single_step && tape.recording && !tape.pausing)
12033   {
12034     /* as it is called "single step mode", just return to pause mode when the
12035        player stopped moving after one tile (or never starts moving at all) */
12036     if (!player->is_moving && !player->is_pushing)
12037     {
12038       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12039       SnapField(player, 0, 0);                  /* stop snapping */
12040     }
12041   }
12042 }
12043
12044 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12045 {
12046   int left      = player_action & JOY_LEFT;
12047   int right     = player_action & JOY_RIGHT;
12048   int up        = player_action & JOY_UP;
12049   int down      = player_action & JOY_DOWN;
12050   int button1   = player_action & JOY_BUTTON_1;
12051   int button2   = player_action & JOY_BUTTON_2;
12052   int dx        = (left ? -1 : right ? 1 : 0);
12053   int dy        = (up   ? -1 : down  ? 1 : 0);
12054
12055   if (!player->active || tape.pausing)
12056     return 0;
12057
12058   if (player_action)
12059   {
12060     if (button1)
12061       SnapField(player, dx, dy);
12062     else
12063     {
12064       if (button2)
12065         DropElement(player);
12066
12067       MovePlayer(player, dx, dy);
12068     }
12069
12070     CheckSingleStepMode(player);
12071
12072     SetPlayerWaiting(player, FALSE);
12073
12074     return player_action;
12075   }
12076   else
12077   {
12078     /* no actions for this player (no input at player's configured device) */
12079
12080     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12081     SnapField(player, 0, 0);
12082     CheckGravityMovementWhenNotMoving(player);
12083
12084     if (player->MovPos == 0)
12085       SetPlayerWaiting(player, TRUE);
12086
12087     if (player->MovPos == 0)    /* needed for tape.playing */
12088       player->is_moving = FALSE;
12089
12090     player->is_dropping = FALSE;
12091     player->is_dropping_pressed = FALSE;
12092     player->drop_pressed_delay = 0;
12093
12094     CheckSingleStepMode(player);
12095
12096     return 0;
12097   }
12098 }
12099
12100 static void CheckLevelTime()
12101 {
12102   int i;
12103
12104   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12105   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12106   {
12107     if (level.native_em_level->lev->home == 0)  /* all players at home */
12108     {
12109       PlayerWins(local_player);
12110
12111       AllPlayersGone = TRUE;
12112
12113       level.native_em_level->lev->home = -1;
12114     }
12115
12116     if (level.native_em_level->ply[0]->alive == 0 &&
12117         level.native_em_level->ply[1]->alive == 0 &&
12118         level.native_em_level->ply[2]->alive == 0 &&
12119         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12120       AllPlayersGone = TRUE;
12121   }
12122   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12123   {
12124     if (game_sp.LevelSolved &&
12125         !game_sp.GameOver)                              /* game won */
12126     {
12127       PlayerWins(local_player);
12128
12129       game_sp.GameOver = TRUE;
12130
12131       AllPlayersGone = TRUE;
12132     }
12133
12134     if (game_sp.GameOver)                               /* game lost */
12135       AllPlayersGone = TRUE;
12136   }
12137
12138   if (TimeFrames >= FRAMES_PER_SECOND)
12139   {
12140     TimeFrames = 0;
12141     TapeTime++;
12142
12143     for (i = 0; i < MAX_PLAYERS; i++)
12144     {
12145       struct PlayerInfo *player = &stored_player[i];
12146
12147       if (SHIELD_ON(player))
12148       {
12149         player->shield_normal_time_left--;
12150
12151         if (player->shield_deadly_time_left > 0)
12152           player->shield_deadly_time_left--;
12153       }
12154     }
12155
12156     if (!local_player->LevelSolved && !level.use_step_counter)
12157     {
12158       TimePlayed++;
12159
12160       if (TimeLeft > 0)
12161       {
12162         TimeLeft--;
12163
12164         if (TimeLeft <= 10 && setup.time_limit)
12165           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12166
12167 #if 1
12168         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12169            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12170
12171         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12172
12173         /* (already called by UpdateAndDisplayGameControlValues() below) */
12174         // DisplayGameControlValues();
12175 #else
12176         DrawGameValue_Time(TimeLeft);
12177 #endif
12178
12179         if (!TimeLeft && setup.time_limit)
12180         {
12181           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12182             level.native_em_level->lev->killed_out_of_time = TRUE;
12183           else
12184             for (i = 0; i < MAX_PLAYERS; i++)
12185               KillPlayer(&stored_player[i]);
12186         }
12187       }
12188 #if 1
12189       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12190       {
12191         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12192
12193         /* (already called by UpdateAndDisplayGameControlValues() below) */
12194         // DisplayGameControlValues();
12195       }
12196 #else
12197       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12198         DrawGameValue_Time(TimePlayed);
12199 #endif
12200
12201       level.native_em_level->lev->time =
12202         (game.no_time_limit ? TimePlayed : TimeLeft);
12203     }
12204
12205     if (tape.recording || tape.playing)
12206       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12207   }
12208
12209 #if 1
12210   UpdateAndDisplayGameControlValues();
12211 #else
12212   UpdateGameDoorValues();
12213   DrawGameDoorValues();
12214 #endif
12215 }
12216
12217 void AdvanceFrameAndPlayerCounters(int player_nr)
12218 {
12219   int i;
12220
12221   /* advance frame counters (global frame counter and time frame counter) */
12222   FrameCounter++;
12223   TimeFrames++;
12224
12225   /* advance player counters (counters for move delay, move animation etc.) */
12226   for (i = 0; i < MAX_PLAYERS; i++)
12227   {
12228     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12229     int move_delay_value = stored_player[i].move_delay_value;
12230     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12231
12232     if (!advance_player_counters)       /* not all players may be affected */
12233       continue;
12234
12235 #if USE_NEW_PLAYER_ANIM
12236     if (move_frames == 0)       /* less than one move per game frame */
12237     {
12238       int stepsize = TILEX / move_delay_value;
12239       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12240       int count = (stored_player[i].is_moving ?
12241                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12242
12243       if (count % delay == 0)
12244         move_frames = 1;
12245     }
12246 #endif
12247
12248     stored_player[i].Frame += move_frames;
12249
12250     if (stored_player[i].MovPos != 0)
12251       stored_player[i].StepFrame += move_frames;
12252
12253     if (stored_player[i].move_delay > 0)
12254       stored_player[i].move_delay--;
12255
12256     /* due to bugs in previous versions, counter must count up, not down */
12257     if (stored_player[i].push_delay != -1)
12258       stored_player[i].push_delay++;
12259
12260     if (stored_player[i].drop_delay > 0)
12261       stored_player[i].drop_delay--;
12262
12263     if (stored_player[i].is_dropping_pressed)
12264       stored_player[i].drop_pressed_delay++;
12265   }
12266 }
12267
12268 void StartGameActions(boolean init_network_game, boolean record_tape,
12269                       int random_seed)
12270 {
12271   unsigned int new_random_seed = InitRND(random_seed);
12272
12273   if (record_tape)
12274     TapeStartRecording(new_random_seed);
12275
12276 #if defined(NETWORK_AVALIABLE)
12277   if (init_network_game)
12278   {
12279     SendToServer_StartPlaying();
12280
12281     return;
12282   }
12283 #endif
12284
12285   InitGame();
12286 }
12287
12288 void GameActions()
12289 {
12290   static unsigned int game_frame_delay = 0;
12291   unsigned int game_frame_delay_value;
12292   byte *recorded_player_action;
12293   byte summarized_player_action = 0;
12294   byte tape_action[MAX_PLAYERS];
12295   int i;
12296
12297   /* detect endless loops, caused by custom element programming */
12298   if (recursion_loop_detected && recursion_loop_depth == 0)
12299   {
12300     char *message = getStringCat3("Internal Error! Element ",
12301                                   EL_NAME(recursion_loop_element),
12302                                   " caused endless loop! Quit the game?");
12303
12304     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12305           EL_NAME(recursion_loop_element));
12306
12307     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12308
12309     recursion_loop_detected = FALSE;    /* if game should be continued */
12310
12311     free(message);
12312
12313     return;
12314   }
12315
12316   if (game.restart_level)
12317     StartGameActions(options.network, setup.autorecord, level.random_seed);
12318
12319   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12320   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12321   {
12322     if (level.native_em_level->lev->home == 0)  /* all players at home */
12323     {
12324       PlayerWins(local_player);
12325
12326       AllPlayersGone = TRUE;
12327
12328       level.native_em_level->lev->home = -1;
12329     }
12330
12331     if (level.native_em_level->ply[0]->alive == 0 &&
12332         level.native_em_level->ply[1]->alive == 0 &&
12333         level.native_em_level->ply[2]->alive == 0 &&
12334         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12335       AllPlayersGone = TRUE;
12336   }
12337   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12338   {
12339     if (game_sp.LevelSolved &&
12340         !game_sp.GameOver)                              /* game won */
12341     {
12342       PlayerWins(local_player);
12343
12344       game_sp.GameOver = TRUE;
12345
12346       AllPlayersGone = TRUE;
12347     }
12348
12349     if (game_sp.GameOver)                               /* game lost */
12350       AllPlayersGone = TRUE;
12351   }
12352
12353   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12354     GameWon();
12355
12356   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12357     TapeStop();
12358
12359   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12360     return;
12361
12362   game_frame_delay_value =
12363     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12364
12365   if (tape.playing && tape.warp_forward && !tape.pausing)
12366     game_frame_delay_value = 0;
12367
12368   /* ---------- main game synchronization point ---------- */
12369
12370   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12371
12372   if (network_playing && !network_player_action_received)
12373   {
12374     /* try to get network player actions in time */
12375
12376 #if defined(NETWORK_AVALIABLE)
12377     /* last chance to get network player actions without main loop delay */
12378     HandleNetworking();
12379 #endif
12380
12381     /* game was quit by network peer */
12382     if (game_status != GAME_MODE_PLAYING)
12383       return;
12384
12385     if (!network_player_action_received)
12386       return;           /* failed to get network player actions in time */
12387
12388     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12389   }
12390
12391   if (tape.pausing)
12392     return;
12393
12394   /* at this point we know that we really continue executing the game */
12395
12396   network_player_action_received = FALSE;
12397
12398   /* when playing tape, read previously recorded player input from tape data */
12399   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12400
12401 #if 1
12402   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12403   if (tape.pausing)
12404     return;
12405 #endif
12406
12407   if (tape.set_centered_player)
12408   {
12409     game.centered_player_nr_next = tape.centered_player_nr_next;
12410     game.set_centered_player = TRUE;
12411   }
12412
12413   for (i = 0; i < MAX_PLAYERS; i++)
12414   {
12415     summarized_player_action |= stored_player[i].action;
12416
12417 #if 1
12418     if (!network_playing && (game.team_mode || tape.playing))
12419       stored_player[i].effective_action = stored_player[i].action;
12420 #else
12421     if (!network_playing)
12422       stored_player[i].effective_action = stored_player[i].action;
12423 #endif
12424   }
12425
12426 #if defined(NETWORK_AVALIABLE)
12427   if (network_playing)
12428     SendToServer_MovePlayer(summarized_player_action);
12429 #endif
12430
12431   if (!options.network && !game.team_mode)
12432     local_player->effective_action = summarized_player_action;
12433
12434   if (tape.recording &&
12435       setup.team_mode &&
12436       setup.input_on_focus &&
12437       game.centered_player_nr != -1)
12438   {
12439     for (i = 0; i < MAX_PLAYERS; i++)
12440       stored_player[i].effective_action =
12441         (i == game.centered_player_nr ? summarized_player_action : 0);
12442   }
12443
12444   if (recorded_player_action != NULL)
12445     for (i = 0; i < MAX_PLAYERS; i++)
12446       stored_player[i].effective_action = recorded_player_action[i];
12447
12448   for (i = 0; i < MAX_PLAYERS; i++)
12449   {
12450     tape_action[i] = stored_player[i].effective_action;
12451
12452 #if 1
12453     /* (this may happen in the RND game engine if a player was not present on
12454        the playfield on level start, but appeared later from a custom element */
12455     if (tape.recording &&
12456         setup.team_mode &&
12457         tape_action[i] &&
12458         !tape.player_participates[i])
12459       tape.player_participates[i] = TRUE;
12460 #else
12461     /* (this can only happen in the R'n'D game engine) */
12462     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12463       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12464 #endif
12465   }
12466
12467   /* only record actions from input devices, but not programmed actions */
12468   if (tape.recording)
12469     TapeRecordAction(tape_action);
12470
12471 #if USE_NEW_PLAYER_ASSIGNMENTS
12472 #if 1
12473   if (game.team_mode)
12474 #endif
12475   {
12476     byte mapped_action[MAX_PLAYERS];
12477
12478 #if DEBUG_PLAYER_ACTIONS
12479     printf(":::");
12480     for (i = 0; i < MAX_PLAYERS; i++)
12481       printf(" %d, ", stored_player[i].effective_action);
12482 #endif
12483
12484     for (i = 0; i < MAX_PLAYERS; i++)
12485       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12486
12487     for (i = 0; i < MAX_PLAYERS; i++)
12488       stored_player[i].effective_action = mapped_action[i];
12489
12490 #if DEBUG_PLAYER_ACTIONS
12491     printf(" =>");
12492     for (i = 0; i < MAX_PLAYERS; i++)
12493       printf(" %d, ", stored_player[i].effective_action);
12494     printf("\n");
12495 #endif
12496   }
12497 #if DEBUG_PLAYER_ACTIONS
12498   else
12499   {
12500     printf(":::");
12501     for (i = 0; i < MAX_PLAYERS; i++)
12502       printf(" %d, ", stored_player[i].effective_action);
12503     printf("\n");
12504   }
12505 #endif
12506 #endif
12507
12508 #if 0
12509   printf("::: summarized_player_action == %d\n",
12510          local_player->effective_action);
12511 #endif
12512
12513
12514
12515
12516 #if 0
12517 #if DEBUG_INIT_PLAYER
12518     if (options.debug)
12519     {
12520       printf("Player status (final):\n");
12521
12522       for (i = 0; i < MAX_PLAYERS; i++)
12523       {
12524         struct PlayerInfo *player = &stored_player[i];
12525
12526         printf("- player %d: present == %d, connected == %d, active == %d",
12527                i + 1,
12528                player->present,
12529                player->connected,
12530                player->active);
12531
12532         if (local_player == player)
12533           printf(" (local player)");
12534
12535         printf("\n");
12536       }
12537     }
12538 #endif
12539 #endif
12540
12541
12542
12543   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12544   {
12545     GameActions_EM_Main();
12546   }
12547   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12548   {
12549     GameActions_SP_Main();
12550   }
12551   else
12552   {
12553     GameActions_RND();
12554   }
12555 }
12556
12557 void GameActions_EM_Main()
12558 {
12559   byte effective_action[MAX_PLAYERS];
12560   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12561   int i;
12562
12563   for (i = 0; i < MAX_PLAYERS; i++)
12564     effective_action[i] = stored_player[i].effective_action;
12565
12566   GameActions_EM(effective_action, warp_mode);
12567
12568   CheckLevelTime();
12569
12570   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12571 }
12572
12573 void GameActions_SP_Main()
12574 {
12575   byte effective_action[MAX_PLAYERS];
12576   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12577   int i;
12578
12579   for (i = 0; i < MAX_PLAYERS; i++)
12580     effective_action[i] = stored_player[i].effective_action;
12581
12582   GameActions_SP(effective_action, warp_mode);
12583
12584   CheckLevelTime();
12585
12586   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12587 }
12588
12589 void GameActions_RND()
12590 {
12591   int magic_wall_x = 0, magic_wall_y = 0;
12592   int i, x, y, element, graphic;
12593
12594   InitPlayfieldScanModeVars();
12595
12596 #if USE_ONE_MORE_CHANGE_PER_FRAME
12597   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12598   {
12599     SCAN_PLAYFIELD(x, y)
12600     {
12601       ChangeCount[x][y] = 0;
12602       ChangeEvent[x][y] = -1;
12603     }
12604   }
12605 #endif
12606
12607   if (game.set_centered_player)
12608   {
12609     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12610
12611     /* switching to "all players" only possible if all players fit to screen */
12612     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12613     {
12614       game.centered_player_nr_next = game.centered_player_nr;
12615       game.set_centered_player = FALSE;
12616     }
12617
12618     /* do not switch focus to non-existing (or non-active) player */
12619     if (game.centered_player_nr_next >= 0 &&
12620         !stored_player[game.centered_player_nr_next].active)
12621     {
12622       game.centered_player_nr_next = game.centered_player_nr;
12623       game.set_centered_player = FALSE;
12624     }
12625   }
12626
12627   if (game.set_centered_player &&
12628       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12629   {
12630     int sx, sy;
12631
12632     if (game.centered_player_nr_next == -1)
12633     {
12634       setScreenCenteredToAllPlayers(&sx, &sy);
12635     }
12636     else
12637     {
12638       sx = stored_player[game.centered_player_nr_next].jx;
12639       sy = stored_player[game.centered_player_nr_next].jy;
12640     }
12641
12642     game.centered_player_nr = game.centered_player_nr_next;
12643     game.set_centered_player = FALSE;
12644
12645     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12646     DrawGameDoorValues();
12647   }
12648
12649   for (i = 0; i < MAX_PLAYERS; i++)
12650   {
12651     int actual_player_action = stored_player[i].effective_action;
12652
12653 #if 1
12654     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12655        - rnd_equinox_tetrachloride 048
12656        - rnd_equinox_tetrachloride_ii 096
12657        - rnd_emanuel_schmieg 002
12658        - doctor_sloan_ww 001, 020
12659     */
12660     if (stored_player[i].MovPos == 0)
12661       CheckGravityMovement(&stored_player[i]);
12662 #endif
12663
12664     /* overwrite programmed action with tape action */
12665     if (stored_player[i].programmed_action)
12666       actual_player_action = stored_player[i].programmed_action;
12667
12668     PlayerActions(&stored_player[i], actual_player_action);
12669
12670     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12671   }
12672
12673   ScrollScreen(NULL, SCROLL_GO_ON);
12674
12675   /* for backwards compatibility, the following code emulates a fixed bug that
12676      occured when pushing elements (causing elements that just made their last
12677      pushing step to already (if possible) make their first falling step in the
12678      same game frame, which is bad); this code is also needed to use the famous
12679      "spring push bug" which is used in older levels and might be wanted to be
12680      used also in newer levels, but in this case the buggy pushing code is only
12681      affecting the "spring" element and no other elements */
12682
12683   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12684   {
12685     for (i = 0; i < MAX_PLAYERS; i++)
12686     {
12687       struct PlayerInfo *player = &stored_player[i];
12688       int x = player->jx;
12689       int y = player->jy;
12690
12691       if (player->active && player->is_pushing && player->is_moving &&
12692           IS_MOVING(x, y) &&
12693           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12694            Feld[x][y] == EL_SPRING))
12695       {
12696         ContinueMoving(x, y);
12697
12698         /* continue moving after pushing (this is actually a bug) */
12699         if (!IS_MOVING(x, y))
12700           Stop[x][y] = FALSE;
12701       }
12702     }
12703   }
12704
12705 #if 0
12706   debug_print_timestamp(0, "start main loop profiling");
12707 #endif
12708
12709   SCAN_PLAYFIELD(x, y)
12710   {
12711     ChangeCount[x][y] = 0;
12712     ChangeEvent[x][y] = -1;
12713
12714     /* this must be handled before main playfield loop */
12715     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12716     {
12717       MovDelay[x][y]--;
12718       if (MovDelay[x][y] <= 0)
12719         RemoveField(x, y);
12720     }
12721
12722 #if USE_NEW_SNAP_DELAY
12723     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12724     {
12725       MovDelay[x][y]--;
12726       if (MovDelay[x][y] <= 0)
12727       {
12728         RemoveField(x, y);
12729         TEST_DrawLevelField(x, y);
12730
12731         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12732       }
12733     }
12734 #endif
12735
12736 #if DEBUG
12737     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12738     {
12739       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12740       printf("GameActions(): This should never happen!\n");
12741
12742       ChangePage[x][y] = -1;
12743     }
12744 #endif
12745
12746     Stop[x][y] = FALSE;
12747     if (WasJustMoving[x][y] > 0)
12748       WasJustMoving[x][y]--;
12749     if (WasJustFalling[x][y] > 0)
12750       WasJustFalling[x][y]--;
12751     if (CheckCollision[x][y] > 0)
12752       CheckCollision[x][y]--;
12753     if (CheckImpact[x][y] > 0)
12754       CheckImpact[x][y]--;
12755
12756     GfxFrame[x][y]++;
12757
12758     /* reset finished pushing action (not done in ContinueMoving() to allow
12759        continuous pushing animation for elements with zero push delay) */
12760     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12761     {
12762       ResetGfxAnimation(x, y);
12763       TEST_DrawLevelField(x, y);
12764     }
12765
12766 #if DEBUG
12767     if (IS_BLOCKED(x, y))
12768     {
12769       int oldx, oldy;
12770
12771       Blocked2Moving(x, y, &oldx, &oldy);
12772       if (!IS_MOVING(oldx, oldy))
12773       {
12774         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12775         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12776         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12777         printf("GameActions(): This should never happen!\n");
12778       }
12779     }
12780 #endif
12781   }
12782
12783 #if 0
12784   debug_print_timestamp(0, "- time for pre-main loop:");
12785 #endif
12786
12787 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12788   SCAN_PLAYFIELD(x, y)
12789   {
12790     element = Feld[x][y];
12791     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12792
12793 #if 1
12794     {
12795 #if 1
12796       int element2 = element;
12797       int graphic2 = graphic;
12798 #else
12799       int element2 = Feld[x][y];
12800       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12801 #endif
12802       int last_gfx_frame = GfxFrame[x][y];
12803
12804       if (graphic_info[graphic2].anim_global_sync)
12805         GfxFrame[x][y] = FrameCounter;
12806       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12807         GfxFrame[x][y] = CustomValue[x][y];
12808       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12809         GfxFrame[x][y] = element_info[element2].collect_score;
12810       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12811         GfxFrame[x][y] = ChangeDelay[x][y];
12812
12813       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12814         DrawLevelGraphicAnimation(x, y, graphic2);
12815     }
12816 #else
12817     ResetGfxFrame(x, y, TRUE);
12818 #endif
12819
12820 #if 1
12821     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12822         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12823       ResetRandomAnimationValue(x, y);
12824 #endif
12825
12826 #if 1
12827     SetRandomAnimationValue(x, y);
12828 #endif
12829
12830 #if 1
12831     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12832 #endif
12833   }
12834 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12835
12836 #if 0
12837   debug_print_timestamp(0, "- time for TEST loop:     -->");
12838 #endif
12839
12840   SCAN_PLAYFIELD(x, y)
12841   {
12842     element = Feld[x][y];
12843     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12844
12845     ResetGfxFrame(x, y, TRUE);
12846
12847     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12848         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12849       ResetRandomAnimationValue(x, y);
12850
12851     SetRandomAnimationValue(x, y);
12852
12853     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12854
12855     if (IS_INACTIVE(element))
12856     {
12857       if (IS_ANIMATED(graphic))
12858         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12859
12860       continue;
12861     }
12862
12863     /* this may take place after moving, so 'element' may have changed */
12864     if (IS_CHANGING(x, y) &&
12865         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12866     {
12867       int page = element_info[element].event_page_nr[CE_DELAY];
12868
12869 #if 1
12870       HandleElementChange(x, y, page);
12871 #else
12872       if (CAN_CHANGE(element))
12873         HandleElementChange(x, y, page);
12874
12875       if (HAS_ACTION(element))
12876         ExecuteCustomElementAction(x, y, element, page);
12877 #endif
12878
12879       element = Feld[x][y];
12880       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12881     }
12882
12883 #if 0   // ---------------------------------------------------------------------
12884
12885     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12886     {
12887       StartMoving(x, y);
12888
12889       element = Feld[x][y];
12890       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12891
12892       if (IS_ANIMATED(graphic) &&
12893           !IS_MOVING(x, y) &&
12894           !Stop[x][y])
12895         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12896
12897       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12898         TEST_DrawTwinkleOnField(x, y);
12899     }
12900     else if (IS_MOVING(x, y))
12901       ContinueMoving(x, y);
12902     else
12903     {
12904       switch (element)
12905       {
12906         case EL_ACID:
12907         case EL_EXIT_OPEN:
12908         case EL_EM_EXIT_OPEN:
12909         case EL_SP_EXIT_OPEN:
12910         case EL_STEEL_EXIT_OPEN:
12911         case EL_EM_STEEL_EXIT_OPEN:
12912         case EL_SP_TERMINAL:
12913         case EL_SP_TERMINAL_ACTIVE:
12914         case EL_EXTRA_TIME:
12915         case EL_SHIELD_NORMAL:
12916         case EL_SHIELD_DEADLY:
12917           if (IS_ANIMATED(graphic))
12918             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12919           break;
12920
12921         case EL_DYNAMITE_ACTIVE:
12922         case EL_EM_DYNAMITE_ACTIVE:
12923         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12924         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12925         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12926         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12927         case EL_SP_DISK_RED_ACTIVE:
12928           CheckDynamite(x, y);
12929           break;
12930
12931         case EL_AMOEBA_GROWING:
12932           AmoebeWaechst(x, y);
12933           break;
12934
12935         case EL_AMOEBA_SHRINKING:
12936           AmoebaDisappearing(x, y);
12937           break;
12938
12939 #if !USE_NEW_AMOEBA_CODE
12940         case EL_AMOEBA_WET:
12941         case EL_AMOEBA_DRY:
12942         case EL_AMOEBA_FULL:
12943         case EL_BD_AMOEBA:
12944         case EL_EMC_DRIPPER:
12945           AmoebeAbleger(x, y);
12946           break;
12947 #endif
12948
12949         case EL_GAME_OF_LIFE:
12950         case EL_BIOMAZE:
12951           Life(x, y);
12952           break;
12953
12954         case EL_EXIT_CLOSED:
12955           CheckExit(x, y);
12956           break;
12957
12958         case EL_EM_EXIT_CLOSED:
12959           CheckExitEM(x, y);
12960           break;
12961
12962         case EL_STEEL_EXIT_CLOSED:
12963           CheckExitSteel(x, y);
12964           break;
12965
12966         case EL_EM_STEEL_EXIT_CLOSED:
12967           CheckExitSteelEM(x, y);
12968           break;
12969
12970         case EL_SP_EXIT_CLOSED:
12971           CheckExitSP(x, y);
12972           break;
12973
12974         case EL_EXPANDABLE_WALL_GROWING:
12975         case EL_EXPANDABLE_STEELWALL_GROWING:
12976           MauerWaechst(x, y);
12977           break;
12978
12979         case EL_EXPANDABLE_WALL:
12980         case EL_EXPANDABLE_WALL_HORIZONTAL:
12981         case EL_EXPANDABLE_WALL_VERTICAL:
12982         case EL_EXPANDABLE_WALL_ANY:
12983         case EL_BD_EXPANDABLE_WALL:
12984           MauerAbleger(x, y);
12985           break;
12986
12987         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12988         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12989         case EL_EXPANDABLE_STEELWALL_ANY:
12990           MauerAblegerStahl(x, y);
12991           break;
12992
12993         case EL_FLAMES:
12994           CheckForDragon(x, y);
12995           break;
12996
12997         case EL_EXPLOSION:
12998           break;
12999
13000         case EL_ELEMENT_SNAPPING:
13001         case EL_DIAGONAL_SHRINKING:
13002         case EL_DIAGONAL_GROWING:
13003         {
13004           graphic =
13005             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13006
13007           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13008           break;
13009         }
13010
13011         default:
13012           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13013             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13014           break;
13015       }
13016     }
13017
13018 #else   // ---------------------------------------------------------------------
13019
13020     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
13021     {
13022       StartMoving(x, y);
13023
13024       element = Feld[x][y];
13025       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13026
13027       if (IS_ANIMATED(graphic) &&
13028           !IS_MOVING(x, y) &&
13029           !Stop[x][y])
13030         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13031
13032       if (IS_GEM(element) || element == EL_SP_INFOTRON)
13033         TEST_DrawTwinkleOnField(x, y);
13034     }
13035     else if ((element == EL_ACID ||
13036               element == EL_EXIT_OPEN ||
13037               element == EL_EM_EXIT_OPEN ||
13038               element == EL_SP_EXIT_OPEN ||
13039               element == EL_STEEL_EXIT_OPEN ||
13040               element == EL_EM_STEEL_EXIT_OPEN ||
13041               element == EL_SP_TERMINAL ||
13042               element == EL_SP_TERMINAL_ACTIVE ||
13043               element == EL_EXTRA_TIME ||
13044               element == EL_SHIELD_NORMAL ||
13045               element == EL_SHIELD_DEADLY) &&
13046              IS_ANIMATED(graphic))
13047       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13048     else if (IS_MOVING(x, y))
13049       ContinueMoving(x, y);
13050     else if (IS_ACTIVE_BOMB(element))
13051       CheckDynamite(x, y);
13052     else if (element == EL_AMOEBA_GROWING)
13053       AmoebeWaechst(x, y);
13054     else if (element == EL_AMOEBA_SHRINKING)
13055       AmoebaDisappearing(x, y);
13056
13057 #if !USE_NEW_AMOEBA_CODE
13058     else if (IS_AMOEBALIVE(element))
13059       AmoebeAbleger(x, y);
13060 #endif
13061
13062     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13063       Life(x, y);
13064     else if (element == EL_EXIT_CLOSED)
13065       CheckExit(x, y);
13066     else if (element == EL_EM_EXIT_CLOSED)
13067       CheckExitEM(x, y);
13068     else if (element == EL_STEEL_EXIT_CLOSED)
13069       CheckExitSteel(x, y);
13070     else if (element == EL_EM_STEEL_EXIT_CLOSED)
13071       CheckExitSteelEM(x, y);
13072     else if (element == EL_SP_EXIT_CLOSED)
13073       CheckExitSP(x, y);
13074     else if (element == EL_EXPANDABLE_WALL_GROWING ||
13075              element == EL_EXPANDABLE_STEELWALL_GROWING)
13076       MauerWaechst(x, y);
13077     else if (element == EL_EXPANDABLE_WALL ||
13078              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13079              element == EL_EXPANDABLE_WALL_VERTICAL ||
13080              element == EL_EXPANDABLE_WALL_ANY ||
13081              element == EL_BD_EXPANDABLE_WALL)
13082       MauerAbleger(x, y);
13083     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13084              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13085              element == EL_EXPANDABLE_STEELWALL_ANY)
13086       MauerAblegerStahl(x, y);
13087     else if (element == EL_FLAMES)
13088       CheckForDragon(x, y);
13089     else if (element == EL_EXPLOSION)
13090       ; /* drawing of correct explosion animation is handled separately */
13091     else if (element == EL_ELEMENT_SNAPPING ||
13092              element == EL_DIAGONAL_SHRINKING ||
13093              element == EL_DIAGONAL_GROWING)
13094     {
13095       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13096
13097       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13098     }
13099     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13100       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13101
13102 #endif  // ---------------------------------------------------------------------
13103
13104     if (IS_BELT_ACTIVE(element))
13105       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13106
13107     if (game.magic_wall_active)
13108     {
13109       int jx = local_player->jx, jy = local_player->jy;
13110
13111       /* play the element sound at the position nearest to the player */
13112       if ((element == EL_MAGIC_WALL_FULL ||
13113            element == EL_MAGIC_WALL_ACTIVE ||
13114            element == EL_MAGIC_WALL_EMPTYING ||
13115            element == EL_BD_MAGIC_WALL_FULL ||
13116            element == EL_BD_MAGIC_WALL_ACTIVE ||
13117            element == EL_BD_MAGIC_WALL_EMPTYING ||
13118            element == EL_DC_MAGIC_WALL_FULL ||
13119            element == EL_DC_MAGIC_WALL_ACTIVE ||
13120            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13121           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13122       {
13123         magic_wall_x = x;
13124         magic_wall_y = y;
13125       }
13126     }
13127   }
13128
13129 #if 0
13130   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13131 #endif
13132
13133 #if USE_NEW_AMOEBA_CODE
13134   /* new experimental amoeba growth stuff */
13135   if (!(FrameCounter % 8))
13136   {
13137     static unsigned int random = 1684108901;
13138
13139     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13140     {
13141       x = RND(lev_fieldx);
13142       y = RND(lev_fieldy);
13143       element = Feld[x][y];
13144
13145       if (!IS_PLAYER(x,y) &&
13146           (element == EL_EMPTY ||
13147            CAN_GROW_INTO(element) ||
13148            element == EL_QUICKSAND_EMPTY ||
13149            element == EL_QUICKSAND_FAST_EMPTY ||
13150            element == EL_ACID_SPLASH_LEFT ||
13151            element == EL_ACID_SPLASH_RIGHT))
13152       {
13153         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13154             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13155             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13156             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13157           Feld[x][y] = EL_AMOEBA_DROP;
13158       }
13159
13160       random = random * 129 + 1;
13161     }
13162   }
13163 #endif
13164
13165 #if 0
13166   if (game.explosions_delayed)
13167 #endif
13168   {
13169     game.explosions_delayed = FALSE;
13170
13171     SCAN_PLAYFIELD(x, y)
13172     {
13173       element = Feld[x][y];
13174
13175       if (ExplodeField[x][y])
13176         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13177       else if (element == EL_EXPLOSION)
13178         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13179
13180       ExplodeField[x][y] = EX_TYPE_NONE;
13181     }
13182
13183     game.explosions_delayed = TRUE;
13184   }
13185
13186   if (game.magic_wall_active)
13187   {
13188     if (!(game.magic_wall_time_left % 4))
13189     {
13190       int element = Feld[magic_wall_x][magic_wall_y];
13191
13192       if (element == EL_BD_MAGIC_WALL_FULL ||
13193           element == EL_BD_MAGIC_WALL_ACTIVE ||
13194           element == EL_BD_MAGIC_WALL_EMPTYING)
13195         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13196       else if (element == EL_DC_MAGIC_WALL_FULL ||
13197                element == EL_DC_MAGIC_WALL_ACTIVE ||
13198                element == EL_DC_MAGIC_WALL_EMPTYING)
13199         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13200       else
13201         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13202     }
13203
13204     if (game.magic_wall_time_left > 0)
13205     {
13206       game.magic_wall_time_left--;
13207
13208       if (!game.magic_wall_time_left)
13209       {
13210         SCAN_PLAYFIELD(x, y)
13211         {
13212           element = Feld[x][y];
13213
13214           if (element == EL_MAGIC_WALL_ACTIVE ||
13215               element == EL_MAGIC_WALL_FULL)
13216           {
13217             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13218             TEST_DrawLevelField(x, y);
13219           }
13220           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13221                    element == EL_BD_MAGIC_WALL_FULL)
13222           {
13223             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13224             TEST_DrawLevelField(x, y);
13225           }
13226           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13227                    element == EL_DC_MAGIC_WALL_FULL)
13228           {
13229             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13230             TEST_DrawLevelField(x, y);
13231           }
13232         }
13233
13234         game.magic_wall_active = FALSE;
13235       }
13236     }
13237   }
13238
13239   if (game.light_time_left > 0)
13240   {
13241     game.light_time_left--;
13242
13243     if (game.light_time_left == 0)
13244       RedrawAllLightSwitchesAndInvisibleElements();
13245   }
13246
13247   if (game.timegate_time_left > 0)
13248   {
13249     game.timegate_time_left--;
13250
13251     if (game.timegate_time_left == 0)
13252       CloseAllOpenTimegates();
13253   }
13254
13255   if (game.lenses_time_left > 0)
13256   {
13257     game.lenses_time_left--;
13258
13259     if (game.lenses_time_left == 0)
13260       RedrawAllInvisibleElementsForLenses();
13261   }
13262
13263   if (game.magnify_time_left > 0)
13264   {
13265     game.magnify_time_left--;
13266
13267     if (game.magnify_time_left == 0)
13268       RedrawAllInvisibleElementsForMagnifier();
13269   }
13270
13271   for (i = 0; i < MAX_PLAYERS; i++)
13272   {
13273     struct PlayerInfo *player = &stored_player[i];
13274
13275     if (SHIELD_ON(player))
13276     {
13277       if (player->shield_deadly_time_left)
13278         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13279       else if (player->shield_normal_time_left)
13280         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13281     }
13282   }
13283
13284 #if USE_DELAYED_GFX_REDRAW
13285   SCAN_PLAYFIELD(x, y)
13286   {
13287 #if 1
13288     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13289 #else
13290     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13291         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13292 #endif
13293     {
13294       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13295          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13296
13297       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13298         DrawLevelField(x, y);
13299
13300       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13301         DrawLevelFieldCrumbled(x, y);
13302
13303       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13304         DrawLevelFieldCrumbledNeighbours(x, y);
13305
13306       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13307         DrawTwinkleOnField(x, y);
13308     }
13309
13310     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13311   }
13312 #endif
13313
13314   CheckLevelTime();
13315
13316   DrawAllPlayers();
13317   PlayAllPlayersSound();
13318
13319   if (options.debug)                    /* calculate frames per second */
13320   {
13321     static unsigned int fps_counter = 0;
13322     static int fps_frames = 0;
13323     unsigned int fps_delay_ms = Counter() - fps_counter;
13324
13325     fps_frames++;
13326
13327     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13328     {
13329       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13330
13331       fps_frames = 0;
13332       fps_counter = Counter();
13333     }
13334
13335     redraw_mask |= REDRAW_FPS;
13336   }
13337
13338   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13339
13340   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13341   {
13342     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13343
13344     local_player->show_envelope = 0;
13345   }
13346
13347 #if 0
13348   debug_print_timestamp(0, "stop main loop profiling ");
13349   printf("----------------------------------------------------------\n");
13350 #endif
13351
13352   /* use random number generator in every frame to make it less predictable */
13353   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13354     RND(1);
13355 }
13356
13357 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13358 {
13359   int min_x = x, min_y = y, max_x = x, max_y = y;
13360   int i;
13361
13362   for (i = 0; i < MAX_PLAYERS; i++)
13363   {
13364     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13365
13366     if (!stored_player[i].active || &stored_player[i] == player)
13367       continue;
13368
13369     min_x = MIN(min_x, jx);
13370     min_y = MIN(min_y, jy);
13371     max_x = MAX(max_x, jx);
13372     max_y = MAX(max_y, jy);
13373   }
13374
13375   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13376 }
13377
13378 static boolean AllPlayersInVisibleScreen()
13379 {
13380   int i;
13381
13382   for (i = 0; i < MAX_PLAYERS; i++)
13383   {
13384     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13385
13386     if (!stored_player[i].active)
13387       continue;
13388
13389     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13390       return FALSE;
13391   }
13392
13393   return TRUE;
13394 }
13395
13396 void ScrollLevel(int dx, int dy)
13397 {
13398 #if 0
13399   /* (directly solved in BlitBitmap() now) */
13400   static Bitmap *bitmap_db_field2 = NULL;
13401   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13402   int x, y;
13403 #else
13404   int x, y;
13405 #endif
13406
13407 #if 0
13408   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13409   /* only horizontal XOR vertical scroll direction allowed */
13410   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13411     return;
13412 #endif
13413
13414 #if 0
13415   /* (directly solved in BlitBitmap() now) */
13416   if (bitmap_db_field2 == NULL)
13417     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13418
13419   /* needed when blitting directly to same bitmap -- should not be needed with
13420      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13421   BlitBitmap(drawto_field, bitmap_db_field2,
13422              FX + TILEX * (dx == -1) - softscroll_offset,
13423              FY + TILEY * (dy == -1) - softscroll_offset,
13424              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13425              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13426              FX + TILEX * (dx == 1) - softscroll_offset,
13427              FY + TILEY * (dy == 1) - softscroll_offset);
13428   BlitBitmap(bitmap_db_field2, drawto_field,
13429              FX + TILEX * (dx == 1) - softscroll_offset,
13430              FY + TILEY * (dy == 1) - softscroll_offset,
13431              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13432              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13433              FX + TILEX * (dx == 1) - softscroll_offset,
13434              FY + TILEY * (dy == 1) - softscroll_offset);
13435
13436 #else
13437
13438 #if 0
13439   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13440   int xsize = (BX2 - BX1 + 1);
13441   int ysize = (BY2 - BY1 + 1);
13442   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13443   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13444   int step  = (start < end ? +1 : -1);
13445
13446   for (i = start; i != end; i += step)
13447   {
13448     BlitBitmap(drawto_field, drawto_field,
13449                FX + TILEX * (dx != 0 ? i + step : 0),
13450                FY + TILEY * (dy != 0 ? i + step : 0),
13451                TILEX * (dx != 0 ? 1 : xsize),
13452                TILEY * (dy != 0 ? 1 : ysize),
13453                FX + TILEX * (dx != 0 ? i : 0),
13454                FY + TILEY * (dy != 0 ? i : 0));
13455   }
13456
13457 #else
13458
13459 #if NEW_TILESIZE
13460 #if NEW_SCROLL
13461   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13462 #else
13463   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13464 #endif
13465 #else
13466 #if NEW_SCROLL
13467   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13468 #else
13469   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13470 #endif
13471 #endif
13472
13473 #if NEW_TILESIZE
13474   BlitBitmap(drawto_field, drawto_field,
13475              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13476              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13477              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13478              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13479              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13480              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13481 #else
13482   BlitBitmap(drawto_field, drawto_field,
13483              FX + TILEX * (dx == -1) - softscroll_offset,
13484              FY + TILEY * (dy == -1) - softscroll_offset,
13485              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13486              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13487              FX + TILEX * (dx == 1) - softscroll_offset,
13488              FY + TILEY * (dy == 1) - softscroll_offset);
13489 #endif
13490
13491 #endif
13492 #endif
13493
13494   if (dx != 0)
13495   {
13496     x = (dx == 1 ? BX1 : BX2);
13497     for (y = BY1; y <= BY2; y++)
13498       DrawScreenField(x, y);
13499   }
13500
13501   if (dy != 0)
13502   {
13503     y = (dy == 1 ? BY1 : BY2);
13504     for (x = BX1; x <= BX2; x++)
13505       DrawScreenField(x, y);
13506   }
13507
13508   redraw_mask |= REDRAW_FIELD;
13509 }
13510
13511 static boolean canFallDown(struct PlayerInfo *player)
13512 {
13513   int jx = player->jx, jy = player->jy;
13514
13515   return (IN_LEV_FIELD(jx, jy + 1) &&
13516           (IS_FREE(jx, jy + 1) ||
13517            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13518           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13519           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13520 }
13521
13522 static boolean canPassField(int x, int y, int move_dir)
13523 {
13524   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13525   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13526   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13527   int nextx = x + dx;
13528   int nexty = y + dy;
13529   int element = Feld[x][y];
13530
13531   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13532           !CAN_MOVE(element) &&
13533           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13534           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13535           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13536 }
13537
13538 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13539 {
13540   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13541   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13542   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13543   int newx = x + dx;
13544   int newy = y + dy;
13545
13546   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13547           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13548           (IS_DIGGABLE(Feld[newx][newy]) ||
13549            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13550            canPassField(newx, newy, move_dir)));
13551 }
13552
13553 static void CheckGravityMovement(struct PlayerInfo *player)
13554 {
13555 #if USE_PLAYER_GRAVITY
13556   if (player->gravity && !player->programmed_action)
13557 #else
13558   if (game.gravity && !player->programmed_action)
13559 #endif
13560   {
13561     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13562     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13563     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13564     int jx = player->jx, jy = player->jy;
13565     boolean player_is_moving_to_valid_field =
13566       (!player_is_snapping &&
13567        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13568         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13569     boolean player_can_fall_down = canFallDown(player);
13570
13571     if (player_can_fall_down &&
13572         !player_is_moving_to_valid_field)
13573       player->programmed_action = MV_DOWN;
13574   }
13575 }
13576
13577 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13578 {
13579   return CheckGravityMovement(player);
13580
13581 #if USE_PLAYER_GRAVITY
13582   if (player->gravity && !player->programmed_action)
13583 #else
13584   if (game.gravity && !player->programmed_action)
13585 #endif
13586   {
13587     int jx = player->jx, jy = player->jy;
13588     boolean field_under_player_is_free =
13589       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13590     boolean player_is_standing_on_valid_field =
13591       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13592        (IS_WALKABLE(Feld[jx][jy]) &&
13593         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13594
13595     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13596       player->programmed_action = MV_DOWN;
13597   }
13598 }
13599
13600 /*
13601   MovePlayerOneStep()
13602   -----------------------------------------------------------------------------
13603   dx, dy:               direction (non-diagonal) to try to move the player to
13604   real_dx, real_dy:     direction as read from input device (can be diagonal)
13605 */
13606
13607 boolean MovePlayerOneStep(struct PlayerInfo *player,
13608                           int dx, int dy, int real_dx, int real_dy)
13609 {
13610   int jx = player->jx, jy = player->jy;
13611   int new_jx = jx + dx, new_jy = jy + dy;
13612 #if !USE_FIXED_DONT_RUN_INTO
13613   int element;
13614 #endif
13615   int can_move;
13616   boolean player_can_move = !player->cannot_move;
13617
13618   if (!player->active || (!dx && !dy))
13619     return MP_NO_ACTION;
13620
13621   player->MovDir = (dx < 0 ? MV_LEFT :
13622                     dx > 0 ? MV_RIGHT :
13623                     dy < 0 ? MV_UP :
13624                     dy > 0 ? MV_DOWN :  MV_NONE);
13625
13626   if (!IN_LEV_FIELD(new_jx, new_jy))
13627     return MP_NO_ACTION;
13628
13629   if (!player_can_move)
13630   {
13631     if (player->MovPos == 0)
13632     {
13633       player->is_moving = FALSE;
13634       player->is_digging = FALSE;
13635       player->is_collecting = FALSE;
13636       player->is_snapping = FALSE;
13637       player->is_pushing = FALSE;
13638     }
13639   }
13640
13641 #if 1
13642   if (!options.network && game.centered_player_nr == -1 &&
13643       !AllPlayersInSight(player, new_jx, new_jy))
13644     return MP_NO_ACTION;
13645 #else
13646   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13647     return MP_NO_ACTION;
13648 #endif
13649
13650 #if !USE_FIXED_DONT_RUN_INTO
13651   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13652
13653   /* (moved to DigField()) */
13654   if (player_can_move && DONT_RUN_INTO(element))
13655   {
13656     if (element == EL_ACID && dx == 0 && dy == 1)
13657     {
13658       SplashAcid(new_jx, new_jy);
13659       Feld[jx][jy] = EL_PLAYER_1;
13660       InitMovingField(jx, jy, MV_DOWN);
13661       Store[jx][jy] = EL_ACID;
13662       ContinueMoving(jx, jy);
13663       BuryPlayer(player);
13664     }
13665     else
13666       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13667
13668     return MP_MOVING;
13669   }
13670 #endif
13671
13672   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13673   if (can_move != MP_MOVING)
13674     return can_move;
13675
13676   /* check if DigField() has caused relocation of the player */
13677   if (player->jx != jx || player->jy != jy)
13678     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13679
13680   StorePlayer[jx][jy] = 0;
13681   player->last_jx = jx;
13682   player->last_jy = jy;
13683   player->jx = new_jx;
13684   player->jy = new_jy;
13685   StorePlayer[new_jx][new_jy] = player->element_nr;
13686
13687   if (player->move_delay_value_next != -1)
13688   {
13689     player->move_delay_value = player->move_delay_value_next;
13690     player->move_delay_value_next = -1;
13691   }
13692
13693   player->MovPos =
13694     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13695
13696   player->step_counter++;
13697
13698   PlayerVisit[jx][jy] = FrameCounter;
13699
13700 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13701   player->is_moving = TRUE;
13702 #endif
13703
13704 #if 1
13705   /* should better be called in MovePlayer(), but this breaks some tapes */
13706   ScrollPlayer(player, SCROLL_INIT);
13707 #endif
13708
13709   return MP_MOVING;
13710 }
13711
13712 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13713 {
13714   int jx = player->jx, jy = player->jy;
13715   int old_jx = jx, old_jy = jy;
13716   int moved = MP_NO_ACTION;
13717
13718   if (!player->active)
13719     return FALSE;
13720
13721   if (!dx && !dy)
13722   {
13723     if (player->MovPos == 0)
13724     {
13725       player->is_moving = FALSE;
13726       player->is_digging = FALSE;
13727       player->is_collecting = FALSE;
13728       player->is_snapping = FALSE;
13729       player->is_pushing = FALSE;
13730     }
13731
13732     return FALSE;
13733   }
13734
13735   if (player->move_delay > 0)
13736     return FALSE;
13737
13738   player->move_delay = -1;              /* set to "uninitialized" value */
13739
13740   /* store if player is automatically moved to next field */
13741   player->is_auto_moving = (player->programmed_action != MV_NONE);
13742
13743   /* remove the last programmed player action */
13744   player->programmed_action = 0;
13745
13746   if (player->MovPos)
13747   {
13748     /* should only happen if pre-1.2 tape recordings are played */
13749     /* this is only for backward compatibility */
13750
13751     int original_move_delay_value = player->move_delay_value;
13752
13753 #if DEBUG
13754     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13755            tape.counter);
13756 #endif
13757
13758     /* scroll remaining steps with finest movement resolution */
13759     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13760
13761     while (player->MovPos)
13762     {
13763       ScrollPlayer(player, SCROLL_GO_ON);
13764       ScrollScreen(NULL, SCROLL_GO_ON);
13765
13766       AdvanceFrameAndPlayerCounters(player->index_nr);
13767
13768       DrawAllPlayers();
13769       BackToFront();
13770     }
13771
13772     player->move_delay_value = original_move_delay_value;
13773   }
13774
13775   player->is_active = FALSE;
13776
13777   if (player->last_move_dir & MV_HORIZONTAL)
13778   {
13779     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13780       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13781   }
13782   else
13783   {
13784     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13785       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13786   }
13787
13788 #if USE_FIXED_BORDER_RUNNING_GFX
13789   if (!moved && !player->is_active)
13790   {
13791     player->is_moving = FALSE;
13792     player->is_digging = FALSE;
13793     player->is_collecting = FALSE;
13794     player->is_snapping = FALSE;
13795     player->is_pushing = FALSE;
13796   }
13797 #endif
13798
13799   jx = player->jx;
13800   jy = player->jy;
13801
13802 #if 1
13803   if (moved & MP_MOVING && !ScreenMovPos &&
13804       (player->index_nr == game.centered_player_nr ||
13805        game.centered_player_nr == -1))
13806 #else
13807   if (moved & MP_MOVING && !ScreenMovPos &&
13808       (player == local_player || !options.network))
13809 #endif
13810   {
13811     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13812     int offset = game.scroll_delay_value;
13813
13814     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13815     {
13816       /* actual player has left the screen -- scroll in that direction */
13817       if (jx != old_jx)         /* player has moved horizontally */
13818         scroll_x += (jx - old_jx);
13819       else                      /* player has moved vertically */
13820         scroll_y += (jy - old_jy);
13821     }
13822     else
13823     {
13824       if (jx != old_jx)         /* player has moved horizontally */
13825       {
13826         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13827             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13828           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13829
13830         /* don't scroll over playfield boundaries */
13831         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13832           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13833
13834         /* don't scroll more than one field at a time */
13835         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13836
13837         /* don't scroll against the player's moving direction */
13838         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13839             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13840           scroll_x = old_scroll_x;
13841       }
13842       else                      /* player has moved vertically */
13843       {
13844         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13845             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13846           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13847
13848         /* don't scroll over playfield boundaries */
13849         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13850           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13851
13852         /* don't scroll more than one field at a time */
13853         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13854
13855         /* don't scroll against the player's moving direction */
13856         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13857             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13858           scroll_y = old_scroll_y;
13859       }
13860     }
13861
13862     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13863     {
13864 #if 1
13865       if (!options.network && game.centered_player_nr == -1 &&
13866           !AllPlayersInVisibleScreen())
13867       {
13868         scroll_x = old_scroll_x;
13869         scroll_y = old_scroll_y;
13870       }
13871       else
13872 #else
13873       if (!options.network && !AllPlayersInVisibleScreen())
13874       {
13875         scroll_x = old_scroll_x;
13876         scroll_y = old_scroll_y;
13877       }
13878       else
13879 #endif
13880       {
13881         ScrollScreen(player, SCROLL_INIT);
13882         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13883       }
13884     }
13885   }
13886
13887   player->StepFrame = 0;
13888
13889   if (moved & MP_MOVING)
13890   {
13891     if (old_jx != jx && old_jy == jy)
13892       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13893     else if (old_jx == jx && old_jy != jy)
13894       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13895
13896     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13897
13898     player->last_move_dir = player->MovDir;
13899     player->is_moving = TRUE;
13900     player->is_snapping = FALSE;
13901     player->is_switching = FALSE;
13902     player->is_dropping = FALSE;
13903     player->is_dropping_pressed = FALSE;
13904     player->drop_pressed_delay = 0;
13905
13906 #if 0
13907     /* should better be called here than above, but this breaks some tapes */
13908     ScrollPlayer(player, SCROLL_INIT);
13909 #endif
13910   }
13911   else
13912   {
13913     CheckGravityMovementWhenNotMoving(player);
13914
13915     player->is_moving = FALSE;
13916
13917     /* at this point, the player is allowed to move, but cannot move right now
13918        (e.g. because of something blocking the way) -- ensure that the player
13919        is also allowed to move in the next frame (in old versions before 3.1.1,
13920        the player was forced to wait again for eight frames before next try) */
13921
13922     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13923       player->move_delay = 0;   /* allow direct movement in the next frame */
13924   }
13925
13926   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13927     player->move_delay = player->move_delay_value;
13928
13929   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13930   {
13931     TestIfPlayerTouchesBadThing(jx, jy);
13932     TestIfPlayerTouchesCustomElement(jx, jy);
13933   }
13934
13935   if (!player->active)
13936     RemovePlayer(player);
13937
13938   return moved;
13939 }
13940
13941 void ScrollPlayer(struct PlayerInfo *player, int mode)
13942 {
13943   int jx = player->jx, jy = player->jy;
13944   int last_jx = player->last_jx, last_jy = player->last_jy;
13945   int move_stepsize = TILEX / player->move_delay_value;
13946
13947 #if USE_NEW_PLAYER_SPEED
13948   if (!player->active)
13949     return;
13950
13951   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13952     return;
13953 #else
13954   if (!player->active || player->MovPos == 0)
13955     return;
13956 #endif
13957
13958   if (mode == SCROLL_INIT)
13959   {
13960     player->actual_frame_counter = FrameCounter;
13961     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13962
13963     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13964         Feld[last_jx][last_jy] == EL_EMPTY)
13965     {
13966       int last_field_block_delay = 0;   /* start with no blocking at all */
13967       int block_delay_adjustment = player->block_delay_adjustment;
13968
13969       /* if player blocks last field, add delay for exactly one move */
13970       if (player->block_last_field)
13971       {
13972         last_field_block_delay += player->move_delay_value;
13973
13974         /* when blocking enabled, prevent moving up despite gravity */
13975 #if USE_PLAYER_GRAVITY
13976         if (player->gravity && player->MovDir == MV_UP)
13977           block_delay_adjustment = -1;
13978 #else
13979         if (game.gravity && player->MovDir == MV_UP)
13980           block_delay_adjustment = -1;
13981 #endif
13982       }
13983
13984       /* add block delay adjustment (also possible when not blocking) */
13985       last_field_block_delay += block_delay_adjustment;
13986
13987       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13988       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13989     }
13990
13991 #if USE_NEW_PLAYER_SPEED
13992     if (player->MovPos != 0)    /* player has not yet reached destination */
13993       return;
13994 #else
13995     return;
13996 #endif
13997   }
13998   else if (!FrameReached(&player->actual_frame_counter, 1))
13999     return;
14000
14001 #if USE_NEW_PLAYER_SPEED
14002   if (player->MovPos != 0)
14003   {
14004     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14005     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14006
14007     /* before DrawPlayer() to draw correct player graphic for this case */
14008     if (player->MovPos == 0)
14009       CheckGravityMovement(player);
14010   }
14011 #else
14012   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14013   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14014
14015   /* before DrawPlayer() to draw correct player graphic for this case */
14016   if (player->MovPos == 0)
14017     CheckGravityMovement(player);
14018 #endif
14019
14020   if (player->MovPos == 0)      /* player reached destination field */
14021   {
14022     if (player->move_delay_reset_counter > 0)
14023     {
14024       player->move_delay_reset_counter--;
14025
14026       if (player->move_delay_reset_counter == 0)
14027       {
14028         /* continue with normal speed after quickly moving through gate */
14029         HALVE_PLAYER_SPEED(player);
14030
14031         /* be able to make the next move without delay */
14032         player->move_delay = 0;
14033       }
14034     }
14035
14036     player->last_jx = jx;
14037     player->last_jy = jy;
14038
14039     if (Feld[jx][jy] == EL_EXIT_OPEN ||
14040         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14041 #if 1
14042         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14043 #endif
14044         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14045         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14046 #if 1
14047         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14048 #endif
14049         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14050         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
14051     {
14052       DrawPlayer(player);       /* needed here only to cleanup last field */
14053       RemovePlayer(player);
14054
14055       if (local_player->friends_still_needed == 0 ||
14056           IS_SP_ELEMENT(Feld[jx][jy]))
14057         PlayerWins(player);
14058     }
14059
14060     /* this breaks one level: "machine", level 000 */
14061     {
14062       int move_direction = player->MovDir;
14063       int enter_side = MV_DIR_OPPOSITE(move_direction);
14064       int leave_side = move_direction;
14065       int old_jx = last_jx;
14066       int old_jy = last_jy;
14067       int old_element = Feld[old_jx][old_jy];
14068       int new_element = Feld[jx][jy];
14069
14070       if (IS_CUSTOM_ELEMENT(old_element))
14071         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14072                                    CE_LEFT_BY_PLAYER,
14073                                    player->index_bit, leave_side);
14074
14075       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14076                                           CE_PLAYER_LEAVES_X,
14077                                           player->index_bit, leave_side);
14078
14079       if (IS_CUSTOM_ELEMENT(new_element))
14080         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14081                                    player->index_bit, enter_side);
14082
14083       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14084                                           CE_PLAYER_ENTERS_X,
14085                                           player->index_bit, enter_side);
14086
14087 #if USE_FIX_CE_ACTION_WITH_PLAYER
14088       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14089                                         CE_MOVE_OF_X, move_direction);
14090 #else
14091       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14092                                         CE_MOVE_OF_X, move_direction);
14093 #endif
14094     }
14095
14096     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14097     {
14098       TestIfPlayerTouchesBadThing(jx, jy);
14099       TestIfPlayerTouchesCustomElement(jx, jy);
14100
14101       /* needed because pushed element has not yet reached its destination,
14102          so it would trigger a change event at its previous field location */
14103       if (!player->is_pushing)
14104         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14105
14106       if (!player->active)
14107         RemovePlayer(player);
14108     }
14109
14110     if (!local_player->LevelSolved && level.use_step_counter)
14111     {
14112       int i;
14113
14114       TimePlayed++;
14115
14116       if (TimeLeft > 0)
14117       {
14118         TimeLeft--;
14119
14120         if (TimeLeft <= 10 && setup.time_limit)
14121           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14122
14123 #if 1
14124         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14125
14126         DisplayGameControlValues();
14127 #else
14128         DrawGameValue_Time(TimeLeft);
14129 #endif
14130
14131         if (!TimeLeft && setup.time_limit)
14132           for (i = 0; i < MAX_PLAYERS; i++)
14133             KillPlayer(&stored_player[i]);
14134       }
14135 #if 1
14136       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14137       {
14138         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14139
14140         DisplayGameControlValues();
14141       }
14142 #else
14143       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14144         DrawGameValue_Time(TimePlayed);
14145 #endif
14146     }
14147
14148     if (tape.single_step && tape.recording && !tape.pausing &&
14149         !player->programmed_action)
14150       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14151   }
14152 }
14153
14154 void ScrollScreen(struct PlayerInfo *player, int mode)
14155 {
14156   static unsigned int screen_frame_counter = 0;
14157
14158   if (mode == SCROLL_INIT)
14159   {
14160     /* set scrolling step size according to actual player's moving speed */
14161     ScrollStepSize = TILEX / player->move_delay_value;
14162
14163     screen_frame_counter = FrameCounter;
14164     ScreenMovDir = player->MovDir;
14165     ScreenMovPos = player->MovPos;
14166     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14167     return;
14168   }
14169   else if (!FrameReached(&screen_frame_counter, 1))
14170     return;
14171
14172   if (ScreenMovPos)
14173   {
14174     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14175     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14176     redraw_mask |= REDRAW_FIELD;
14177   }
14178   else
14179     ScreenMovDir = MV_NONE;
14180 }
14181
14182 void TestIfPlayerTouchesCustomElement(int x, int y)
14183 {
14184   static int xy[4][2] =
14185   {
14186     { 0, -1 },
14187     { -1, 0 },
14188     { +1, 0 },
14189     { 0, +1 }
14190   };
14191   static int trigger_sides[4][2] =
14192   {
14193     /* center side       border side */
14194     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14195     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14196     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14197     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14198   };
14199   static int touch_dir[4] =
14200   {
14201     MV_LEFT | MV_RIGHT,
14202     MV_UP   | MV_DOWN,
14203     MV_UP   | MV_DOWN,
14204     MV_LEFT | MV_RIGHT
14205   };
14206   int center_element = Feld[x][y];      /* should always be non-moving! */
14207   int i;
14208
14209   for (i = 0; i < NUM_DIRECTIONS; i++)
14210   {
14211     int xx = x + xy[i][0];
14212     int yy = y + xy[i][1];
14213     int center_side = trigger_sides[i][0];
14214     int border_side = trigger_sides[i][1];
14215     int border_element;
14216
14217     if (!IN_LEV_FIELD(xx, yy))
14218       continue;
14219
14220     if (IS_PLAYER(x, y))                /* player found at center element */
14221     {
14222       struct PlayerInfo *player = PLAYERINFO(x, y);
14223
14224       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14225         border_element = Feld[xx][yy];          /* may be moving! */
14226       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14227         border_element = Feld[xx][yy];
14228       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14229         border_element = MovingOrBlocked2Element(xx, yy);
14230       else
14231         continue;               /* center and border element do not touch */
14232
14233       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14234                                  player->index_bit, border_side);
14235       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14236                                           CE_PLAYER_TOUCHES_X,
14237                                           player->index_bit, border_side);
14238
14239 #if USE_FIX_CE_ACTION_WITH_PLAYER
14240       {
14241         /* use player element that is initially defined in the level playfield,
14242            not the player element that corresponds to the runtime player number
14243            (example: a level that contains EL_PLAYER_3 as the only player would
14244            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14245         int player_element = PLAYERINFO(x, y)->initial_element;
14246
14247         CheckElementChangeBySide(xx, yy, border_element, player_element,
14248                                  CE_TOUCHING_X, border_side);
14249       }
14250 #endif
14251     }
14252     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14253     {
14254       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14255
14256       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14257       {
14258         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14259           continue;             /* center and border element do not touch */
14260       }
14261
14262       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14263                                  player->index_bit, center_side);
14264       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14265                                           CE_PLAYER_TOUCHES_X,
14266                                           player->index_bit, center_side);
14267
14268 #if USE_FIX_CE_ACTION_WITH_PLAYER
14269       {
14270         /* use player element that is initially defined in the level playfield,
14271            not the player element that corresponds to the runtime player number
14272            (example: a level that contains EL_PLAYER_3 as the only player would
14273            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14274         int player_element = PLAYERINFO(xx, yy)->initial_element;
14275
14276         CheckElementChangeBySide(x, y, center_element, player_element,
14277                                  CE_TOUCHING_X, center_side);
14278       }
14279 #endif
14280
14281       break;
14282     }
14283   }
14284 }
14285
14286 #if USE_ELEMENT_TOUCHING_BUGFIX
14287
14288 void TestIfElementTouchesCustomElement(int x, int y)
14289 {
14290   static int xy[4][2] =
14291   {
14292     { 0, -1 },
14293     { -1, 0 },
14294     { +1, 0 },
14295     { 0, +1 }
14296   };
14297   static int trigger_sides[4][2] =
14298   {
14299     /* center side      border side */
14300     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14301     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14302     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14303     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14304   };
14305   static int touch_dir[4] =
14306   {
14307     MV_LEFT | MV_RIGHT,
14308     MV_UP   | MV_DOWN,
14309     MV_UP   | MV_DOWN,
14310     MV_LEFT | MV_RIGHT
14311   };
14312   boolean change_center_element = FALSE;
14313   int center_element = Feld[x][y];      /* should always be non-moving! */
14314   int border_element_old[NUM_DIRECTIONS];
14315   int i;
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 border_element;
14322
14323     border_element_old[i] = -1;
14324
14325     if (!IN_LEV_FIELD(xx, yy))
14326       continue;
14327
14328     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14329       border_element = Feld[xx][yy];    /* may be moving! */
14330     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14331       border_element = Feld[xx][yy];
14332     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14333       border_element = MovingOrBlocked2Element(xx, yy);
14334     else
14335       continue;                 /* center and border element do not touch */
14336
14337     border_element_old[i] = border_element;
14338   }
14339
14340   for (i = 0; i < NUM_DIRECTIONS; i++)
14341   {
14342     int xx = x + xy[i][0];
14343     int yy = y + xy[i][1];
14344     int center_side = trigger_sides[i][0];
14345     int border_element = border_element_old[i];
14346
14347     if (border_element == -1)
14348       continue;
14349
14350     /* check for change of border element */
14351     CheckElementChangeBySide(xx, yy, border_element, center_element,
14352                              CE_TOUCHING_X, center_side);
14353
14354     /* (center element cannot be player, so we dont have to check this here) */
14355   }
14356
14357   for (i = 0; i < NUM_DIRECTIONS; i++)
14358   {
14359     int xx = x + xy[i][0];
14360     int yy = y + xy[i][1];
14361     int border_side = trigger_sides[i][1];
14362     int border_element = border_element_old[i];
14363
14364     if (border_element == -1)
14365       continue;
14366
14367     /* check for change of center element (but change it only once) */
14368     if (!change_center_element)
14369       change_center_element =
14370         CheckElementChangeBySide(x, y, center_element, border_element,
14371                                  CE_TOUCHING_X, border_side);
14372
14373 #if USE_FIX_CE_ACTION_WITH_PLAYER
14374     if (IS_PLAYER(xx, yy))
14375     {
14376       /* use player element that is initially defined in the level playfield,
14377          not the player element that corresponds to the runtime player number
14378          (example: a level that contains EL_PLAYER_3 as the only player would
14379          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14380       int player_element = PLAYERINFO(xx, yy)->initial_element;
14381
14382       CheckElementChangeBySide(x, y, center_element, player_element,
14383                                CE_TOUCHING_X, border_side);
14384     }
14385 #endif
14386   }
14387 }
14388
14389 #else
14390
14391 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14392 {
14393   static int xy[4][2] =
14394   {
14395     { 0, -1 },
14396     { -1, 0 },
14397     { +1, 0 },
14398     { 0, +1 }
14399   };
14400   static int trigger_sides[4][2] =
14401   {
14402     /* center side      border side */
14403     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14404     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14405     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14406     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14407   };
14408   static int touch_dir[4] =
14409   {
14410     MV_LEFT | MV_RIGHT,
14411     MV_UP   | MV_DOWN,
14412     MV_UP   | MV_DOWN,
14413     MV_LEFT | MV_RIGHT
14414   };
14415   boolean change_center_element = FALSE;
14416   int center_element = Feld[x][y];      /* should always be non-moving! */
14417   int i;
14418
14419   for (i = 0; i < NUM_DIRECTIONS; i++)
14420   {
14421     int xx = x + xy[i][0];
14422     int yy = y + xy[i][1];
14423     int center_side = trigger_sides[i][0];
14424     int border_side = trigger_sides[i][1];
14425     int border_element;
14426
14427     if (!IN_LEV_FIELD(xx, yy))
14428       continue;
14429
14430     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14431       border_element = Feld[xx][yy];    /* may be moving! */
14432     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14433       border_element = Feld[xx][yy];
14434     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14435       border_element = MovingOrBlocked2Element(xx, yy);
14436     else
14437       continue;                 /* center and border element do not touch */
14438
14439     /* check for change of center element (but change it only once) */
14440     if (!change_center_element)
14441       change_center_element =
14442         CheckElementChangeBySide(x, y, center_element, border_element,
14443                                  CE_TOUCHING_X, border_side);
14444
14445     /* check for change of border element */
14446     CheckElementChangeBySide(xx, yy, border_element, center_element,
14447                              CE_TOUCHING_X, center_side);
14448   }
14449 }
14450
14451 #endif
14452
14453 void TestIfElementHitsCustomElement(int x, int y, int direction)
14454 {
14455   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14456   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14457   int hitx = x + dx, hity = y + dy;
14458   int hitting_element = Feld[x][y];
14459   int touched_element;
14460
14461   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14462     return;
14463
14464   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14465                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14466
14467   if (IN_LEV_FIELD(hitx, hity))
14468   {
14469     int opposite_direction = MV_DIR_OPPOSITE(direction);
14470     int hitting_side = direction;
14471     int touched_side = opposite_direction;
14472     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14473                           MovDir[hitx][hity] != direction ||
14474                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14475
14476     object_hit = TRUE;
14477
14478     if (object_hit)
14479     {
14480       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14481                                CE_HITTING_X, touched_side);
14482
14483       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14484                                CE_HIT_BY_X, hitting_side);
14485
14486       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14487                                CE_HIT_BY_SOMETHING, opposite_direction);
14488
14489 #if USE_FIX_CE_ACTION_WITH_PLAYER
14490       if (IS_PLAYER(hitx, hity))
14491       {
14492         /* use player element that is initially defined in the level playfield,
14493            not the player element that corresponds to the runtime player number
14494            (example: a level that contains EL_PLAYER_3 as the only player would
14495            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14496         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14497
14498         CheckElementChangeBySide(x, y, hitting_element, player_element,
14499                                  CE_HITTING_X, touched_side);
14500       }
14501 #endif
14502     }
14503   }
14504
14505   /* "hitting something" is also true when hitting the playfield border */
14506   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14507                            CE_HITTING_SOMETHING, direction);
14508 }
14509
14510 #if 0
14511 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14512 {
14513   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14514   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14515   int hitx = x + dx, hity = y + dy;
14516   int hitting_element = Feld[x][y];
14517   int touched_element;
14518 #if 0
14519   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14520                         !IS_FREE(hitx, hity) &&
14521                         (!IS_MOVING(hitx, hity) ||
14522                          MovDir[hitx][hity] != direction ||
14523                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14524 #endif
14525
14526   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14527     return;
14528
14529 #if 0
14530   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14531     return;
14532 #endif
14533
14534   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14535                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14536
14537   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14538                            EP_CAN_SMASH_EVERYTHING, direction);
14539
14540   if (IN_LEV_FIELD(hitx, hity))
14541   {
14542     int opposite_direction = MV_DIR_OPPOSITE(direction);
14543     int hitting_side = direction;
14544     int touched_side = opposite_direction;
14545 #if 0
14546     int touched_element = MovingOrBlocked2Element(hitx, hity);
14547 #endif
14548 #if 1
14549     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14550                           MovDir[hitx][hity] != direction ||
14551                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14552
14553     object_hit = TRUE;
14554 #endif
14555
14556     if (object_hit)
14557     {
14558       int i;
14559
14560       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14561                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14562
14563       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14564                                CE_OTHER_IS_SMASHING, touched_side);
14565
14566       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14567                                CE_OTHER_GETS_SMASHED, hitting_side);
14568     }
14569   }
14570 }
14571 #endif
14572
14573 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14574 {
14575   int i, kill_x = -1, kill_y = -1;
14576
14577   int bad_element = -1;
14578   static int test_xy[4][2] =
14579   {
14580     { 0, -1 },
14581     { -1, 0 },
14582     { +1, 0 },
14583     { 0, +1 }
14584   };
14585   static int test_dir[4] =
14586   {
14587     MV_UP,
14588     MV_LEFT,
14589     MV_RIGHT,
14590     MV_DOWN
14591   };
14592
14593   for (i = 0; i < NUM_DIRECTIONS; i++)
14594   {
14595     int test_x, test_y, test_move_dir, test_element;
14596
14597     test_x = good_x + test_xy[i][0];
14598     test_y = good_y + test_xy[i][1];
14599
14600     if (!IN_LEV_FIELD(test_x, test_y))
14601       continue;
14602
14603     test_move_dir =
14604       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14605
14606     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14607
14608     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14609        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14610     */
14611     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14612         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14613     {
14614       kill_x = test_x;
14615       kill_y = test_y;
14616       bad_element = test_element;
14617
14618       break;
14619     }
14620   }
14621
14622   if (kill_x != -1 || kill_y != -1)
14623   {
14624     if (IS_PLAYER(good_x, good_y))
14625     {
14626       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14627
14628       if (player->shield_deadly_time_left > 0 &&
14629           !IS_INDESTRUCTIBLE(bad_element))
14630         Bang(kill_x, kill_y);
14631       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14632         KillPlayer(player);
14633     }
14634     else
14635       Bang(good_x, good_y);
14636   }
14637 }
14638
14639 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14640 {
14641   int i, kill_x = -1, kill_y = -1;
14642   int bad_element = Feld[bad_x][bad_y];
14643   static int test_xy[4][2] =
14644   {
14645     { 0, -1 },
14646     { -1, 0 },
14647     { +1, 0 },
14648     { 0, +1 }
14649   };
14650   static int touch_dir[4] =
14651   {
14652     MV_LEFT | MV_RIGHT,
14653     MV_UP   | MV_DOWN,
14654     MV_UP   | MV_DOWN,
14655     MV_LEFT | MV_RIGHT
14656   };
14657   static int test_dir[4] =
14658   {
14659     MV_UP,
14660     MV_LEFT,
14661     MV_RIGHT,
14662     MV_DOWN
14663   };
14664
14665   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14666     return;
14667
14668   for (i = 0; i < NUM_DIRECTIONS; i++)
14669   {
14670     int test_x, test_y, test_move_dir, test_element;
14671
14672     test_x = bad_x + test_xy[i][0];
14673     test_y = bad_y + test_xy[i][1];
14674
14675     if (!IN_LEV_FIELD(test_x, test_y))
14676       continue;
14677
14678     test_move_dir =
14679       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14680
14681     test_element = Feld[test_x][test_y];
14682
14683     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14684        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14685     */
14686     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14687         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14688     {
14689       /* good thing is player or penguin that does not move away */
14690       if (IS_PLAYER(test_x, test_y))
14691       {
14692         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14693
14694         if (bad_element == EL_ROBOT && player->is_moving)
14695           continue;     /* robot does not kill player if he is moving */
14696
14697         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14698         {
14699           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14700             continue;           /* center and border element do not touch */
14701         }
14702
14703         kill_x = test_x;
14704         kill_y = test_y;
14705
14706         break;
14707       }
14708       else if (test_element == EL_PENGUIN)
14709       {
14710         kill_x = test_x;
14711         kill_y = test_y;
14712
14713         break;
14714       }
14715     }
14716   }
14717
14718   if (kill_x != -1 || kill_y != -1)
14719   {
14720     if (IS_PLAYER(kill_x, kill_y))
14721     {
14722       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14723
14724       if (player->shield_deadly_time_left > 0 &&
14725           !IS_INDESTRUCTIBLE(bad_element))
14726         Bang(bad_x, bad_y);
14727       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14728         KillPlayer(player);
14729     }
14730     else
14731       Bang(kill_x, kill_y);
14732   }
14733 }
14734
14735 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14736 {
14737   int bad_element = Feld[bad_x][bad_y];
14738   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14739   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14740   int test_x = bad_x + dx, test_y = bad_y + dy;
14741   int test_move_dir, test_element;
14742   int kill_x = -1, kill_y = -1;
14743
14744   if (!IN_LEV_FIELD(test_x, test_y))
14745     return;
14746
14747   test_move_dir =
14748     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14749
14750   test_element = Feld[test_x][test_y];
14751
14752   if (test_move_dir != bad_move_dir)
14753   {
14754     /* good thing can be player or penguin that does not move away */
14755     if (IS_PLAYER(test_x, test_y))
14756     {
14757       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14758
14759       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14760          player as being hit when he is moving towards the bad thing, because
14761          the "get hit by" condition would be lost after the player stops) */
14762       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14763         return;         /* player moves away from bad thing */
14764
14765       kill_x = test_x;
14766       kill_y = test_y;
14767     }
14768     else if (test_element == EL_PENGUIN)
14769     {
14770       kill_x = test_x;
14771       kill_y = test_y;
14772     }
14773   }
14774
14775   if (kill_x != -1 || kill_y != -1)
14776   {
14777     if (IS_PLAYER(kill_x, kill_y))
14778     {
14779       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14780
14781       if (player->shield_deadly_time_left > 0 &&
14782           !IS_INDESTRUCTIBLE(bad_element))
14783         Bang(bad_x, bad_y);
14784       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14785         KillPlayer(player);
14786     }
14787     else
14788       Bang(kill_x, kill_y);
14789   }
14790 }
14791
14792 void TestIfPlayerTouchesBadThing(int x, int y)
14793 {
14794   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14795 }
14796
14797 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14798 {
14799   TestIfGoodThingHitsBadThing(x, y, move_dir);
14800 }
14801
14802 void TestIfBadThingTouchesPlayer(int x, int y)
14803 {
14804   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14805 }
14806
14807 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14808 {
14809   TestIfBadThingHitsGoodThing(x, y, move_dir);
14810 }
14811
14812 void TestIfFriendTouchesBadThing(int x, int y)
14813 {
14814   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14815 }
14816
14817 void TestIfBadThingTouchesFriend(int x, int y)
14818 {
14819   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14820 }
14821
14822 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14823 {
14824   int i, kill_x = bad_x, kill_y = bad_y;
14825   static int xy[4][2] =
14826   {
14827     { 0, -1 },
14828     { -1, 0 },
14829     { +1, 0 },
14830     { 0, +1 }
14831   };
14832
14833   for (i = 0; i < NUM_DIRECTIONS; i++)
14834   {
14835     int x, y, element;
14836
14837     x = bad_x + xy[i][0];
14838     y = bad_y + xy[i][1];
14839     if (!IN_LEV_FIELD(x, y))
14840       continue;
14841
14842     element = Feld[x][y];
14843     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14844         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14845     {
14846       kill_x = x;
14847       kill_y = y;
14848       break;
14849     }
14850   }
14851
14852   if (kill_x != bad_x || kill_y != bad_y)
14853     Bang(bad_x, bad_y);
14854 }
14855
14856 void KillPlayer(struct PlayerInfo *player)
14857 {
14858   int jx = player->jx, jy = player->jy;
14859
14860   if (!player->active)
14861     return;
14862
14863 #if 0
14864   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14865          player->killed, player->active, player->reanimated);
14866 #endif
14867
14868   /* the following code was introduced to prevent an infinite loop when calling
14869      -> Bang()
14870      -> CheckTriggeredElementChangeExt()
14871      -> ExecuteCustomElementAction()
14872      -> KillPlayer()
14873      -> (infinitely repeating the above sequence of function calls)
14874      which occurs when killing the player while having a CE with the setting
14875      "kill player X when explosion of <player X>"; the solution using a new
14876      field "player->killed" was chosen for backwards compatibility, although
14877      clever use of the fields "player->active" etc. would probably also work */
14878 #if 1
14879   if (player->killed)
14880     return;
14881 #endif
14882
14883   player->killed = TRUE;
14884
14885   /* remove accessible field at the player's position */
14886   Feld[jx][jy] = EL_EMPTY;
14887
14888   /* deactivate shield (else Bang()/Explode() would not work right) */
14889   player->shield_normal_time_left = 0;
14890   player->shield_deadly_time_left = 0;
14891
14892 #if 0
14893   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14894          player->killed, player->active, player->reanimated);
14895 #endif
14896
14897   Bang(jx, jy);
14898
14899 #if 0
14900   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14901          player->killed, player->active, player->reanimated);
14902 #endif
14903
14904 #if USE_PLAYER_REANIMATION
14905 #if 1
14906   if (player->reanimated)       /* killed player may have been reanimated */
14907     player->killed = player->reanimated = FALSE;
14908   else
14909     BuryPlayer(player);
14910 #else
14911   if (player->killed)           /* player may have been reanimated */
14912     BuryPlayer(player);
14913 #endif
14914 #else
14915   BuryPlayer(player);
14916 #endif
14917 }
14918
14919 static void KillPlayerUnlessEnemyProtected(int x, int y)
14920 {
14921   if (!PLAYER_ENEMY_PROTECTED(x, y))
14922     KillPlayer(PLAYERINFO(x, y));
14923 }
14924
14925 static void KillPlayerUnlessExplosionProtected(int x, int y)
14926 {
14927   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14928     KillPlayer(PLAYERINFO(x, y));
14929 }
14930
14931 void BuryPlayer(struct PlayerInfo *player)
14932 {
14933   int jx = player->jx, jy = player->jy;
14934
14935   if (!player->active)
14936     return;
14937
14938   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14939   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14940
14941   player->GameOver = TRUE;
14942   RemovePlayer(player);
14943 }
14944
14945 void RemovePlayer(struct PlayerInfo *player)
14946 {
14947   int jx = player->jx, jy = player->jy;
14948   int i, found = FALSE;
14949
14950   player->present = FALSE;
14951   player->active = FALSE;
14952
14953   if (!ExplodeField[jx][jy])
14954     StorePlayer[jx][jy] = 0;
14955
14956   if (player->is_moving)
14957     TEST_DrawLevelField(player->last_jx, player->last_jy);
14958
14959   for (i = 0; i < MAX_PLAYERS; i++)
14960     if (stored_player[i].active)
14961       found = TRUE;
14962
14963   if (!found)
14964     AllPlayersGone = TRUE;
14965
14966   ExitX = ZX = jx;
14967   ExitY = ZY = jy;
14968 }
14969
14970 #if USE_NEW_SNAP_DELAY
14971 static void setFieldForSnapping(int x, int y, int element, int direction)
14972 {
14973   struct ElementInfo *ei = &element_info[element];
14974   int direction_bit = MV_DIR_TO_BIT(direction);
14975   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14976   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14977                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14978
14979   Feld[x][y] = EL_ELEMENT_SNAPPING;
14980   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14981
14982   ResetGfxAnimation(x, y);
14983
14984   GfxElement[x][y] = element;
14985   GfxAction[x][y] = action;
14986   GfxDir[x][y] = direction;
14987   GfxFrame[x][y] = -1;
14988 }
14989 #endif
14990
14991 /*
14992   =============================================================================
14993   checkDiagonalPushing()
14994   -----------------------------------------------------------------------------
14995   check if diagonal input device direction results in pushing of object
14996   (by checking if the alternative direction is walkable, diggable, ...)
14997   =============================================================================
14998 */
14999
15000 static boolean checkDiagonalPushing(struct PlayerInfo *player,
15001                                     int x, int y, int real_dx, int real_dy)
15002 {
15003   int jx, jy, dx, dy, xx, yy;
15004
15005   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
15006     return TRUE;
15007
15008   /* diagonal direction: check alternative direction */
15009   jx = player->jx;
15010   jy = player->jy;
15011   dx = x - jx;
15012   dy = y - jy;
15013   xx = jx + (dx == 0 ? real_dx : 0);
15014   yy = jy + (dy == 0 ? real_dy : 0);
15015
15016   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
15017 }
15018
15019 /*
15020   =============================================================================
15021   DigField()
15022   -----------------------------------------------------------------------------
15023   x, y:                 field next to player (non-diagonal) to try to dig to
15024   real_dx, real_dy:     direction as read from input device (can be diagonal)
15025   =============================================================================
15026 */
15027
15028 static int DigField(struct PlayerInfo *player,
15029                     int oldx, int oldy, int x, int y,
15030                     int real_dx, int real_dy, int mode)
15031 {
15032   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15033   boolean player_was_pushing = player->is_pushing;
15034   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15035   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15036   int jx = oldx, jy = oldy;
15037   int dx = x - jx, dy = y - jy;
15038   int nextx = x + dx, nexty = y + dy;
15039   int move_direction = (dx == -1 ? MV_LEFT  :
15040                         dx == +1 ? MV_RIGHT :
15041                         dy == -1 ? MV_UP    :
15042                         dy == +1 ? MV_DOWN  : MV_NONE);
15043   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15044   int dig_side = MV_DIR_OPPOSITE(move_direction);
15045   int old_element = Feld[jx][jy];
15046 #if USE_FIXED_DONT_RUN_INTO
15047   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15048 #else
15049   int element;
15050 #endif
15051   int collect_count;
15052
15053   if (is_player)                /* function can also be called by EL_PENGUIN */
15054   {
15055     if (player->MovPos == 0)
15056     {
15057       player->is_digging = FALSE;
15058       player->is_collecting = FALSE;
15059     }
15060
15061     if (player->MovPos == 0)    /* last pushing move finished */
15062       player->is_pushing = FALSE;
15063
15064     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
15065     {
15066       player->is_switching = FALSE;
15067       player->push_delay = -1;
15068
15069       return MP_NO_ACTION;
15070     }
15071   }
15072
15073 #if !USE_FIXED_DONT_RUN_INTO
15074   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15075     return MP_NO_ACTION;
15076 #endif
15077
15078   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15079     old_element = Back[jx][jy];
15080
15081   /* in case of element dropped at player position, check background */
15082   else if (Back[jx][jy] != EL_EMPTY &&
15083            game.engine_version >= VERSION_IDENT(2,2,0,0))
15084     old_element = Back[jx][jy];
15085
15086   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15087     return MP_NO_ACTION;        /* field has no opening in this direction */
15088
15089   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15090     return MP_NO_ACTION;        /* field has no opening in this direction */
15091
15092 #if USE_FIXED_DONT_RUN_INTO
15093   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15094   {
15095     SplashAcid(x, y);
15096
15097     Feld[jx][jy] = player->artwork_element;
15098     InitMovingField(jx, jy, MV_DOWN);
15099     Store[jx][jy] = EL_ACID;
15100     ContinueMoving(jx, jy);
15101     BuryPlayer(player);
15102
15103     return MP_DONT_RUN_INTO;
15104   }
15105 #endif
15106
15107 #if USE_FIXED_DONT_RUN_INTO
15108   if (player_can_move && DONT_RUN_INTO(element))
15109   {
15110     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15111
15112     return MP_DONT_RUN_INTO;
15113   }
15114 #endif
15115
15116 #if USE_FIXED_DONT_RUN_INTO
15117   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15118     return MP_NO_ACTION;
15119 #endif
15120
15121 #if !USE_FIXED_DONT_RUN_INTO
15122   element = Feld[x][y];
15123 #endif
15124
15125   collect_count = element_info[element].collect_count_initial;
15126
15127   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15128     return MP_NO_ACTION;
15129
15130   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15131     player_can_move = player_can_move_or_snap;
15132
15133   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15134       game.engine_version >= VERSION_IDENT(2,2,0,0))
15135   {
15136     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15137                                player->index_bit, dig_side);
15138     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15139                                         player->index_bit, dig_side);
15140
15141     if (element == EL_DC_LANDMINE)
15142       Bang(x, y);
15143
15144     if (Feld[x][y] != element)          /* field changed by snapping */
15145       return MP_ACTION;
15146
15147     return MP_NO_ACTION;
15148   }
15149
15150 #if USE_PLAYER_GRAVITY
15151   if (player->gravity && is_player && !player->is_auto_moving &&
15152       canFallDown(player) && move_direction != MV_DOWN &&
15153       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15154     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15155 #else
15156   if (game.gravity && is_player && !player->is_auto_moving &&
15157       canFallDown(player) && move_direction != MV_DOWN &&
15158       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15159     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15160 #endif
15161
15162   if (player_can_move &&
15163       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15164   {
15165     int sound_element = SND_ELEMENT(element);
15166     int sound_action = ACTION_WALKING;
15167
15168     if (IS_RND_GATE(element))
15169     {
15170       if (!player->key[RND_GATE_NR(element)])
15171         return MP_NO_ACTION;
15172     }
15173     else if (IS_RND_GATE_GRAY(element))
15174     {
15175       if (!player->key[RND_GATE_GRAY_NR(element)])
15176         return MP_NO_ACTION;
15177     }
15178     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15179     {
15180       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15181         return MP_NO_ACTION;
15182     }
15183     else if (element == EL_EXIT_OPEN ||
15184              element == EL_EM_EXIT_OPEN ||
15185 #if 1
15186              element == EL_EM_EXIT_OPENING ||
15187 #endif
15188              element == EL_STEEL_EXIT_OPEN ||
15189              element == EL_EM_STEEL_EXIT_OPEN ||
15190 #if 1
15191              element == EL_EM_STEEL_EXIT_OPENING ||
15192 #endif
15193              element == EL_SP_EXIT_OPEN ||
15194              element == EL_SP_EXIT_OPENING)
15195     {
15196       sound_action = ACTION_PASSING;    /* player is passing exit */
15197     }
15198     else if (element == EL_EMPTY)
15199     {
15200       sound_action = ACTION_MOVING;             /* nothing to walk on */
15201     }
15202
15203     /* play sound from background or player, whatever is available */
15204     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15205       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15206     else
15207       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15208   }
15209   else if (player_can_move &&
15210            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15211   {
15212     if (!ACCESS_FROM(element, opposite_direction))
15213       return MP_NO_ACTION;      /* field not accessible from this direction */
15214
15215     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15216       return MP_NO_ACTION;
15217
15218     if (IS_EM_GATE(element))
15219     {
15220       if (!player->key[EM_GATE_NR(element)])
15221         return MP_NO_ACTION;
15222     }
15223     else if (IS_EM_GATE_GRAY(element))
15224     {
15225       if (!player->key[EM_GATE_GRAY_NR(element)])
15226         return MP_NO_ACTION;
15227     }
15228     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15229     {
15230       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15231         return MP_NO_ACTION;
15232     }
15233     else if (IS_EMC_GATE(element))
15234     {
15235       if (!player->key[EMC_GATE_NR(element)])
15236         return MP_NO_ACTION;
15237     }
15238     else if (IS_EMC_GATE_GRAY(element))
15239     {
15240       if (!player->key[EMC_GATE_GRAY_NR(element)])
15241         return MP_NO_ACTION;
15242     }
15243     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15244     {
15245       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15246         return MP_NO_ACTION;
15247     }
15248     else if (element == EL_DC_GATE_WHITE ||
15249              element == EL_DC_GATE_WHITE_GRAY ||
15250              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15251     {
15252       if (player->num_white_keys == 0)
15253         return MP_NO_ACTION;
15254
15255       player->num_white_keys--;
15256     }
15257     else if (IS_SP_PORT(element))
15258     {
15259       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15260           element == EL_SP_GRAVITY_PORT_RIGHT ||
15261           element == EL_SP_GRAVITY_PORT_UP ||
15262           element == EL_SP_GRAVITY_PORT_DOWN)
15263 #if USE_PLAYER_GRAVITY
15264         player->gravity = !player->gravity;
15265 #else
15266         game.gravity = !game.gravity;
15267 #endif
15268       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15269                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15270                element == EL_SP_GRAVITY_ON_PORT_UP ||
15271                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15272 #if USE_PLAYER_GRAVITY
15273         player->gravity = TRUE;
15274 #else
15275         game.gravity = TRUE;
15276 #endif
15277       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15278                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15279                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15280                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15281 #if USE_PLAYER_GRAVITY
15282         player->gravity = FALSE;
15283 #else
15284         game.gravity = FALSE;
15285 #endif
15286     }
15287
15288     /* automatically move to the next field with double speed */
15289     player->programmed_action = move_direction;
15290
15291     if (player->move_delay_reset_counter == 0)
15292     {
15293       player->move_delay_reset_counter = 2;     /* two double speed steps */
15294
15295       DOUBLE_PLAYER_SPEED(player);
15296     }
15297
15298     PlayLevelSoundAction(x, y, ACTION_PASSING);
15299   }
15300   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15301   {
15302     RemoveField(x, y);
15303
15304     if (mode != DF_SNAP)
15305     {
15306       GfxElement[x][y] = GFX_ELEMENT(element);
15307       player->is_digging = TRUE;
15308     }
15309
15310     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15311
15312     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15313                                         player->index_bit, dig_side);
15314
15315     if (mode == DF_SNAP)
15316     {
15317 #if USE_NEW_SNAP_DELAY
15318       if (level.block_snap_field)
15319         setFieldForSnapping(x, y, element, move_direction);
15320       else
15321         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15322 #else
15323       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15324 #endif
15325
15326       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15327                                           player->index_bit, dig_side);
15328     }
15329   }
15330   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15331   {
15332     RemoveField(x, y);
15333
15334     if (is_player && mode != DF_SNAP)
15335     {
15336       GfxElement[x][y] = element;
15337       player->is_collecting = TRUE;
15338     }
15339
15340     if (element == EL_SPEED_PILL)
15341     {
15342       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15343     }
15344     else if (element == EL_EXTRA_TIME && level.time > 0)
15345     {
15346       TimeLeft += level.extra_time;
15347
15348 #if 1
15349       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15350
15351       DisplayGameControlValues();
15352 #else
15353       DrawGameValue_Time(TimeLeft);
15354 #endif
15355     }
15356     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15357     {
15358       player->shield_normal_time_left += level.shield_normal_time;
15359       if (element == EL_SHIELD_DEADLY)
15360         player->shield_deadly_time_left += level.shield_deadly_time;
15361     }
15362     else if (element == EL_DYNAMITE ||
15363              element == EL_EM_DYNAMITE ||
15364              element == EL_SP_DISK_RED)
15365     {
15366       if (player->inventory_size < MAX_INVENTORY_SIZE)
15367         player->inventory_element[player->inventory_size++] = element;
15368
15369       DrawGameDoorValues();
15370     }
15371     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15372     {
15373       player->dynabomb_count++;
15374       player->dynabombs_left++;
15375     }
15376     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15377     {
15378       player->dynabomb_size++;
15379     }
15380     else if (element == EL_DYNABOMB_INCREASE_POWER)
15381     {
15382       player->dynabomb_xl = TRUE;
15383     }
15384     else if (IS_KEY(element))
15385     {
15386       player->key[KEY_NR(element)] = TRUE;
15387
15388       DrawGameDoorValues();
15389     }
15390     else if (element == EL_DC_KEY_WHITE)
15391     {
15392       player->num_white_keys++;
15393
15394       /* display white keys? */
15395       /* DrawGameDoorValues(); */
15396     }
15397     else if (IS_ENVELOPE(element))
15398     {
15399       player->show_envelope = element;
15400     }
15401     else if (element == EL_EMC_LENSES)
15402     {
15403       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15404
15405       RedrawAllInvisibleElementsForLenses();
15406     }
15407     else if (element == EL_EMC_MAGNIFIER)
15408     {
15409       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15410
15411       RedrawAllInvisibleElementsForMagnifier();
15412     }
15413     else if (IS_DROPPABLE(element) ||
15414              IS_THROWABLE(element))     /* can be collected and dropped */
15415     {
15416       int i;
15417
15418       if (collect_count == 0)
15419         player->inventory_infinite_element = element;
15420       else
15421         for (i = 0; i < collect_count; i++)
15422           if (player->inventory_size < MAX_INVENTORY_SIZE)
15423             player->inventory_element[player->inventory_size++] = element;
15424
15425       DrawGameDoorValues();
15426     }
15427     else if (collect_count > 0)
15428     {
15429       local_player->gems_still_needed -= collect_count;
15430       if (local_player->gems_still_needed < 0)
15431         local_player->gems_still_needed = 0;
15432
15433 #if 1
15434       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15435
15436       DisplayGameControlValues();
15437 #else
15438       DrawGameValue_Emeralds(local_player->gems_still_needed);
15439 #endif
15440     }
15441
15442     RaiseScoreElement(element);
15443     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15444
15445     if (is_player)
15446       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15447                                           player->index_bit, dig_side);
15448
15449     if (mode == DF_SNAP)
15450     {
15451 #if USE_NEW_SNAP_DELAY
15452       if (level.block_snap_field)
15453         setFieldForSnapping(x, y, element, move_direction);
15454       else
15455         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15456 #else
15457       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15458 #endif
15459
15460       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15461                                           player->index_bit, dig_side);
15462     }
15463   }
15464   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15465   {
15466     if (mode == DF_SNAP && element != EL_BD_ROCK)
15467       return MP_NO_ACTION;
15468
15469     if (CAN_FALL(element) && dy)
15470       return MP_NO_ACTION;
15471
15472     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15473         !(element == EL_SPRING && level.use_spring_bug))
15474       return MP_NO_ACTION;
15475
15476     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15477         ((move_direction & MV_VERTICAL &&
15478           ((element_info[element].move_pattern & MV_LEFT &&
15479             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15480            (element_info[element].move_pattern & MV_RIGHT &&
15481             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15482          (move_direction & MV_HORIZONTAL &&
15483           ((element_info[element].move_pattern & MV_UP &&
15484             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15485            (element_info[element].move_pattern & MV_DOWN &&
15486             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15487       return MP_NO_ACTION;
15488
15489     /* do not push elements already moving away faster than player */
15490     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15491         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15492       return MP_NO_ACTION;
15493
15494     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15495     {
15496       if (player->push_delay_value == -1 || !player_was_pushing)
15497         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15498     }
15499     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15500     {
15501       if (player->push_delay_value == -1)
15502         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15503     }
15504     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15505     {
15506       if (!player->is_pushing)
15507         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15508     }
15509
15510     player->is_pushing = TRUE;
15511     player->is_active = TRUE;
15512
15513     if (!(IN_LEV_FIELD(nextx, nexty) &&
15514           (IS_FREE(nextx, nexty) ||
15515            (IS_SB_ELEMENT(element) &&
15516             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15517            (IS_CUSTOM_ELEMENT(element) &&
15518             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15519       return MP_NO_ACTION;
15520
15521     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15522       return MP_NO_ACTION;
15523
15524     if (player->push_delay == -1)       /* new pushing; restart delay */
15525       player->push_delay = 0;
15526
15527     if (player->push_delay < player->push_delay_value &&
15528         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15529         element != EL_SPRING && element != EL_BALLOON)
15530     {
15531       /* make sure that there is no move delay before next try to push */
15532       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15533         player->move_delay = 0;
15534
15535       return MP_NO_ACTION;
15536     }
15537
15538     if (IS_CUSTOM_ELEMENT(element) &&
15539         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15540     {
15541       if (!DigFieldByCE(nextx, nexty, element))
15542         return MP_NO_ACTION;
15543     }
15544
15545     if (IS_SB_ELEMENT(element))
15546     {
15547       if (element == EL_SOKOBAN_FIELD_FULL)
15548       {
15549         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15550         local_player->sokobanfields_still_needed++;
15551       }
15552
15553       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15554       {
15555         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15556         local_player->sokobanfields_still_needed--;
15557       }
15558
15559       Feld[x][y] = EL_SOKOBAN_OBJECT;
15560
15561       if (Back[x][y] == Back[nextx][nexty])
15562         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15563       else if (Back[x][y] != 0)
15564         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15565                                     ACTION_EMPTYING);
15566       else
15567         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15568                                     ACTION_FILLING);
15569
15570 #if 1
15571       if (local_player->sokobanfields_still_needed == 0 &&
15572           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15573 #else
15574       if (local_player->sokobanfields_still_needed == 0 &&
15575           game.emulation == EMU_SOKOBAN)
15576 #endif
15577       {
15578         PlayerWins(player);
15579
15580         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15581       }
15582     }
15583     else
15584       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15585
15586     InitMovingField(x, y, move_direction);
15587     GfxAction[x][y] = ACTION_PUSHING;
15588
15589     if (mode == DF_SNAP)
15590       ContinueMoving(x, y);
15591     else
15592       MovPos[x][y] = (dx != 0 ? dx : dy);
15593
15594     Pushed[x][y] = TRUE;
15595     Pushed[nextx][nexty] = TRUE;
15596
15597     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15598       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15599     else
15600       player->push_delay_value = -1;    /* get new value later */
15601
15602     /* check for element change _after_ element has been pushed */
15603     if (game.use_change_when_pushing_bug)
15604     {
15605       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15606                                  player->index_bit, dig_side);
15607       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15608                                           player->index_bit, dig_side);
15609     }
15610   }
15611   else if (IS_SWITCHABLE(element))
15612   {
15613     if (PLAYER_SWITCHING(player, x, y))
15614     {
15615       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15616                                           player->index_bit, dig_side);
15617
15618       return MP_ACTION;
15619     }
15620
15621     player->is_switching = TRUE;
15622     player->switch_x = x;
15623     player->switch_y = y;
15624
15625     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15626
15627     if (element == EL_ROBOT_WHEEL)
15628     {
15629       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15630       ZX = x;
15631       ZY = y;
15632
15633       game.robot_wheel_active = TRUE;
15634
15635       TEST_DrawLevelField(x, y);
15636     }
15637     else if (element == EL_SP_TERMINAL)
15638     {
15639       int xx, yy;
15640
15641       SCAN_PLAYFIELD(xx, yy)
15642       {
15643         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15644           Bang(xx, yy);
15645         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15646           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15647       }
15648     }
15649     else if (IS_BELT_SWITCH(element))
15650     {
15651       ToggleBeltSwitch(x, y);
15652     }
15653     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15654              element == EL_SWITCHGATE_SWITCH_DOWN ||
15655              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15656              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15657     {
15658       ToggleSwitchgateSwitch(x, y);
15659     }
15660     else if (element == EL_LIGHT_SWITCH ||
15661              element == EL_LIGHT_SWITCH_ACTIVE)
15662     {
15663       ToggleLightSwitch(x, y);
15664     }
15665     else if (element == EL_TIMEGATE_SWITCH ||
15666              element == EL_DC_TIMEGATE_SWITCH)
15667     {
15668       ActivateTimegateSwitch(x, y);
15669     }
15670     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15671              element == EL_BALLOON_SWITCH_RIGHT ||
15672              element == EL_BALLOON_SWITCH_UP    ||
15673              element == EL_BALLOON_SWITCH_DOWN  ||
15674              element == EL_BALLOON_SWITCH_NONE  ||
15675              element == EL_BALLOON_SWITCH_ANY)
15676     {
15677       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15678                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15679                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15680                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15681                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15682                              move_direction);
15683     }
15684     else if (element == EL_LAMP)
15685     {
15686       Feld[x][y] = EL_LAMP_ACTIVE;
15687       local_player->lights_still_needed--;
15688
15689       ResetGfxAnimation(x, y);
15690       TEST_DrawLevelField(x, y);
15691     }
15692     else if (element == EL_TIME_ORB_FULL)
15693     {
15694       Feld[x][y] = EL_TIME_ORB_EMPTY;
15695
15696       if (level.time > 0 || level.use_time_orb_bug)
15697       {
15698         TimeLeft += level.time_orb_time;
15699         game.no_time_limit = FALSE;
15700
15701 #if 1
15702         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15703
15704         DisplayGameControlValues();
15705 #else
15706         DrawGameValue_Time(TimeLeft);
15707 #endif
15708       }
15709
15710       ResetGfxAnimation(x, y);
15711       TEST_DrawLevelField(x, y);
15712     }
15713     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15714              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15715     {
15716       int xx, yy;
15717
15718       game.ball_state = !game.ball_state;
15719
15720       SCAN_PLAYFIELD(xx, yy)
15721       {
15722         int e = Feld[xx][yy];
15723
15724         if (game.ball_state)
15725         {
15726           if (e == EL_EMC_MAGIC_BALL)
15727             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15728           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15729             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15730         }
15731         else
15732         {
15733           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15734             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15735           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15736             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15737         }
15738       }
15739     }
15740
15741     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15742                                         player->index_bit, dig_side);
15743
15744     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15745                                         player->index_bit, dig_side);
15746
15747     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15748                                         player->index_bit, dig_side);
15749
15750     return MP_ACTION;
15751   }
15752   else
15753   {
15754     if (!PLAYER_SWITCHING(player, x, y))
15755     {
15756       player->is_switching = TRUE;
15757       player->switch_x = x;
15758       player->switch_y = y;
15759
15760       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15761                                  player->index_bit, dig_side);
15762       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15763                                           player->index_bit, dig_side);
15764
15765       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15766                                  player->index_bit, dig_side);
15767       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15768                                           player->index_bit, dig_side);
15769     }
15770
15771     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15772                                player->index_bit, dig_side);
15773     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15774                                         player->index_bit, dig_side);
15775
15776     return MP_NO_ACTION;
15777   }
15778
15779   player->push_delay = -1;
15780
15781   if (is_player)                /* function can also be called by EL_PENGUIN */
15782   {
15783     if (Feld[x][y] != element)          /* really digged/collected something */
15784     {
15785       player->is_collecting = !player->is_digging;
15786       player->is_active = TRUE;
15787     }
15788   }
15789
15790   return MP_MOVING;
15791 }
15792
15793 static boolean DigFieldByCE(int x, int y, int digging_element)
15794 {
15795   int element = Feld[x][y];
15796
15797   if (!IS_FREE(x, y))
15798   {
15799     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15800                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15801                   ACTION_BREAKING);
15802
15803     /* no element can dig solid indestructible elements */
15804     if (IS_INDESTRUCTIBLE(element) &&
15805         !IS_DIGGABLE(element) &&
15806         !IS_COLLECTIBLE(element))
15807       return FALSE;
15808
15809     if (AmoebaNr[x][y] &&
15810         (element == EL_AMOEBA_FULL ||
15811          element == EL_BD_AMOEBA ||
15812          element == EL_AMOEBA_GROWING))
15813     {
15814       AmoebaCnt[AmoebaNr[x][y]]--;
15815       AmoebaCnt2[AmoebaNr[x][y]]--;
15816     }
15817
15818     if (IS_MOVING(x, y))
15819       RemoveMovingField(x, y);
15820     else
15821     {
15822       RemoveField(x, y);
15823       TEST_DrawLevelField(x, y);
15824     }
15825
15826     /* if digged element was about to explode, prevent the explosion */
15827     ExplodeField[x][y] = EX_TYPE_NONE;
15828
15829     PlayLevelSoundAction(x, y, action);
15830   }
15831
15832   Store[x][y] = EL_EMPTY;
15833
15834 #if 1
15835   /* this makes it possible to leave the removed element again */
15836   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15837     Store[x][y] = element;
15838 #else
15839   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15840   {
15841     int move_leave_element = element_info[digging_element].move_leave_element;
15842
15843     /* this makes it possible to leave the removed element again */
15844     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15845                    element : move_leave_element);
15846   }
15847 #endif
15848
15849   return TRUE;
15850 }
15851
15852 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15853 {
15854   int jx = player->jx, jy = player->jy;
15855   int x = jx + dx, y = jy + dy;
15856   int snap_direction = (dx == -1 ? MV_LEFT  :
15857                         dx == +1 ? MV_RIGHT :
15858                         dy == -1 ? MV_UP    :
15859                         dy == +1 ? MV_DOWN  : MV_NONE);
15860   boolean can_continue_snapping = (level.continuous_snapping &&
15861                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15862
15863   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15864     return FALSE;
15865
15866   if (!player->active || !IN_LEV_FIELD(x, y))
15867     return FALSE;
15868
15869   if (dx && dy)
15870     return FALSE;
15871
15872   if (!dx && !dy)
15873   {
15874     if (player->MovPos == 0)
15875       player->is_pushing = FALSE;
15876
15877     player->is_snapping = FALSE;
15878
15879     if (player->MovPos == 0)
15880     {
15881       player->is_moving = FALSE;
15882       player->is_digging = FALSE;
15883       player->is_collecting = FALSE;
15884     }
15885
15886     return FALSE;
15887   }
15888
15889 #if USE_NEW_CONTINUOUS_SNAPPING
15890   /* prevent snapping with already pressed snap key when not allowed */
15891   if (player->is_snapping && !can_continue_snapping)
15892     return FALSE;
15893 #else
15894   if (player->is_snapping)
15895     return FALSE;
15896 #endif
15897
15898   player->MovDir = snap_direction;
15899
15900   if (player->MovPos == 0)
15901   {
15902     player->is_moving = FALSE;
15903     player->is_digging = FALSE;
15904     player->is_collecting = FALSE;
15905   }
15906
15907   player->is_dropping = FALSE;
15908   player->is_dropping_pressed = FALSE;
15909   player->drop_pressed_delay = 0;
15910
15911   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15912     return FALSE;
15913
15914   player->is_snapping = TRUE;
15915   player->is_active = TRUE;
15916
15917   if (player->MovPos == 0)
15918   {
15919     player->is_moving = FALSE;
15920     player->is_digging = FALSE;
15921     player->is_collecting = FALSE;
15922   }
15923
15924   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15925     TEST_DrawLevelField(player->last_jx, player->last_jy);
15926
15927   TEST_DrawLevelField(x, y);
15928
15929   return TRUE;
15930 }
15931
15932 static boolean DropElement(struct PlayerInfo *player)
15933 {
15934   int old_element, new_element;
15935   int dropx = player->jx, dropy = player->jy;
15936   int drop_direction = player->MovDir;
15937   int drop_side = drop_direction;
15938 #if 1
15939   int drop_element = get_next_dropped_element(player);
15940 #else
15941   int drop_element = (player->inventory_size > 0 ?
15942                       player->inventory_element[player->inventory_size - 1] :
15943                       player->inventory_infinite_element != EL_UNDEFINED ?
15944                       player->inventory_infinite_element :
15945                       player->dynabombs_left > 0 ?
15946                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15947                       EL_UNDEFINED);
15948 #endif
15949
15950   player->is_dropping_pressed = TRUE;
15951
15952   /* do not drop an element on top of another element; when holding drop key
15953      pressed without moving, dropped element must move away before the next
15954      element can be dropped (this is especially important if the next element
15955      is dynamite, which can be placed on background for historical reasons) */
15956   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15957     return MP_ACTION;
15958
15959   if (IS_THROWABLE(drop_element))
15960   {
15961     dropx += GET_DX_FROM_DIR(drop_direction);
15962     dropy += GET_DY_FROM_DIR(drop_direction);
15963
15964     if (!IN_LEV_FIELD(dropx, dropy))
15965       return FALSE;
15966   }
15967
15968   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15969   new_element = drop_element;           /* default: no change when dropping */
15970
15971   /* check if player is active, not moving and ready to drop */
15972   if (!player->active || player->MovPos || player->drop_delay > 0)
15973     return FALSE;
15974
15975   /* check if player has anything that can be dropped */
15976   if (new_element == EL_UNDEFINED)
15977     return FALSE;
15978
15979   /* check if drop key was pressed long enough for EM style dynamite */
15980   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15981     return FALSE;
15982
15983   /* check if anything can be dropped at the current position */
15984   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15985     return FALSE;
15986
15987   /* collected custom elements can only be dropped on empty fields */
15988   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15989     return FALSE;
15990
15991   if (old_element != EL_EMPTY)
15992     Back[dropx][dropy] = old_element;   /* store old element on this field */
15993
15994   ResetGfxAnimation(dropx, dropy);
15995   ResetRandomAnimationValue(dropx, dropy);
15996
15997   if (player->inventory_size > 0 ||
15998       player->inventory_infinite_element != EL_UNDEFINED)
15999   {
16000     if (player->inventory_size > 0)
16001     {
16002       player->inventory_size--;
16003
16004       DrawGameDoorValues();
16005
16006       if (new_element == EL_DYNAMITE)
16007         new_element = EL_DYNAMITE_ACTIVE;
16008       else if (new_element == EL_EM_DYNAMITE)
16009         new_element = EL_EM_DYNAMITE_ACTIVE;
16010       else if (new_element == EL_SP_DISK_RED)
16011         new_element = EL_SP_DISK_RED_ACTIVE;
16012     }
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     /* needed if previous element just changed to "empty" in the last frame */
16023     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16024
16025     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16026                                player->index_bit, drop_side);
16027     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16028                                         CE_PLAYER_DROPS_X,
16029                                         player->index_bit, drop_side);
16030
16031     TestIfElementTouchesCustomElement(dropx, dropy);
16032   }
16033   else          /* player is dropping a dyna bomb */
16034   {
16035     player->dynabombs_left--;
16036
16037     Feld[dropx][dropy] = new_element;
16038
16039     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16040       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16041                           el2img(Feld[dropx][dropy]), 0);
16042
16043     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16044   }
16045
16046   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16047     InitField_WithBug1(dropx, dropy, FALSE);
16048
16049   new_element = Feld[dropx][dropy];     /* element might have changed */
16050
16051   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16052       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16053   {
16054 #if 0
16055     int move_direction;
16056     int nextx, nexty;
16057 #endif
16058
16059     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16060       MovDir[dropx][dropy] = drop_direction;
16061
16062 #if 0
16063     move_direction = MovDir[dropx][dropy];
16064     nextx = dropx + GET_DX_FROM_DIR(move_direction);
16065     nexty = dropy + GET_DY_FROM_DIR(move_direction);
16066 #endif
16067
16068     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16069
16070 #if USE_FIX_IMPACT_COLLISION
16071     /* do not cause impact style collision by dropping elements that can fall */
16072     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16073 #else
16074     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16075 #endif
16076   }
16077
16078   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16079   player->is_dropping = TRUE;
16080
16081   player->drop_pressed_delay = 0;
16082   player->is_dropping_pressed = FALSE;
16083
16084   player->drop_x = dropx;
16085   player->drop_y = dropy;
16086
16087   return TRUE;
16088 }
16089
16090 /* ------------------------------------------------------------------------- */
16091 /* game sound playing functions                                              */
16092 /* ------------------------------------------------------------------------- */
16093
16094 static int *loop_sound_frame = NULL;
16095 static int *loop_sound_volume = NULL;
16096
16097 void InitPlayLevelSound()
16098 {
16099   int num_sounds = getSoundListSize();
16100
16101   checked_free(loop_sound_frame);
16102   checked_free(loop_sound_volume);
16103
16104   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16105   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16106 }
16107
16108 static void PlayLevelSound(int x, int y, int nr)
16109 {
16110   int sx = SCREENX(x), sy = SCREENY(y);
16111   int volume, stereo_position;
16112   int max_distance = 8;
16113   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16114
16115   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16116       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16117     return;
16118
16119   if (!IN_LEV_FIELD(x, y) ||
16120       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16121       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16122     return;
16123
16124   volume = SOUND_MAX_VOLUME;
16125
16126   if (!IN_SCR_FIELD(sx, sy))
16127   {
16128     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16129     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16130
16131     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16132   }
16133
16134   stereo_position = (SOUND_MAX_LEFT +
16135                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16136                      (SCR_FIELDX + 2 * max_distance));
16137
16138   if (IS_LOOP_SOUND(nr))
16139   {
16140     /* This assures that quieter loop sounds do not overwrite louder ones,
16141        while restarting sound volume comparison with each new game frame. */
16142
16143     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16144       return;
16145
16146     loop_sound_volume[nr] = volume;
16147     loop_sound_frame[nr] = FrameCounter;
16148   }
16149
16150   PlaySoundExt(nr, volume, stereo_position, type);
16151 }
16152
16153 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16154 {
16155   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16156                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16157                  y < LEVELY(BY1) ? LEVELY(BY1) :
16158                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16159                  sound_action);
16160 }
16161
16162 static void PlayLevelSoundAction(int x, int y, int action)
16163 {
16164   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16165 }
16166
16167 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16168 {
16169   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16170
16171   if (sound_effect != SND_UNDEFINED)
16172     PlayLevelSound(x, y, sound_effect);
16173 }
16174
16175 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16176                                               int action)
16177 {
16178   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16179
16180   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16181     PlayLevelSound(x, y, sound_effect);
16182 }
16183
16184 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16185 {
16186   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16187
16188   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16189     PlayLevelSound(x, y, sound_effect);
16190 }
16191
16192 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16193 {
16194   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16195
16196   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16197     StopSound(sound_effect);
16198 }
16199
16200 static void PlayLevelMusic()
16201 {
16202   if (levelset.music[level_nr] != MUS_UNDEFINED)
16203     PlayMusic(levelset.music[level_nr]);        /* from config file */
16204   else
16205     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16206 }
16207
16208 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16209 {
16210   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16211   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16212   int x = xx - 1 - offset;
16213   int y = yy - 1 - offset;
16214
16215   switch (sample)
16216   {
16217     case SAMPLE_blank:
16218       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16219       break;
16220
16221     case SAMPLE_roll:
16222       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16223       break;
16224
16225     case SAMPLE_stone:
16226       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16227       break;
16228
16229     case SAMPLE_nut:
16230       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16231       break;
16232
16233     case SAMPLE_crack:
16234       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16235       break;
16236
16237     case SAMPLE_bug:
16238       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16239       break;
16240
16241     case SAMPLE_tank:
16242       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16243       break;
16244
16245     case SAMPLE_android_clone:
16246       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16247       break;
16248
16249     case SAMPLE_android_move:
16250       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16251       break;
16252
16253     case SAMPLE_spring:
16254       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16255       break;
16256
16257     case SAMPLE_slurp:
16258       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16259       break;
16260
16261     case SAMPLE_eater:
16262       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16263       break;
16264
16265     case SAMPLE_eater_eat:
16266       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16267       break;
16268
16269     case SAMPLE_alien:
16270       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16271       break;
16272
16273     case SAMPLE_collect:
16274       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16275       break;
16276
16277     case SAMPLE_diamond:
16278       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16279       break;
16280
16281     case SAMPLE_squash:
16282       /* !!! CHECK THIS !!! */
16283 #if 1
16284       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16285 #else
16286       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16287 #endif
16288       break;
16289
16290     case SAMPLE_wonderfall:
16291       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16292       break;
16293
16294     case SAMPLE_drip:
16295       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16296       break;
16297
16298     case SAMPLE_push:
16299       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16300       break;
16301
16302     case SAMPLE_dirt:
16303       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16304       break;
16305
16306     case SAMPLE_acid:
16307       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16308       break;
16309
16310     case SAMPLE_ball:
16311       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16312       break;
16313
16314     case SAMPLE_grow:
16315       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16316       break;
16317
16318     case SAMPLE_wonder:
16319       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16320       break;
16321
16322     case SAMPLE_door:
16323       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16324       break;
16325
16326     case SAMPLE_exit_open:
16327       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16328       break;
16329
16330     case SAMPLE_exit_leave:
16331       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16332       break;
16333
16334     case SAMPLE_dynamite:
16335       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16336       break;
16337
16338     case SAMPLE_tick:
16339       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16340       break;
16341
16342     case SAMPLE_press:
16343       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16344       break;
16345
16346     case SAMPLE_wheel:
16347       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16348       break;
16349
16350     case SAMPLE_boom:
16351       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16352       break;
16353
16354     case SAMPLE_die:
16355       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16356       break;
16357
16358     case SAMPLE_time:
16359       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16360       break;
16361
16362     default:
16363       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16364       break;
16365   }
16366 }
16367
16368 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16369 {
16370   int element = map_element_SP_to_RND(element_sp);
16371   int action = map_action_SP_to_RND(action_sp);
16372   int offset = (setup.sp_show_border_elements ? 0 : 1);
16373   int x = xx - offset;
16374   int y = yy - offset;
16375
16376 #if 0
16377   printf("::: %d -> %d\n", element_sp, action_sp);
16378 #endif
16379
16380   PlayLevelSoundElementAction(x, y, element, action);
16381 }
16382
16383 void RaiseScore(int value)
16384 {
16385   local_player->score += value;
16386
16387 #if 1
16388   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16389
16390   DisplayGameControlValues();
16391 #else
16392   DrawGameValue_Score(local_player->score);
16393 #endif
16394 }
16395
16396 void RaiseScoreElement(int element)
16397 {
16398   switch (element)
16399   {
16400     case EL_EMERALD:
16401     case EL_BD_DIAMOND:
16402     case EL_EMERALD_YELLOW:
16403     case EL_EMERALD_RED:
16404     case EL_EMERALD_PURPLE:
16405     case EL_SP_INFOTRON:
16406       RaiseScore(level.score[SC_EMERALD]);
16407       break;
16408     case EL_DIAMOND:
16409       RaiseScore(level.score[SC_DIAMOND]);
16410       break;
16411     case EL_CRYSTAL:
16412       RaiseScore(level.score[SC_CRYSTAL]);
16413       break;
16414     case EL_PEARL:
16415       RaiseScore(level.score[SC_PEARL]);
16416       break;
16417     case EL_BUG:
16418     case EL_BD_BUTTERFLY:
16419     case EL_SP_ELECTRON:
16420       RaiseScore(level.score[SC_BUG]);
16421       break;
16422     case EL_SPACESHIP:
16423     case EL_BD_FIREFLY:
16424     case EL_SP_SNIKSNAK:
16425       RaiseScore(level.score[SC_SPACESHIP]);
16426       break;
16427     case EL_YAMYAM:
16428     case EL_DARK_YAMYAM:
16429       RaiseScore(level.score[SC_YAMYAM]);
16430       break;
16431     case EL_ROBOT:
16432       RaiseScore(level.score[SC_ROBOT]);
16433       break;
16434     case EL_PACMAN:
16435       RaiseScore(level.score[SC_PACMAN]);
16436       break;
16437     case EL_NUT:
16438       RaiseScore(level.score[SC_NUT]);
16439       break;
16440     case EL_DYNAMITE:
16441     case EL_EM_DYNAMITE:
16442     case EL_SP_DISK_RED:
16443     case EL_DYNABOMB_INCREASE_NUMBER:
16444     case EL_DYNABOMB_INCREASE_SIZE:
16445     case EL_DYNABOMB_INCREASE_POWER:
16446       RaiseScore(level.score[SC_DYNAMITE]);
16447       break;
16448     case EL_SHIELD_NORMAL:
16449     case EL_SHIELD_DEADLY:
16450       RaiseScore(level.score[SC_SHIELD]);
16451       break;
16452     case EL_EXTRA_TIME:
16453       RaiseScore(level.extra_time_score);
16454       break;
16455     case EL_KEY_1:
16456     case EL_KEY_2:
16457     case EL_KEY_3:
16458     case EL_KEY_4:
16459     case EL_EM_KEY_1:
16460     case EL_EM_KEY_2:
16461     case EL_EM_KEY_3:
16462     case EL_EM_KEY_4:
16463     case EL_EMC_KEY_5:
16464     case EL_EMC_KEY_6:
16465     case EL_EMC_KEY_7:
16466     case EL_EMC_KEY_8:
16467     case EL_DC_KEY_WHITE:
16468       RaiseScore(level.score[SC_KEY]);
16469       break;
16470     default:
16471       RaiseScore(element_info[element].collect_score);
16472       break;
16473   }
16474 }
16475
16476 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16477 {
16478   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16479   {
16480 #if 1
16481     /* closing door required in case of envelope style request dialogs */
16482     if (!skip_request)
16483       CloseDoor(DOOR_CLOSE_1);
16484 #endif
16485
16486 #if defined(NETWORK_AVALIABLE)
16487     if (options.network)
16488       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16489     else
16490 #endif
16491     {
16492       if (quick_quit)
16493       {
16494 #if 1
16495
16496 #if 1
16497         FadeSkipNextFadeIn();
16498 #else
16499         fading = fading_none;
16500 #endif
16501
16502 #else
16503         OpenDoor(DOOR_CLOSE_1);
16504 #endif
16505
16506         game_status = GAME_MODE_MAIN;
16507
16508 #if 1
16509         DrawAndFadeInMainMenu(REDRAW_FIELD);
16510 #else
16511         DrawMainMenu();
16512 #endif
16513       }
16514       else
16515       {
16516 #if 0
16517         FadeOut(REDRAW_FIELD);
16518 #endif
16519
16520         game_status = GAME_MODE_MAIN;
16521
16522         DrawAndFadeInMainMenu(REDRAW_FIELD);
16523       }
16524     }
16525   }
16526   else          /* continue playing the game */
16527   {
16528     if (tape.playing && tape.deactivate_display)
16529       TapeDeactivateDisplayOff(TRUE);
16530
16531     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16532
16533     if (tape.playing && tape.deactivate_display)
16534       TapeDeactivateDisplayOn();
16535   }
16536 }
16537
16538 void RequestQuitGame(boolean ask_if_really_quit)
16539 {
16540   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16541   boolean skip_request = AllPlayersGone || quick_quit;
16542
16543   RequestQuitGameExt(skip_request, quick_quit,
16544                      "Do you really want to quit the game?");
16545 }
16546
16547
16548 /* ------------------------------------------------------------------------- */
16549 /* random generator functions                                                */
16550 /* ------------------------------------------------------------------------- */
16551
16552 unsigned int InitEngineRandom_RND(int seed)
16553 {
16554   game.num_random_calls = 0;
16555
16556 #if 0
16557   unsigned int rnd_seed = InitEngineRandom(seed);
16558
16559   printf("::: START RND: %d\n", rnd_seed);
16560
16561   return rnd_seed;
16562 #else
16563
16564   return InitEngineRandom(seed);
16565
16566 #endif
16567
16568 }
16569
16570 unsigned int RND(int max)
16571 {
16572   if (max > 0)
16573   {
16574     game.num_random_calls++;
16575
16576     return GetEngineRandom(max);
16577   }
16578
16579   return 0;
16580 }
16581
16582
16583 /* ------------------------------------------------------------------------- */
16584 /* game engine snapshot handling functions                                   */
16585 /* ------------------------------------------------------------------------- */
16586
16587 struct EngineSnapshotInfo
16588 {
16589   /* runtime values for custom element collect score */
16590   int collect_score[NUM_CUSTOM_ELEMENTS];
16591
16592   /* runtime values for group element choice position */
16593   int choice_pos[NUM_GROUP_ELEMENTS];
16594
16595   /* runtime values for belt position animations */
16596   int belt_graphic[4][NUM_BELT_PARTS];
16597   int belt_anim_mode[4][NUM_BELT_PARTS];
16598 };
16599
16600 static struct EngineSnapshotInfo engine_snapshot_rnd;
16601 static char *snapshot_level_identifier = NULL;
16602 static int snapshot_level_nr = -1;
16603
16604 static void SaveEngineSnapshotValues_RND()
16605 {
16606   static int belt_base_active_element[4] =
16607   {
16608     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16609     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16610     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16611     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16612   };
16613   int i, j;
16614
16615   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16616   {
16617     int element = EL_CUSTOM_START + i;
16618
16619     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16620   }
16621
16622   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16623   {
16624     int element = EL_GROUP_START + i;
16625
16626     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16627   }
16628
16629   for (i = 0; i < 4; i++)
16630   {
16631     for (j = 0; j < NUM_BELT_PARTS; j++)
16632     {
16633       int element = belt_base_active_element[i] + j;
16634       int graphic = el2img(element);
16635       int anim_mode = graphic_info[graphic].anim_mode;
16636
16637       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16638       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16639     }
16640   }
16641 }
16642
16643 static void LoadEngineSnapshotValues_RND()
16644 {
16645   unsigned int num_random_calls = game.num_random_calls;
16646   int i, j;
16647
16648   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16649   {
16650     int element = EL_CUSTOM_START + i;
16651
16652     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16653   }
16654
16655   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16656   {
16657     int element = EL_GROUP_START + i;
16658
16659     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16660   }
16661
16662   for (i = 0; i < 4; i++)
16663   {
16664     for (j = 0; j < NUM_BELT_PARTS; j++)
16665     {
16666       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16667       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16668
16669       graphic_info[graphic].anim_mode = anim_mode;
16670     }
16671   }
16672
16673   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16674   {
16675     InitRND(tape.random_seed);
16676     for (i = 0; i < num_random_calls; i++)
16677       RND(1);
16678   }
16679
16680   if (game.num_random_calls != num_random_calls)
16681   {
16682     Error(ERR_INFO, "number of random calls out of sync");
16683     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16684     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16685     Error(ERR_EXIT, "this should not happen -- please debug");
16686   }
16687 }
16688
16689 void SaveEngineSnapshot()
16690 {
16691   /* do not save snapshots from editor */
16692   if (level_editor_test_game)
16693     return;
16694
16695   /* free previous snapshot buffers, if needed */
16696   FreeEngineSnapshotBuffers();
16697
16698   /* copy some special values to a structure better suited for the snapshot */
16699
16700   SaveEngineSnapshotValues_RND();
16701   SaveEngineSnapshotValues_EM();
16702   SaveEngineSnapshotValues_SP();
16703
16704   /* save values stored in special snapshot structure */
16705
16706   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16707   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16708   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16709
16710   /* save further RND engine values */
16711
16712   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16713   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16714   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16715
16716   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16717   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16718   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16719   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16720
16721   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16722   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16723   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16724   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16725   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16726
16727   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16728   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16729   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16730
16731   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16732
16733   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16734
16735   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16736   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16737
16738   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16739   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16740   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16741   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16742   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16743   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16744   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16745   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16746   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16747   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16748   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16749   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16750   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16751   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16752   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16753   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16754   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16755   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16756
16757   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16758   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16759
16760   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16761   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16762   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16763
16764   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16765   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16766
16767   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16768   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16769   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16770   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16771   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16772
16773   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16774   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16775
16776   /* save level identification information */
16777
16778   setString(&snapshot_level_identifier, leveldir_current->identifier);
16779   snapshot_level_nr = level_nr;
16780
16781 #if 0
16782   ListNode *node = engine_snapshot_list_rnd;
16783   int num_bytes = 0;
16784
16785   while (node != NULL)
16786   {
16787     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16788
16789     node = node->next;
16790   }
16791
16792   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16793 #endif
16794 }
16795
16796 void LoadEngineSnapshot()
16797 {
16798   /* restore generically stored snapshot buffers */
16799
16800   LoadEngineSnapshotBuffers();
16801
16802   /* restore special values from snapshot structure */
16803
16804   LoadEngineSnapshotValues_RND();
16805   LoadEngineSnapshotValues_EM();
16806   LoadEngineSnapshotValues_SP();
16807 }
16808
16809 boolean CheckEngineSnapshot()
16810 {
16811   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16812           snapshot_level_nr == level_nr);
16813 }
16814
16815
16816 /* ---------- new game button stuff ---------------------------------------- */
16817
16818 static struct
16819 {
16820   int graphic;
16821   struct Rect *pos;
16822   int gadget_id;
16823   char *infotext;
16824 } gamebutton_info[NUM_GAME_BUTTONS] =
16825 {
16826   {
16827     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16828     GAME_CTRL_ID_STOP,                  "stop game"
16829   },
16830   {
16831     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16832     GAME_CTRL_ID_PAUSE,                 "pause game"
16833   },
16834   {
16835     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16836     GAME_CTRL_ID_PLAY,                  "play game"
16837   },
16838   {
16839     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16840     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16841   },
16842   {
16843     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16844     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16845   },
16846   {
16847     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16848     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16849   },
16850   {
16851     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
16852     GAME_CTRL_ID_SAVE,                  "save game"
16853   },
16854   {
16855     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
16856     GAME_CTRL_ID_LOAD,                  "load game"
16857   }
16858 };
16859
16860 void CreateGameButtons()
16861 {
16862   int i;
16863
16864   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16865   {
16866     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16867     struct Rect *pos = gamebutton_info[i].pos;
16868     struct GadgetInfo *gi;
16869     int button_type;
16870     boolean checked;
16871     unsigned int event_mask;
16872     int base_x = (tape.show_game_buttons ? VX : DX);
16873     int base_y = (tape.show_game_buttons ? VY : DY);
16874     int gd_x   = gfx->src_x;
16875     int gd_y   = gfx->src_y;
16876     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16877     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16878     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16879     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16880     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16881     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16882     int id = i;
16883
16884     if (gfx->bitmap == NULL)
16885     {
16886       game_gadget[id] = NULL;
16887
16888       continue;
16889     }
16890
16891     if (id == GAME_CTRL_ID_STOP ||
16892         id == GAME_CTRL_ID_PAUSE ||
16893         id == GAME_CTRL_ID_PLAY ||
16894         id == GAME_CTRL_ID_SAVE ||
16895         id == GAME_CTRL_ID_LOAD)
16896     {
16897       button_type = GD_TYPE_NORMAL_BUTTON;
16898       checked = FALSE;
16899       event_mask = GD_EVENT_RELEASED;
16900     }
16901     else
16902     {
16903       button_type = GD_TYPE_CHECK_BUTTON;
16904       checked =
16905         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16906          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16907          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16908       event_mask = GD_EVENT_PRESSED;
16909     }
16910
16911     gi = CreateGadget(GDI_CUSTOM_ID, id,
16912                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16913                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
16914                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
16915                       GDI_WIDTH, gfx->width,
16916                       GDI_HEIGHT, gfx->height,
16917                       GDI_TYPE, button_type,
16918                       GDI_STATE, GD_BUTTON_UNPRESSED,
16919                       GDI_CHECKED, checked,
16920                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16921                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16922                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16923                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16924                       GDI_DIRECT_DRAW, FALSE,
16925                       GDI_EVENT_MASK, event_mask,
16926                       GDI_CALLBACK_ACTION, HandleGameButtons,
16927                       GDI_END);
16928
16929     if (gi == NULL)
16930       Error(ERR_EXIT, "cannot create gadget");
16931
16932     game_gadget[id] = gi;
16933   }
16934 }
16935
16936 void FreeGameButtons()
16937 {
16938   int i;
16939
16940   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16941     FreeGadget(game_gadget[i]);
16942 }
16943
16944 void MapGameButtons()
16945 {
16946   int i;
16947
16948   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16949     MapGadget(game_gadget[i]);
16950 }
16951
16952 void UnmapGameButtons()
16953 {
16954   int i;
16955
16956   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16957     UnmapGadget(game_gadget[i]);
16958 }
16959
16960 void RedrawGameButtons()
16961 {
16962   int i;
16963
16964   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16965     RedrawGadget(game_gadget[i]);
16966 }
16967
16968 static void HandleGameButtonsExt(int id)
16969 {
16970   boolean handle_game_buttons =
16971     (game_status == GAME_MODE_PLAYING ||
16972      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16973
16974   if (!handle_game_buttons)
16975     return;
16976
16977   switch (id)
16978   {
16979     case GAME_CTRL_ID_STOP:
16980       if (game_status == GAME_MODE_MAIN)
16981         break;
16982
16983       if (tape.playing)
16984         TapeStop();
16985       else
16986         RequestQuitGame(TRUE);
16987
16988       break;
16989
16990     case GAME_CTRL_ID_PAUSE:
16991       if (options.network && game_status == GAME_MODE_PLAYING)
16992       {
16993 #if defined(NETWORK_AVALIABLE)
16994         if (tape.pausing)
16995           SendToServer_ContinuePlaying();
16996         else
16997           SendToServer_PausePlaying();
16998 #endif
16999       }
17000       else
17001         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17002       break;
17003
17004     case GAME_CTRL_ID_PLAY:
17005       if (game_status == GAME_MODE_MAIN)
17006       {
17007         StartGameActions(options.network, setup.autorecord, level.random_seed);
17008       }
17009       else if (tape.pausing)
17010       {
17011 #if defined(NETWORK_AVALIABLE)
17012         if (options.network)
17013           SendToServer_ContinuePlaying();
17014         else
17015 #endif
17016         {
17017           tape.pausing = FALSE;
17018           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
17019         }
17020       }
17021       break;
17022
17023     case SOUND_CTRL_ID_MUSIC:
17024       if (setup.sound_music)
17025       { 
17026         setup.sound_music = FALSE;
17027
17028         FadeMusic();
17029       }
17030       else if (audio.music_available)
17031       { 
17032         setup.sound = setup.sound_music = TRUE;
17033
17034         SetAudioMode(setup.sound);
17035
17036         PlayLevelMusic();
17037       }
17038       break;
17039
17040     case SOUND_CTRL_ID_LOOPS:
17041       if (setup.sound_loops)
17042         setup.sound_loops = FALSE;
17043       else if (audio.loops_available)
17044       {
17045         setup.sound = setup.sound_loops = TRUE;
17046
17047         SetAudioMode(setup.sound);
17048       }
17049       break;
17050
17051     case SOUND_CTRL_ID_SIMPLE:
17052       if (setup.sound_simple)
17053         setup.sound_simple = FALSE;
17054       else if (audio.sound_available)
17055       {
17056         setup.sound = setup.sound_simple = TRUE;
17057
17058         SetAudioMode(setup.sound);
17059       }
17060       break;
17061
17062     case GAME_CTRL_ID_SAVE:
17063       TapeQuickSave();
17064       break;
17065
17066     case GAME_CTRL_ID_LOAD:
17067       TapeQuickLoad();
17068       break;
17069
17070     default:
17071       break;
17072   }
17073 }
17074
17075 static void HandleGameButtons(struct GadgetInfo *gi)
17076 {
17077   HandleGameButtonsExt(gi->custom_id);
17078 }
17079
17080 void HandleSoundButtonKeys(Key key)
17081 {
17082 #if 1
17083   if (key == setup.shortcut.sound_simple)
17084     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17085   else if (key == setup.shortcut.sound_loops)
17086     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17087   else if (key == setup.shortcut.sound_music)
17088     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17089 #else
17090   if (key == setup.shortcut.sound_simple)
17091     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17092   else if (key == setup.shortcut.sound_loops)
17093     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17094   else if (key == setup.shortcut.sound_music)
17095     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17096 #endif
17097 }