rnd-20140331-2-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   game_status = GAME_MODE_PLAYING;
3538 #endif
3539
3540 #if 1
3541
3542   StopAnimation();
3543
3544   if (!game.restart_level)
3545     CloseDoor(DOOR_CLOSE_1);
3546
3547 #if 1
3548   if (level_editor_test_game)
3549     FadeSkipNextFadeIn();
3550   else
3551     FadeSetEnterScreen();
3552 #else
3553   if (level_editor_test_game)
3554     fading = fading_none;
3555   else
3556     fading = menu.destination;
3557 #endif
3558
3559 #if 1
3560   FadeOut(REDRAW_FIELD);
3561 #else
3562   if (do_fading)
3563     FadeOut(REDRAW_FIELD);
3564 #endif
3565
3566 #endif
3567
3568 #if 0
3569   printf("::: FADING OUT: DONE\n");
3570   Delay(1000);
3571 #endif
3572
3573 #if 0
3574   game_status = GAME_MODE_PLAYING;
3575 #endif
3576
3577 #if 1
3578   /* needed if different viewport properties defined for playing */
3579   ChangeViewportPropertiesIfNeeded();
3580 #endif
3581
3582 #if 1
3583   DrawCompleteVideoDisplay();
3584 #endif
3585
3586   InitGameEngine();
3587   InitGameControlValues();
3588
3589   /* don't play tapes over network */
3590   network_playing = (options.network && !tape.playing);
3591
3592   for (i = 0; i < MAX_PLAYERS; i++)
3593   {
3594     struct PlayerInfo *player = &stored_player[i];
3595
3596     player->index_nr = i;
3597     player->index_bit = (1 << i);
3598     player->element_nr = EL_PLAYER_1 + i;
3599
3600     player->present = FALSE;
3601     player->active = FALSE;
3602     player->mapped = FALSE;
3603
3604     player->killed = FALSE;
3605     player->reanimated = FALSE;
3606
3607     player->action = 0;
3608     player->effective_action = 0;
3609     player->programmed_action = 0;
3610
3611     player->score = 0;
3612     player->score_final = 0;
3613
3614     player->gems_still_needed = level.gems_needed;
3615     player->sokobanfields_still_needed = 0;
3616     player->lights_still_needed = 0;
3617     player->friends_still_needed = 0;
3618
3619     for (j = 0; j < MAX_NUM_KEYS; j++)
3620       player->key[j] = FALSE;
3621
3622     player->num_white_keys = 0;
3623
3624     player->dynabomb_count = 0;
3625     player->dynabomb_size = 1;
3626     player->dynabombs_left = 0;
3627     player->dynabomb_xl = FALSE;
3628
3629     player->MovDir = initial_move_dir;
3630     player->MovPos = 0;
3631     player->GfxPos = 0;
3632     player->GfxDir = initial_move_dir;
3633     player->GfxAction = ACTION_DEFAULT;
3634     player->Frame = 0;
3635     player->StepFrame = 0;
3636
3637     player->initial_element = player->element_nr;
3638     player->artwork_element =
3639       (level.use_artwork_element[i] ? level.artwork_element[i] :
3640        player->element_nr);
3641     player->use_murphy = FALSE;
3642
3643     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3644     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3645
3646     player->gravity = level.initial_player_gravity[i];
3647
3648     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3649
3650     player->actual_frame_counter = 0;
3651
3652     player->step_counter = 0;
3653
3654     player->last_move_dir = initial_move_dir;
3655
3656     player->is_active = FALSE;
3657
3658     player->is_waiting = FALSE;
3659     player->is_moving = FALSE;
3660     player->is_auto_moving = FALSE;
3661     player->is_digging = FALSE;
3662     player->is_snapping = FALSE;
3663     player->is_collecting = FALSE;
3664     player->is_pushing = FALSE;
3665     player->is_switching = FALSE;
3666     player->is_dropping = FALSE;
3667     player->is_dropping_pressed = FALSE;
3668
3669     player->is_bored = FALSE;
3670     player->is_sleeping = FALSE;
3671
3672     player->frame_counter_bored = -1;
3673     player->frame_counter_sleeping = -1;
3674
3675     player->anim_delay_counter = 0;
3676     player->post_delay_counter = 0;
3677
3678     player->dir_waiting = initial_move_dir;
3679     player->action_waiting = ACTION_DEFAULT;
3680     player->last_action_waiting = ACTION_DEFAULT;
3681     player->special_action_bored = ACTION_DEFAULT;
3682     player->special_action_sleeping = ACTION_DEFAULT;
3683
3684     player->switch_x = -1;
3685     player->switch_y = -1;
3686
3687     player->drop_x = -1;
3688     player->drop_y = -1;
3689
3690     player->show_envelope = 0;
3691
3692     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3693
3694     player->push_delay       = -1;      /* initialized when pushing starts */
3695     player->push_delay_value = game.initial_push_delay_value;
3696
3697     player->drop_delay = 0;
3698     player->drop_pressed_delay = 0;
3699
3700     player->last_jx = -1;
3701     player->last_jy = -1;
3702     player->jx = -1;
3703     player->jy = -1;
3704
3705     player->shield_normal_time_left = 0;
3706     player->shield_deadly_time_left = 0;
3707
3708     player->inventory_infinite_element = EL_UNDEFINED;
3709     player->inventory_size = 0;
3710
3711     if (level.use_initial_inventory[i])
3712     {
3713       for (j = 0; j < level.initial_inventory_size[i]; j++)
3714       {
3715         int element = level.initial_inventory_content[i][j];
3716         int collect_count = element_info[element].collect_count_initial;
3717         int k;
3718
3719         if (!IS_CUSTOM_ELEMENT(element))
3720           collect_count = 1;
3721
3722         if (collect_count == 0)
3723           player->inventory_infinite_element = element;
3724         else
3725           for (k = 0; k < collect_count; k++)
3726             if (player->inventory_size < MAX_INVENTORY_SIZE)
3727               player->inventory_element[player->inventory_size++] = element;
3728       }
3729     }
3730
3731     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3732     SnapField(player, 0, 0);
3733
3734     player->LevelSolved = FALSE;
3735     player->GameOver = FALSE;
3736
3737     player->LevelSolved_GameWon = FALSE;
3738     player->LevelSolved_GameEnd = FALSE;
3739     player->LevelSolved_PanelOff = FALSE;
3740     player->LevelSolved_SaveTape = FALSE;
3741     player->LevelSolved_SaveScore = FALSE;
3742     player->LevelSolved_CountingTime = 0;
3743     player->LevelSolved_CountingScore = 0;
3744
3745     map_player_action[i] = i;
3746   }
3747
3748   network_player_action_received = FALSE;
3749
3750 #if defined(NETWORK_AVALIABLE)
3751   /* initial null action */
3752   if (network_playing)
3753     SendToServer_MovePlayer(MV_NONE);
3754 #endif
3755
3756   ZX = ZY = -1;
3757   ExitX = ExitY = -1;
3758
3759   FrameCounter = 0;
3760   TimeFrames = 0;
3761   TimePlayed = 0;
3762   TimeLeft = level.time;
3763   TapeTime = 0;
3764
3765   ScreenMovDir = MV_NONE;
3766   ScreenMovPos = 0;
3767   ScreenGfxPos = 0;
3768
3769   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3770
3771   AllPlayersGone = FALSE;
3772
3773   game.no_time_limit = (level.time == 0);
3774
3775   game.yamyam_content_nr = 0;
3776   game.robot_wheel_active = FALSE;
3777   game.magic_wall_active = FALSE;
3778   game.magic_wall_time_left = 0;
3779   game.light_time_left = 0;
3780   game.timegate_time_left = 0;
3781   game.switchgate_pos = 0;
3782   game.wind_direction = level.wind_direction_initial;
3783
3784 #if !USE_PLAYER_GRAVITY
3785   game.gravity = FALSE;
3786   game.explosions_delayed = TRUE;
3787 #endif
3788
3789   game.lenses_time_left = 0;
3790   game.magnify_time_left = 0;
3791
3792   game.ball_state = level.ball_state_initial;
3793   game.ball_content_nr = 0;
3794
3795   game.envelope_active = FALSE;
3796
3797   /* set focus to local player for network games, else to all players */
3798   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3799   game.centered_player_nr_next = game.centered_player_nr;
3800   game.set_centered_player = FALSE;
3801
3802   if (network_playing && tape.recording)
3803   {
3804     /* store client dependent player focus when recording network games */
3805     tape.centered_player_nr_next = game.centered_player_nr_next;
3806     tape.set_centered_player = TRUE;
3807   }
3808
3809   for (i = 0; i < NUM_BELTS; i++)
3810   {
3811     game.belt_dir[i] = MV_NONE;
3812     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3813   }
3814
3815   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3816     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3817
3818 #if DEBUG_INIT_PLAYER
3819   if (options.debug)
3820   {
3821     printf("Player status at level initialization:\n");
3822   }
3823 #endif
3824
3825   SCAN_PLAYFIELD(x, y)
3826   {
3827     Feld[x][y] = level.field[x][y];
3828     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3829     ChangeDelay[x][y] = 0;
3830     ChangePage[x][y] = -1;
3831 #if USE_NEW_CUSTOM_VALUE
3832     CustomValue[x][y] = 0;              /* initialized in InitField() */
3833 #endif
3834     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3835     AmoebaNr[x][y] = 0;
3836     WasJustMoving[x][y] = 0;
3837     WasJustFalling[x][y] = 0;
3838     CheckCollision[x][y] = 0;
3839     CheckImpact[x][y] = 0;
3840     Stop[x][y] = FALSE;
3841     Pushed[x][y] = FALSE;
3842
3843     ChangeCount[x][y] = 0;
3844     ChangeEvent[x][y] = -1;
3845
3846     ExplodePhase[x][y] = 0;
3847     ExplodeDelay[x][y] = 0;
3848     ExplodeField[x][y] = EX_TYPE_NONE;
3849
3850     RunnerVisit[x][y] = 0;
3851     PlayerVisit[x][y] = 0;
3852
3853     GfxFrame[x][y] = 0;
3854     GfxRandom[x][y] = INIT_GFX_RANDOM();
3855     GfxElement[x][y] = EL_UNDEFINED;
3856     GfxAction[x][y] = ACTION_DEFAULT;
3857     GfxDir[x][y] = MV_NONE;
3858     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3859   }
3860
3861   SCAN_PLAYFIELD(x, y)
3862   {
3863     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3864       emulate_bd = FALSE;
3865     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3866       emulate_sb = FALSE;
3867     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3868       emulate_sp = FALSE;
3869
3870     InitField(x, y, TRUE);
3871
3872     ResetGfxAnimation(x, y);
3873   }
3874
3875   InitBeltMovement();
3876
3877   for (i = 0; i < MAX_PLAYERS; i++)
3878   {
3879     struct PlayerInfo *player = &stored_player[i];
3880
3881     /* set number of special actions for bored and sleeping animation */
3882     player->num_special_action_bored =
3883       get_num_special_action(player->artwork_element,
3884                              ACTION_BORING_1, ACTION_BORING_LAST);
3885     player->num_special_action_sleeping =
3886       get_num_special_action(player->artwork_element,
3887                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3888   }
3889
3890   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3891                     emulate_sb ? EMU_SOKOBAN :
3892                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3893
3894 #if USE_NEW_ALL_SLIPPERY
3895   /* initialize type of slippery elements */
3896   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3897   {
3898     if (!IS_CUSTOM_ELEMENT(i))
3899     {
3900       /* default: elements slip down either to the left or right randomly */
3901       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3902
3903       /* SP style elements prefer to slip down on the left side */
3904       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3905         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3906
3907       /* BD style elements prefer to slip down on the left side */
3908       if (game.emulation == EMU_BOULDERDASH)
3909         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3910     }
3911   }
3912 #endif
3913
3914   /* initialize explosion and ignition delay */
3915   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3916   {
3917     if (!IS_CUSTOM_ELEMENT(i))
3918     {
3919       int num_phase = 8;
3920       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3921                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3922                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3923       int last_phase = (num_phase + 1) * delay;
3924       int half_phase = (num_phase / 2) * delay;
3925
3926       element_info[i].explosion_delay = last_phase - 1;
3927       element_info[i].ignition_delay = half_phase;
3928
3929       if (i == EL_BLACK_ORB)
3930         element_info[i].ignition_delay = 1;
3931     }
3932
3933 #if 0
3934     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3935       element_info[i].explosion_delay = 1;
3936
3937     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3938       element_info[i].ignition_delay = 1;
3939 #endif
3940   }
3941
3942   /* correct non-moving belts to start moving left */
3943   for (i = 0; i < NUM_BELTS; i++)
3944     if (game.belt_dir[i] == MV_NONE)
3945       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3946
3947 #if USE_NEW_PLAYER_ASSIGNMENTS
3948   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3949   /* choose default local player */
3950   local_player = &stored_player[0];
3951
3952   for (i = 0; i < MAX_PLAYERS; i++)
3953     stored_player[i].connected = FALSE;
3954
3955   local_player->connected = TRUE;
3956   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3957
3958 #if 0
3959   printf("::: TEAM MODE: %d\n", game.team_mode);
3960 #endif
3961
3962   if (tape.playing)
3963   {
3964 #if 1
3965     for (i = 0; i < MAX_PLAYERS; i++)
3966       stored_player[i].connected = tape.player_participates[i];
3967 #else
3968     /* try to guess locally connected team mode players (needed for correct
3969        assignment of player figures from level to locally playing players) */
3970
3971     for (i = 0; i < MAX_PLAYERS; i++)
3972       if (tape.player_participates[i])
3973         stored_player[i].connected = TRUE;
3974 #endif
3975   }
3976   else if (game.team_mode && !options.network)
3977   {
3978     /* try to guess locally connected team mode players (needed for correct
3979        assignment of player figures from level to locally playing players) */
3980
3981     for (i = 0; i < MAX_PLAYERS; i++)
3982       if (setup.input[i].use_joystick ||
3983           setup.input[i].key.left != KSYM_UNDEFINED)
3984         stored_player[i].connected = TRUE;
3985   }
3986
3987 #if DEBUG_INIT_PLAYER
3988   if (options.debug)
3989   {
3990     printf("Player status after level initialization:\n");
3991
3992     for (i = 0; i < MAX_PLAYERS; i++)
3993     {
3994       struct PlayerInfo *player = &stored_player[i];
3995
3996       printf("- player %d: present == %d, connected == %d, active == %d",
3997              i + 1,
3998              player->present,
3999              player->connected,
4000              player->active);
4001
4002       if (local_player == player)
4003         printf(" (local player)");
4004
4005       printf("\n");
4006     }
4007   }
4008 #endif
4009
4010 #if DEBUG_INIT_PLAYER
4011   if (options.debug)
4012     printf("Reassigning players ...\n");
4013 #endif
4014
4015   /* check if any connected player was not found in playfield */
4016   for (i = 0; i < MAX_PLAYERS; i++)
4017   {
4018     struct PlayerInfo *player = &stored_player[i];
4019
4020     if (player->connected && !player->present)
4021     {
4022       struct PlayerInfo *field_player = NULL;
4023
4024 #if DEBUG_INIT_PLAYER
4025       if (options.debug)
4026         printf("- looking for field player for player %d ...\n", i + 1);
4027 #endif
4028
4029       /* assign first free player found that is present in the playfield */
4030
4031 #if 1
4032       /* first try: look for unmapped playfield player that is not connected */
4033       for (j = 0; j < MAX_PLAYERS; j++)
4034         if (field_player == NULL &&
4035             stored_player[j].present &&
4036             !stored_player[j].mapped &&
4037             !stored_player[j].connected)
4038           field_player = &stored_player[j];
4039
4040       /* second try: look for *any* unmapped playfield player */
4041       for (j = 0; j < MAX_PLAYERS; j++)
4042         if (field_player == NULL &&
4043             stored_player[j].present &&
4044             !stored_player[j].mapped)
4045           field_player = &stored_player[j];
4046 #else
4047       /* first try: look for unmapped playfield player that is not connected */
4048       if (field_player == NULL)
4049         for (j = 0; j < MAX_PLAYERS; j++)
4050           if (stored_player[j].present &&
4051               !stored_player[j].mapped &&
4052               !stored_player[j].connected)
4053             field_player = &stored_player[j];
4054
4055       /* second try: look for *any* unmapped playfield player */
4056       if (field_player == NULL)
4057         for (j = 0; j < MAX_PLAYERS; j++)
4058           if (stored_player[j].present &&
4059               !stored_player[j].mapped)
4060             field_player = &stored_player[j];
4061 #endif
4062
4063       if (field_player != NULL)
4064       {
4065         int jx = field_player->jx, jy = field_player->jy;
4066
4067 #if DEBUG_INIT_PLAYER
4068         if (options.debug)
4069           printf("- found player %d\n", field_player->index_nr + 1);
4070 #endif
4071
4072         player->present = FALSE;
4073         player->active = FALSE;
4074
4075         field_player->present = TRUE;
4076         field_player->active = TRUE;
4077
4078         /*
4079         player->initial_element = field_player->initial_element;
4080         player->artwork_element = field_player->artwork_element;
4081
4082         player->block_last_field       = field_player->block_last_field;
4083         player->block_delay_adjustment = field_player->block_delay_adjustment;
4084         */
4085
4086         StorePlayer[jx][jy] = field_player->element_nr;
4087
4088         field_player->jx = field_player->last_jx = jx;
4089         field_player->jy = field_player->last_jy = jy;
4090
4091         if (local_player == player)
4092           local_player = field_player;
4093
4094         map_player_action[field_player->index_nr] = i;
4095
4096         field_player->mapped = TRUE;
4097
4098 #if DEBUG_INIT_PLAYER
4099         if (options.debug)
4100           printf("- map_player_action[%d] == %d\n",
4101                  field_player->index_nr + 1, i + 1);
4102 #endif
4103       }
4104     }
4105
4106     if (player->connected && player->present)
4107       player->mapped = TRUE;
4108   }
4109
4110 #if DEBUG_INIT_PLAYER
4111   if (options.debug)
4112   {
4113     printf("Player status after player assignment (first stage):\n");
4114
4115     for (i = 0; i < MAX_PLAYERS; i++)
4116     {
4117       struct PlayerInfo *player = &stored_player[i];
4118
4119       printf("- player %d: present == %d, connected == %d, active == %d",
4120              i + 1,
4121              player->present,
4122              player->connected,
4123              player->active);
4124
4125       if (local_player == player)
4126         printf(" (local player)");
4127
4128       printf("\n");
4129     }
4130   }
4131 #endif
4132
4133 #else
4134
4135   /* check if any connected player was not found in playfield */
4136   for (i = 0; i < MAX_PLAYERS; i++)
4137   {
4138     struct PlayerInfo *player = &stored_player[i];
4139
4140     if (player->connected && !player->present)
4141     {
4142       for (j = 0; j < MAX_PLAYERS; j++)
4143       {
4144         struct PlayerInfo *field_player = &stored_player[j];
4145         int jx = field_player->jx, jy = field_player->jy;
4146
4147         /* assign first free player found that is present in the playfield */
4148         if (field_player->present && !field_player->connected)
4149         {
4150           player->present = TRUE;
4151           player->active = TRUE;
4152
4153           field_player->present = FALSE;
4154           field_player->active = FALSE;
4155
4156           player->initial_element = field_player->initial_element;
4157           player->artwork_element = field_player->artwork_element;
4158
4159           player->block_last_field       = field_player->block_last_field;
4160           player->block_delay_adjustment = field_player->block_delay_adjustment;
4161
4162           StorePlayer[jx][jy] = player->element_nr;
4163
4164           player->jx = player->last_jx = jx;
4165           player->jy = player->last_jy = jy;
4166
4167           break;
4168         }
4169       }
4170     }
4171   }
4172 #endif
4173
4174 #if 0
4175   printf("::: local_player->present == %d\n", local_player->present);
4176 #endif
4177
4178   if (tape.playing)
4179   {
4180     /* when playing a tape, eliminate all players who do not participate */
4181
4182 #if USE_NEW_PLAYER_ASSIGNMENTS
4183
4184 #if 1
4185     if (!game.team_mode)
4186 #endif
4187
4188     for (i = 0; i < MAX_PLAYERS; i++)
4189     {
4190       if (stored_player[i].active &&
4191           !tape.player_participates[map_player_action[i]])
4192       {
4193         struct PlayerInfo *player = &stored_player[i];
4194         int jx = player->jx, jy = player->jy;
4195
4196 #if DEBUG_INIT_PLAYER
4197         if (options.debug)
4198           printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4199 #endif
4200
4201         player->active = FALSE;
4202         StorePlayer[jx][jy] = 0;
4203         Feld[jx][jy] = EL_EMPTY;
4204       }
4205     }
4206
4207 #else
4208
4209     for (i = 0; i < MAX_PLAYERS; i++)
4210     {
4211       if (stored_player[i].active &&
4212           !tape.player_participates[i])
4213       {
4214         struct PlayerInfo *player = &stored_player[i];
4215         int jx = player->jx, jy = player->jy;
4216
4217         player->active = FALSE;
4218         StorePlayer[jx][jy] = 0;
4219         Feld[jx][jy] = EL_EMPTY;
4220       }
4221     }
4222 #endif
4223   }
4224   else if (!options.network && !game.team_mode)         /* && !tape.playing */
4225   {
4226     /* when in single player mode, eliminate all but the first active player */
4227
4228     for (i = 0; i < MAX_PLAYERS; i++)
4229     {
4230       if (stored_player[i].active)
4231       {
4232         for (j = i + 1; j < MAX_PLAYERS; j++)
4233         {
4234           if (stored_player[j].active)
4235           {
4236             struct PlayerInfo *player = &stored_player[j];
4237             int jx = player->jx, jy = player->jy;
4238
4239             player->active = FALSE;
4240             player->present = FALSE;
4241
4242             StorePlayer[jx][jy] = 0;
4243             Feld[jx][jy] = EL_EMPTY;
4244           }
4245         }
4246       }
4247     }
4248   }
4249
4250   /* when recording the game, store which players take part in the game */
4251   if (tape.recording)
4252   {
4253 #if USE_NEW_PLAYER_ASSIGNMENTS
4254     for (i = 0; i < MAX_PLAYERS; i++)
4255       if (stored_player[i].connected)
4256         tape.player_participates[i] = TRUE;
4257 #else
4258     for (i = 0; i < MAX_PLAYERS; i++)
4259       if (stored_player[i].active)
4260         tape.player_participates[i] = TRUE;
4261 #endif
4262   }
4263
4264 #if DEBUG_INIT_PLAYER
4265   if (options.debug)
4266   {
4267     printf("Player status after player assignment (final stage):\n");
4268
4269     for (i = 0; i < MAX_PLAYERS; i++)
4270     {
4271       struct PlayerInfo *player = &stored_player[i];
4272
4273       printf("- player %d: present == %d, connected == %d, active == %d",
4274              i + 1,
4275              player->present,
4276              player->connected,
4277              player->active);
4278
4279       if (local_player == player)
4280         printf(" (local player)");
4281
4282       printf("\n");
4283     }
4284   }
4285 #endif
4286
4287   if (BorderElement == EL_EMPTY)
4288   {
4289     SBX_Left = 0;
4290     SBX_Right = lev_fieldx - SCR_FIELDX;
4291     SBY_Upper = 0;
4292     SBY_Lower = lev_fieldy - SCR_FIELDY;
4293   }
4294   else
4295   {
4296     SBX_Left = -1;
4297     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4298     SBY_Upper = -1;
4299     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4300   }
4301
4302 #if NEW_TILESIZE
4303
4304   if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4305     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4306
4307   if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4308     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4309
4310   if (EVEN(SCR_FIELDX))
4311     SBX_Left--;
4312   if (EVEN(SCR_FIELDY))
4313     SBY_Upper--;
4314
4315 #else
4316
4317   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4318     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4319
4320   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4321     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4322 #endif
4323
4324   /* if local player not found, look for custom element that might create
4325      the player (make some assumptions about the right custom element) */
4326   if (!local_player->present)
4327   {
4328     int start_x = 0, start_y = 0;
4329     int found_rating = 0;
4330     int found_element = EL_UNDEFINED;
4331     int player_nr = local_player->index_nr;
4332
4333     SCAN_PLAYFIELD(x, y)
4334     {
4335       int element = Feld[x][y];
4336       int content;
4337       int xx, yy;
4338       boolean is_player;
4339
4340       if (level.use_start_element[player_nr] &&
4341           level.start_element[player_nr] == element &&
4342           found_rating < 4)
4343       {
4344         start_x = x;
4345         start_y = y;
4346
4347         found_rating = 4;
4348         found_element = element;
4349       }
4350
4351       if (!IS_CUSTOM_ELEMENT(element))
4352         continue;
4353
4354       if (CAN_CHANGE(element))
4355       {
4356         for (i = 0; i < element_info[element].num_change_pages; i++)
4357         {
4358           /* check for player created from custom element as single target */
4359           content = element_info[element].change_page[i].target_element;
4360           is_player = ELEM_IS_PLAYER(content);
4361
4362           if (is_player && (found_rating < 3 ||
4363                             (found_rating == 3 && element < found_element)))
4364           {
4365             start_x = x;
4366             start_y = y;
4367
4368             found_rating = 3;
4369             found_element = element;
4370           }
4371         }
4372       }
4373
4374       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4375       {
4376         /* check for player created from custom element as explosion content */
4377         content = element_info[element].content.e[xx][yy];
4378         is_player = ELEM_IS_PLAYER(content);
4379
4380         if (is_player && (found_rating < 2 ||
4381                           (found_rating == 2 && element < found_element)))
4382         {
4383           start_x = x + xx - 1;
4384           start_y = y + yy - 1;
4385
4386           found_rating = 2;
4387           found_element = element;
4388         }
4389
4390         if (!CAN_CHANGE(element))
4391           continue;
4392
4393         for (i = 0; i < element_info[element].num_change_pages; i++)
4394         {
4395           /* check for player created from custom element as extended target */
4396           content =
4397             element_info[element].change_page[i].target_content.e[xx][yy];
4398
4399           is_player = ELEM_IS_PLAYER(content);
4400
4401           if (is_player && (found_rating < 1 ||
4402                             (found_rating == 1 && element < found_element)))
4403           {
4404             start_x = x + xx - 1;
4405             start_y = y + yy - 1;
4406
4407             found_rating = 1;
4408             found_element = element;
4409           }
4410         }
4411       }
4412     }
4413
4414     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4415                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4416                 start_x - MIDPOSX);
4417
4418     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4419                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4420                 start_y - MIDPOSY);
4421   }
4422   else
4423   {
4424     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4425                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4426                 local_player->jx - MIDPOSX);
4427
4428     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4429                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4430                 local_player->jy - MIDPOSY);
4431   }
4432
4433 #if 0
4434   printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4435 #endif
4436
4437 #if 0
4438   /* do not use PLAYING mask for fading out from main screen */
4439   game_status = GAME_MODE_MAIN;
4440 #endif
4441
4442 #if 0
4443
4444   StopAnimation();
4445
4446   if (!game.restart_level)
4447     CloseDoor(DOOR_CLOSE_1);
4448
4449 #if 1
4450   if (level_editor_test_game)
4451     FadeSkipNextFadeIn();
4452   else
4453     FadeSetEnterScreen();
4454 #else
4455   if (level_editor_test_game)
4456     fading = fading_none;
4457   else
4458     fading = menu.destination;
4459 #endif
4460
4461 #if 1
4462   FadeOut(REDRAW_FIELD);
4463 #else
4464   if (do_fading)
4465     FadeOut(REDRAW_FIELD);
4466 #endif
4467
4468 #endif
4469
4470 #if 0
4471   game_status = GAME_MODE_PLAYING;
4472 #endif
4473
4474   /* !!! FIX THIS (START) !!! */
4475   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4476   {
4477     InitGameEngine_EM();
4478
4479     /* blit playfield from scroll buffer to normal back buffer for fading in */
4480     BlitScreenToBitmap_EM(backbuffer);
4481   }
4482   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4483   {
4484     InitGameEngine_SP();
4485
4486     /* blit playfield from scroll buffer to normal back buffer for fading in */
4487     BlitScreenToBitmap_SP(backbuffer);
4488   }
4489   else
4490   {
4491     DrawLevel();
4492     DrawAllPlayers();
4493
4494     /* after drawing the level, correct some elements */
4495     if (game.timegate_time_left == 0)
4496       CloseAllOpenTimegates();
4497
4498 #if NEW_TILESIZE
4499     BlitScreenToBitmap(backbuffer);
4500 #else
4501     /* blit playfield from scroll buffer to normal back buffer for fading in */
4502     if (setup.soft_scrolling)
4503       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4504 #endif
4505
4506     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4507   }
4508   /* !!! FIX THIS (END) !!! */
4509
4510 #if 1
4511   FadeIn(REDRAW_FIELD);
4512 #else
4513   if (do_fading)
4514     FadeIn(REDRAW_FIELD);
4515
4516   BackToFront();
4517 #endif
4518
4519   if (!game.restart_level)
4520   {
4521     /* copy default game door content to main double buffer */
4522 #if 1
4523 #if 1
4524     /* !!! CHECK AGAIN !!! */
4525     SetPanelBackground();
4526     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4527     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4528 #else
4529     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4530
4531     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4532     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4533     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4534                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4535 #endif
4536 #else
4537     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4538                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4539 #endif
4540   }
4541
4542   SetPanelBackground();
4543   SetDrawBackgroundMask(REDRAW_DOOR_1);
4544
4545 #if 1
4546   UpdateAndDisplayGameControlValues();
4547 #else
4548   UpdateGameDoorValues();
4549   DrawGameDoorValues();
4550 #endif
4551
4552   if (!game.restart_level)
4553   {
4554     UnmapGameButtons();
4555     UnmapTapeButtons();
4556     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4557     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4558     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4559     MapGameButtons();
4560     MapTapeButtons();
4561
4562     /* copy actual game door content to door double buffer for OpenDoor() */
4563 #if 1
4564     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4565 #else
4566     BlitBitmap(drawto, bitmap_db_door,
4567                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4568 #endif
4569
4570     OpenDoor(DOOR_OPEN_ALL);
4571
4572     PlaySound(SND_GAME_STARTING);
4573
4574     if (setup.sound_music)
4575       PlayLevelMusic();
4576
4577     KeyboardAutoRepeatOffUnlessAutoplay();
4578
4579 #if DEBUG_INIT_PLAYER
4580     if (options.debug)
4581     {
4582       printf("Player status (final):\n");
4583
4584       for (i = 0; i < MAX_PLAYERS; i++)
4585       {
4586         struct PlayerInfo *player = &stored_player[i];
4587
4588         printf("- player %d: present == %d, connected == %d, active == %d",
4589                i + 1,
4590                player->present,
4591                player->connected,
4592                player->active);
4593
4594         if (local_player == player)
4595           printf(" (local player)");
4596
4597         printf("\n");
4598       }
4599     }
4600 #endif
4601   }
4602
4603 #if 1
4604   UnmapAllGadgets();
4605
4606   MapGameButtons();
4607   MapTapeButtons();
4608 #endif
4609
4610   if (!game.restart_level && !tape.playing)
4611   {
4612     LevelStats_incPlayed(level_nr);
4613
4614     SaveLevelSetup_SeriesInfo();
4615
4616 #if 0
4617     printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4618 #endif
4619   }
4620
4621   game.restart_level = FALSE;
4622 }
4623
4624 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4625 {
4626   /* this is used for non-R'n'D game engines to update certain engine values */
4627
4628   /* needed to determine if sounds are played within the visible screen area */
4629   scroll_x = actual_scroll_x;
4630   scroll_y = actual_scroll_y;
4631 }
4632
4633 void InitMovDir(int x, int y)
4634 {
4635   int i, element = Feld[x][y];
4636   static int xy[4][2] =
4637   {
4638     {  0, +1 },
4639     { +1,  0 },
4640     {  0, -1 },
4641     { -1,  0 }
4642   };
4643   static int direction[3][4] =
4644   {
4645     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4646     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4647     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4648   };
4649
4650   switch (element)
4651   {
4652     case EL_BUG_RIGHT:
4653     case EL_BUG_UP:
4654     case EL_BUG_LEFT:
4655     case EL_BUG_DOWN:
4656       Feld[x][y] = EL_BUG;
4657       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4658       break;
4659
4660     case EL_SPACESHIP_RIGHT:
4661     case EL_SPACESHIP_UP:
4662     case EL_SPACESHIP_LEFT:
4663     case EL_SPACESHIP_DOWN:
4664       Feld[x][y] = EL_SPACESHIP;
4665       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4666       break;
4667
4668     case EL_BD_BUTTERFLY_RIGHT:
4669     case EL_BD_BUTTERFLY_UP:
4670     case EL_BD_BUTTERFLY_LEFT:
4671     case EL_BD_BUTTERFLY_DOWN:
4672       Feld[x][y] = EL_BD_BUTTERFLY;
4673       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4674       break;
4675
4676     case EL_BD_FIREFLY_RIGHT:
4677     case EL_BD_FIREFLY_UP:
4678     case EL_BD_FIREFLY_LEFT:
4679     case EL_BD_FIREFLY_DOWN:
4680       Feld[x][y] = EL_BD_FIREFLY;
4681       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4682       break;
4683
4684     case EL_PACMAN_RIGHT:
4685     case EL_PACMAN_UP:
4686     case EL_PACMAN_LEFT:
4687     case EL_PACMAN_DOWN:
4688       Feld[x][y] = EL_PACMAN;
4689       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4690       break;
4691
4692     case EL_YAMYAM_LEFT:
4693     case EL_YAMYAM_RIGHT:
4694     case EL_YAMYAM_UP:
4695     case EL_YAMYAM_DOWN:
4696       Feld[x][y] = EL_YAMYAM;
4697       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4698       break;
4699
4700     case EL_SP_SNIKSNAK:
4701       MovDir[x][y] = MV_UP;
4702       break;
4703
4704     case EL_SP_ELECTRON:
4705       MovDir[x][y] = MV_LEFT;
4706       break;
4707
4708     case EL_MOLE_LEFT:
4709     case EL_MOLE_RIGHT:
4710     case EL_MOLE_UP:
4711     case EL_MOLE_DOWN:
4712       Feld[x][y] = EL_MOLE;
4713       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4714       break;
4715
4716     default:
4717       if (IS_CUSTOM_ELEMENT(element))
4718       {
4719         struct ElementInfo *ei = &element_info[element];
4720         int move_direction_initial = ei->move_direction_initial;
4721         int move_pattern = ei->move_pattern;
4722
4723         if (move_direction_initial == MV_START_PREVIOUS)
4724         {
4725           if (MovDir[x][y] != MV_NONE)
4726             return;
4727
4728           move_direction_initial = MV_START_AUTOMATIC;
4729         }
4730
4731         if (move_direction_initial == MV_START_RANDOM)
4732           MovDir[x][y] = 1 << RND(4);
4733         else if (move_direction_initial & MV_ANY_DIRECTION)
4734           MovDir[x][y] = move_direction_initial;
4735         else if (move_pattern == MV_ALL_DIRECTIONS ||
4736                  move_pattern == MV_TURNING_LEFT ||
4737                  move_pattern == MV_TURNING_RIGHT ||
4738                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4739                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4740                  move_pattern == MV_TURNING_RANDOM)
4741           MovDir[x][y] = 1 << RND(4);
4742         else if (move_pattern == MV_HORIZONTAL)
4743           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4744         else if (move_pattern == MV_VERTICAL)
4745           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4746         else if (move_pattern & MV_ANY_DIRECTION)
4747           MovDir[x][y] = element_info[element].move_pattern;
4748         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4749                  move_pattern == MV_ALONG_RIGHT_SIDE)
4750         {
4751           /* use random direction as default start direction */
4752           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4753             MovDir[x][y] = 1 << RND(4);
4754
4755           for (i = 0; i < NUM_DIRECTIONS; i++)
4756           {
4757             int x1 = x + xy[i][0];
4758             int y1 = y + xy[i][1];
4759
4760             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4761             {
4762               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4763                 MovDir[x][y] = direction[0][i];
4764               else
4765                 MovDir[x][y] = direction[1][i];
4766
4767               break;
4768             }
4769           }
4770         }                
4771       }
4772       else
4773       {
4774         MovDir[x][y] = 1 << RND(4);
4775
4776         if (element != EL_BUG &&
4777             element != EL_SPACESHIP &&
4778             element != EL_BD_BUTTERFLY &&
4779             element != EL_BD_FIREFLY)
4780           break;
4781
4782         for (i = 0; i < NUM_DIRECTIONS; i++)
4783         {
4784           int x1 = x + xy[i][0];
4785           int y1 = y + xy[i][1];
4786
4787           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4788           {
4789             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4790             {
4791               MovDir[x][y] = direction[0][i];
4792               break;
4793             }
4794             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4795                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4796             {
4797               MovDir[x][y] = direction[1][i];
4798               break;
4799             }
4800           }
4801         }
4802       }
4803       break;
4804   }
4805
4806   GfxDir[x][y] = MovDir[x][y];
4807 }
4808
4809 void InitAmoebaNr(int x, int y)
4810 {
4811   int i;
4812   int group_nr = AmoebeNachbarNr(x, y);
4813
4814   if (group_nr == 0)
4815   {
4816     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4817     {
4818       if (AmoebaCnt[i] == 0)
4819       {
4820         group_nr = i;
4821         break;
4822       }
4823     }
4824   }
4825
4826   AmoebaNr[x][y] = group_nr;
4827   AmoebaCnt[group_nr]++;
4828   AmoebaCnt2[group_nr]++;
4829 }
4830
4831 static void PlayerWins(struct PlayerInfo *player)
4832 {
4833   player->LevelSolved = TRUE;
4834   player->GameOver = TRUE;
4835
4836   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4837                          level.native_em_level->lev->score : player->score);
4838
4839   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4840                                       TimeLeft);
4841   player->LevelSolved_CountingScore = player->score_final;
4842 }
4843
4844 void GameWon()
4845 {
4846   static int time, time_final;
4847   static int score, score_final;
4848   static int game_over_delay_1 = 0;
4849   static int game_over_delay_2 = 0;
4850   int game_over_delay_value_1 = 50;
4851   int game_over_delay_value_2 = 50;
4852
4853   if (!local_player->LevelSolved_GameWon)
4854   {
4855     int i;
4856
4857     /* do not start end game actions before the player stops moving (to exit) */
4858     if (local_player->MovPos)
4859       return;
4860
4861     local_player->LevelSolved_GameWon = TRUE;
4862     local_player->LevelSolved_SaveTape = tape.recording;
4863     local_player->LevelSolved_SaveScore = !tape.playing;
4864
4865     if (!tape.playing)
4866     {
4867       LevelStats_incSolved(level_nr);
4868
4869       SaveLevelSetup_SeriesInfo();
4870
4871 #if 0
4872       printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4873 #endif
4874     }
4875
4876     if (tape.auto_play)         /* tape might already be stopped here */
4877       tape.auto_play_level_solved = TRUE;
4878
4879 #if 1
4880     TapeStop();
4881 #endif
4882
4883     game_over_delay_1 = game_over_delay_value_1;
4884     game_over_delay_2 = game_over_delay_value_2;
4885
4886     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4887     score = score_final = local_player->score_final;
4888
4889     if (TimeLeft > 0)
4890     {
4891       time_final = 0;
4892       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4893     }
4894     else if (game.no_time_limit && TimePlayed < 999)
4895     {
4896       time_final = 999;
4897       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4898     }
4899
4900     local_player->score_final = score_final;
4901
4902     if (level_editor_test_game)
4903     {
4904       time = time_final;
4905       score = score_final;
4906
4907 #if 1
4908       local_player->LevelSolved_CountingTime = time;
4909       local_player->LevelSolved_CountingScore = score;
4910
4911       game_panel_controls[GAME_PANEL_TIME].value = time;
4912       game_panel_controls[GAME_PANEL_SCORE].value = score;
4913
4914       DisplayGameControlValues();
4915 #else
4916       DrawGameValue_Time(time);
4917       DrawGameValue_Score(score);
4918 #endif
4919     }
4920
4921     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4922     {
4923       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4924       {
4925         /* close exit door after last player */
4926         if ((AllPlayersGone &&
4927              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4928               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4929               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4930             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4931             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4932         {
4933           int element = Feld[ExitX][ExitY];
4934
4935 #if 0
4936           if (element == EL_EM_EXIT_OPEN ||
4937               element == EL_EM_STEEL_EXIT_OPEN)
4938           {
4939             Bang(ExitX, ExitY);
4940           }
4941           else
4942 #endif
4943           {
4944             Feld[ExitX][ExitY] =
4945               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4946                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4947                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4948                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4949                EL_EM_STEEL_EXIT_CLOSING);
4950
4951             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4952           }
4953         }
4954
4955         /* player disappears */
4956         DrawLevelField(ExitX, ExitY);
4957       }
4958
4959       for (i = 0; i < MAX_PLAYERS; i++)
4960       {
4961         struct PlayerInfo *player = &stored_player[i];
4962
4963         if (player->present)
4964         {
4965           RemovePlayer(player);
4966
4967           /* player disappears */
4968           DrawLevelField(player->jx, player->jy);
4969         }
4970       }
4971     }
4972
4973     PlaySound(SND_GAME_WINNING);
4974   }
4975
4976   if (game_over_delay_1 > 0)
4977   {
4978     game_over_delay_1--;
4979
4980     return;
4981   }
4982
4983   if (time != time_final)
4984   {
4985     int time_to_go = ABS(time_final - time);
4986     int time_count_dir = (time < time_final ? +1 : -1);
4987     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4988
4989     time  += time_count_steps * time_count_dir;
4990     score += time_count_steps * level.score[SC_TIME_BONUS];
4991
4992 #if 1
4993     local_player->LevelSolved_CountingTime = time;
4994     local_player->LevelSolved_CountingScore = score;
4995
4996     game_panel_controls[GAME_PANEL_TIME].value = time;
4997     game_panel_controls[GAME_PANEL_SCORE].value = score;
4998
4999     DisplayGameControlValues();
5000 #else
5001     DrawGameValue_Time(time);
5002     DrawGameValue_Score(score);
5003 #endif
5004
5005     if (time == time_final)
5006       StopSound(SND_GAME_LEVELTIME_BONUS);
5007     else if (setup.sound_loops)
5008       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5009     else
5010       PlaySound(SND_GAME_LEVELTIME_BONUS);
5011
5012     return;
5013   }
5014
5015   local_player->LevelSolved_PanelOff = TRUE;
5016
5017   if (game_over_delay_2 > 0)
5018   {
5019     game_over_delay_2--;
5020
5021     return;
5022   }
5023
5024 #if 1
5025   GameEnd();
5026 #endif
5027 }
5028
5029 void GameEnd()
5030 {
5031   int hi_pos;
5032   boolean raise_level = FALSE;
5033
5034   local_player->LevelSolved_GameEnd = TRUE;
5035
5036   CloseDoor(DOOR_CLOSE_1);
5037
5038   if (local_player->LevelSolved_SaveTape)
5039   {
5040 #if 0
5041     TapeStop();
5042 #endif
5043
5044 #if 1
5045     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5046 #else
5047     SaveTape(tape.level_nr);            /* ask to save tape */
5048 #endif
5049   }
5050
5051   if (level_editor_test_game)
5052   {
5053     game_status = GAME_MODE_MAIN;
5054
5055 #if 1
5056     DrawAndFadeInMainMenu(REDRAW_FIELD);
5057 #else
5058     DrawMainMenu();
5059 #endif
5060
5061     return;
5062   }
5063
5064   if (!local_player->LevelSolved_SaveScore)
5065   {
5066 #if 1
5067     FadeOut(REDRAW_FIELD);
5068 #endif
5069
5070     game_status = GAME_MODE_MAIN;
5071
5072     DrawAndFadeInMainMenu(REDRAW_FIELD);
5073
5074     return;
5075   }
5076
5077   if (level_nr == leveldir_current->handicap_level)
5078   {
5079     leveldir_current->handicap_level++;
5080
5081     SaveLevelSetup_SeriesInfo();
5082   }
5083
5084   if (level_nr < leveldir_current->last_level)
5085     raise_level = TRUE;                 /* advance to next level */
5086
5087   if ((hi_pos = NewHiScore()) >= 0) 
5088   {
5089     game_status = GAME_MODE_SCORES;
5090
5091     DrawHallOfFame(hi_pos);
5092
5093     if (raise_level)
5094     {
5095       level_nr++;
5096       TapeErase();
5097     }
5098   }
5099   else
5100   {
5101 #if 1
5102     FadeOut(REDRAW_FIELD);
5103 #endif
5104
5105     game_status = GAME_MODE_MAIN;
5106
5107     if (raise_level)
5108     {
5109       level_nr++;
5110       TapeErase();
5111     }
5112
5113     DrawAndFadeInMainMenu(REDRAW_FIELD);
5114   }
5115 }
5116
5117 int NewHiScore()
5118 {
5119   int k, l;
5120   int position = -1;
5121
5122   LoadScore(level_nr);
5123
5124   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5125       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5126     return -1;
5127
5128   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5129   {
5130     if (local_player->score_final > highscore[k].Score)
5131     {
5132       /* player has made it to the hall of fame */
5133
5134       if (k < MAX_SCORE_ENTRIES - 1)
5135       {
5136         int m = MAX_SCORE_ENTRIES - 1;
5137
5138 #ifdef ONE_PER_NAME
5139         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5140           if (strEqual(setup.player_name, highscore[l].Name))
5141             m = l;
5142         if (m == k)     /* player's new highscore overwrites his old one */
5143           goto put_into_list;
5144 #endif
5145
5146         for (l = m; l > k; l--)
5147         {
5148           strcpy(highscore[l].Name, highscore[l - 1].Name);
5149           highscore[l].Score = highscore[l - 1].Score;
5150         }
5151       }
5152
5153 #ifdef ONE_PER_NAME
5154       put_into_list:
5155 #endif
5156       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5157       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5158       highscore[k].Score = local_player->score_final; 
5159       position = k;
5160       break;
5161     }
5162
5163 #ifdef ONE_PER_NAME
5164     else if (!strncmp(setup.player_name, highscore[k].Name,
5165                       MAX_PLAYER_NAME_LEN))
5166       break;    /* player already there with a higher score */
5167 #endif
5168
5169   }
5170
5171   if (position >= 0) 
5172     SaveScore(level_nr);
5173
5174   return position;
5175 }
5176
5177 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5178 {
5179   int element = Feld[x][y];
5180   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5181   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5182   int horiz_move = (dx != 0);
5183   int sign = (horiz_move ? dx : dy);
5184   int step = sign * element_info[element].move_stepsize;
5185
5186   /* special values for move stepsize for spring and things on conveyor belt */
5187   if (horiz_move)
5188   {
5189     if (CAN_FALL(element) &&
5190         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5191       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5192     else if (element == EL_SPRING)
5193       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5194   }
5195
5196   return step;
5197 }
5198
5199 inline static int getElementMoveStepsize(int x, int y)
5200 {
5201   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5202 }
5203
5204 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5205 {
5206   if (player->GfxAction != action || player->GfxDir != dir)
5207   {
5208 #if 0
5209     printf("Player frame reset! (%d => %d, %d => %d)\n",
5210            player->GfxAction, action, player->GfxDir, dir);
5211 #endif
5212
5213     player->GfxAction = action;
5214     player->GfxDir = dir;
5215     player->Frame = 0;
5216     player->StepFrame = 0;
5217   }
5218 }
5219
5220 #if USE_GFX_RESET_GFX_ANIMATION
5221 static void ResetGfxFrame(int x, int y, boolean redraw)
5222 {
5223   int element = Feld[x][y];
5224   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5225   int last_gfx_frame = GfxFrame[x][y];
5226
5227   if (graphic_info[graphic].anim_global_sync)
5228     GfxFrame[x][y] = FrameCounter;
5229   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5230     GfxFrame[x][y] = CustomValue[x][y];
5231   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5232     GfxFrame[x][y] = element_info[element].collect_score;
5233   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5234     GfxFrame[x][y] = ChangeDelay[x][y];
5235
5236   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5237     DrawLevelGraphicAnimation(x, y, graphic);
5238 }
5239 #endif
5240
5241 static void ResetGfxAnimation(int x, int y)
5242 {
5243   GfxAction[x][y] = ACTION_DEFAULT;
5244   GfxDir[x][y] = MovDir[x][y];
5245   GfxFrame[x][y] = 0;
5246
5247 #if USE_GFX_RESET_GFX_ANIMATION
5248   ResetGfxFrame(x, y, FALSE);
5249 #endif
5250 }
5251
5252 static void ResetRandomAnimationValue(int x, int y)
5253 {
5254   GfxRandom[x][y] = INIT_GFX_RANDOM();
5255 }
5256
5257 void InitMovingField(int x, int y, int direction)
5258 {
5259   int element = Feld[x][y];
5260   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5261   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5262   int newx = x + dx;
5263   int newy = y + dy;
5264   boolean is_moving_before, is_moving_after;
5265 #if 0
5266   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5267 #endif
5268
5269   /* check if element was/is moving or being moved before/after mode change */
5270 #if 1
5271 #if 1
5272   is_moving_before = (WasJustMoving[x][y] != 0);
5273 #else
5274   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5275   is_moving_before = WasJustMoving[x][y];
5276 #endif
5277 #else
5278   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5279 #endif
5280   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5281
5282   /* reset animation only for moving elements which change direction of moving
5283      or which just started or stopped moving
5284      (else CEs with property "can move" / "not moving" are reset each frame) */
5285 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5286 #if 1
5287   if (is_moving_before != is_moving_after ||
5288       direction != MovDir[x][y])
5289     ResetGfxAnimation(x, y);
5290 #else
5291   if ((is_moving_before || is_moving_after) && !continues_moving)
5292     ResetGfxAnimation(x, y);
5293 #endif
5294 #else
5295   if (!continues_moving)
5296     ResetGfxAnimation(x, y);
5297 #endif
5298
5299   MovDir[x][y] = direction;
5300   GfxDir[x][y] = direction;
5301
5302 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5303   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5304                      direction == MV_DOWN && CAN_FALL(element) ?
5305                      ACTION_FALLING : ACTION_MOVING);
5306 #else
5307   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5308                      ACTION_FALLING : ACTION_MOVING);
5309 #endif
5310
5311   /* this is needed for CEs with property "can move" / "not moving" */
5312
5313   if (is_moving_after)
5314   {
5315     if (Feld[newx][newy] == EL_EMPTY)
5316       Feld[newx][newy] = EL_BLOCKED;
5317
5318     MovDir[newx][newy] = MovDir[x][y];
5319
5320 #if USE_NEW_CUSTOM_VALUE
5321     CustomValue[newx][newy] = CustomValue[x][y];
5322 #endif
5323
5324     GfxFrame[newx][newy] = GfxFrame[x][y];
5325     GfxRandom[newx][newy] = GfxRandom[x][y];
5326     GfxAction[newx][newy] = GfxAction[x][y];
5327     GfxDir[newx][newy] = GfxDir[x][y];
5328   }
5329 }
5330
5331 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5332 {
5333   int direction = MovDir[x][y];
5334   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5335   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5336
5337   *goes_to_x = newx;
5338   *goes_to_y = newy;
5339 }
5340
5341 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5342 {
5343   int oldx = x, oldy = y;
5344   int direction = MovDir[x][y];
5345
5346   if (direction == MV_LEFT)
5347     oldx++;
5348   else if (direction == MV_RIGHT)
5349     oldx--;
5350   else if (direction == MV_UP)
5351     oldy++;
5352   else if (direction == MV_DOWN)
5353     oldy--;
5354
5355   *comes_from_x = oldx;
5356   *comes_from_y = oldy;
5357 }
5358
5359 int MovingOrBlocked2Element(int x, int y)
5360 {
5361   int element = Feld[x][y];
5362
5363   if (element == EL_BLOCKED)
5364   {
5365     int oldx, oldy;
5366
5367     Blocked2Moving(x, y, &oldx, &oldy);
5368     return Feld[oldx][oldy];
5369   }
5370   else
5371     return element;
5372 }
5373
5374 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5375 {
5376   /* like MovingOrBlocked2Element(), but if element is moving
5377      and (x,y) is the field the moving element is just leaving,
5378      return EL_BLOCKED instead of the element value */
5379   int element = Feld[x][y];
5380
5381   if (IS_MOVING(x, y))
5382   {
5383     if (element == EL_BLOCKED)
5384     {
5385       int oldx, oldy;
5386
5387       Blocked2Moving(x, y, &oldx, &oldy);
5388       return Feld[oldx][oldy];
5389     }
5390     else
5391       return EL_BLOCKED;
5392   }
5393   else
5394     return element;
5395 }
5396
5397 static void RemoveField(int x, int y)
5398 {
5399   Feld[x][y] = EL_EMPTY;
5400
5401   MovPos[x][y] = 0;
5402   MovDir[x][y] = 0;
5403   MovDelay[x][y] = 0;
5404
5405 #if USE_NEW_CUSTOM_VALUE
5406   CustomValue[x][y] = 0;
5407 #endif
5408
5409   AmoebaNr[x][y] = 0;
5410   ChangeDelay[x][y] = 0;
5411   ChangePage[x][y] = -1;
5412   Pushed[x][y] = FALSE;
5413
5414 #if 0
5415   ExplodeField[x][y] = EX_TYPE_NONE;
5416 #endif
5417
5418   GfxElement[x][y] = EL_UNDEFINED;
5419   GfxAction[x][y] = ACTION_DEFAULT;
5420   GfxDir[x][y] = MV_NONE;
5421 #if 0
5422   /* !!! this would prevent the removed tile from being redrawn !!! */
5423   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5424 #endif
5425 }
5426
5427 void RemoveMovingField(int x, int y)
5428 {
5429   int oldx = x, oldy = y, newx = x, newy = y;
5430   int element = Feld[x][y];
5431   int next_element = EL_UNDEFINED;
5432
5433   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5434     return;
5435
5436   if (IS_MOVING(x, y))
5437   {
5438     Moving2Blocked(x, y, &newx, &newy);
5439
5440     if (Feld[newx][newy] != EL_BLOCKED)
5441     {
5442       /* element is moving, but target field is not free (blocked), but
5443          already occupied by something different (example: acid pool);
5444          in this case, only remove the moving field, but not the target */
5445
5446       RemoveField(oldx, oldy);
5447
5448       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5449
5450       TEST_DrawLevelField(oldx, oldy);
5451
5452       return;
5453     }
5454   }
5455   else if (element == EL_BLOCKED)
5456   {
5457     Blocked2Moving(x, y, &oldx, &oldy);
5458     if (!IS_MOVING(oldx, oldy))
5459       return;
5460   }
5461
5462   if (element == EL_BLOCKED &&
5463       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5464        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5465        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5466        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5467        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5468        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5469     next_element = get_next_element(Feld[oldx][oldy]);
5470
5471   RemoveField(oldx, oldy);
5472   RemoveField(newx, newy);
5473
5474   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5475
5476   if (next_element != EL_UNDEFINED)
5477     Feld[oldx][oldy] = next_element;
5478
5479   TEST_DrawLevelField(oldx, oldy);
5480   TEST_DrawLevelField(newx, newy);
5481 }
5482
5483 void DrawDynamite(int x, int y)
5484 {
5485   int sx = SCREENX(x), sy = SCREENY(y);
5486   int graphic = el2img(Feld[x][y]);
5487   int frame;
5488
5489   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5490     return;
5491
5492   if (IS_WALKABLE_INSIDE(Back[x][y]))
5493     return;
5494
5495   if (Back[x][y])
5496     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5497   else if (Store[x][y])
5498     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5499
5500   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5501
5502   if (Back[x][y] || Store[x][y])
5503     DrawGraphicThruMask(sx, sy, graphic, frame);
5504   else
5505     DrawGraphic(sx, sy, graphic, frame);
5506 }
5507
5508 void CheckDynamite(int x, int y)
5509 {
5510   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5511   {
5512     MovDelay[x][y]--;
5513
5514     if (MovDelay[x][y] != 0)
5515     {
5516       DrawDynamite(x, y);
5517       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5518
5519       return;
5520     }
5521   }
5522
5523   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5524
5525   Bang(x, y);
5526 }
5527
5528 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5529 {
5530   boolean num_checked_players = 0;
5531   int i;
5532
5533   for (i = 0; i < MAX_PLAYERS; i++)
5534   {
5535     if (stored_player[i].active)
5536     {
5537       int sx = stored_player[i].jx;
5538       int sy = stored_player[i].jy;
5539
5540       if (num_checked_players == 0)
5541       {
5542         *sx1 = *sx2 = sx;
5543         *sy1 = *sy2 = sy;
5544       }
5545       else
5546       {
5547         *sx1 = MIN(*sx1, sx);
5548         *sy1 = MIN(*sy1, sy);
5549         *sx2 = MAX(*sx2, sx);
5550         *sy2 = MAX(*sy2, sy);
5551       }
5552
5553       num_checked_players++;
5554     }
5555   }
5556 }
5557
5558 static boolean checkIfAllPlayersFitToScreen_RND()
5559 {
5560   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5561
5562   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5563
5564   return (sx2 - sx1 < SCR_FIELDX &&
5565           sy2 - sy1 < SCR_FIELDY);
5566 }
5567
5568 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5569 {
5570   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5571
5572   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5573
5574   *sx = (sx1 + sx2) / 2;
5575   *sy = (sy1 + sy2) / 2;
5576 }
5577
5578 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5579                         boolean center_screen, boolean quick_relocation)
5580 {
5581   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5582   boolean no_delay = (tape.warp_forward);
5583   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5584   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5585
5586   if (quick_relocation)
5587   {
5588     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5589     {
5590       if (!level.shifted_relocation || center_screen)
5591       {
5592         /* quick relocation (without scrolling), with centering of screen */
5593
5594         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5595                     x > SBX_Right + MIDPOSX ? SBX_Right :
5596                     x - MIDPOSX);
5597
5598         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5599                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5600                     y - MIDPOSY);
5601       }
5602       else
5603       {
5604         /* quick relocation (without scrolling), but do not center screen */
5605
5606         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5607                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5608                                old_x - MIDPOSX);
5609
5610         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5611                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5612                                old_y - MIDPOSY);
5613
5614         int offset_x = x + (scroll_x - center_scroll_x);
5615         int offset_y = y + (scroll_y - center_scroll_y);
5616
5617         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5618                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5619                     offset_x - MIDPOSX);
5620
5621         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5622                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5623                     offset_y - MIDPOSY);
5624       }
5625     }
5626     else
5627     {
5628 #if 1
5629       if (!level.shifted_relocation || center_screen)
5630       {
5631         /* quick relocation (without scrolling), with centering of screen */
5632
5633         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5634                     x > SBX_Right + MIDPOSX ? SBX_Right :
5635                     x - MIDPOSX);
5636
5637         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5638                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5639                     y - MIDPOSY);
5640       }
5641       else
5642       {
5643         /* quick relocation (without scrolling), but do not center screen */
5644
5645         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5646                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5647                                old_x - MIDPOSX);
5648
5649         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5650                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5651                                old_y - MIDPOSY);
5652
5653         int offset_x = x + (scroll_x - center_scroll_x);
5654         int offset_y = y + (scroll_y - center_scroll_y);
5655
5656         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5657                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5658                     offset_x - MIDPOSX);
5659
5660         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5661                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5662                     offset_y - MIDPOSY);
5663       }
5664 #else
5665       /* quick relocation (without scrolling), inside visible screen area */
5666
5667       int offset = game.scroll_delay_value;
5668
5669       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5670           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5671         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5672
5673       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5674           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5675         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5676
5677       /* don't scroll over playfield boundaries */
5678       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5679         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5680
5681       /* don't scroll over playfield boundaries */
5682       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5683         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5684 #endif
5685     }
5686
5687     RedrawPlayfield(TRUE, 0,0,0,0);
5688   }
5689   else
5690   {
5691 #if 1
5692     int scroll_xx, scroll_yy;
5693
5694     if (!level.shifted_relocation || center_screen)
5695     {
5696       /* visible relocation (with scrolling), with centering of screen */
5697
5698       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5699                    x > SBX_Right + MIDPOSX ? SBX_Right :
5700                    x - MIDPOSX);
5701
5702       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5703                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5704                    y - MIDPOSY);
5705     }
5706     else
5707     {
5708       /* visible relocation (with scrolling), but do not center screen */
5709
5710       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5711                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5712                              old_x - MIDPOSX);
5713
5714       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5715                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5716                              old_y - MIDPOSY);
5717
5718       int offset_x = x + (scroll_x - center_scroll_x);
5719       int offset_y = y + (scroll_y - center_scroll_y);
5720
5721       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5722                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5723                    offset_x - MIDPOSX);
5724
5725       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5726                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5727                    offset_y - MIDPOSY);
5728     }
5729
5730 #else
5731
5732     /* visible relocation (with scrolling), with centering of screen */
5733
5734     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5735                      x > SBX_Right + MIDPOSX ? SBX_Right :
5736                      x - MIDPOSX);
5737
5738     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5739                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5740                      y - MIDPOSY);
5741 #endif
5742
5743     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5744
5745     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5746     {
5747       int dx = 0, dy = 0;
5748       int fx = FX, fy = FY;
5749
5750       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5751       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5752
5753       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5754         break;
5755
5756       scroll_x -= dx;
5757       scroll_y -= dy;
5758
5759       fx += dx * TILEX / 2;
5760       fy += dy * TILEY / 2;
5761
5762       ScrollLevel(dx, dy);
5763       DrawAllPlayers();
5764
5765       /* scroll in two steps of half tile size to make things smoother */
5766       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5767       FlushDisplay();
5768       Delay(wait_delay_value);
5769
5770       /* scroll second step to align at full tile size */
5771       BackToFront();
5772       Delay(wait_delay_value);
5773     }
5774
5775     DrawAllPlayers();
5776     BackToFront();
5777     Delay(wait_delay_value);
5778   }
5779 }
5780
5781 void RelocatePlayer(int jx, int jy, int el_player_raw)
5782 {
5783   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5784   int player_nr = GET_PLAYER_NR(el_player);
5785   struct PlayerInfo *player = &stored_player[player_nr];
5786   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5787   boolean no_delay = (tape.warp_forward);
5788   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5789   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5790   int old_jx = player->jx;
5791   int old_jy = player->jy;
5792   int old_element = Feld[old_jx][old_jy];
5793   int element = Feld[jx][jy];
5794   boolean player_relocated = (old_jx != jx || old_jy != jy);
5795
5796   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5797   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5798   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5799   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5800   int leave_side_horiz = move_dir_horiz;
5801   int leave_side_vert  = move_dir_vert;
5802   int enter_side = enter_side_horiz | enter_side_vert;
5803   int leave_side = leave_side_horiz | leave_side_vert;
5804
5805   if (player->GameOver)         /* do not reanimate dead player */
5806     return;
5807
5808   if (!player_relocated)        /* no need to relocate the player */
5809     return;
5810
5811   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5812   {
5813     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5814     DrawLevelField(jx, jy);
5815   }
5816
5817   if (player->present)
5818   {
5819     while (player->MovPos)
5820     {
5821       ScrollPlayer(player, SCROLL_GO_ON);
5822       ScrollScreen(NULL, SCROLL_GO_ON);
5823
5824       AdvanceFrameAndPlayerCounters(player->index_nr);
5825
5826       DrawPlayer(player);
5827
5828       BackToFront();
5829       Delay(wait_delay_value);
5830     }
5831
5832     DrawPlayer(player);         /* needed here only to cleanup last field */
5833     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5834
5835     player->is_moving = FALSE;
5836   }
5837
5838   if (IS_CUSTOM_ELEMENT(old_element))
5839     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5840                                CE_LEFT_BY_PLAYER,
5841                                player->index_bit, leave_side);
5842
5843   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5844                                       CE_PLAYER_LEAVES_X,
5845                                       player->index_bit, leave_side);
5846
5847   Feld[jx][jy] = el_player;
5848   InitPlayerField(jx, jy, el_player, TRUE);
5849
5850   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5851      possible that the relocation target field did not contain a player element,
5852      but a walkable element, to which the new player was relocated -- in this
5853      case, restore that (already initialized!) element on the player field */
5854   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5855   {
5856     Feld[jx][jy] = element;     /* restore previously existing element */
5857 #if 0
5858     /* !!! do not initialize already initialized element a second time !!! */
5859     /* (this causes at least problems with "element creation" CE trigger for
5860        already existing elements, and existing Sokoban fields counted twice) */
5861     InitField(jx, jy, FALSE);
5862 #endif
5863   }
5864
5865   /* only visually relocate centered player */
5866   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5867                      FALSE, level.instant_relocation);
5868
5869   TestIfPlayerTouchesBadThing(jx, jy);
5870   TestIfPlayerTouchesCustomElement(jx, jy);
5871
5872   if (IS_CUSTOM_ELEMENT(element))
5873     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5874                                player->index_bit, enter_side);
5875
5876   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5877                                       player->index_bit, enter_side);
5878
5879 #if 1
5880   if (player->is_switching)
5881   {
5882     /* ensure that relocation while still switching an element does not cause
5883        a new element to be treated as also switched directly after relocation
5884        (this is important for teleporter switches that teleport the player to
5885        a place where another teleporter switch is in the same direction, which
5886        would then incorrectly be treated as immediately switched before the
5887        direction key that caused the switch was released) */
5888
5889     player->switch_x += jx - old_jx;
5890     player->switch_y += jy - old_jy;
5891   }
5892 #endif
5893 }
5894
5895 void Explode(int ex, int ey, int phase, int mode)
5896 {
5897   int x, y;
5898   int last_phase;
5899   int border_element;
5900
5901   /* !!! eliminate this variable !!! */
5902   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5903
5904   if (game.explosions_delayed)
5905   {
5906     ExplodeField[ex][ey] = mode;
5907     return;
5908   }
5909
5910   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5911   {
5912     int center_element = Feld[ex][ey];
5913     int artwork_element, explosion_element;     /* set these values later */
5914
5915 #if 0
5916     /* --- This is only really needed (and now handled) in "Impact()". --- */
5917     /* do not explode moving elements that left the explode field in time */
5918     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5919         center_element == EL_EMPTY &&
5920         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5921       return;
5922 #endif
5923
5924 #if 0
5925     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5926     if (mode == EX_TYPE_NORMAL ||
5927         mode == EX_TYPE_CENTER ||
5928         mode == EX_TYPE_CROSS)
5929       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5930 #endif
5931
5932     /* remove things displayed in background while burning dynamite */
5933     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5934       Back[ex][ey] = 0;
5935
5936     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5937     {
5938       /* put moving element to center field (and let it explode there) */
5939       center_element = MovingOrBlocked2Element(ex, ey);
5940       RemoveMovingField(ex, ey);
5941       Feld[ex][ey] = center_element;
5942     }
5943
5944     /* now "center_element" is finally determined -- set related values now */
5945     artwork_element = center_element;           /* for custom player artwork */
5946     explosion_element = center_element;         /* for custom player artwork */
5947
5948     if (IS_PLAYER(ex, ey))
5949     {
5950       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5951
5952       artwork_element = stored_player[player_nr].artwork_element;
5953
5954       if (level.use_explosion_element[player_nr])
5955       {
5956         explosion_element = level.explosion_element[player_nr];
5957         artwork_element = explosion_element;
5958       }
5959     }
5960
5961 #if 1
5962     if (mode == EX_TYPE_NORMAL ||
5963         mode == EX_TYPE_CENTER ||
5964         mode == EX_TYPE_CROSS)
5965       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5966 #endif
5967
5968     last_phase = element_info[explosion_element].explosion_delay + 1;
5969
5970     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5971     {
5972       int xx = x - ex + 1;
5973       int yy = y - ey + 1;
5974       int element;
5975
5976       if (!IN_LEV_FIELD(x, y) ||
5977           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5978           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5979         continue;
5980
5981       element = Feld[x][y];
5982
5983       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5984       {
5985         element = MovingOrBlocked2Element(x, y);
5986
5987         if (!IS_EXPLOSION_PROOF(element))
5988           RemoveMovingField(x, y);
5989       }
5990
5991       /* indestructible elements can only explode in center (but not flames) */
5992       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5993                                            mode == EX_TYPE_BORDER)) ||
5994           element == EL_FLAMES)
5995         continue;
5996
5997       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5998          behaviour, for example when touching a yamyam that explodes to rocks
5999          with active deadly shield, a rock is created under the player !!! */
6000       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6001 #if 0
6002       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6003           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6004            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6005 #else
6006       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6007 #endif
6008       {
6009         if (IS_ACTIVE_BOMB(element))
6010         {
6011           /* re-activate things under the bomb like gate or penguin */
6012           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6013           Back[x][y] = 0;
6014         }
6015
6016         continue;
6017       }
6018
6019       /* save walkable background elements while explosion on same tile */
6020       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6021           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6022         Back[x][y] = element;
6023
6024       /* ignite explodable elements reached by other explosion */
6025       if (element == EL_EXPLOSION)
6026         element = Store2[x][y];
6027
6028       if (AmoebaNr[x][y] &&
6029           (element == EL_AMOEBA_FULL ||
6030            element == EL_BD_AMOEBA ||
6031            element == EL_AMOEBA_GROWING))
6032       {
6033         AmoebaCnt[AmoebaNr[x][y]]--;
6034         AmoebaCnt2[AmoebaNr[x][y]]--;
6035       }
6036
6037       RemoveField(x, y);
6038
6039       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6040       {
6041         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6042
6043         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6044
6045         if (PLAYERINFO(ex, ey)->use_murphy)
6046           Store[x][y] = EL_EMPTY;
6047       }
6048
6049       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6050          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6051       else if (ELEM_IS_PLAYER(center_element))
6052         Store[x][y] = EL_EMPTY;
6053       else if (center_element == EL_YAMYAM)
6054         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6055       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6056         Store[x][y] = element_info[center_element].content.e[xx][yy];
6057 #if 1
6058       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6059          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6060          otherwise) -- FIX THIS !!! */
6061       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6062         Store[x][y] = element_info[element].content.e[1][1];
6063 #else
6064       else if (!CAN_EXPLODE(element))
6065         Store[x][y] = element_info[element].content.e[1][1];
6066 #endif
6067       else
6068         Store[x][y] = EL_EMPTY;
6069
6070       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6071           center_element == EL_AMOEBA_TO_DIAMOND)
6072         Store2[x][y] = element;
6073
6074       Feld[x][y] = EL_EXPLOSION;
6075       GfxElement[x][y] = artwork_element;
6076
6077       ExplodePhase[x][y] = 1;
6078       ExplodeDelay[x][y] = last_phase;
6079
6080       Stop[x][y] = TRUE;
6081     }
6082
6083     if (center_element == EL_YAMYAM)
6084       game.yamyam_content_nr =
6085         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6086
6087     return;
6088   }
6089
6090   if (Stop[ex][ey])
6091     return;
6092
6093   x = ex;
6094   y = ey;
6095
6096   if (phase == 1)
6097     GfxFrame[x][y] = 0;         /* restart explosion animation */
6098
6099   last_phase = ExplodeDelay[x][y];
6100
6101   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6102
6103 #ifdef DEBUG
6104
6105   /* activate this even in non-DEBUG version until cause for crash in
6106      getGraphicAnimationFrame() (see below) is found and eliminated */
6107
6108 #endif
6109 #if 1
6110
6111 #if 1
6112   /* this can happen if the player leaves an explosion just in time */
6113   if (GfxElement[x][y] == EL_UNDEFINED)
6114     GfxElement[x][y] = EL_EMPTY;
6115 #else
6116   if (GfxElement[x][y] == EL_UNDEFINED)
6117   {
6118     printf("\n\n");
6119     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6120     printf("Explode(): This should never happen!\n");
6121     printf("\n\n");
6122
6123     GfxElement[x][y] = EL_EMPTY;
6124   }
6125 #endif
6126
6127 #endif
6128
6129   border_element = Store2[x][y];
6130   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6131     border_element = StorePlayer[x][y];
6132
6133   if (phase == element_info[border_element].ignition_delay ||
6134       phase == last_phase)
6135   {
6136     boolean border_explosion = FALSE;
6137
6138     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6139         !PLAYER_EXPLOSION_PROTECTED(x, y))
6140     {
6141       KillPlayerUnlessExplosionProtected(x, y);
6142       border_explosion = TRUE;
6143     }
6144     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6145     {
6146       Feld[x][y] = Store2[x][y];
6147       Store2[x][y] = 0;
6148       Bang(x, y);
6149       border_explosion = TRUE;
6150     }
6151     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6152     {
6153       AmoebeUmwandeln(x, y);
6154       Store2[x][y] = 0;
6155       border_explosion = TRUE;
6156     }
6157
6158     /* if an element just explodes due to another explosion (chain-reaction),
6159        do not immediately end the new explosion when it was the last frame of
6160        the explosion (as it would be done in the following "if"-statement!) */
6161     if (border_explosion && phase == last_phase)
6162       return;
6163   }
6164
6165   if (phase == last_phase)
6166   {
6167     int element;
6168
6169     element = Feld[x][y] = Store[x][y];
6170     Store[x][y] = Store2[x][y] = 0;
6171     GfxElement[x][y] = EL_UNDEFINED;
6172
6173     /* player can escape from explosions and might therefore be still alive */
6174     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6175         element <= EL_PLAYER_IS_EXPLODING_4)
6176     {
6177       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6178       int explosion_element = EL_PLAYER_1 + player_nr;
6179       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6180       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6181
6182       if (level.use_explosion_element[player_nr])
6183         explosion_element = level.explosion_element[player_nr];
6184
6185       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6186                     element_info[explosion_element].content.e[xx][yy]);
6187     }
6188
6189     /* restore probably existing indestructible background element */
6190     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6191       element = Feld[x][y] = Back[x][y];
6192     Back[x][y] = 0;
6193
6194     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6195     GfxDir[x][y] = MV_NONE;
6196     ChangeDelay[x][y] = 0;
6197     ChangePage[x][y] = -1;
6198
6199 #if USE_NEW_CUSTOM_VALUE
6200     CustomValue[x][y] = 0;
6201 #endif
6202
6203     InitField_WithBug2(x, y, FALSE);
6204
6205     TEST_DrawLevelField(x, y);
6206
6207     TestIfElementTouchesCustomElement(x, y);
6208
6209     if (GFX_CRUMBLED(element))
6210       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6211
6212     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6213       StorePlayer[x][y] = 0;
6214
6215     if (ELEM_IS_PLAYER(element))
6216       RelocatePlayer(x, y, element);
6217   }
6218   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6219   {
6220     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6221     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6222
6223     if (phase == delay)
6224       TEST_DrawLevelFieldCrumbled(x, y);
6225
6226     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6227     {
6228       DrawLevelElement(x, y, Back[x][y]);
6229       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6230     }
6231     else if (IS_WALKABLE_UNDER(Back[x][y]))
6232     {
6233       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6234       DrawLevelElementThruMask(x, y, Back[x][y]);
6235     }
6236     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6237       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6238   }
6239 }
6240
6241 void DynaExplode(int ex, int ey)
6242 {
6243   int i, j;
6244   int dynabomb_element = Feld[ex][ey];
6245   int dynabomb_size = 1;
6246   boolean dynabomb_xl = FALSE;
6247   struct PlayerInfo *player;
6248   static int xy[4][2] =
6249   {
6250     { 0, -1 },
6251     { -1, 0 },
6252     { +1, 0 },
6253     { 0, +1 }
6254   };
6255
6256   if (IS_ACTIVE_BOMB(dynabomb_element))
6257   {
6258     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6259     dynabomb_size = player->dynabomb_size;
6260     dynabomb_xl = player->dynabomb_xl;
6261     player->dynabombs_left++;
6262   }
6263
6264   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6265
6266   for (i = 0; i < NUM_DIRECTIONS; i++)
6267   {
6268     for (j = 1; j <= dynabomb_size; j++)
6269     {
6270       int x = ex + j * xy[i][0];
6271       int y = ey + j * xy[i][1];
6272       int element;
6273
6274       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6275         break;
6276
6277       element = Feld[x][y];
6278
6279       /* do not restart explosions of fields with active bombs */
6280       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6281         continue;
6282
6283       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6284
6285       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6286           !IS_DIGGABLE(element) && !dynabomb_xl)
6287         break;
6288     }
6289   }
6290 }
6291
6292 void Bang(int x, int y)
6293 {
6294   int element = MovingOrBlocked2Element(x, y);
6295   int explosion_type = EX_TYPE_NORMAL;
6296
6297   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6298   {
6299     struct PlayerInfo *player = PLAYERINFO(x, y);
6300
6301 #if USE_FIX_CE_ACTION_WITH_PLAYER
6302     element = Feld[x][y] = player->initial_element;
6303 #else
6304     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6305                             player->element_nr);
6306 #endif
6307
6308     if (level.use_explosion_element[player->index_nr])
6309     {
6310       int explosion_element = level.explosion_element[player->index_nr];
6311
6312       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6313         explosion_type = EX_TYPE_CROSS;
6314       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6315         explosion_type = EX_TYPE_CENTER;
6316     }
6317   }
6318
6319   switch (element)
6320   {
6321     case EL_BUG:
6322     case EL_SPACESHIP:
6323     case EL_BD_BUTTERFLY:
6324     case EL_BD_FIREFLY:
6325     case EL_YAMYAM:
6326     case EL_DARK_YAMYAM:
6327     case EL_ROBOT:
6328     case EL_PACMAN:
6329     case EL_MOLE:
6330       RaiseScoreElement(element);
6331       break;
6332
6333     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6334     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6335     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6336     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6337     case EL_DYNABOMB_INCREASE_NUMBER:
6338     case EL_DYNABOMB_INCREASE_SIZE:
6339     case EL_DYNABOMB_INCREASE_POWER:
6340       explosion_type = EX_TYPE_DYNA;
6341       break;
6342
6343     case EL_DC_LANDMINE:
6344 #if 0
6345     case EL_EM_EXIT_OPEN:
6346     case EL_EM_STEEL_EXIT_OPEN:
6347 #endif
6348       explosion_type = EX_TYPE_CENTER;
6349       break;
6350
6351     case EL_PENGUIN:
6352     case EL_LAMP:
6353     case EL_LAMP_ACTIVE:
6354     case EL_AMOEBA_TO_DIAMOND:
6355       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6356         explosion_type = EX_TYPE_CENTER;
6357       break;
6358
6359     default:
6360       if (element_info[element].explosion_type == EXPLODES_CROSS)
6361         explosion_type = EX_TYPE_CROSS;
6362       else if (element_info[element].explosion_type == EXPLODES_1X1)
6363         explosion_type = EX_TYPE_CENTER;
6364       break;
6365   }
6366
6367   if (explosion_type == EX_TYPE_DYNA)
6368     DynaExplode(x, y);
6369   else
6370     Explode(x, y, EX_PHASE_START, explosion_type);
6371
6372   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6373 }
6374
6375 void SplashAcid(int x, int y)
6376 {
6377   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6378       (!IN_LEV_FIELD(x - 1, y - 2) ||
6379        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6380     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6381
6382   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6383       (!IN_LEV_FIELD(x + 1, y - 2) ||
6384        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6385     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6386
6387   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6388 }
6389
6390 static void InitBeltMovement()
6391 {
6392   static int belt_base_element[4] =
6393   {
6394     EL_CONVEYOR_BELT_1_LEFT,
6395     EL_CONVEYOR_BELT_2_LEFT,
6396     EL_CONVEYOR_BELT_3_LEFT,
6397     EL_CONVEYOR_BELT_4_LEFT
6398   };
6399   static int belt_base_active_element[4] =
6400   {
6401     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6402     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6403     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6404     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6405   };
6406
6407   int x, y, i, j;
6408
6409   /* set frame order for belt animation graphic according to belt direction */
6410   for (i = 0; i < NUM_BELTS; i++)
6411   {
6412     int belt_nr = i;
6413
6414     for (j = 0; j < NUM_BELT_PARTS; j++)
6415     {
6416       int element = belt_base_active_element[belt_nr] + j;
6417       int graphic_1 = el2img(element);
6418       int graphic_2 = el2panelimg(element);
6419
6420       if (game.belt_dir[i] == MV_LEFT)
6421       {
6422         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6423         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6424       }
6425       else
6426       {
6427         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6428         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6429       }
6430     }
6431   }
6432
6433   SCAN_PLAYFIELD(x, y)
6434   {
6435     int element = Feld[x][y];
6436
6437     for (i = 0; i < NUM_BELTS; i++)
6438     {
6439       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6440       {
6441         int e_belt_nr = getBeltNrFromBeltElement(element);
6442         int belt_nr = i;
6443
6444         if (e_belt_nr == belt_nr)
6445         {
6446           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6447
6448           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6449         }
6450       }
6451     }
6452   }
6453 }
6454
6455 static void ToggleBeltSwitch(int x, int y)
6456 {
6457   static int belt_base_element[4] =
6458   {
6459     EL_CONVEYOR_BELT_1_LEFT,
6460     EL_CONVEYOR_BELT_2_LEFT,
6461     EL_CONVEYOR_BELT_3_LEFT,
6462     EL_CONVEYOR_BELT_4_LEFT
6463   };
6464   static int belt_base_active_element[4] =
6465   {
6466     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6467     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6468     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6469     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6470   };
6471   static int belt_base_switch_element[4] =
6472   {
6473     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6474     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6475     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6476     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6477   };
6478   static int belt_move_dir[4] =
6479   {
6480     MV_LEFT,
6481     MV_NONE,
6482     MV_RIGHT,
6483     MV_NONE,
6484   };
6485
6486   int element = Feld[x][y];
6487   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6488   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6489   int belt_dir = belt_move_dir[belt_dir_nr];
6490   int xx, yy, i;
6491
6492   if (!IS_BELT_SWITCH(element))
6493     return;
6494
6495   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6496   game.belt_dir[belt_nr] = belt_dir;
6497
6498   if (belt_dir_nr == 3)
6499     belt_dir_nr = 1;
6500
6501   /* set frame order for belt animation graphic according to belt direction */
6502   for (i = 0; i < NUM_BELT_PARTS; i++)
6503   {
6504     int element = belt_base_active_element[belt_nr] + i;
6505     int graphic_1 = el2img(element);
6506     int graphic_2 = el2panelimg(element);
6507
6508     if (belt_dir == MV_LEFT)
6509     {
6510       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6511       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6512     }
6513     else
6514     {
6515       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6516       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6517     }
6518   }
6519
6520   SCAN_PLAYFIELD(xx, yy)
6521   {
6522     int element = Feld[xx][yy];
6523
6524     if (IS_BELT_SWITCH(element))
6525     {
6526       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6527
6528       if (e_belt_nr == belt_nr)
6529       {
6530         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6531         TEST_DrawLevelField(xx, yy);
6532       }
6533     }
6534     else if (IS_BELT(element) && belt_dir != MV_NONE)
6535     {
6536       int e_belt_nr = getBeltNrFromBeltElement(element);
6537
6538       if (e_belt_nr == belt_nr)
6539       {
6540         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6541
6542         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6543         TEST_DrawLevelField(xx, yy);
6544       }
6545     }
6546     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6547     {
6548       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6549
6550       if (e_belt_nr == belt_nr)
6551       {
6552         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6553
6554         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6555         TEST_DrawLevelField(xx, yy);
6556       }
6557     }
6558   }
6559 }
6560
6561 static void ToggleSwitchgateSwitch(int x, int y)
6562 {
6563   int xx, yy;
6564
6565   game.switchgate_pos = !game.switchgate_pos;
6566
6567   SCAN_PLAYFIELD(xx, yy)
6568   {
6569     int element = Feld[xx][yy];
6570
6571 #if !USE_BOTH_SWITCHGATE_SWITCHES
6572     if (element == EL_SWITCHGATE_SWITCH_UP ||
6573         element == EL_SWITCHGATE_SWITCH_DOWN)
6574     {
6575       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6576       TEST_DrawLevelField(xx, yy);
6577     }
6578     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6579              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6580     {
6581       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6582       TEST_DrawLevelField(xx, yy);
6583     }
6584 #else
6585     if (element == EL_SWITCHGATE_SWITCH_UP)
6586     {
6587       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6588       TEST_DrawLevelField(xx, yy);
6589     }
6590     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6591     {
6592       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6593       TEST_DrawLevelField(xx, yy);
6594     }
6595     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6596     {
6597       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6598       TEST_DrawLevelField(xx, yy);
6599     }
6600     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6601     {
6602       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6603       TEST_DrawLevelField(xx, yy);
6604     }
6605 #endif
6606     else if (element == EL_SWITCHGATE_OPEN ||
6607              element == EL_SWITCHGATE_OPENING)
6608     {
6609       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6610
6611       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6612     }
6613     else if (element == EL_SWITCHGATE_CLOSED ||
6614              element == EL_SWITCHGATE_CLOSING)
6615     {
6616       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6617
6618       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6619     }
6620   }
6621 }
6622
6623 static int getInvisibleActiveFromInvisibleElement(int element)
6624 {
6625   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6626           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6627           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6628           element);
6629 }
6630
6631 static int getInvisibleFromInvisibleActiveElement(int element)
6632 {
6633   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6634           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6635           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6636           element);
6637 }
6638
6639 static void RedrawAllLightSwitchesAndInvisibleElements()
6640 {
6641   int x, y;
6642
6643   SCAN_PLAYFIELD(x, y)
6644   {
6645     int element = Feld[x][y];
6646
6647     if (element == EL_LIGHT_SWITCH &&
6648         game.light_time_left > 0)
6649     {
6650       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6651       TEST_DrawLevelField(x, y);
6652     }
6653     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6654              game.light_time_left == 0)
6655     {
6656       Feld[x][y] = EL_LIGHT_SWITCH;
6657       TEST_DrawLevelField(x, y);
6658     }
6659     else if (element == EL_EMC_DRIPPER &&
6660              game.light_time_left > 0)
6661     {
6662       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6663       TEST_DrawLevelField(x, y);
6664     }
6665     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6666              game.light_time_left == 0)
6667     {
6668       Feld[x][y] = EL_EMC_DRIPPER;
6669       TEST_DrawLevelField(x, y);
6670     }
6671     else if (element == EL_INVISIBLE_STEELWALL ||
6672              element == EL_INVISIBLE_WALL ||
6673              element == EL_INVISIBLE_SAND)
6674     {
6675       if (game.light_time_left > 0)
6676         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6677
6678       TEST_DrawLevelField(x, y);
6679
6680       /* uncrumble neighbour fields, if needed */
6681       if (element == EL_INVISIBLE_SAND)
6682         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6683     }
6684     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6685              element == EL_INVISIBLE_WALL_ACTIVE ||
6686              element == EL_INVISIBLE_SAND_ACTIVE)
6687     {
6688       if (game.light_time_left == 0)
6689         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6690
6691       TEST_DrawLevelField(x, y);
6692
6693       /* re-crumble neighbour fields, if needed */
6694       if (element == EL_INVISIBLE_SAND)
6695         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6696     }
6697   }
6698 }
6699
6700 static void RedrawAllInvisibleElementsForLenses()
6701 {
6702   int x, y;
6703
6704   SCAN_PLAYFIELD(x, y)
6705   {
6706     int element = Feld[x][y];
6707
6708     if (element == EL_EMC_DRIPPER &&
6709         game.lenses_time_left > 0)
6710     {
6711       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6712       TEST_DrawLevelField(x, y);
6713     }
6714     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6715              game.lenses_time_left == 0)
6716     {
6717       Feld[x][y] = EL_EMC_DRIPPER;
6718       TEST_DrawLevelField(x, y);
6719     }
6720     else if (element == EL_INVISIBLE_STEELWALL ||
6721              element == EL_INVISIBLE_WALL ||
6722              element == EL_INVISIBLE_SAND)
6723     {
6724       if (game.lenses_time_left > 0)
6725         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6726
6727       TEST_DrawLevelField(x, y);
6728
6729       /* uncrumble neighbour fields, if needed */
6730       if (element == EL_INVISIBLE_SAND)
6731         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6732     }
6733     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6734              element == EL_INVISIBLE_WALL_ACTIVE ||
6735              element == EL_INVISIBLE_SAND_ACTIVE)
6736     {
6737       if (game.lenses_time_left == 0)
6738         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6739
6740       TEST_DrawLevelField(x, y);
6741
6742       /* re-crumble neighbour fields, if needed */
6743       if (element == EL_INVISIBLE_SAND)
6744         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6745     }
6746   }
6747 }
6748
6749 static void RedrawAllInvisibleElementsForMagnifier()
6750 {
6751   int x, y;
6752
6753   SCAN_PLAYFIELD(x, y)
6754   {
6755     int element = Feld[x][y];
6756
6757     if (element == EL_EMC_FAKE_GRASS &&
6758         game.magnify_time_left > 0)
6759     {
6760       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6761       TEST_DrawLevelField(x, y);
6762     }
6763     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6764              game.magnify_time_left == 0)
6765     {
6766       Feld[x][y] = EL_EMC_FAKE_GRASS;
6767       TEST_DrawLevelField(x, y);
6768     }
6769     else if (IS_GATE_GRAY(element) &&
6770              game.magnify_time_left > 0)
6771     {
6772       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6773                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6774                     IS_EM_GATE_GRAY(element) ?
6775                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6776                     IS_EMC_GATE_GRAY(element) ?
6777                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6778                     IS_DC_GATE_GRAY(element) ?
6779                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6780                     element);
6781       TEST_DrawLevelField(x, y);
6782     }
6783     else if (IS_GATE_GRAY_ACTIVE(element) &&
6784              game.magnify_time_left == 0)
6785     {
6786       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6787                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6788                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6789                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6790                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6791                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6792                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6793                     EL_DC_GATE_WHITE_GRAY :
6794                     element);
6795       TEST_DrawLevelField(x, y);
6796     }
6797   }
6798 }
6799
6800 static void ToggleLightSwitch(int x, int y)
6801 {
6802   int element = Feld[x][y];
6803
6804   game.light_time_left =
6805     (element == EL_LIGHT_SWITCH ?
6806      level.time_light * FRAMES_PER_SECOND : 0);
6807
6808   RedrawAllLightSwitchesAndInvisibleElements();
6809 }
6810
6811 static void ActivateTimegateSwitch(int x, int y)
6812 {
6813   int xx, yy;
6814
6815   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6816
6817   SCAN_PLAYFIELD(xx, yy)
6818   {
6819     int element = Feld[xx][yy];
6820
6821     if (element == EL_TIMEGATE_CLOSED ||
6822         element == EL_TIMEGATE_CLOSING)
6823     {
6824       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6825       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6826     }
6827
6828     /*
6829     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6830     {
6831       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6832       TEST_DrawLevelField(xx, yy);
6833     }
6834     */
6835
6836   }
6837
6838 #if 1
6839   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6840                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6841 #else
6842   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6843 #endif
6844 }
6845
6846 void Impact(int x, int y)
6847 {
6848   boolean last_line = (y == lev_fieldy - 1);
6849   boolean object_hit = FALSE;
6850   boolean impact = (last_line || object_hit);
6851   int element = Feld[x][y];
6852   int smashed = EL_STEELWALL;
6853
6854   if (!last_line)       /* check if element below was hit */
6855   {
6856     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6857       return;
6858
6859     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6860                                          MovDir[x][y + 1] != MV_DOWN ||
6861                                          MovPos[x][y + 1] <= TILEY / 2));
6862
6863     /* do not smash moving elements that left the smashed field in time */
6864     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6865         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6866       object_hit = FALSE;
6867
6868 #if USE_QUICKSAND_IMPACT_BUGFIX
6869     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6870     {
6871       RemoveMovingField(x, y + 1);
6872       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6873       Feld[x][y + 2] = EL_ROCK;
6874       TEST_DrawLevelField(x, y + 2);
6875
6876       object_hit = TRUE;
6877     }
6878
6879     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6880     {
6881       RemoveMovingField(x, y + 1);
6882       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6883       Feld[x][y + 2] = EL_ROCK;
6884       TEST_DrawLevelField(x, y + 2);
6885
6886       object_hit = TRUE;
6887     }
6888 #endif
6889
6890     if (object_hit)
6891       smashed = MovingOrBlocked2Element(x, y + 1);
6892
6893     impact = (last_line || object_hit);
6894   }
6895
6896   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6897   {
6898     SplashAcid(x, y + 1);
6899     return;
6900   }
6901
6902   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6903   /* only reset graphic animation if graphic really changes after impact */
6904   if (impact &&
6905       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6906   {
6907     ResetGfxAnimation(x, y);
6908     TEST_DrawLevelField(x, y);
6909   }
6910
6911   if (impact && CAN_EXPLODE_IMPACT(element))
6912   {
6913     Bang(x, y);
6914     return;
6915   }
6916   else if (impact && element == EL_PEARL &&
6917            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6918   {
6919     ResetGfxAnimation(x, y);
6920
6921     Feld[x][y] = EL_PEARL_BREAKING;
6922     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6923     return;
6924   }
6925   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6926   {
6927     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6928
6929     return;
6930   }
6931
6932   if (impact && element == EL_AMOEBA_DROP)
6933   {
6934     if (object_hit && IS_PLAYER(x, y + 1))
6935       KillPlayerUnlessEnemyProtected(x, y + 1);
6936     else if (object_hit && smashed == EL_PENGUIN)
6937       Bang(x, y + 1);
6938     else
6939     {
6940       Feld[x][y] = EL_AMOEBA_GROWING;
6941       Store[x][y] = EL_AMOEBA_WET;
6942
6943       ResetRandomAnimationValue(x, y);
6944     }
6945     return;
6946   }
6947
6948   if (object_hit)               /* check which object was hit */
6949   {
6950     if ((CAN_PASS_MAGIC_WALL(element) && 
6951          (smashed == EL_MAGIC_WALL ||
6952           smashed == EL_BD_MAGIC_WALL)) ||
6953         (CAN_PASS_DC_MAGIC_WALL(element) &&
6954          smashed == EL_DC_MAGIC_WALL))
6955     {
6956       int xx, yy;
6957       int activated_magic_wall =
6958         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6959          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6960          EL_DC_MAGIC_WALL_ACTIVE);
6961
6962       /* activate magic wall / mill */
6963       SCAN_PLAYFIELD(xx, yy)
6964       {
6965         if (Feld[xx][yy] == smashed)
6966           Feld[xx][yy] = activated_magic_wall;
6967       }
6968
6969       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6970       game.magic_wall_active = TRUE;
6971
6972       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6973                             SND_MAGIC_WALL_ACTIVATING :
6974                             smashed == EL_BD_MAGIC_WALL ?
6975                             SND_BD_MAGIC_WALL_ACTIVATING :
6976                             SND_DC_MAGIC_WALL_ACTIVATING));
6977     }
6978
6979     if (IS_PLAYER(x, y + 1))
6980     {
6981       if (CAN_SMASH_PLAYER(element))
6982       {
6983         KillPlayerUnlessEnemyProtected(x, y + 1);
6984         return;
6985       }
6986     }
6987     else if (smashed == EL_PENGUIN)
6988     {
6989       if (CAN_SMASH_PLAYER(element))
6990       {
6991         Bang(x, y + 1);
6992         return;
6993       }
6994     }
6995     else if (element == EL_BD_DIAMOND)
6996     {
6997       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6998       {
6999         Bang(x, y + 1);
7000         return;
7001       }
7002     }
7003     else if (((element == EL_SP_INFOTRON ||
7004                element == EL_SP_ZONK) &&
7005               (smashed == EL_SP_SNIKSNAK ||
7006                smashed == EL_SP_ELECTRON ||
7007                smashed == EL_SP_DISK_ORANGE)) ||
7008              (element == EL_SP_INFOTRON &&
7009               smashed == EL_SP_DISK_YELLOW))
7010     {
7011       Bang(x, y + 1);
7012       return;
7013     }
7014     else if (CAN_SMASH_EVERYTHING(element))
7015     {
7016       if (IS_CLASSIC_ENEMY(smashed) ||
7017           CAN_EXPLODE_SMASHED(smashed))
7018       {
7019         Bang(x, y + 1);
7020         return;
7021       }
7022       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7023       {
7024         if (smashed == EL_LAMP ||
7025             smashed == EL_LAMP_ACTIVE)
7026         {
7027           Bang(x, y + 1);
7028           return;
7029         }
7030         else if (smashed == EL_NUT)
7031         {
7032           Feld[x][y + 1] = EL_NUT_BREAKING;
7033           PlayLevelSound(x, y, SND_NUT_BREAKING);
7034           RaiseScoreElement(EL_NUT);
7035           return;
7036         }
7037         else if (smashed == EL_PEARL)
7038         {
7039           ResetGfxAnimation(x, y);
7040
7041           Feld[x][y + 1] = EL_PEARL_BREAKING;
7042           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7043           return;
7044         }
7045         else if (smashed == EL_DIAMOND)
7046         {
7047           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7048           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7049           return;
7050         }
7051         else if (IS_BELT_SWITCH(smashed))
7052         {
7053           ToggleBeltSwitch(x, y + 1);
7054         }
7055         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7056                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7057                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7058                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7059         {
7060           ToggleSwitchgateSwitch(x, y + 1);
7061         }
7062         else if (smashed == EL_LIGHT_SWITCH ||
7063                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7064         {
7065           ToggleLightSwitch(x, y + 1);
7066         }
7067         else
7068         {
7069 #if 0
7070           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7071 #endif
7072
7073           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7074
7075           CheckElementChangeBySide(x, y + 1, smashed, element,
7076                                    CE_SWITCHED, CH_SIDE_TOP);
7077           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7078                                             CH_SIDE_TOP);
7079         }
7080       }
7081       else
7082       {
7083         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7084       }
7085     }
7086   }
7087
7088   /* play sound of magic wall / mill */
7089   if (!last_line &&
7090       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7091        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7092        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7093   {
7094     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7095       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7096     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7097       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7098     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7099       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7100
7101     return;
7102   }
7103
7104   /* play sound of object that hits the ground */
7105   if (last_line || object_hit)
7106     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7107 }
7108
7109 inline static void TurnRoundExt(int x, int y)
7110 {
7111   static struct
7112   {
7113     int dx, dy;
7114   } move_xy[] =
7115   {
7116     {  0,  0 },
7117     { -1,  0 },
7118     { +1,  0 },
7119     {  0,  0 },
7120     {  0, -1 },
7121     {  0,  0 }, { 0, 0 }, { 0, 0 },
7122     {  0, +1 }
7123   };
7124   static struct
7125   {
7126     int left, right, back;
7127   } turn[] =
7128   {
7129     { 0,        0,              0        },
7130     { MV_DOWN,  MV_UP,          MV_RIGHT },
7131     { MV_UP,    MV_DOWN,        MV_LEFT  },
7132     { 0,        0,              0        },
7133     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7134     { 0,        0,              0        },
7135     { 0,        0,              0        },
7136     { 0,        0,              0        },
7137     { MV_RIGHT, MV_LEFT,        MV_UP    }
7138   };
7139
7140   int element = Feld[x][y];
7141   int move_pattern = element_info[element].move_pattern;
7142
7143   int old_move_dir = MovDir[x][y];
7144   int left_dir  = turn[old_move_dir].left;
7145   int right_dir = turn[old_move_dir].right;
7146   int back_dir  = turn[old_move_dir].back;
7147
7148   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7149   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7150   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7151   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7152
7153   int left_x  = x + left_dx,  left_y  = y + left_dy;
7154   int right_x = x + right_dx, right_y = y + right_dy;
7155   int move_x  = x + move_dx,  move_y  = y + move_dy;
7156
7157   int xx, yy;
7158
7159   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7160   {
7161     TestIfBadThingTouchesOtherBadThing(x, y);
7162
7163     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7164       MovDir[x][y] = right_dir;
7165     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7166       MovDir[x][y] = left_dir;
7167
7168     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7169       MovDelay[x][y] = 9;
7170     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7171       MovDelay[x][y] = 1;
7172   }
7173   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7174   {
7175     TestIfBadThingTouchesOtherBadThing(x, y);
7176
7177     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7178       MovDir[x][y] = left_dir;
7179     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7180       MovDir[x][y] = right_dir;
7181
7182     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7183       MovDelay[x][y] = 9;
7184     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7185       MovDelay[x][y] = 1;
7186   }
7187   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7188   {
7189     TestIfBadThingTouchesOtherBadThing(x, y);
7190
7191     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7192       MovDir[x][y] = left_dir;
7193     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7194       MovDir[x][y] = right_dir;
7195
7196     if (MovDir[x][y] != old_move_dir)
7197       MovDelay[x][y] = 9;
7198   }
7199   else if (element == EL_YAMYAM)
7200   {
7201     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7202     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7203
7204     if (can_turn_left && can_turn_right)
7205       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7206     else if (can_turn_left)
7207       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7208     else if (can_turn_right)
7209       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7210     else
7211       MovDir[x][y] = back_dir;
7212
7213     MovDelay[x][y] = 16 + 16 * RND(3);
7214   }
7215   else if (element == EL_DARK_YAMYAM)
7216   {
7217     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7218                                                          left_x, left_y);
7219     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7220                                                          right_x, right_y);
7221
7222     if (can_turn_left && can_turn_right)
7223       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7224     else if (can_turn_left)
7225       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7226     else if (can_turn_right)
7227       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7228     else
7229       MovDir[x][y] = back_dir;
7230
7231     MovDelay[x][y] = 16 + 16 * RND(3);
7232   }
7233   else if (element == EL_PACMAN)
7234   {
7235     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7236     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7237
7238     if (can_turn_left && can_turn_right)
7239       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7240     else if (can_turn_left)
7241       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7242     else if (can_turn_right)
7243       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7244     else
7245       MovDir[x][y] = back_dir;
7246
7247     MovDelay[x][y] = 6 + RND(40);
7248   }
7249   else if (element == EL_PIG)
7250   {
7251     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7252     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7253     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7254     boolean should_turn_left, should_turn_right, should_move_on;
7255     int rnd_value = 24;
7256     int rnd = RND(rnd_value);
7257
7258     should_turn_left = (can_turn_left &&
7259                         (!can_move_on ||
7260                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7261                                                    y + back_dy + left_dy)));
7262     should_turn_right = (can_turn_right &&
7263                          (!can_move_on ||
7264                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7265                                                     y + back_dy + right_dy)));
7266     should_move_on = (can_move_on &&
7267                       (!can_turn_left ||
7268                        !can_turn_right ||
7269                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7270                                                  y + move_dy + left_dy) ||
7271                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7272                                                  y + move_dy + right_dy)));
7273
7274     if (should_turn_left || should_turn_right || should_move_on)
7275     {
7276       if (should_turn_left && should_turn_right && should_move_on)
7277         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7278                         rnd < 2 * rnd_value / 3 ? right_dir :
7279                         old_move_dir);
7280       else if (should_turn_left && should_turn_right)
7281         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7282       else if (should_turn_left && should_move_on)
7283         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7284       else if (should_turn_right && should_move_on)
7285         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7286       else if (should_turn_left)
7287         MovDir[x][y] = left_dir;
7288       else if (should_turn_right)
7289         MovDir[x][y] = right_dir;
7290       else if (should_move_on)
7291         MovDir[x][y] = old_move_dir;
7292     }
7293     else if (can_move_on && rnd > rnd_value / 8)
7294       MovDir[x][y] = old_move_dir;
7295     else if (can_turn_left && can_turn_right)
7296       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7297     else if (can_turn_left && rnd > rnd_value / 8)
7298       MovDir[x][y] = left_dir;
7299     else if (can_turn_right && rnd > rnd_value/8)
7300       MovDir[x][y] = right_dir;
7301     else
7302       MovDir[x][y] = back_dir;
7303
7304     xx = x + move_xy[MovDir[x][y]].dx;
7305     yy = y + move_xy[MovDir[x][y]].dy;
7306
7307     if (!IN_LEV_FIELD(xx, yy) ||
7308         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7309       MovDir[x][y] = old_move_dir;
7310
7311     MovDelay[x][y] = 0;
7312   }
7313   else if (element == EL_DRAGON)
7314   {
7315     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7316     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7317     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7318     int rnd_value = 24;
7319     int rnd = RND(rnd_value);
7320
7321     if (can_move_on && rnd > rnd_value / 8)
7322       MovDir[x][y] = old_move_dir;
7323     else if (can_turn_left && can_turn_right)
7324       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7325     else if (can_turn_left && rnd > rnd_value / 8)
7326       MovDir[x][y] = left_dir;
7327     else if (can_turn_right && rnd > rnd_value / 8)
7328       MovDir[x][y] = right_dir;
7329     else
7330       MovDir[x][y] = back_dir;
7331
7332     xx = x + move_xy[MovDir[x][y]].dx;
7333     yy = y + move_xy[MovDir[x][y]].dy;
7334
7335     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7336       MovDir[x][y] = old_move_dir;
7337
7338     MovDelay[x][y] = 0;
7339   }
7340   else if (element == EL_MOLE)
7341   {
7342     boolean can_move_on =
7343       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7344                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7345                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7346     if (!can_move_on)
7347     {
7348       boolean can_turn_left =
7349         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7350                               IS_AMOEBOID(Feld[left_x][left_y])));
7351
7352       boolean can_turn_right =
7353         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7354                               IS_AMOEBOID(Feld[right_x][right_y])));
7355
7356       if (can_turn_left && can_turn_right)
7357         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7358       else if (can_turn_left)
7359         MovDir[x][y] = left_dir;
7360       else
7361         MovDir[x][y] = right_dir;
7362     }
7363
7364     if (MovDir[x][y] != old_move_dir)
7365       MovDelay[x][y] = 9;
7366   }
7367   else if (element == EL_BALLOON)
7368   {
7369     MovDir[x][y] = game.wind_direction;
7370     MovDelay[x][y] = 0;
7371   }
7372   else if (element == EL_SPRING)
7373   {
7374 #if USE_NEW_SPRING_BUMPER
7375     if (MovDir[x][y] & MV_HORIZONTAL)
7376     {
7377       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7378           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7379       {
7380         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7381         ResetGfxAnimation(move_x, move_y);
7382         TEST_DrawLevelField(move_x, move_y);
7383
7384         MovDir[x][y] = back_dir;
7385       }
7386       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7387                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7388         MovDir[x][y] = MV_NONE;
7389     }
7390 #else
7391     if (MovDir[x][y] & MV_HORIZONTAL &&
7392         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7393          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7394       MovDir[x][y] = MV_NONE;
7395 #endif
7396
7397     MovDelay[x][y] = 0;
7398   }
7399   else if (element == EL_ROBOT ||
7400            element == EL_SATELLITE ||
7401            element == EL_PENGUIN ||
7402            element == EL_EMC_ANDROID)
7403   {
7404     int attr_x = -1, attr_y = -1;
7405
7406     if (AllPlayersGone)
7407     {
7408       attr_x = ExitX;
7409       attr_y = ExitY;
7410     }
7411     else
7412     {
7413       int i;
7414
7415       for (i = 0; i < MAX_PLAYERS; i++)
7416       {
7417         struct PlayerInfo *player = &stored_player[i];
7418         int jx = player->jx, jy = player->jy;
7419
7420         if (!player->active)
7421           continue;
7422
7423         if (attr_x == -1 ||
7424             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7425         {
7426           attr_x = jx;
7427           attr_y = jy;
7428         }
7429       }
7430     }
7431
7432     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7433         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7434          game.engine_version < VERSION_IDENT(3,1,0,0)))
7435     {
7436       attr_x = ZX;
7437       attr_y = ZY;
7438     }
7439
7440     if (element == EL_PENGUIN)
7441     {
7442       int i;
7443       static int xy[4][2] =
7444       {
7445         { 0, -1 },
7446         { -1, 0 },
7447         { +1, 0 },
7448         { 0, +1 }
7449       };
7450
7451       for (i = 0; i < NUM_DIRECTIONS; i++)
7452       {
7453         int ex = x + xy[i][0];
7454         int ey = y + xy[i][1];
7455
7456         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7457                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7458                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7459                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7460         {
7461           attr_x = ex;
7462           attr_y = ey;
7463           break;
7464         }
7465       }
7466     }
7467
7468     MovDir[x][y] = MV_NONE;
7469     if (attr_x < x)
7470       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7471     else if (attr_x > x)
7472       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7473     if (attr_y < y)
7474       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7475     else if (attr_y > y)
7476       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7477
7478     if (element == EL_ROBOT)
7479     {
7480       int newx, newy;
7481
7482       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7483         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7484       Moving2Blocked(x, y, &newx, &newy);
7485
7486       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7487         MovDelay[x][y] = 8 + 8 * !RND(3);
7488       else
7489         MovDelay[x][y] = 16;
7490     }
7491     else if (element == EL_PENGUIN)
7492     {
7493       int newx, newy;
7494
7495       MovDelay[x][y] = 1;
7496
7497       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7498       {
7499         boolean first_horiz = RND(2);
7500         int new_move_dir = MovDir[x][y];
7501
7502         MovDir[x][y] =
7503           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7504         Moving2Blocked(x, y, &newx, &newy);
7505
7506         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7507           return;
7508
7509         MovDir[x][y] =
7510           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7511         Moving2Blocked(x, y, &newx, &newy);
7512
7513         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7514           return;
7515
7516         MovDir[x][y] = old_move_dir;
7517         return;
7518       }
7519     }
7520     else if (element == EL_SATELLITE)
7521     {
7522       int newx, newy;
7523
7524       MovDelay[x][y] = 1;
7525
7526       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7527       {
7528         boolean first_horiz = RND(2);
7529         int new_move_dir = MovDir[x][y];
7530
7531         MovDir[x][y] =
7532           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7533         Moving2Blocked(x, y, &newx, &newy);
7534
7535         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7536           return;
7537
7538         MovDir[x][y] =
7539           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7540         Moving2Blocked(x, y, &newx, &newy);
7541
7542         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7543           return;
7544
7545         MovDir[x][y] = old_move_dir;
7546         return;
7547       }
7548     }
7549     else if (element == EL_EMC_ANDROID)
7550     {
7551       static int check_pos[16] =
7552       {
7553         -1,             /*  0 => (invalid)          */
7554         7,              /*  1 => MV_LEFT            */
7555         3,              /*  2 => MV_RIGHT           */
7556         -1,             /*  3 => (invalid)          */
7557         1,              /*  4 =>            MV_UP   */
7558         0,              /*  5 => MV_LEFT  | MV_UP   */
7559         2,              /*  6 => MV_RIGHT | MV_UP   */
7560         -1,             /*  7 => (invalid)          */
7561         5,              /*  8 =>            MV_DOWN */
7562         6,              /*  9 => MV_LEFT  | MV_DOWN */
7563         4,              /* 10 => MV_RIGHT | MV_DOWN */
7564         -1,             /* 11 => (invalid)          */
7565         -1,             /* 12 => (invalid)          */
7566         -1,             /* 13 => (invalid)          */
7567         -1,             /* 14 => (invalid)          */
7568         -1,             /* 15 => (invalid)          */
7569       };
7570       static struct
7571       {
7572         int dx, dy;
7573         int dir;
7574       } check_xy[8] =
7575       {
7576         { -1, -1,       MV_LEFT  | MV_UP   },
7577         {  0, -1,                  MV_UP   },
7578         { +1, -1,       MV_RIGHT | MV_UP   },
7579         { +1,  0,       MV_RIGHT           },
7580         { +1, +1,       MV_RIGHT | MV_DOWN },
7581         {  0, +1,                  MV_DOWN },
7582         { -1, +1,       MV_LEFT  | MV_DOWN },
7583         { -1,  0,       MV_LEFT            },
7584       };
7585       int start_pos, check_order;
7586       boolean can_clone = FALSE;
7587       int i;
7588
7589       /* check if there is any free field around current position */
7590       for (i = 0; i < 8; i++)
7591       {
7592         int newx = x + check_xy[i].dx;
7593         int newy = y + check_xy[i].dy;
7594
7595         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7596         {
7597           can_clone = TRUE;
7598
7599           break;
7600         }
7601       }
7602
7603       if (can_clone)            /* randomly find an element to clone */
7604       {
7605         can_clone = FALSE;
7606
7607         start_pos = check_pos[RND(8)];
7608         check_order = (RND(2) ? -1 : +1);
7609
7610         for (i = 0; i < 8; i++)
7611         {
7612           int pos_raw = start_pos + i * check_order;
7613           int pos = (pos_raw + 8) % 8;
7614           int newx = x + check_xy[pos].dx;
7615           int newy = y + check_xy[pos].dy;
7616
7617           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7618           {
7619             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7620             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7621
7622             Store[x][y] = Feld[newx][newy];
7623
7624             can_clone = TRUE;
7625
7626             break;
7627           }
7628         }
7629       }
7630
7631       if (can_clone)            /* randomly find a direction to move */
7632       {
7633         can_clone = FALSE;
7634
7635         start_pos = check_pos[RND(8)];
7636         check_order = (RND(2) ? -1 : +1);
7637
7638         for (i = 0; i < 8; i++)
7639         {
7640           int pos_raw = start_pos + i * check_order;
7641           int pos = (pos_raw + 8) % 8;
7642           int newx = x + check_xy[pos].dx;
7643           int newy = y + check_xy[pos].dy;
7644           int new_move_dir = check_xy[pos].dir;
7645
7646           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7647           {
7648             MovDir[x][y] = new_move_dir;
7649             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7650
7651             can_clone = TRUE;
7652
7653             break;
7654           }
7655         }
7656       }
7657
7658       if (can_clone)            /* cloning and moving successful */
7659         return;
7660
7661       /* cannot clone -- try to move towards player */
7662
7663       start_pos = check_pos[MovDir[x][y] & 0x0f];
7664       check_order = (RND(2) ? -1 : +1);
7665
7666       for (i = 0; i < 3; i++)
7667       {
7668         /* first check start_pos, then previous/next or (next/previous) pos */
7669         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7670         int pos = (pos_raw + 8) % 8;
7671         int newx = x + check_xy[pos].dx;
7672         int newy = y + check_xy[pos].dy;
7673         int new_move_dir = check_xy[pos].dir;
7674
7675         if (IS_PLAYER(newx, newy))
7676           break;
7677
7678         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7679         {
7680           MovDir[x][y] = new_move_dir;
7681           MovDelay[x][y] = level.android_move_time * 8 + 1;
7682
7683           break;
7684         }
7685       }
7686     }
7687   }
7688   else if (move_pattern == MV_TURNING_LEFT ||
7689            move_pattern == MV_TURNING_RIGHT ||
7690            move_pattern == MV_TURNING_LEFT_RIGHT ||
7691            move_pattern == MV_TURNING_RIGHT_LEFT ||
7692            move_pattern == MV_TURNING_RANDOM ||
7693            move_pattern == MV_ALL_DIRECTIONS)
7694   {
7695     boolean can_turn_left =
7696       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7697     boolean can_turn_right =
7698       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7699
7700     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7701       return;
7702
7703     if (move_pattern == MV_TURNING_LEFT)
7704       MovDir[x][y] = left_dir;
7705     else if (move_pattern == MV_TURNING_RIGHT)
7706       MovDir[x][y] = right_dir;
7707     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7708       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7709     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7710       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7711     else if (move_pattern == MV_TURNING_RANDOM)
7712       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7713                       can_turn_right && !can_turn_left ? right_dir :
7714                       RND(2) ? left_dir : right_dir);
7715     else if (can_turn_left && can_turn_right)
7716       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7717     else if (can_turn_left)
7718       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7719     else if (can_turn_right)
7720       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7721     else
7722       MovDir[x][y] = back_dir;
7723
7724     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7725   }
7726   else if (move_pattern == MV_HORIZONTAL ||
7727            move_pattern == MV_VERTICAL)
7728   {
7729     if (move_pattern & old_move_dir)
7730       MovDir[x][y] = back_dir;
7731     else if (move_pattern == MV_HORIZONTAL)
7732       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7733     else if (move_pattern == MV_VERTICAL)
7734       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7735
7736     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7737   }
7738   else if (move_pattern & MV_ANY_DIRECTION)
7739   {
7740     MovDir[x][y] = move_pattern;
7741     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7742   }
7743   else if (move_pattern & MV_WIND_DIRECTION)
7744   {
7745     MovDir[x][y] = game.wind_direction;
7746     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7747   }
7748   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7749   {
7750     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7751       MovDir[x][y] = left_dir;
7752     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7753       MovDir[x][y] = right_dir;
7754
7755     if (MovDir[x][y] != old_move_dir)
7756       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7757   }
7758   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7759   {
7760     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7761       MovDir[x][y] = right_dir;
7762     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7763       MovDir[x][y] = left_dir;
7764
7765     if (MovDir[x][y] != old_move_dir)
7766       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7767   }
7768   else if (move_pattern == MV_TOWARDS_PLAYER ||
7769            move_pattern == MV_AWAY_FROM_PLAYER)
7770   {
7771     int attr_x = -1, attr_y = -1;
7772     int newx, newy;
7773     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7774
7775     if (AllPlayersGone)
7776     {
7777       attr_x = ExitX;
7778       attr_y = ExitY;
7779     }
7780     else
7781     {
7782       int i;
7783
7784       for (i = 0; i < MAX_PLAYERS; i++)
7785       {
7786         struct PlayerInfo *player = &stored_player[i];
7787         int jx = player->jx, jy = player->jy;
7788
7789         if (!player->active)
7790           continue;
7791
7792         if (attr_x == -1 ||
7793             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7794         {
7795           attr_x = jx;
7796           attr_y = jy;
7797         }
7798       }
7799     }
7800
7801     MovDir[x][y] = MV_NONE;
7802     if (attr_x < x)
7803       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7804     else if (attr_x > x)
7805       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7806     if (attr_y < y)
7807       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7808     else if (attr_y > y)
7809       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7810
7811     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7812
7813     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7814     {
7815       boolean first_horiz = RND(2);
7816       int new_move_dir = MovDir[x][y];
7817
7818       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7819       {
7820         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7821         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7822
7823         return;
7824       }
7825
7826       MovDir[x][y] =
7827         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7828       Moving2Blocked(x, y, &newx, &newy);
7829
7830       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7831         return;
7832
7833       MovDir[x][y] =
7834         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7835       Moving2Blocked(x, y, &newx, &newy);
7836
7837       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7838         return;
7839
7840       MovDir[x][y] = old_move_dir;
7841     }
7842   }
7843   else if (move_pattern == MV_WHEN_PUSHED ||
7844            move_pattern == MV_WHEN_DROPPED)
7845   {
7846     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7847       MovDir[x][y] = MV_NONE;
7848
7849     MovDelay[x][y] = 0;
7850   }
7851   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7852   {
7853     static int test_xy[7][2] =
7854     {
7855       { 0, -1 },
7856       { -1, 0 },
7857       { +1, 0 },
7858       { 0, +1 },
7859       { 0, -1 },
7860       { -1, 0 },
7861       { +1, 0 },
7862     };
7863     static int test_dir[7] =
7864     {
7865       MV_UP,
7866       MV_LEFT,
7867       MV_RIGHT,
7868       MV_DOWN,
7869       MV_UP,
7870       MV_LEFT,
7871       MV_RIGHT,
7872     };
7873     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7874     int move_preference = -1000000;     /* start with very low preference */
7875     int new_move_dir = MV_NONE;
7876     int start_test = RND(4);
7877     int i;
7878
7879     for (i = 0; i < NUM_DIRECTIONS; i++)
7880     {
7881       int move_dir = test_dir[start_test + i];
7882       int move_dir_preference;
7883
7884       xx = x + test_xy[start_test + i][0];
7885       yy = y + test_xy[start_test + i][1];
7886
7887       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7888           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7889       {
7890         new_move_dir = move_dir;
7891
7892         break;
7893       }
7894
7895       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7896         continue;
7897
7898       move_dir_preference = -1 * RunnerVisit[xx][yy];
7899       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7900         move_dir_preference = PlayerVisit[xx][yy];
7901
7902       if (move_dir_preference > move_preference)
7903       {
7904         /* prefer field that has not been visited for the longest time */
7905         move_preference = move_dir_preference;
7906         new_move_dir = move_dir;
7907       }
7908       else if (move_dir_preference == move_preference &&
7909                move_dir == old_move_dir)
7910       {
7911         /* prefer last direction when all directions are preferred equally */
7912         move_preference = move_dir_preference;
7913         new_move_dir = move_dir;
7914       }
7915     }
7916
7917     MovDir[x][y] = new_move_dir;
7918     if (old_move_dir != new_move_dir)
7919       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7920   }
7921 }
7922
7923 static void TurnRound(int x, int y)
7924 {
7925   int direction = MovDir[x][y];
7926
7927   TurnRoundExt(x, y);
7928
7929   GfxDir[x][y] = MovDir[x][y];
7930
7931   if (direction != MovDir[x][y])
7932     GfxFrame[x][y] = 0;
7933
7934   if (MovDelay[x][y])
7935     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7936
7937   ResetGfxFrame(x, y, FALSE);
7938 }
7939
7940 static boolean JustBeingPushed(int x, int y)
7941 {
7942   int i;
7943
7944   for (i = 0; i < MAX_PLAYERS; i++)
7945   {
7946     struct PlayerInfo *player = &stored_player[i];
7947
7948     if (player->active && player->is_pushing && player->MovPos)
7949     {
7950       int next_jx = player->jx + (player->jx - player->last_jx);
7951       int next_jy = player->jy + (player->jy - player->last_jy);
7952
7953       if (x == next_jx && y == next_jy)
7954         return TRUE;
7955     }
7956   }
7957
7958   return FALSE;
7959 }
7960
7961 void StartMoving(int x, int y)
7962 {
7963   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7964   int element = Feld[x][y];
7965
7966   if (Stop[x][y])
7967     return;
7968
7969   if (MovDelay[x][y] == 0)
7970     GfxAction[x][y] = ACTION_DEFAULT;
7971
7972   if (CAN_FALL(element) && y < lev_fieldy - 1)
7973   {
7974     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7975         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7976       if (JustBeingPushed(x, y))
7977         return;
7978
7979     if (element == EL_QUICKSAND_FULL)
7980     {
7981       if (IS_FREE(x, y + 1))
7982       {
7983         InitMovingField(x, y, MV_DOWN);
7984         started_moving = TRUE;
7985
7986         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7987 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7988         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7989           Store[x][y] = EL_ROCK;
7990 #else
7991         Store[x][y] = EL_ROCK;
7992 #endif
7993
7994         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7995       }
7996       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7997       {
7998         if (!MovDelay[x][y])
7999         {
8000           MovDelay[x][y] = TILEY + 1;
8001
8002           ResetGfxAnimation(x, y);
8003           ResetGfxAnimation(x, y + 1);
8004         }
8005
8006         if (MovDelay[x][y])
8007         {
8008           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8009           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8010
8011           MovDelay[x][y]--;
8012           if (MovDelay[x][y])
8013             return;
8014         }
8015
8016         Feld[x][y] = EL_QUICKSAND_EMPTY;
8017         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8018         Store[x][y + 1] = Store[x][y];
8019         Store[x][y] = 0;
8020
8021         PlayLevelSoundAction(x, y, ACTION_FILLING);
8022       }
8023       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8024       {
8025         if (!MovDelay[x][y])
8026         {
8027           MovDelay[x][y] = TILEY + 1;
8028
8029           ResetGfxAnimation(x, y);
8030           ResetGfxAnimation(x, y + 1);
8031         }
8032
8033         if (MovDelay[x][y])
8034         {
8035           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8036           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8037
8038           MovDelay[x][y]--;
8039           if (MovDelay[x][y])
8040             return;
8041         }
8042
8043         Feld[x][y] = EL_QUICKSAND_EMPTY;
8044         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8045         Store[x][y + 1] = Store[x][y];
8046         Store[x][y] = 0;
8047
8048         PlayLevelSoundAction(x, y, ACTION_FILLING);
8049       }
8050     }
8051     else if (element == EL_QUICKSAND_FAST_FULL)
8052     {
8053       if (IS_FREE(x, y + 1))
8054       {
8055         InitMovingField(x, y, MV_DOWN);
8056         started_moving = TRUE;
8057
8058         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8059 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8060         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8061           Store[x][y] = EL_ROCK;
8062 #else
8063         Store[x][y] = EL_ROCK;
8064 #endif
8065
8066         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8067       }
8068       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8069       {
8070         if (!MovDelay[x][y])
8071         {
8072           MovDelay[x][y] = TILEY + 1;
8073
8074           ResetGfxAnimation(x, y);
8075           ResetGfxAnimation(x, y + 1);
8076         }
8077
8078         if (MovDelay[x][y])
8079         {
8080           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8081           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8082
8083           MovDelay[x][y]--;
8084           if (MovDelay[x][y])
8085             return;
8086         }
8087
8088         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8089         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8090         Store[x][y + 1] = Store[x][y];
8091         Store[x][y] = 0;
8092
8093         PlayLevelSoundAction(x, y, ACTION_FILLING);
8094       }
8095       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8096       {
8097         if (!MovDelay[x][y])
8098         {
8099           MovDelay[x][y] = TILEY + 1;
8100
8101           ResetGfxAnimation(x, y);
8102           ResetGfxAnimation(x, y + 1);
8103         }
8104
8105         if (MovDelay[x][y])
8106         {
8107           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8108           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8109
8110           MovDelay[x][y]--;
8111           if (MovDelay[x][y])
8112             return;
8113         }
8114
8115         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8116         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8117         Store[x][y + 1] = Store[x][y];
8118         Store[x][y] = 0;
8119
8120         PlayLevelSoundAction(x, y, ACTION_FILLING);
8121       }
8122     }
8123     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8124              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8125     {
8126       InitMovingField(x, y, MV_DOWN);
8127       started_moving = TRUE;
8128
8129       Feld[x][y] = EL_QUICKSAND_FILLING;
8130       Store[x][y] = element;
8131
8132       PlayLevelSoundAction(x, y, ACTION_FILLING);
8133     }
8134     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8135              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8136     {
8137       InitMovingField(x, y, MV_DOWN);
8138       started_moving = TRUE;
8139
8140       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8141       Store[x][y] = element;
8142
8143       PlayLevelSoundAction(x, y, ACTION_FILLING);
8144     }
8145     else if (element == EL_MAGIC_WALL_FULL)
8146     {
8147       if (IS_FREE(x, y + 1))
8148       {
8149         InitMovingField(x, y, MV_DOWN);
8150         started_moving = TRUE;
8151
8152         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8153         Store[x][y] = EL_CHANGED(Store[x][y]);
8154       }
8155       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8156       {
8157         if (!MovDelay[x][y])
8158           MovDelay[x][y] = TILEY / 4 + 1;
8159
8160         if (MovDelay[x][y])
8161         {
8162           MovDelay[x][y]--;
8163           if (MovDelay[x][y])
8164             return;
8165         }
8166
8167         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8168         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8169         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8170         Store[x][y] = 0;
8171       }
8172     }
8173     else if (element == EL_BD_MAGIC_WALL_FULL)
8174     {
8175       if (IS_FREE(x, y + 1))
8176       {
8177         InitMovingField(x, y, MV_DOWN);
8178         started_moving = TRUE;
8179
8180         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8181         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8182       }
8183       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8184       {
8185         if (!MovDelay[x][y])
8186           MovDelay[x][y] = TILEY / 4 + 1;
8187
8188         if (MovDelay[x][y])
8189         {
8190           MovDelay[x][y]--;
8191           if (MovDelay[x][y])
8192             return;
8193         }
8194
8195         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8196         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8197         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8198         Store[x][y] = 0;
8199       }
8200     }
8201     else if (element == EL_DC_MAGIC_WALL_FULL)
8202     {
8203       if (IS_FREE(x, y + 1))
8204       {
8205         InitMovingField(x, y, MV_DOWN);
8206         started_moving = TRUE;
8207
8208         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8209         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8210       }
8211       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8212       {
8213         if (!MovDelay[x][y])
8214           MovDelay[x][y] = TILEY / 4 + 1;
8215
8216         if (MovDelay[x][y])
8217         {
8218           MovDelay[x][y]--;
8219           if (MovDelay[x][y])
8220             return;
8221         }
8222
8223         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8224         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8225         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8226         Store[x][y] = 0;
8227       }
8228     }
8229     else if ((CAN_PASS_MAGIC_WALL(element) &&
8230               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8231                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8232              (CAN_PASS_DC_MAGIC_WALL(element) &&
8233               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8234
8235     {
8236       InitMovingField(x, y, MV_DOWN);
8237       started_moving = TRUE;
8238
8239       Feld[x][y] =
8240         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8241          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8242          EL_DC_MAGIC_WALL_FILLING);
8243       Store[x][y] = element;
8244     }
8245     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8246     {
8247       SplashAcid(x, y + 1);
8248
8249       InitMovingField(x, y, MV_DOWN);
8250       started_moving = TRUE;
8251
8252       Store[x][y] = EL_ACID;
8253     }
8254     else if (
8255 #if USE_FIX_IMPACT_COLLISION
8256              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8257               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8258 #else
8259              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8260               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8261 #endif
8262              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8263               CAN_FALL(element) && WasJustFalling[x][y] &&
8264               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8265
8266              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8267               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8268               (Feld[x][y + 1] == EL_BLOCKED)))
8269     {
8270       /* this is needed for a special case not covered by calling "Impact()"
8271          from "ContinueMoving()": if an element moves to a tile directly below
8272          another element which was just falling on that tile (which was empty
8273          in the previous frame), the falling element above would just stop
8274          instead of smashing the element below (in previous version, the above
8275          element was just checked for "moving" instead of "falling", resulting
8276          in incorrect smashes caused by horizontal movement of the above
8277          element; also, the case of the player being the element to smash was
8278          simply not covered here... :-/ ) */
8279
8280       CheckCollision[x][y] = 0;
8281       CheckImpact[x][y] = 0;
8282
8283       Impact(x, y);
8284     }
8285     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8286     {
8287       if (MovDir[x][y] == MV_NONE)
8288       {
8289         InitMovingField(x, y, MV_DOWN);
8290         started_moving = TRUE;
8291       }
8292     }
8293     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8294     {
8295       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8296         MovDir[x][y] = MV_DOWN;
8297
8298       InitMovingField(x, y, MV_DOWN);
8299       started_moving = TRUE;
8300     }
8301     else if (element == EL_AMOEBA_DROP)
8302     {
8303       Feld[x][y] = EL_AMOEBA_GROWING;
8304       Store[x][y] = EL_AMOEBA_WET;
8305     }
8306     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8307               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8308              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8309              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8310     {
8311       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8312                                 (IS_FREE(x - 1, y + 1) ||
8313                                  Feld[x - 1][y + 1] == EL_ACID));
8314       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8315                                 (IS_FREE(x + 1, y + 1) ||
8316                                  Feld[x + 1][y + 1] == EL_ACID));
8317       boolean can_fall_any  = (can_fall_left || can_fall_right);
8318       boolean can_fall_both = (can_fall_left && can_fall_right);
8319       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8320
8321 #if USE_NEW_ALL_SLIPPERY
8322       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8323       {
8324         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8325           can_fall_right = FALSE;
8326         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8327           can_fall_left = FALSE;
8328         else 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
8333         can_fall_any  = (can_fall_left || can_fall_right);
8334         can_fall_both = FALSE;
8335       }
8336 #else
8337       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8338       {
8339         if (slippery_type == SLIPPERY_ONLY_LEFT)
8340           can_fall_right = FALSE;
8341         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8342           can_fall_left = FALSE;
8343         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8344           can_fall_right = FALSE;
8345         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8346           can_fall_left = FALSE;
8347
8348         can_fall_any  = (can_fall_left || can_fall_right);
8349         can_fall_both = (can_fall_left && can_fall_right);
8350       }
8351 #endif
8352
8353 #if USE_NEW_ALL_SLIPPERY
8354 #else
8355 #if USE_NEW_SP_SLIPPERY
8356       /* !!! better use the same properties as for custom elements here !!! */
8357       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8358                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8359       {
8360         can_fall_right = FALSE;         /* slip down on left side */
8361         can_fall_both = FALSE;
8362       }
8363 #endif
8364 #endif
8365
8366 #if USE_NEW_ALL_SLIPPERY
8367       if (can_fall_both)
8368       {
8369         if (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 #else
8377       if (can_fall_both)
8378       {
8379         if (game.emulation == EMU_BOULDERDASH ||
8380             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8381           can_fall_right = FALSE;       /* slip down on left side */
8382         else
8383           can_fall_left = !(can_fall_right = RND(2));
8384
8385         can_fall_both = FALSE;
8386       }
8387 #endif
8388
8389       if (can_fall_any)
8390       {
8391         /* if not determined otherwise, prefer left side for slipping down */
8392         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8393         started_moving = TRUE;
8394       }
8395     }
8396 #if 0
8397     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8398 #else
8399     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8400 #endif
8401     {
8402       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8403       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8404       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8405       int belt_dir = game.belt_dir[belt_nr];
8406
8407       if ((belt_dir == MV_LEFT  && left_is_free) ||
8408           (belt_dir == MV_RIGHT && right_is_free))
8409       {
8410         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8411
8412         InitMovingField(x, y, belt_dir);
8413         started_moving = TRUE;
8414
8415         Pushed[x][y] = TRUE;
8416         Pushed[nextx][y] = TRUE;
8417
8418         GfxAction[x][y] = ACTION_DEFAULT;
8419       }
8420       else
8421       {
8422         MovDir[x][y] = 0;       /* if element was moving, stop it */
8423       }
8424     }
8425   }
8426
8427   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8428 #if 0
8429   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8430 #else
8431   if (CAN_MOVE(element) && !started_moving)
8432 #endif
8433   {
8434     int move_pattern = element_info[element].move_pattern;
8435     int newx, newy;
8436
8437 #if 0
8438 #if DEBUG
8439     if (MovDir[x][y] == MV_NONE)
8440     {
8441       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8442              x, y, element, element_info[element].token_name);
8443       printf("StartMoving(): This should never happen!\n");
8444     }
8445 #endif
8446 #endif
8447
8448     Moving2Blocked(x, y, &newx, &newy);
8449
8450     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8451       return;
8452
8453     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8454         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8455     {
8456       WasJustMoving[x][y] = 0;
8457       CheckCollision[x][y] = 0;
8458
8459       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8460
8461       if (Feld[x][y] != element)        /* element has changed */
8462         return;
8463     }
8464
8465     if (!MovDelay[x][y])        /* start new movement phase */
8466     {
8467       /* all objects that can change their move direction after each step
8468          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8469
8470       if (element != EL_YAMYAM &&
8471           element != EL_DARK_YAMYAM &&
8472           element != EL_PACMAN &&
8473           !(move_pattern & MV_ANY_DIRECTION) &&
8474           move_pattern != MV_TURNING_LEFT &&
8475           move_pattern != MV_TURNING_RIGHT &&
8476           move_pattern != MV_TURNING_LEFT_RIGHT &&
8477           move_pattern != MV_TURNING_RIGHT_LEFT &&
8478           move_pattern != MV_TURNING_RANDOM)
8479       {
8480         TurnRound(x, y);
8481
8482         if (MovDelay[x][y] && (element == EL_BUG ||
8483                                element == EL_SPACESHIP ||
8484                                element == EL_SP_SNIKSNAK ||
8485                                element == EL_SP_ELECTRON ||
8486                                element == EL_MOLE))
8487           TEST_DrawLevelField(x, y);
8488       }
8489     }
8490
8491     if (MovDelay[x][y])         /* wait some time before next movement */
8492     {
8493       MovDelay[x][y]--;
8494
8495       if (element == EL_ROBOT ||
8496           element == EL_YAMYAM ||
8497           element == EL_DARK_YAMYAM)
8498       {
8499         DrawLevelElementAnimationIfNeeded(x, y, element);
8500         PlayLevelSoundAction(x, y, ACTION_WAITING);
8501       }
8502       else if (element == EL_SP_ELECTRON)
8503         DrawLevelElementAnimationIfNeeded(x, y, element);
8504       else if (element == EL_DRAGON)
8505       {
8506         int i;
8507         int dir = MovDir[x][y];
8508         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8509         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8510         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8511                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8512                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8513                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8514         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8515
8516         GfxAction[x][y] = ACTION_ATTACKING;
8517
8518         if (IS_PLAYER(x, y))
8519           DrawPlayerField(x, y);
8520         else
8521           TEST_DrawLevelField(x, y);
8522
8523         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8524
8525         for (i = 1; i <= 3; i++)
8526         {
8527           int xx = x + i * dx;
8528           int yy = y + i * dy;
8529           int sx = SCREENX(xx);
8530           int sy = SCREENY(yy);
8531           int flame_graphic = graphic + (i - 1);
8532
8533           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8534             break;
8535
8536           if (MovDelay[x][y])
8537           {
8538             int flamed = MovingOrBlocked2Element(xx, yy);
8539
8540             /* !!! */
8541 #if 0
8542             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8543               Bang(xx, yy);
8544             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8545               RemoveMovingField(xx, yy);
8546             else
8547               RemoveField(xx, yy);
8548 #else
8549             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8550               Bang(xx, yy);
8551             else
8552               RemoveMovingField(xx, yy);
8553 #endif
8554
8555             ChangeDelay[xx][yy] = 0;
8556
8557             Feld[xx][yy] = EL_FLAMES;
8558
8559             if (IN_SCR_FIELD(sx, sy))
8560             {
8561               TEST_DrawLevelFieldCrumbled(xx, yy);
8562               DrawGraphic(sx, sy, flame_graphic, frame);
8563             }
8564           }
8565           else
8566           {
8567             if (Feld[xx][yy] == EL_FLAMES)
8568               Feld[xx][yy] = EL_EMPTY;
8569             TEST_DrawLevelField(xx, yy);
8570           }
8571         }
8572       }
8573
8574       if (MovDelay[x][y])       /* element still has to wait some time */
8575       {
8576         PlayLevelSoundAction(x, y, ACTION_WAITING);
8577
8578         return;
8579       }
8580     }
8581
8582     /* now make next step */
8583
8584     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8585
8586     if (DONT_COLLIDE_WITH(element) &&
8587         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8588         !PLAYER_ENEMY_PROTECTED(newx, newy))
8589     {
8590       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8591
8592       return;
8593     }
8594
8595     else if (CAN_MOVE_INTO_ACID(element) &&
8596              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8597              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8598              (MovDir[x][y] == MV_DOWN ||
8599               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8600     {
8601       SplashAcid(newx, newy);
8602       Store[x][y] = EL_ACID;
8603     }
8604     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8605     {
8606       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8607           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8608           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8609           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8610       {
8611         RemoveField(x, y);
8612         TEST_DrawLevelField(x, y);
8613
8614         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8615         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8616           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8617
8618         local_player->friends_still_needed--;
8619         if (!local_player->friends_still_needed &&
8620             !local_player->GameOver && AllPlayersGone)
8621           PlayerWins(local_player);
8622
8623         return;
8624       }
8625       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8626       {
8627         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8628           TEST_DrawLevelField(newx, newy);
8629         else
8630           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8631       }
8632       else if (!IS_FREE(newx, newy))
8633       {
8634         GfxAction[x][y] = ACTION_WAITING;
8635
8636         if (IS_PLAYER(x, y))
8637           DrawPlayerField(x, y);
8638         else
8639           TEST_DrawLevelField(x, y);
8640
8641         return;
8642       }
8643     }
8644     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8645     {
8646       if (IS_FOOD_PIG(Feld[newx][newy]))
8647       {
8648         if (IS_MOVING(newx, newy))
8649           RemoveMovingField(newx, newy);
8650         else
8651         {
8652           Feld[newx][newy] = EL_EMPTY;
8653           TEST_DrawLevelField(newx, newy);
8654         }
8655
8656         PlayLevelSound(x, y, SND_PIG_DIGGING);
8657       }
8658       else if (!IS_FREE(newx, newy))
8659       {
8660         if (IS_PLAYER(x, y))
8661           DrawPlayerField(x, y);
8662         else
8663           TEST_DrawLevelField(x, y);
8664
8665         return;
8666       }
8667     }
8668     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8669     {
8670       if (Store[x][y] != EL_EMPTY)
8671       {
8672         boolean can_clone = FALSE;
8673         int xx, yy;
8674
8675         /* check if element to clone is still there */
8676         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8677         {
8678           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8679           {
8680             can_clone = TRUE;
8681
8682             break;
8683           }
8684         }
8685
8686         /* cannot clone or target field not free anymore -- do not clone */
8687         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8688           Store[x][y] = EL_EMPTY;
8689       }
8690
8691       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8692       {
8693         if (IS_MV_DIAGONAL(MovDir[x][y]))
8694         {
8695           int diagonal_move_dir = MovDir[x][y];
8696           int stored = Store[x][y];
8697           int change_delay = 8;
8698           int graphic;
8699
8700           /* android is moving diagonally */
8701
8702           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8703
8704           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8705           GfxElement[x][y] = EL_EMC_ANDROID;
8706           GfxAction[x][y] = ACTION_SHRINKING;
8707           GfxDir[x][y] = diagonal_move_dir;
8708           ChangeDelay[x][y] = change_delay;
8709
8710           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8711                                    GfxDir[x][y]);
8712
8713           DrawLevelGraphicAnimation(x, y, graphic);
8714           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8715
8716           if (Feld[newx][newy] == EL_ACID)
8717           {
8718             SplashAcid(newx, newy);
8719
8720             return;
8721           }
8722
8723           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8724
8725           Store[newx][newy] = EL_EMC_ANDROID;
8726           GfxElement[newx][newy] = EL_EMC_ANDROID;
8727           GfxAction[newx][newy] = ACTION_GROWING;
8728           GfxDir[newx][newy] = diagonal_move_dir;
8729           ChangeDelay[newx][newy] = change_delay;
8730
8731           graphic = el_act_dir2img(GfxElement[newx][newy],
8732                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8733
8734           DrawLevelGraphicAnimation(newx, newy, graphic);
8735           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8736
8737           return;
8738         }
8739         else
8740         {
8741           Feld[newx][newy] = EL_EMPTY;
8742           TEST_DrawLevelField(newx, newy);
8743
8744           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8745         }
8746       }
8747       else if (!IS_FREE(newx, newy))
8748       {
8749 #if 0
8750         if (IS_PLAYER(x, y))
8751           DrawPlayerField(x, y);
8752         else
8753           TEST_DrawLevelField(x, y);
8754 #endif
8755
8756         return;
8757       }
8758     }
8759     else if (IS_CUSTOM_ELEMENT(element) &&
8760              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8761     {
8762 #if 1
8763       if (!DigFieldByCE(newx, newy, element))
8764         return;
8765 #else
8766       int new_element = Feld[newx][newy];
8767
8768       if (!IS_FREE(newx, newy))
8769       {
8770         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8771                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8772                       ACTION_BREAKING);
8773
8774         /* no element can dig solid indestructible elements */
8775         if (IS_INDESTRUCTIBLE(new_element) &&
8776             !IS_DIGGABLE(new_element) &&
8777             !IS_COLLECTIBLE(new_element))
8778           return;
8779
8780         if (AmoebaNr[newx][newy] &&
8781             (new_element == EL_AMOEBA_FULL ||
8782              new_element == EL_BD_AMOEBA ||
8783              new_element == EL_AMOEBA_GROWING))
8784         {
8785           AmoebaCnt[AmoebaNr[newx][newy]]--;
8786           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8787         }
8788
8789         if (IS_MOVING(newx, newy))
8790           RemoveMovingField(newx, newy);
8791         else
8792         {
8793           RemoveField(newx, newy);
8794           TEST_DrawLevelField(newx, newy);
8795         }
8796
8797         /* if digged element was about to explode, prevent the explosion */
8798         ExplodeField[newx][newy] = EX_TYPE_NONE;
8799
8800         PlayLevelSoundAction(x, y, action);
8801       }
8802
8803       Store[newx][newy] = EL_EMPTY;
8804
8805 #if 1
8806       /* this makes it possible to leave the removed element again */
8807       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8808         Store[newx][newy] = new_element;
8809 #else
8810       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8811       {
8812         int move_leave_element = element_info[element].move_leave_element;
8813
8814         /* this makes it possible to leave the removed element again */
8815         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8816                              new_element : move_leave_element);
8817       }
8818 #endif
8819
8820 #endif
8821
8822       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8823       {
8824         RunnerVisit[x][y] = FrameCounter;
8825         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8826       }
8827     }
8828     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8829     {
8830       if (!IS_FREE(newx, newy))
8831       {
8832         if (IS_PLAYER(x, y))
8833           DrawPlayerField(x, y);
8834         else
8835           TEST_DrawLevelField(x, y);
8836
8837         return;
8838       }
8839       else
8840       {
8841         boolean wanna_flame = !RND(10);
8842         int dx = newx - x, dy = newy - y;
8843         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8844         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8845         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8846                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8847         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8848                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8849
8850         if ((wanna_flame ||
8851              IS_CLASSIC_ENEMY(element1) ||
8852              IS_CLASSIC_ENEMY(element2)) &&
8853             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8854             element1 != EL_FLAMES && element2 != EL_FLAMES)
8855         {
8856           ResetGfxAnimation(x, y);
8857           GfxAction[x][y] = ACTION_ATTACKING;
8858
8859           if (IS_PLAYER(x, y))
8860             DrawPlayerField(x, y);
8861           else
8862             TEST_DrawLevelField(x, y);
8863
8864           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8865
8866           MovDelay[x][y] = 50;
8867
8868           /* !!! */
8869 #if 0
8870           RemoveField(newx, newy);
8871 #endif
8872           Feld[newx][newy] = EL_FLAMES;
8873           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8874           {
8875 #if 0
8876             RemoveField(newx1, newy1);
8877 #endif
8878             Feld[newx1][newy1] = EL_FLAMES;
8879           }
8880           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8881           {
8882 #if 0
8883             RemoveField(newx2, newy2);
8884 #endif
8885             Feld[newx2][newy2] = EL_FLAMES;
8886           }
8887
8888           return;
8889         }
8890       }
8891     }
8892     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8893              Feld[newx][newy] == EL_DIAMOND)
8894     {
8895       if (IS_MOVING(newx, newy))
8896         RemoveMovingField(newx, newy);
8897       else
8898       {
8899         Feld[newx][newy] = EL_EMPTY;
8900         TEST_DrawLevelField(newx, newy);
8901       }
8902
8903       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8904     }
8905     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8906              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8907     {
8908       if (AmoebaNr[newx][newy])
8909       {
8910         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8911         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8912             Feld[newx][newy] == EL_BD_AMOEBA)
8913           AmoebaCnt[AmoebaNr[newx][newy]]--;
8914       }
8915
8916 #if 0
8917       /* !!! test !!! */
8918       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8919       {
8920         RemoveMovingField(newx, newy);
8921       }
8922 #else
8923       if (IS_MOVING(newx, newy))
8924       {
8925         RemoveMovingField(newx, newy);
8926       }
8927 #endif
8928       else
8929       {
8930         Feld[newx][newy] = EL_EMPTY;
8931         TEST_DrawLevelField(newx, newy);
8932       }
8933
8934       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8935     }
8936     else if ((element == EL_PACMAN || element == EL_MOLE)
8937              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8938     {
8939       if (AmoebaNr[newx][newy])
8940       {
8941         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8942         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8943             Feld[newx][newy] == EL_BD_AMOEBA)
8944           AmoebaCnt[AmoebaNr[newx][newy]]--;
8945       }
8946
8947       if (element == EL_MOLE)
8948       {
8949         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8950         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8951
8952         ResetGfxAnimation(x, y);
8953         GfxAction[x][y] = ACTION_DIGGING;
8954         TEST_DrawLevelField(x, y);
8955
8956         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8957
8958         return;                         /* wait for shrinking amoeba */
8959       }
8960       else      /* element == EL_PACMAN */
8961       {
8962         Feld[newx][newy] = EL_EMPTY;
8963         TEST_DrawLevelField(newx, newy);
8964         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8965       }
8966     }
8967     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8968              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8969               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8970     {
8971       /* wait for shrinking amoeba to completely disappear */
8972       return;
8973     }
8974     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8975     {
8976       /* object was running against a wall */
8977
8978       TurnRound(x, y);
8979
8980 #if 0
8981       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8982       if (move_pattern & MV_ANY_DIRECTION &&
8983           move_pattern == MovDir[x][y])
8984       {
8985         int blocking_element =
8986           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8987
8988         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8989                                  MovDir[x][y]);
8990
8991         element = Feld[x][y];   /* element might have changed */
8992       }
8993 #endif
8994
8995       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8996         DrawLevelElementAnimation(x, y, element);
8997
8998       if (DONT_TOUCH(element))
8999         TestIfBadThingTouchesPlayer(x, y);
9000
9001       return;
9002     }
9003
9004     InitMovingField(x, y, MovDir[x][y]);
9005
9006     PlayLevelSoundAction(x, y, ACTION_MOVING);
9007   }
9008
9009   if (MovDir[x][y])
9010     ContinueMoving(x, y);
9011 }
9012
9013 void ContinueMoving(int x, int y)
9014 {
9015   int element = Feld[x][y];
9016   struct ElementInfo *ei = &element_info[element];
9017   int direction = MovDir[x][y];
9018   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9019   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9020   int newx = x + dx, newy = y + dy;
9021   int stored = Store[x][y];
9022   int stored_new = Store[newx][newy];
9023   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
9024   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9025   boolean last_line = (newy == lev_fieldy - 1);
9026
9027   MovPos[x][y] += getElementMoveStepsize(x, y);
9028
9029   if (pushed_by_player) /* special case: moving object pushed by player */
9030     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9031
9032   if (ABS(MovPos[x][y]) < TILEX)
9033   {
9034 #if 0
9035     int ee = Feld[x][y];
9036     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9037     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9038
9039     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9040            x, y, ABS(MovPos[x][y]),
9041            ee, gg, ff,
9042            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9043 #endif
9044
9045     TEST_DrawLevelField(x, y);
9046
9047     return;     /* element is still moving */
9048   }
9049
9050   /* element reached destination field */
9051
9052   Feld[x][y] = EL_EMPTY;
9053   Feld[newx][newy] = element;
9054   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9055
9056   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9057   {
9058     element = Feld[newx][newy] = EL_ACID;
9059   }
9060   else if (element == EL_MOLE)
9061   {
9062     Feld[x][y] = EL_SAND;
9063
9064     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9065   }
9066   else if (element == EL_QUICKSAND_FILLING)
9067   {
9068     element = Feld[newx][newy] = get_next_element(element);
9069     Store[newx][newy] = Store[x][y];
9070   }
9071   else if (element == EL_QUICKSAND_EMPTYING)
9072   {
9073     Feld[x][y] = get_next_element(element);
9074     element = Feld[newx][newy] = Store[x][y];
9075   }
9076   else if (element == EL_QUICKSAND_FAST_FILLING)
9077   {
9078     element = Feld[newx][newy] = get_next_element(element);
9079     Store[newx][newy] = Store[x][y];
9080   }
9081   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9082   {
9083     Feld[x][y] = get_next_element(element);
9084     element = Feld[newx][newy] = Store[x][y];
9085   }
9086   else if (element == EL_MAGIC_WALL_FILLING)
9087   {
9088     element = Feld[newx][newy] = get_next_element(element);
9089     if (!game.magic_wall_active)
9090       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9091     Store[newx][newy] = Store[x][y];
9092   }
9093   else if (element == EL_MAGIC_WALL_EMPTYING)
9094   {
9095     Feld[x][y] = get_next_element(element);
9096     if (!game.magic_wall_active)
9097       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9098     element = Feld[newx][newy] = Store[x][y];
9099
9100 #if USE_NEW_CUSTOM_VALUE
9101     InitField(newx, newy, FALSE);
9102 #endif
9103   }
9104   else if (element == EL_BD_MAGIC_WALL_FILLING)
9105   {
9106     element = Feld[newx][newy] = get_next_element(element);
9107     if (!game.magic_wall_active)
9108       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9109     Store[newx][newy] = Store[x][y];
9110   }
9111   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9112   {
9113     Feld[x][y] = get_next_element(element);
9114     if (!game.magic_wall_active)
9115       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9116     element = Feld[newx][newy] = Store[x][y];
9117
9118 #if USE_NEW_CUSTOM_VALUE
9119     InitField(newx, newy, FALSE);
9120 #endif
9121   }
9122   else if (element == EL_DC_MAGIC_WALL_FILLING)
9123   {
9124     element = Feld[newx][newy] = get_next_element(element);
9125     if (!game.magic_wall_active)
9126       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9127     Store[newx][newy] = Store[x][y];
9128   }
9129   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9130   {
9131     Feld[x][y] = get_next_element(element);
9132     if (!game.magic_wall_active)
9133       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9134     element = Feld[newx][newy] = Store[x][y];
9135
9136 #if USE_NEW_CUSTOM_VALUE
9137     InitField(newx, newy, FALSE);
9138 #endif
9139   }
9140   else if (element == EL_AMOEBA_DROPPING)
9141   {
9142     Feld[x][y] = get_next_element(element);
9143     element = Feld[newx][newy] = Store[x][y];
9144   }
9145   else if (element == EL_SOKOBAN_OBJECT)
9146   {
9147     if (Back[x][y])
9148       Feld[x][y] = Back[x][y];
9149
9150     if (Back[newx][newy])
9151       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9152
9153     Back[x][y] = Back[newx][newy] = 0;
9154   }
9155
9156   Store[x][y] = EL_EMPTY;
9157   MovPos[x][y] = 0;
9158   MovDir[x][y] = 0;
9159   MovDelay[x][y] = 0;
9160
9161   MovDelay[newx][newy] = 0;
9162
9163   if (CAN_CHANGE_OR_HAS_ACTION(element))
9164   {
9165     /* copy element change control values to new field */
9166     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9167     ChangePage[newx][newy]  = ChangePage[x][y];
9168     ChangeCount[newx][newy] = ChangeCount[x][y];
9169     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9170   }
9171
9172 #if USE_NEW_CUSTOM_VALUE
9173   CustomValue[newx][newy] = CustomValue[x][y];
9174 #endif
9175
9176   ChangeDelay[x][y] = 0;
9177   ChangePage[x][y] = -1;
9178   ChangeCount[x][y] = 0;
9179   ChangeEvent[x][y] = -1;
9180
9181 #if USE_NEW_CUSTOM_VALUE
9182   CustomValue[x][y] = 0;
9183 #endif
9184
9185   /* copy animation control values to new field */
9186   GfxFrame[newx][newy]  = GfxFrame[x][y];
9187   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9188   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9189   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9190
9191   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9192
9193   /* some elements can leave other elements behind after moving */
9194 #if 1
9195   if (ei->move_leave_element != EL_EMPTY &&
9196       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9197       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9198 #else
9199   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9200       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9201       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9202 #endif
9203   {
9204     int move_leave_element = ei->move_leave_element;
9205
9206 #if 1
9207 #if 1
9208     /* this makes it possible to leave the removed element again */
9209     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9210       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9211 #else
9212     /* this makes it possible to leave the removed element again */
9213     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9214       move_leave_element = stored;
9215 #endif
9216 #else
9217     /* this makes it possible to leave the removed element again */
9218     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9219         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9220       move_leave_element = stored;
9221 #endif
9222
9223     Feld[x][y] = move_leave_element;
9224
9225     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9226       MovDir[x][y] = direction;
9227
9228     InitField(x, y, FALSE);
9229
9230     if (GFX_CRUMBLED(Feld[x][y]))
9231       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9232
9233     if (ELEM_IS_PLAYER(move_leave_element))
9234       RelocatePlayer(x, y, move_leave_element);
9235   }
9236
9237   /* do this after checking for left-behind element */
9238   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9239
9240   if (!CAN_MOVE(element) ||
9241       (CAN_FALL(element) && direction == MV_DOWN &&
9242        (element == EL_SPRING ||
9243         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9244         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9245     GfxDir[x][y] = MovDir[newx][newy] = 0;
9246
9247   TEST_DrawLevelField(x, y);
9248   TEST_DrawLevelField(newx, newy);
9249
9250   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9251
9252   /* prevent pushed element from moving on in pushed direction */
9253   if (pushed_by_player && CAN_MOVE(element) &&
9254       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9255       !(element_info[element].move_pattern & direction))
9256     TurnRound(newx, newy);
9257
9258   /* prevent elements on conveyor belt from moving on in last direction */
9259   if (pushed_by_conveyor && CAN_FALL(element) &&
9260       direction & MV_HORIZONTAL)
9261     MovDir[newx][newy] = 0;
9262
9263   if (!pushed_by_player)
9264   {
9265     int nextx = newx + dx, nexty = newy + dy;
9266     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9267
9268     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9269
9270     if (CAN_FALL(element) && direction == MV_DOWN)
9271       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9272
9273     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9274       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9275
9276 #if USE_FIX_IMPACT_COLLISION
9277     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9278       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9279 #endif
9280   }
9281
9282   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9283   {
9284     TestIfBadThingTouchesPlayer(newx, newy);
9285     TestIfBadThingTouchesFriend(newx, newy);
9286
9287     if (!IS_CUSTOM_ELEMENT(element))
9288       TestIfBadThingTouchesOtherBadThing(newx, newy);
9289   }
9290   else if (element == EL_PENGUIN)
9291     TestIfFriendTouchesBadThing(newx, newy);
9292
9293   if (DONT_GET_HIT_BY(element))
9294   {
9295     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9296   }
9297
9298   /* give the player one last chance (one more frame) to move away */
9299   if (CAN_FALL(element) && direction == MV_DOWN &&
9300       (last_line || (!IS_FREE(x, newy + 1) &&
9301                      (!IS_PLAYER(x, newy + 1) ||
9302                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9303     Impact(x, newy);
9304
9305   if (pushed_by_player && !game.use_change_when_pushing_bug)
9306   {
9307     int push_side = MV_DIR_OPPOSITE(direction);
9308     struct PlayerInfo *player = PLAYERINFO(x, y);
9309
9310     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9311                                player->index_bit, push_side);
9312     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9313                                         player->index_bit, push_side);
9314   }
9315
9316   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9317     MovDelay[newx][newy] = 1;
9318
9319   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9320
9321   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9322
9323 #if 0
9324   if (ChangePage[newx][newy] != -1)             /* delayed change */
9325   {
9326     int page = ChangePage[newx][newy];
9327     struct ElementChangeInfo *change = &ei->change_page[page];
9328
9329     ChangePage[newx][newy] = -1;
9330
9331     if (change->can_change)
9332     {
9333       if (ChangeElement(newx, newy, element, page))
9334       {
9335         if (change->post_change_function)
9336           change->post_change_function(newx, newy);
9337       }
9338     }
9339
9340     if (change->has_action)
9341       ExecuteCustomElementAction(newx, newy, element, page);
9342   }
9343 #endif
9344
9345   TestIfElementHitsCustomElement(newx, newy, direction);
9346   TestIfPlayerTouchesCustomElement(newx, newy);
9347   TestIfElementTouchesCustomElement(newx, newy);
9348
9349   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9350       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9351     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9352                              MV_DIR_OPPOSITE(direction));
9353 }
9354
9355 int AmoebeNachbarNr(int ax, int ay)
9356 {
9357   int i;
9358   int element = Feld[ax][ay];
9359   int group_nr = 0;
9360   static int xy[4][2] =
9361   {
9362     { 0, -1 },
9363     { -1, 0 },
9364     { +1, 0 },
9365     { 0, +1 }
9366   };
9367
9368   for (i = 0; i < NUM_DIRECTIONS; i++)
9369   {
9370     int x = ax + xy[i][0];
9371     int y = ay + xy[i][1];
9372
9373     if (!IN_LEV_FIELD(x, y))
9374       continue;
9375
9376     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9377       group_nr = AmoebaNr[x][y];
9378   }
9379
9380   return group_nr;
9381 }
9382
9383 void AmoebenVereinigen(int ax, int ay)
9384 {
9385   int i, x, y, xx, yy;
9386   int new_group_nr = AmoebaNr[ax][ay];
9387   static int xy[4][2] =
9388   {
9389     { 0, -1 },
9390     { -1, 0 },
9391     { +1, 0 },
9392     { 0, +1 }
9393   };
9394
9395   if (new_group_nr == 0)
9396     return;
9397
9398   for (i = 0; i < NUM_DIRECTIONS; i++)
9399   {
9400     x = ax + xy[i][0];
9401     y = ay + xy[i][1];
9402
9403     if (!IN_LEV_FIELD(x, y))
9404       continue;
9405
9406     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9407          Feld[x][y] == EL_BD_AMOEBA ||
9408          Feld[x][y] == EL_AMOEBA_DEAD) &&
9409         AmoebaNr[x][y] != new_group_nr)
9410     {
9411       int old_group_nr = AmoebaNr[x][y];
9412
9413       if (old_group_nr == 0)
9414         return;
9415
9416       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9417       AmoebaCnt[old_group_nr] = 0;
9418       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9419       AmoebaCnt2[old_group_nr] = 0;
9420
9421       SCAN_PLAYFIELD(xx, yy)
9422       {
9423         if (AmoebaNr[xx][yy] == old_group_nr)
9424           AmoebaNr[xx][yy] = new_group_nr;
9425       }
9426     }
9427   }
9428 }
9429
9430 void AmoebeUmwandeln(int ax, int ay)
9431 {
9432   int i, x, y;
9433
9434   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9435   {
9436     int group_nr = AmoebaNr[ax][ay];
9437
9438 #ifdef DEBUG
9439     if (group_nr == 0)
9440     {
9441       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9442       printf("AmoebeUmwandeln(): This should never happen!\n");
9443       return;
9444     }
9445 #endif
9446
9447     SCAN_PLAYFIELD(x, y)
9448     {
9449       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9450       {
9451         AmoebaNr[x][y] = 0;
9452         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9453       }
9454     }
9455
9456     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9457                             SND_AMOEBA_TURNING_TO_GEM :
9458                             SND_AMOEBA_TURNING_TO_ROCK));
9459     Bang(ax, ay);
9460   }
9461   else
9462   {
9463     static int xy[4][2] =
9464     {
9465       { 0, -1 },
9466       { -1, 0 },
9467       { +1, 0 },
9468       { 0, +1 }
9469     };
9470
9471     for (i = 0; i < NUM_DIRECTIONS; i++)
9472     {
9473       x = ax + xy[i][0];
9474       y = ay + xy[i][1];
9475
9476       if (!IN_LEV_FIELD(x, y))
9477         continue;
9478
9479       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9480       {
9481         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9482                               SND_AMOEBA_TURNING_TO_GEM :
9483                               SND_AMOEBA_TURNING_TO_ROCK));
9484         Bang(x, y);
9485       }
9486     }
9487   }
9488 }
9489
9490 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9491 {
9492   int x, y;
9493   int group_nr = AmoebaNr[ax][ay];
9494   boolean done = FALSE;
9495
9496 #ifdef DEBUG
9497   if (group_nr == 0)
9498   {
9499     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9500     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9501     return;
9502   }
9503 #endif
9504
9505   SCAN_PLAYFIELD(x, y)
9506   {
9507     if (AmoebaNr[x][y] == group_nr &&
9508         (Feld[x][y] == EL_AMOEBA_DEAD ||
9509          Feld[x][y] == EL_BD_AMOEBA ||
9510          Feld[x][y] == EL_AMOEBA_GROWING))
9511     {
9512       AmoebaNr[x][y] = 0;
9513       Feld[x][y] = new_element;
9514       InitField(x, y, FALSE);
9515       TEST_DrawLevelField(x, y);
9516       done = TRUE;
9517     }
9518   }
9519
9520   if (done)
9521     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9522                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9523                             SND_BD_AMOEBA_TURNING_TO_GEM));
9524 }
9525
9526 void AmoebeWaechst(int x, int y)
9527 {
9528   static unsigned int sound_delay = 0;
9529   static unsigned int sound_delay_value = 0;
9530
9531   if (!MovDelay[x][y])          /* start new growing cycle */
9532   {
9533     MovDelay[x][y] = 7;
9534
9535     if (DelayReached(&sound_delay, sound_delay_value))
9536     {
9537       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9538       sound_delay_value = 30;
9539     }
9540   }
9541
9542   if (MovDelay[x][y])           /* wait some time before growing bigger */
9543   {
9544     MovDelay[x][y]--;
9545     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9546     {
9547       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9548                                            6 - MovDelay[x][y]);
9549
9550       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9551     }
9552
9553     if (!MovDelay[x][y])
9554     {
9555       Feld[x][y] = Store[x][y];
9556       Store[x][y] = 0;
9557       TEST_DrawLevelField(x, y);
9558     }
9559   }
9560 }
9561
9562 void AmoebaDisappearing(int x, int y)
9563 {
9564   static unsigned int sound_delay = 0;
9565   static unsigned int sound_delay_value = 0;
9566
9567   if (!MovDelay[x][y])          /* start new shrinking cycle */
9568   {
9569     MovDelay[x][y] = 7;
9570
9571     if (DelayReached(&sound_delay, sound_delay_value))
9572       sound_delay_value = 30;
9573   }
9574
9575   if (MovDelay[x][y])           /* wait some time before shrinking */
9576   {
9577     MovDelay[x][y]--;
9578     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9579     {
9580       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9581                                            6 - MovDelay[x][y]);
9582
9583       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9584     }
9585
9586     if (!MovDelay[x][y])
9587     {
9588       Feld[x][y] = EL_EMPTY;
9589       TEST_DrawLevelField(x, y);
9590
9591       /* don't let mole enter this field in this cycle;
9592          (give priority to objects falling to this field from above) */
9593       Stop[x][y] = TRUE;
9594     }
9595   }
9596 }
9597
9598 void AmoebeAbleger(int ax, int ay)
9599 {
9600   int i;
9601   int element = Feld[ax][ay];
9602   int graphic = el2img(element);
9603   int newax = ax, neway = ay;
9604   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9605   static int xy[4][2] =
9606   {
9607     { 0, -1 },
9608     { -1, 0 },
9609     { +1, 0 },
9610     { 0, +1 }
9611   };
9612
9613   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9614   {
9615     Feld[ax][ay] = EL_AMOEBA_DEAD;
9616     TEST_DrawLevelField(ax, ay);
9617     return;
9618   }
9619
9620   if (IS_ANIMATED(graphic))
9621     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9622
9623   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9624     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9625
9626   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9627   {
9628     MovDelay[ax][ay]--;
9629     if (MovDelay[ax][ay])
9630       return;
9631   }
9632
9633   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9634   {
9635     int start = RND(4);
9636     int x = ax + xy[start][0];
9637     int y = ay + xy[start][1];
9638
9639     if (!IN_LEV_FIELD(x, y))
9640       return;
9641
9642     if (IS_FREE(x, y) ||
9643         CAN_GROW_INTO(Feld[x][y]) ||
9644         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9645         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9646     {
9647       newax = x;
9648       neway = y;
9649     }
9650
9651     if (newax == ax && neway == ay)
9652       return;
9653   }
9654   else                          /* normal or "filled" (BD style) amoeba */
9655   {
9656     int start = RND(4);
9657     boolean waiting_for_player = FALSE;
9658
9659     for (i = 0; i < NUM_DIRECTIONS; i++)
9660     {
9661       int j = (start + i) % 4;
9662       int x = ax + xy[j][0];
9663       int y = ay + xy[j][1];
9664
9665       if (!IN_LEV_FIELD(x, y))
9666         continue;
9667
9668       if (IS_FREE(x, y) ||
9669           CAN_GROW_INTO(Feld[x][y]) ||
9670           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9671           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9672       {
9673         newax = x;
9674         neway = y;
9675         break;
9676       }
9677       else if (IS_PLAYER(x, y))
9678         waiting_for_player = TRUE;
9679     }
9680
9681     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9682     {
9683       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9684       {
9685         Feld[ax][ay] = EL_AMOEBA_DEAD;
9686         TEST_DrawLevelField(ax, ay);
9687         AmoebaCnt[AmoebaNr[ax][ay]]--;
9688
9689         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9690         {
9691           if (element == EL_AMOEBA_FULL)
9692             AmoebeUmwandeln(ax, ay);
9693           else if (element == EL_BD_AMOEBA)
9694             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9695         }
9696       }
9697       return;
9698     }
9699     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9700     {
9701       /* amoeba gets larger by growing in some direction */
9702
9703       int new_group_nr = AmoebaNr[ax][ay];
9704
9705 #ifdef DEBUG
9706   if (new_group_nr == 0)
9707   {
9708     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9709     printf("AmoebeAbleger(): This should never happen!\n");
9710     return;
9711   }
9712 #endif
9713
9714       AmoebaNr[newax][neway] = new_group_nr;
9715       AmoebaCnt[new_group_nr]++;
9716       AmoebaCnt2[new_group_nr]++;
9717
9718       /* if amoeba touches other amoeba(s) after growing, unify them */
9719       AmoebenVereinigen(newax, neway);
9720
9721       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9722       {
9723         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9724         return;
9725       }
9726     }
9727   }
9728
9729   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9730       (neway == lev_fieldy - 1 && newax != ax))
9731   {
9732     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9733     Store[newax][neway] = element;
9734   }
9735   else if (neway == ay || element == EL_EMC_DRIPPER)
9736   {
9737     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9738
9739     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9740   }
9741   else
9742   {
9743     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9744     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9745     Store[ax][ay] = EL_AMOEBA_DROP;
9746     ContinueMoving(ax, ay);
9747     return;
9748   }
9749
9750   TEST_DrawLevelField(newax, neway);
9751 }
9752
9753 void Life(int ax, int ay)
9754 {
9755   int x1, y1, x2, y2;
9756   int life_time = 40;
9757   int element = Feld[ax][ay];
9758   int graphic = el2img(element);
9759   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9760                          level.biomaze);
9761   boolean changed = FALSE;
9762
9763   if (IS_ANIMATED(graphic))
9764     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9765
9766   if (Stop[ax][ay])
9767     return;
9768
9769   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9770     MovDelay[ax][ay] = life_time;
9771
9772   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9773   {
9774     MovDelay[ax][ay]--;
9775     if (MovDelay[ax][ay])
9776       return;
9777   }
9778
9779   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9780   {
9781     int xx = ax+x1, yy = ay+y1;
9782     int nachbarn = 0;
9783
9784     if (!IN_LEV_FIELD(xx, yy))
9785       continue;
9786
9787     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9788     {
9789       int x = xx+x2, y = yy+y2;
9790
9791       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9792         continue;
9793
9794       if (((Feld[x][y] == element ||
9795             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9796            !Stop[x][y]) ||
9797           (IS_FREE(x, y) && Stop[x][y]))
9798         nachbarn++;
9799     }
9800
9801     if (xx == ax && yy == ay)           /* field in the middle */
9802     {
9803       if (nachbarn < life_parameter[0] ||
9804           nachbarn > life_parameter[1])
9805       {
9806         Feld[xx][yy] = EL_EMPTY;
9807         if (!Stop[xx][yy])
9808           TEST_DrawLevelField(xx, yy);
9809         Stop[xx][yy] = TRUE;
9810         changed = TRUE;
9811       }
9812     }
9813     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9814     {                                   /* free border field */
9815       if (nachbarn >= life_parameter[2] &&
9816           nachbarn <= life_parameter[3])
9817       {
9818         Feld[xx][yy] = element;
9819         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9820         if (!Stop[xx][yy])
9821           TEST_DrawLevelField(xx, yy);
9822         Stop[xx][yy] = TRUE;
9823         changed = TRUE;
9824       }
9825     }
9826   }
9827
9828   if (changed)
9829     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9830                    SND_GAME_OF_LIFE_GROWING);
9831 }
9832
9833 static void InitRobotWheel(int x, int y)
9834 {
9835   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9836 }
9837
9838 static void RunRobotWheel(int x, int y)
9839 {
9840   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9841 }
9842
9843 static void StopRobotWheel(int x, int y)
9844 {
9845   if (ZX == x && ZY == y)
9846   {
9847     ZX = ZY = -1;
9848
9849     game.robot_wheel_active = FALSE;
9850   }
9851 }
9852
9853 static void InitTimegateWheel(int x, int y)
9854 {
9855   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9856 }
9857
9858 static void RunTimegateWheel(int x, int y)
9859 {
9860   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9861 }
9862
9863 static void InitMagicBallDelay(int x, int y)
9864 {
9865 #if 1
9866   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9867 #else
9868   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9869 #endif
9870 }
9871
9872 static void ActivateMagicBall(int bx, int by)
9873 {
9874   int x, y;
9875
9876   if (level.ball_random)
9877   {
9878     int pos_border = RND(8);    /* select one of the eight border elements */
9879     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9880     int xx = pos_content % 3;
9881     int yy = pos_content / 3;
9882
9883     x = bx - 1 + xx;
9884     y = by - 1 + yy;
9885
9886     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9887       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9888   }
9889   else
9890   {
9891     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9892     {
9893       int xx = x - bx + 1;
9894       int yy = y - by + 1;
9895
9896       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9897         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9898     }
9899   }
9900
9901   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9902 }
9903
9904 void CheckExit(int x, int y)
9905 {
9906   if (local_player->gems_still_needed > 0 ||
9907       local_player->sokobanfields_still_needed > 0 ||
9908       local_player->lights_still_needed > 0)
9909   {
9910     int element = Feld[x][y];
9911     int graphic = el2img(element);
9912
9913     if (IS_ANIMATED(graphic))
9914       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9915
9916     return;
9917   }
9918
9919   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9920     return;
9921
9922   Feld[x][y] = EL_EXIT_OPENING;
9923
9924   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9925 }
9926
9927 void CheckExitEM(int x, int y)
9928 {
9929   if (local_player->gems_still_needed > 0 ||
9930       local_player->sokobanfields_still_needed > 0 ||
9931       local_player->lights_still_needed > 0)
9932   {
9933     int element = Feld[x][y];
9934     int graphic = el2img(element);
9935
9936     if (IS_ANIMATED(graphic))
9937       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9938
9939     return;
9940   }
9941
9942   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9943     return;
9944
9945   Feld[x][y] = EL_EM_EXIT_OPENING;
9946
9947   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9948 }
9949
9950 void CheckExitSteel(int x, int y)
9951 {
9952   if (local_player->gems_still_needed > 0 ||
9953       local_player->sokobanfields_still_needed > 0 ||
9954       local_player->lights_still_needed > 0)
9955   {
9956     int element = Feld[x][y];
9957     int graphic = el2img(element);
9958
9959     if (IS_ANIMATED(graphic))
9960       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9961
9962     return;
9963   }
9964
9965   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9966     return;
9967
9968   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9969
9970   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9971 }
9972
9973 void CheckExitSteelEM(int x, int y)
9974 {
9975   if (local_player->gems_still_needed > 0 ||
9976       local_player->sokobanfields_still_needed > 0 ||
9977       local_player->lights_still_needed > 0)
9978   {
9979     int element = Feld[x][y];
9980     int graphic = el2img(element);
9981
9982     if (IS_ANIMATED(graphic))
9983       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9984
9985     return;
9986   }
9987
9988   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9989     return;
9990
9991   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9992
9993   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9994 }
9995
9996 void CheckExitSP(int x, int y)
9997 {
9998   if (local_player->gems_still_needed > 0)
9999   {
10000     int element = Feld[x][y];
10001     int graphic = el2img(element);
10002
10003     if (IS_ANIMATED(graphic))
10004       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10005
10006     return;
10007   }
10008
10009   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
10010     return;
10011
10012   Feld[x][y] = EL_SP_EXIT_OPENING;
10013
10014   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10015 }
10016
10017 static void CloseAllOpenTimegates()
10018 {
10019   int x, y;
10020
10021   SCAN_PLAYFIELD(x, y)
10022   {
10023     int element = Feld[x][y];
10024
10025     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10026     {
10027       Feld[x][y] = EL_TIMEGATE_CLOSING;
10028
10029       PlayLevelSoundAction(x, y, ACTION_CLOSING);
10030     }
10031   }
10032 }
10033
10034 void DrawTwinkleOnField(int x, int y)
10035 {
10036   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10037     return;
10038
10039   if (Feld[x][y] == EL_BD_DIAMOND)
10040     return;
10041
10042   if (MovDelay[x][y] == 0)      /* next animation frame */
10043     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10044
10045   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10046   {
10047     MovDelay[x][y]--;
10048
10049     DrawLevelElementAnimation(x, y, Feld[x][y]);
10050
10051     if (MovDelay[x][y] != 0)
10052     {
10053       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10054                                            10 - MovDelay[x][y]);
10055
10056       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10057     }
10058   }
10059 }
10060
10061 void MauerWaechst(int x, int y)
10062 {
10063   int delay = 6;
10064
10065   if (!MovDelay[x][y])          /* next animation frame */
10066     MovDelay[x][y] = 3 * delay;
10067
10068   if (MovDelay[x][y])           /* wait some time before next frame */
10069   {
10070     MovDelay[x][y]--;
10071
10072     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10073     {
10074       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10075       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10076
10077       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10078     }
10079
10080     if (!MovDelay[x][y])
10081     {
10082       if (MovDir[x][y] == MV_LEFT)
10083       {
10084         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10085           TEST_DrawLevelField(x - 1, y);
10086       }
10087       else if (MovDir[x][y] == MV_RIGHT)
10088       {
10089         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10090           TEST_DrawLevelField(x + 1, y);
10091       }
10092       else if (MovDir[x][y] == MV_UP)
10093       {
10094         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10095           TEST_DrawLevelField(x, y - 1);
10096       }
10097       else
10098       {
10099         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10100           TEST_DrawLevelField(x, y + 1);
10101       }
10102
10103       Feld[x][y] = Store[x][y];
10104       Store[x][y] = 0;
10105       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10106       TEST_DrawLevelField(x, y);
10107     }
10108   }
10109 }
10110
10111 void MauerAbleger(int ax, int ay)
10112 {
10113   int element = Feld[ax][ay];
10114   int graphic = el2img(element);
10115   boolean oben_frei = FALSE, unten_frei = FALSE;
10116   boolean links_frei = FALSE, rechts_frei = FALSE;
10117   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10118   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10119   boolean new_wall = FALSE;
10120
10121   if (IS_ANIMATED(graphic))
10122     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10123
10124   if (!MovDelay[ax][ay])        /* start building new wall */
10125     MovDelay[ax][ay] = 6;
10126
10127   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10128   {
10129     MovDelay[ax][ay]--;
10130     if (MovDelay[ax][ay])
10131       return;
10132   }
10133
10134   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10135     oben_frei = TRUE;
10136   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10137     unten_frei = TRUE;
10138   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10139     links_frei = TRUE;
10140   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10141     rechts_frei = TRUE;
10142
10143   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10144       element == EL_EXPANDABLE_WALL_ANY)
10145   {
10146     if (oben_frei)
10147     {
10148       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10149       Store[ax][ay-1] = element;
10150       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10151       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10152         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10153                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10154       new_wall = TRUE;
10155     }
10156     if (unten_frei)
10157     {
10158       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10159       Store[ax][ay+1] = element;
10160       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10161       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10162         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10163                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10164       new_wall = TRUE;
10165     }
10166   }
10167
10168   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10169       element == EL_EXPANDABLE_WALL_ANY ||
10170       element == EL_EXPANDABLE_WALL ||
10171       element == EL_BD_EXPANDABLE_WALL)
10172   {
10173     if (links_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_LEFT;
10178       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10179         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10180                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10181       new_wall = TRUE;
10182     }
10183
10184     if (rechts_frei)
10185     {
10186       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10187       Store[ax+1][ay] = element;
10188       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10189       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10190         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10191                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10192       new_wall = TRUE;
10193     }
10194   }
10195
10196   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10197     TEST_DrawLevelField(ax, ay);
10198
10199   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10200     oben_massiv = TRUE;
10201   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10202     unten_massiv = TRUE;
10203   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10204     links_massiv = TRUE;
10205   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10206     rechts_massiv = TRUE;
10207
10208   if (((oben_massiv && unten_massiv) ||
10209        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10210        element == EL_EXPANDABLE_WALL) &&
10211       ((links_massiv && rechts_massiv) ||
10212        element == EL_EXPANDABLE_WALL_VERTICAL))
10213     Feld[ax][ay] = EL_WALL;
10214
10215   if (new_wall)
10216     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10217 }
10218
10219 void MauerAblegerStahl(int ax, int ay)
10220 {
10221   int element = Feld[ax][ay];
10222   int graphic = el2img(element);
10223   boolean oben_frei = FALSE, unten_frei = FALSE;
10224   boolean links_frei = FALSE, rechts_frei = FALSE;
10225   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10226   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10227   boolean new_wall = FALSE;
10228
10229   if (IS_ANIMATED(graphic))
10230     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10231
10232   if (!MovDelay[ax][ay])        /* start building new wall */
10233     MovDelay[ax][ay] = 6;
10234
10235   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10236   {
10237     MovDelay[ax][ay]--;
10238     if (MovDelay[ax][ay])
10239       return;
10240   }
10241
10242   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10243     oben_frei = TRUE;
10244   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10245     unten_frei = TRUE;
10246   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10247     links_frei = TRUE;
10248   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10249     rechts_frei = TRUE;
10250
10251   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10252       element == EL_EXPANDABLE_STEELWALL_ANY)
10253   {
10254     if (oben_frei)
10255     {
10256       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10257       Store[ax][ay-1] = element;
10258       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10259       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10260         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10261                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10262       new_wall = TRUE;
10263     }
10264     if (unten_frei)
10265     {
10266       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10267       Store[ax][ay+1] = element;
10268       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10269       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10270         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10271                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10272       new_wall = TRUE;
10273     }
10274   }
10275
10276   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10277       element == EL_EXPANDABLE_STEELWALL_ANY)
10278   {
10279     if (links_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_LEFT;
10284       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10285         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10286                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10287       new_wall = TRUE;
10288     }
10289
10290     if (rechts_frei)
10291     {
10292       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10293       Store[ax+1][ay] = element;
10294       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10295       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10296         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10297                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10298       new_wall = TRUE;
10299     }
10300   }
10301
10302   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10303     oben_massiv = TRUE;
10304   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10305     unten_massiv = TRUE;
10306   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10307     links_massiv = TRUE;
10308   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10309     rechts_massiv = TRUE;
10310
10311   if (((oben_massiv && unten_massiv) ||
10312        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10313       ((links_massiv && rechts_massiv) ||
10314        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10315     Feld[ax][ay] = EL_STEELWALL;
10316
10317   if (new_wall)
10318     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10319 }
10320
10321 void CheckForDragon(int x, int y)
10322 {
10323   int i, j;
10324   boolean dragon_found = FALSE;
10325   static int xy[4][2] =
10326   {
10327     { 0, -1 },
10328     { -1, 0 },
10329     { +1, 0 },
10330     { 0, +1 }
10331   };
10332
10333   for (i = 0; i < NUM_DIRECTIONS; i++)
10334   {
10335     for (j = 0; j < 4; j++)
10336     {
10337       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10338
10339       if (IN_LEV_FIELD(xx, yy) &&
10340           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10341       {
10342         if (Feld[xx][yy] == EL_DRAGON)
10343           dragon_found = TRUE;
10344       }
10345       else
10346         break;
10347     }
10348   }
10349
10350   if (!dragon_found)
10351   {
10352     for (i = 0; i < NUM_DIRECTIONS; i++)
10353     {
10354       for (j = 0; j < 3; j++)
10355       {
10356         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10357   
10358         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10359         {
10360           Feld[xx][yy] = EL_EMPTY;
10361           TEST_DrawLevelField(xx, yy);
10362         }
10363         else
10364           break;
10365       }
10366     }
10367   }
10368 }
10369
10370 static void InitBuggyBase(int x, int y)
10371 {
10372   int element = Feld[x][y];
10373   int activating_delay = FRAMES_PER_SECOND / 4;
10374
10375   ChangeDelay[x][y] =
10376     (element == EL_SP_BUGGY_BASE ?
10377      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10378      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10379      activating_delay :
10380      element == EL_SP_BUGGY_BASE_ACTIVE ?
10381      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10382 }
10383
10384 static void WarnBuggyBase(int x, int y)
10385 {
10386   int i;
10387   static int xy[4][2] =
10388   {
10389     { 0, -1 },
10390     { -1, 0 },
10391     { +1, 0 },
10392     { 0, +1 }
10393   };
10394
10395   for (i = 0; i < NUM_DIRECTIONS; i++)
10396   {
10397     int xx = x + xy[i][0];
10398     int yy = y + xy[i][1];
10399
10400     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10401     {
10402       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10403
10404       break;
10405     }
10406   }
10407 }
10408
10409 static void InitTrap(int x, int y)
10410 {
10411   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10412 }
10413
10414 static void ActivateTrap(int x, int y)
10415 {
10416   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10417 }
10418
10419 static void ChangeActiveTrap(int x, int y)
10420 {
10421   int graphic = IMG_TRAP_ACTIVE;
10422
10423   /* if new animation frame was drawn, correct crumbled sand border */
10424   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10425     TEST_DrawLevelFieldCrumbled(x, y);
10426 }
10427
10428 static int getSpecialActionElement(int element, int number, int base_element)
10429 {
10430   return (element != EL_EMPTY ? element :
10431           number != -1 ? base_element + number - 1 :
10432           EL_EMPTY);
10433 }
10434
10435 static int getModifiedActionNumber(int value_old, int operator, int operand,
10436                                    int value_min, int value_max)
10437 {
10438   int value_new = (operator == CA_MODE_SET      ? operand :
10439                    operator == CA_MODE_ADD      ? value_old + operand :
10440                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10441                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10442                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10443                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10444                    value_old);
10445
10446   return (value_new < value_min ? value_min :
10447           value_new > value_max ? value_max :
10448           value_new);
10449 }
10450
10451 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10452 {
10453   struct ElementInfo *ei = &element_info[element];
10454   struct ElementChangeInfo *change = &ei->change_page[page];
10455   int target_element = change->target_element;
10456   int action_type = change->action_type;
10457   int action_mode = change->action_mode;
10458   int action_arg = change->action_arg;
10459   int action_element = change->action_element;
10460   int i;
10461
10462   if (!change->has_action)
10463     return;
10464
10465   /* ---------- determine action paramater values -------------------------- */
10466
10467   int level_time_value =
10468     (level.time > 0 ? TimeLeft :
10469      TimePlayed);
10470
10471   int action_arg_element_raw =
10472     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10473      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10474      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10475      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10476      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10477      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10478      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10479      EL_EMPTY);
10480   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10481
10482 #if 0
10483   if (action_arg_element_raw == EL_GROUP_START)
10484     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10485 #endif
10486
10487   int action_arg_direction =
10488     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10489      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10490      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10491      change->actual_trigger_side :
10492      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10493      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10494      MV_NONE);
10495
10496   int action_arg_number_min =
10497     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10498      CA_ARG_MIN);
10499
10500   int action_arg_number_max =
10501     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10502      action_type == CA_SET_LEVEL_GEMS ? 999 :
10503      action_type == CA_SET_LEVEL_TIME ? 9999 :
10504      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10505      action_type == CA_SET_CE_VALUE ? 9999 :
10506      action_type == CA_SET_CE_SCORE ? 9999 :
10507      CA_ARG_MAX);
10508
10509   int action_arg_number_reset =
10510     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10511      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10512      action_type == CA_SET_LEVEL_TIME ? level.time :
10513      action_type == CA_SET_LEVEL_SCORE ? 0 :
10514 #if USE_NEW_CUSTOM_VALUE
10515      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10516 #else
10517      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10518 #endif
10519      action_type == CA_SET_CE_SCORE ? 0 :
10520      0);
10521
10522   int action_arg_number =
10523     (action_arg <= CA_ARG_MAX ? action_arg :
10524      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10525      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10526      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10527      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10528      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10529      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10530 #if USE_NEW_CUSTOM_VALUE
10531      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10532 #else
10533      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10534 #endif
10535      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10536      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10537      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10538      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10539      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10540      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10541      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10542      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10543      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10544      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10545      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10546      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10547      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10548      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10549      -1);
10550
10551   int action_arg_number_old =
10552     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10553      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10554      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10555      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10556      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10557      0);
10558
10559   int action_arg_number_new =
10560     getModifiedActionNumber(action_arg_number_old,
10561                             action_mode, action_arg_number,
10562                             action_arg_number_min, action_arg_number_max);
10563
10564 #if 1
10565   int trigger_player_bits =
10566     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10567      change->actual_trigger_player_bits : change->trigger_player);
10568 #else
10569   int trigger_player_bits =
10570     (change->actual_trigger_player >= EL_PLAYER_1 &&
10571      change->actual_trigger_player <= EL_PLAYER_4 ?
10572      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10573      PLAYER_BITS_ANY);
10574 #endif
10575
10576   int action_arg_player_bits =
10577     (action_arg >= CA_ARG_PLAYER_1 &&
10578      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10579      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10580      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10581      PLAYER_BITS_ANY);
10582
10583   /* ---------- execute action  -------------------------------------------- */
10584
10585   switch (action_type)
10586   {
10587     case CA_NO_ACTION:
10588     {
10589       return;
10590     }
10591
10592     /* ---------- level actions  ------------------------------------------- */
10593
10594     case CA_RESTART_LEVEL:
10595     {
10596       game.restart_level = TRUE;
10597
10598       break;
10599     }
10600
10601     case CA_SHOW_ENVELOPE:
10602     {
10603       int element = getSpecialActionElement(action_arg_element,
10604                                             action_arg_number, EL_ENVELOPE_1);
10605
10606       if (IS_ENVELOPE(element))
10607         local_player->show_envelope = element;
10608
10609       break;
10610     }
10611
10612     case CA_SET_LEVEL_TIME:
10613     {
10614       if (level.time > 0)       /* only modify limited time value */
10615       {
10616         TimeLeft = action_arg_number_new;
10617
10618 #if 1
10619         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10620
10621         DisplayGameControlValues();
10622 #else
10623         DrawGameValue_Time(TimeLeft);
10624 #endif
10625
10626         if (!TimeLeft && setup.time_limit)
10627           for (i = 0; i < MAX_PLAYERS; i++)
10628             KillPlayer(&stored_player[i]);
10629       }
10630
10631       break;
10632     }
10633
10634     case CA_SET_LEVEL_SCORE:
10635     {
10636       local_player->score = action_arg_number_new;
10637
10638 #if 1
10639       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10640
10641       DisplayGameControlValues();
10642 #else
10643       DrawGameValue_Score(local_player->score);
10644 #endif
10645
10646       break;
10647     }
10648
10649     case CA_SET_LEVEL_GEMS:
10650     {
10651       local_player->gems_still_needed = action_arg_number_new;
10652
10653 #if 1
10654       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10655
10656       DisplayGameControlValues();
10657 #else
10658       DrawGameValue_Emeralds(local_player->gems_still_needed);
10659 #endif
10660
10661       break;
10662     }
10663
10664 #if !USE_PLAYER_GRAVITY
10665     case CA_SET_LEVEL_GRAVITY:
10666     {
10667       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10668                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10669                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10670                       game.gravity);
10671       break;
10672     }
10673 #endif
10674
10675     case CA_SET_LEVEL_WIND:
10676     {
10677       game.wind_direction = action_arg_direction;
10678
10679       break;
10680     }
10681
10682     case CA_SET_LEVEL_RANDOM_SEED:
10683     {
10684 #if 1
10685       /* ensure that setting a new random seed while playing is predictable */
10686       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10687 #else
10688       InitRND(action_arg_number_new);
10689 #endif
10690
10691 #if 0
10692       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10693 #endif
10694
10695 #if 0
10696       {
10697         int i;
10698
10699         printf("::: ");
10700         for (i = 0; i < 9; i++)
10701           printf("%d, ", RND(2));
10702         printf("\n");
10703       }
10704 #endif
10705
10706       break;
10707     }
10708
10709     /* ---------- player actions  ------------------------------------------ */
10710
10711     case CA_MOVE_PLAYER:
10712     {
10713       /* automatically move to the next field in specified direction */
10714       for (i = 0; i < MAX_PLAYERS; i++)
10715         if (trigger_player_bits & (1 << i))
10716           stored_player[i].programmed_action = action_arg_direction;
10717
10718       break;
10719     }
10720
10721     case CA_EXIT_PLAYER:
10722     {
10723       for (i = 0; i < MAX_PLAYERS; i++)
10724         if (action_arg_player_bits & (1 << i))
10725           PlayerWins(&stored_player[i]);
10726
10727       break;
10728     }
10729
10730     case CA_KILL_PLAYER:
10731     {
10732       for (i = 0; i < MAX_PLAYERS; i++)
10733         if (action_arg_player_bits & (1 << i))
10734           KillPlayer(&stored_player[i]);
10735
10736       break;
10737     }
10738
10739     case CA_SET_PLAYER_KEYS:
10740     {
10741       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10742       int element = getSpecialActionElement(action_arg_element,
10743                                             action_arg_number, EL_KEY_1);
10744
10745       if (IS_KEY(element))
10746       {
10747         for (i = 0; i < MAX_PLAYERS; i++)
10748         {
10749           if (trigger_player_bits & (1 << i))
10750           {
10751             stored_player[i].key[KEY_NR(element)] = key_state;
10752
10753             DrawGameDoorValues();
10754           }
10755         }
10756       }
10757
10758       break;
10759     }
10760
10761     case CA_SET_PLAYER_SPEED:
10762     {
10763 #if 0
10764       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10765 #endif
10766
10767       for (i = 0; i < MAX_PLAYERS; i++)
10768       {
10769         if (trigger_player_bits & (1 << i))
10770         {
10771           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10772
10773           if (action_arg == CA_ARG_SPEED_FASTER &&
10774               stored_player[i].cannot_move)
10775           {
10776             action_arg_number = STEPSIZE_VERY_SLOW;
10777           }
10778           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10779                    action_arg == CA_ARG_SPEED_FASTER)
10780           {
10781             action_arg_number = 2;
10782             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10783                            CA_MODE_MULTIPLY);
10784           }
10785           else if (action_arg == CA_ARG_NUMBER_RESET)
10786           {
10787             action_arg_number = level.initial_player_stepsize[i];
10788           }
10789
10790           move_stepsize =
10791             getModifiedActionNumber(move_stepsize,
10792                                     action_mode,
10793                                     action_arg_number,
10794                                     action_arg_number_min,
10795                                     action_arg_number_max);
10796
10797           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10798         }
10799       }
10800
10801       break;
10802     }
10803
10804     case CA_SET_PLAYER_SHIELD:
10805     {
10806       for (i = 0; i < MAX_PLAYERS; i++)
10807       {
10808         if (trigger_player_bits & (1 << i))
10809         {
10810           if (action_arg == CA_ARG_SHIELD_OFF)
10811           {
10812             stored_player[i].shield_normal_time_left = 0;
10813             stored_player[i].shield_deadly_time_left = 0;
10814           }
10815           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10816           {
10817             stored_player[i].shield_normal_time_left = 999999;
10818           }
10819           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10820           {
10821             stored_player[i].shield_normal_time_left = 999999;
10822             stored_player[i].shield_deadly_time_left = 999999;
10823           }
10824         }
10825       }
10826
10827       break;
10828     }
10829
10830 #if USE_PLAYER_GRAVITY
10831     case CA_SET_PLAYER_GRAVITY:
10832     {
10833       for (i = 0; i < MAX_PLAYERS; i++)
10834       {
10835         if (trigger_player_bits & (1 << i))
10836         {
10837           stored_player[i].gravity =
10838             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10839              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10840              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10841              stored_player[i].gravity);
10842         }
10843       }
10844
10845       break;
10846     }
10847 #endif
10848
10849     case CA_SET_PLAYER_ARTWORK:
10850     {
10851       for (i = 0; i < MAX_PLAYERS; i++)
10852       {
10853         if (trigger_player_bits & (1 << i))
10854         {
10855           int artwork_element = action_arg_element;
10856
10857           if (action_arg == CA_ARG_ELEMENT_RESET)
10858             artwork_element =
10859               (level.use_artwork_element[i] ? level.artwork_element[i] :
10860                stored_player[i].element_nr);
10861
10862 #if USE_GFX_RESET_PLAYER_ARTWORK
10863           if (stored_player[i].artwork_element != artwork_element)
10864             stored_player[i].Frame = 0;
10865 #endif
10866
10867           stored_player[i].artwork_element = artwork_element;
10868
10869           SetPlayerWaiting(&stored_player[i], FALSE);
10870
10871           /* set number of special actions for bored and sleeping animation */
10872           stored_player[i].num_special_action_bored =
10873             get_num_special_action(artwork_element,
10874                                    ACTION_BORING_1, ACTION_BORING_LAST);
10875           stored_player[i].num_special_action_sleeping =
10876             get_num_special_action(artwork_element,
10877                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10878         }
10879       }
10880
10881       break;
10882     }
10883
10884     case CA_SET_PLAYER_INVENTORY:
10885     {
10886       for (i = 0; i < MAX_PLAYERS; i++)
10887       {
10888         struct PlayerInfo *player = &stored_player[i];
10889         int j, k;
10890
10891         if (trigger_player_bits & (1 << i))
10892         {
10893           int inventory_element = action_arg_element;
10894
10895           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10896               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10897               action_arg == CA_ARG_ELEMENT_ACTION)
10898           {
10899             int element = inventory_element;
10900             int collect_count = element_info[element].collect_count_initial;
10901
10902             if (!IS_CUSTOM_ELEMENT(element))
10903               collect_count = 1;
10904
10905             if (collect_count == 0)
10906               player->inventory_infinite_element = element;
10907             else
10908               for (k = 0; k < collect_count; k++)
10909                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10910                   player->inventory_element[player->inventory_size++] =
10911                     element;
10912           }
10913           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10914                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10915                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10916           {
10917             if (player->inventory_infinite_element != EL_UNDEFINED &&
10918                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10919                                      action_arg_element_raw))
10920               player->inventory_infinite_element = EL_UNDEFINED;
10921
10922             for (k = 0, j = 0; j < player->inventory_size; j++)
10923             {
10924               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10925                                         action_arg_element_raw))
10926                 player->inventory_element[k++] = player->inventory_element[j];
10927             }
10928
10929             player->inventory_size = k;
10930           }
10931           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10932           {
10933             if (player->inventory_size > 0)
10934             {
10935               for (j = 0; j < player->inventory_size - 1; j++)
10936                 player->inventory_element[j] = player->inventory_element[j + 1];
10937
10938               player->inventory_size--;
10939             }
10940           }
10941           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10942           {
10943             if (player->inventory_size > 0)
10944               player->inventory_size--;
10945           }
10946           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10947           {
10948             player->inventory_infinite_element = EL_UNDEFINED;
10949             player->inventory_size = 0;
10950           }
10951           else if (action_arg == CA_ARG_INVENTORY_RESET)
10952           {
10953             player->inventory_infinite_element = EL_UNDEFINED;
10954             player->inventory_size = 0;
10955
10956             if (level.use_initial_inventory[i])
10957             {
10958               for (j = 0; j < level.initial_inventory_size[i]; j++)
10959               {
10960                 int element = level.initial_inventory_content[i][j];
10961                 int collect_count = element_info[element].collect_count_initial;
10962
10963                 if (!IS_CUSTOM_ELEMENT(element))
10964                   collect_count = 1;
10965
10966                 if (collect_count == 0)
10967                   player->inventory_infinite_element = element;
10968                 else
10969                   for (k = 0; k < collect_count; k++)
10970                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10971                       player->inventory_element[player->inventory_size++] =
10972                         element;
10973               }
10974             }
10975           }
10976         }
10977       }
10978
10979       break;
10980     }
10981
10982     /* ---------- CE actions  ---------------------------------------------- */
10983
10984     case CA_SET_CE_VALUE:
10985     {
10986 #if USE_NEW_CUSTOM_VALUE
10987       int last_ce_value = CustomValue[x][y];
10988
10989       CustomValue[x][y] = action_arg_number_new;
10990
10991       if (CustomValue[x][y] != last_ce_value)
10992       {
10993         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10994         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10995
10996         if (CustomValue[x][y] == 0)
10997         {
10998           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10999           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11000         }
11001       }
11002 #endif
11003
11004       break;
11005     }
11006
11007     case CA_SET_CE_SCORE:
11008     {
11009 #if USE_NEW_CUSTOM_VALUE
11010       int last_ce_score = ei->collect_score;
11011
11012       ei->collect_score = action_arg_number_new;
11013
11014       if (ei->collect_score != last_ce_score)
11015       {
11016         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11017         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11018
11019         if (ei->collect_score == 0)
11020         {
11021           int xx, yy;
11022
11023           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11024           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11025
11026           /*
11027             This is a very special case that seems to be a mixture between
11028             CheckElementChange() and CheckTriggeredElementChange(): while
11029             the first one only affects single elements that are triggered
11030             directly, the second one affects multiple elements in the playfield
11031             that are triggered indirectly by another element. This is a third
11032             case: Changing the CE score always affects multiple identical CEs,
11033             so every affected CE must be checked, not only the single CE for
11034             which the CE score was changed in the first place (as every instance
11035             of that CE shares the same CE score, and therefore also can change)!
11036           */
11037           SCAN_PLAYFIELD(xx, yy)
11038           {
11039             if (Feld[xx][yy] == element)
11040               CheckElementChange(xx, yy, element, EL_UNDEFINED,
11041                                  CE_SCORE_GETS_ZERO);
11042           }
11043         }
11044       }
11045 #endif
11046
11047       break;
11048     }
11049
11050     case CA_SET_CE_ARTWORK:
11051     {
11052       int artwork_element = action_arg_element;
11053       boolean reset_frame = FALSE;
11054       int xx, yy;
11055
11056       if (action_arg == CA_ARG_ELEMENT_RESET)
11057         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11058                            element);
11059
11060       if (ei->gfx_element != artwork_element)
11061         reset_frame = TRUE;
11062
11063       ei->gfx_element = artwork_element;
11064
11065       SCAN_PLAYFIELD(xx, yy)
11066       {
11067         if (Feld[xx][yy] == element)
11068         {
11069           if (reset_frame)
11070           {
11071             ResetGfxAnimation(xx, yy);
11072             ResetRandomAnimationValue(xx, yy);
11073           }
11074
11075           TEST_DrawLevelField(xx, yy);
11076         }
11077       }
11078
11079       break;
11080     }
11081
11082     /* ---------- engine actions  ------------------------------------------ */
11083
11084     case CA_SET_ENGINE_SCAN_MODE:
11085     {
11086       InitPlayfieldScanMode(action_arg);
11087
11088       break;
11089     }
11090
11091     default:
11092       break;
11093   }
11094 }
11095
11096 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11097 {
11098   int old_element = Feld[x][y];
11099   int new_element = GetElementFromGroupElement(element);
11100   int previous_move_direction = MovDir[x][y];
11101 #if USE_NEW_CUSTOM_VALUE
11102   int last_ce_value = CustomValue[x][y];
11103 #endif
11104   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11105   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11106   boolean add_player_onto_element = (new_element_is_player &&
11107 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11108                                      /* this breaks SnakeBite when a snake is
11109                                         halfway through a door that closes */
11110                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11111                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11112 #endif
11113                                      IS_WALKABLE(old_element));
11114
11115 #if 0
11116   /* check if element under the player changes from accessible to unaccessible
11117      (needed for special case of dropping element which then changes) */
11118   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11119       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11120   {
11121     Bang(x, y);
11122
11123     return;
11124   }
11125 #endif
11126
11127   if (!add_player_onto_element)
11128   {
11129     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11130       RemoveMovingField(x, y);
11131     else
11132       RemoveField(x, y);
11133
11134     Feld[x][y] = new_element;
11135
11136 #if !USE_GFX_RESET_GFX_ANIMATION
11137     ResetGfxAnimation(x, y);
11138     ResetRandomAnimationValue(x, y);
11139 #endif
11140
11141     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11142       MovDir[x][y] = previous_move_direction;
11143
11144 #if USE_NEW_CUSTOM_VALUE
11145     if (element_info[new_element].use_last_ce_value)
11146       CustomValue[x][y] = last_ce_value;
11147 #endif
11148
11149     InitField_WithBug1(x, y, FALSE);
11150
11151     new_element = Feld[x][y];   /* element may have changed */
11152
11153 #if USE_GFX_RESET_GFX_ANIMATION
11154     ResetGfxAnimation(x, y);
11155     ResetRandomAnimationValue(x, y);
11156 #endif
11157
11158     TEST_DrawLevelField(x, y);
11159
11160     if (GFX_CRUMBLED(new_element))
11161       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11162   }
11163
11164 #if 1
11165   /* check if element under the player changes from accessible to unaccessible
11166      (needed for special case of dropping element which then changes) */
11167   /* (must be checked after creating new element for walkable group elements) */
11168 #if USE_FIX_KILLED_BY_NON_WALKABLE
11169   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11170       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11171   {
11172     Bang(x, y);
11173
11174     return;
11175   }
11176 #else
11177   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11178       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11179   {
11180     Bang(x, y);
11181
11182     return;
11183   }
11184 #endif
11185 #endif
11186
11187   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11188   if (new_element_is_player)
11189     RelocatePlayer(x, y, new_element);
11190
11191   if (is_change)
11192     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11193
11194   TestIfBadThingTouchesPlayer(x, y);
11195   TestIfPlayerTouchesCustomElement(x, y);
11196   TestIfElementTouchesCustomElement(x, y);
11197 }
11198
11199 static void CreateField(int x, int y, int element)
11200 {
11201   CreateFieldExt(x, y, element, FALSE);
11202 }
11203
11204 static void CreateElementFromChange(int x, int y, int element)
11205 {
11206   element = GET_VALID_RUNTIME_ELEMENT(element);
11207
11208 #if USE_STOP_CHANGED_ELEMENTS
11209   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11210   {
11211     int old_element = Feld[x][y];
11212
11213     /* prevent changed element from moving in same engine frame
11214        unless both old and new element can either fall or move */
11215     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11216         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11217       Stop[x][y] = TRUE;
11218   }
11219 #endif
11220
11221   CreateFieldExt(x, y, element, TRUE);
11222 }
11223
11224 static boolean ChangeElement(int x, int y, int element, int page)
11225 {
11226   struct ElementInfo *ei = &element_info[element];
11227   struct ElementChangeInfo *change = &ei->change_page[page];
11228   int ce_value = CustomValue[x][y];
11229   int ce_score = ei->collect_score;
11230   int target_element;
11231   int old_element = Feld[x][y];
11232
11233   /* always use default change event to prevent running into a loop */
11234   if (ChangeEvent[x][y] == -1)
11235     ChangeEvent[x][y] = CE_DELAY;
11236
11237   if (ChangeEvent[x][y] == CE_DELAY)
11238   {
11239     /* reset actual trigger element, trigger player and action element */
11240     change->actual_trigger_element = EL_EMPTY;
11241     change->actual_trigger_player = EL_EMPTY;
11242     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11243     change->actual_trigger_side = CH_SIDE_NONE;
11244     change->actual_trigger_ce_value = 0;
11245     change->actual_trigger_ce_score = 0;
11246   }
11247
11248   /* do not change elements more than a specified maximum number of changes */
11249   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11250     return FALSE;
11251
11252   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11253
11254   if (change->explode)
11255   {
11256     Bang(x, y);
11257
11258     return TRUE;
11259   }
11260
11261   if (change->use_target_content)
11262   {
11263     boolean complete_replace = TRUE;
11264     boolean can_replace[3][3];
11265     int xx, yy;
11266
11267     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11268     {
11269       boolean is_empty;
11270       boolean is_walkable;
11271       boolean is_diggable;
11272       boolean is_collectible;
11273       boolean is_removable;
11274       boolean is_destructible;
11275       int ex = x + xx - 1;
11276       int ey = y + yy - 1;
11277       int content_element = change->target_content.e[xx][yy];
11278       int e;
11279
11280       can_replace[xx][yy] = TRUE;
11281
11282       if (ex == x && ey == y)   /* do not check changing element itself */
11283         continue;
11284
11285       if (content_element == EL_EMPTY_SPACE)
11286       {
11287         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11288
11289         continue;
11290       }
11291
11292       if (!IN_LEV_FIELD(ex, ey))
11293       {
11294         can_replace[xx][yy] = FALSE;
11295         complete_replace = FALSE;
11296
11297         continue;
11298       }
11299
11300       e = Feld[ex][ey];
11301
11302       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11303         e = MovingOrBlocked2Element(ex, ey);
11304
11305       is_empty = (IS_FREE(ex, ey) ||
11306                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11307
11308       is_walkable     = (is_empty || IS_WALKABLE(e));
11309       is_diggable     = (is_empty || IS_DIGGABLE(e));
11310       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11311       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11312       is_removable    = (is_diggable || is_collectible);
11313
11314       can_replace[xx][yy] =
11315         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11316           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11317           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11318           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11319           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11320           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11321          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11322
11323       if (!can_replace[xx][yy])
11324         complete_replace = FALSE;
11325     }
11326
11327     if (!change->only_if_complete || complete_replace)
11328     {
11329       boolean something_has_changed = FALSE;
11330
11331       if (change->only_if_complete && change->use_random_replace &&
11332           RND(100) < change->random_percentage)
11333         return FALSE;
11334
11335       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11336       {
11337         int ex = x + xx - 1;
11338         int ey = y + yy - 1;
11339         int content_element;
11340
11341         if (can_replace[xx][yy] && (!change->use_random_replace ||
11342                                     RND(100) < change->random_percentage))
11343         {
11344           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11345             RemoveMovingField(ex, ey);
11346
11347           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11348
11349           content_element = change->target_content.e[xx][yy];
11350           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11351                                               ce_value, ce_score);
11352
11353           CreateElementFromChange(ex, ey, target_element);
11354
11355           something_has_changed = TRUE;
11356
11357           /* for symmetry reasons, freeze newly created border elements */
11358           if (ex != x || ey != y)
11359             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11360         }
11361       }
11362
11363       if (something_has_changed)
11364       {
11365         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11366         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11367       }
11368     }
11369   }
11370   else
11371   {
11372     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11373                                         ce_value, ce_score);
11374
11375     if (element == EL_DIAGONAL_GROWING ||
11376         element == EL_DIAGONAL_SHRINKING)
11377     {
11378       target_element = Store[x][y];
11379
11380       Store[x][y] = EL_EMPTY;
11381     }
11382
11383     CreateElementFromChange(x, y, target_element);
11384
11385     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11386     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11387   }
11388
11389   /* this uses direct change before indirect change */
11390   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11391
11392   return TRUE;
11393 }
11394
11395 #if USE_NEW_DELAYED_ACTION
11396
11397 static void HandleElementChange(int x, int y, int page)
11398 {
11399   int element = MovingOrBlocked2Element(x, y);
11400   struct ElementInfo *ei = &element_info[element];
11401   struct ElementChangeInfo *change = &ei->change_page[page];
11402   boolean handle_action_before_change = FALSE;
11403
11404 #ifdef DEBUG
11405   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11406       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11407   {
11408     printf("\n\n");
11409     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11410            x, y, element, element_info[element].token_name);
11411     printf("HandleElementChange(): This should never happen!\n");
11412     printf("\n\n");
11413   }
11414 #endif
11415
11416   /* this can happen with classic bombs on walkable, changing elements */
11417   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11418   {
11419 #if 0
11420     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11421       ChangeDelay[x][y] = 0;
11422 #endif
11423
11424     return;
11425   }
11426
11427   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11428   {
11429     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11430
11431     if (change->can_change)
11432     {
11433 #if 1
11434       /* !!! not clear why graphic animation should be reset at all here !!! */
11435       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11436 #if USE_GFX_RESET_WHEN_NOT_MOVING
11437       /* when a custom element is about to change (for example by change delay),
11438          do not reset graphic animation when the custom element is moving */
11439       if (!IS_MOVING(x, y))
11440 #endif
11441       {
11442         ResetGfxAnimation(x, y);
11443         ResetRandomAnimationValue(x, y);
11444       }
11445 #endif
11446
11447       if (change->pre_change_function)
11448         change->pre_change_function(x, y);
11449     }
11450   }
11451
11452   ChangeDelay[x][y]--;
11453
11454   if (ChangeDelay[x][y] != 0)           /* continue element change */
11455   {
11456     if (change->can_change)
11457     {
11458       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11459
11460       if (IS_ANIMATED(graphic))
11461         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11462
11463       if (change->change_function)
11464         change->change_function(x, y);
11465     }
11466   }
11467   else                                  /* finish element change */
11468   {
11469     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11470     {
11471       page = ChangePage[x][y];
11472       ChangePage[x][y] = -1;
11473
11474       change = &ei->change_page[page];
11475     }
11476
11477     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11478     {
11479       ChangeDelay[x][y] = 1;            /* try change after next move step */
11480       ChangePage[x][y] = page;          /* remember page to use for change */
11481
11482       return;
11483     }
11484
11485 #if 1
11486     /* special case: set new level random seed before changing element */
11487     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11488       handle_action_before_change = TRUE;
11489
11490     if (change->has_action && handle_action_before_change)
11491       ExecuteCustomElementAction(x, y, element, page);
11492 #endif
11493
11494     if (change->can_change)
11495     {
11496       if (ChangeElement(x, y, element, page))
11497       {
11498         if (change->post_change_function)
11499           change->post_change_function(x, y);
11500       }
11501     }
11502
11503     if (change->has_action && !handle_action_before_change)
11504       ExecuteCustomElementAction(x, y, element, page);
11505   }
11506 }
11507
11508 #else
11509
11510 static void HandleElementChange(int x, int y, int page)
11511 {
11512   int element = MovingOrBlocked2Element(x, y);
11513   struct ElementInfo *ei = &element_info[element];
11514   struct ElementChangeInfo *change = &ei->change_page[page];
11515
11516 #ifdef DEBUG
11517   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11518   {
11519     printf("\n\n");
11520     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11521            x, y, element, element_info[element].token_name);
11522     printf("HandleElementChange(): This should never happen!\n");
11523     printf("\n\n");
11524   }
11525 #endif
11526
11527   /* this can happen with classic bombs on walkable, changing elements */
11528   if (!CAN_CHANGE(element))
11529   {
11530 #if 0
11531     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11532       ChangeDelay[x][y] = 0;
11533 #endif
11534
11535     return;
11536   }
11537
11538   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11539   {
11540     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11541
11542     ResetGfxAnimation(x, y);
11543     ResetRandomAnimationValue(x, y);
11544
11545     if (change->pre_change_function)
11546       change->pre_change_function(x, y);
11547   }
11548
11549   ChangeDelay[x][y]--;
11550
11551   if (ChangeDelay[x][y] != 0)           /* continue element change */
11552   {
11553     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11554
11555     if (IS_ANIMATED(graphic))
11556       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11557
11558     if (change->change_function)
11559       change->change_function(x, y);
11560   }
11561   else                                  /* finish element change */
11562   {
11563     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11564     {
11565       page = ChangePage[x][y];
11566       ChangePage[x][y] = -1;
11567
11568       change = &ei->change_page[page];
11569     }
11570
11571     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11572     {
11573       ChangeDelay[x][y] = 1;            /* try change after next move step */
11574       ChangePage[x][y] = page;          /* remember page to use for change */
11575
11576       return;
11577     }
11578
11579     if (ChangeElement(x, y, element, page))
11580     {
11581       if (change->post_change_function)
11582         change->post_change_function(x, y);
11583     }
11584   }
11585 }
11586
11587 #endif
11588
11589 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11590                                               int trigger_element,
11591                                               int trigger_event,
11592                                               int trigger_player,
11593                                               int trigger_side,
11594                                               int trigger_page)
11595 {
11596   boolean change_done_any = FALSE;
11597   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11598   int i;
11599
11600   if (!(trigger_events[trigger_element][trigger_event]))
11601     return FALSE;
11602
11603 #if 0
11604   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11605          trigger_event, recursion_loop_depth, recursion_loop_detected,
11606          recursion_loop_element, EL_NAME(recursion_loop_element));
11607 #endif
11608
11609   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11610
11611   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11612   {
11613     int element = EL_CUSTOM_START + i;
11614     boolean change_done = FALSE;
11615     int p;
11616
11617     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11618         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11619       continue;
11620
11621     for (p = 0; p < element_info[element].num_change_pages; p++)
11622     {
11623       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11624
11625       if (change->can_change_or_has_action &&
11626           change->has_event[trigger_event] &&
11627           change->trigger_side & trigger_side &&
11628           change->trigger_player & trigger_player &&
11629           change->trigger_page & trigger_page_bits &&
11630           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11631       {
11632         change->actual_trigger_element = trigger_element;
11633         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11634         change->actual_trigger_player_bits = trigger_player;
11635         change->actual_trigger_side = trigger_side;
11636         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11637         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11638
11639 #if 0
11640         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11641                element, EL_NAME(element), p);
11642 #endif
11643
11644         if ((change->can_change && !change_done) || change->has_action)
11645         {
11646           int x, y;
11647
11648           SCAN_PLAYFIELD(x, y)
11649           {
11650             if (Feld[x][y] == element)
11651             {
11652               if (change->can_change && !change_done)
11653               {
11654 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11655                 /* if element already changed in this frame, not only prevent
11656                    another element change (checked in ChangeElement()), but
11657                    also prevent additional element actions for this element */
11658
11659                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11660                     !level.use_action_after_change_bug)
11661                   continue;
11662 #endif
11663
11664 #if 0
11665                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11666                        element, EL_NAME(element), p);
11667 #endif
11668
11669                 ChangeDelay[x][y] = 1;
11670                 ChangeEvent[x][y] = trigger_event;
11671
11672                 HandleElementChange(x, y, p);
11673               }
11674 #if USE_NEW_DELAYED_ACTION
11675               else if (change->has_action)
11676               {
11677 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11678                 /* if element already changed in this frame, not only prevent
11679                    another element change (checked in ChangeElement()), but
11680                    also prevent additional element actions for this element */
11681
11682                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11683                     !level.use_action_after_change_bug)
11684                   continue;
11685 #endif
11686
11687
11688 #if 0
11689                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11690                        element, EL_NAME(element), p);
11691 #endif
11692
11693                 ExecuteCustomElementAction(x, y, element, p);
11694                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11695               }
11696 #else
11697               if (change->has_action)
11698               {
11699                 ExecuteCustomElementAction(x, y, element, p);
11700                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11701               }
11702 #endif
11703             }
11704           }
11705
11706           if (change->can_change)
11707           {
11708             change_done = TRUE;
11709             change_done_any = TRUE;
11710
11711 #if 0
11712             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11713                    element, EL_NAME(element), p);
11714 #endif
11715
11716           }
11717         }
11718       }
11719     }
11720   }
11721
11722   RECURSION_LOOP_DETECTION_END();
11723
11724   return change_done_any;
11725 }
11726
11727 static boolean CheckElementChangeExt(int x, int y,
11728                                      int element,
11729                                      int trigger_element,
11730                                      int trigger_event,
11731                                      int trigger_player,
11732                                      int trigger_side)
11733 {
11734   boolean change_done = FALSE;
11735   int p;
11736
11737   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11738       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11739     return FALSE;
11740
11741   if (Feld[x][y] == EL_BLOCKED)
11742   {
11743     Blocked2Moving(x, y, &x, &y);
11744     element = Feld[x][y];
11745   }
11746
11747 #if 0
11748   /* check if element has already changed */
11749   if (Feld[x][y] != element)
11750     return FALSE;
11751 #else
11752   /* check if element has already changed or is about to change after moving */
11753   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11754        Feld[x][y] != element) ||
11755
11756       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11757        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11758         ChangePage[x][y] != -1)))
11759     return FALSE;
11760 #endif
11761
11762 #if 0
11763   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11764          trigger_event, recursion_loop_depth, recursion_loop_detected,
11765          recursion_loop_element, EL_NAME(recursion_loop_element));
11766 #endif
11767
11768   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11769
11770 #if 0
11771   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11772 #endif
11773
11774   for (p = 0; p < element_info[element].num_change_pages; p++)
11775   {
11776     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11777
11778     /* check trigger element for all events where the element that is checked
11779        for changing interacts with a directly adjacent element -- this is
11780        different to element changes that affect other elements to change on the
11781        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11782     boolean check_trigger_element =
11783       (trigger_event == CE_TOUCHING_X ||
11784        trigger_event == CE_HITTING_X ||
11785        trigger_event == CE_HIT_BY_X ||
11786 #if 1
11787        /* this one was forgotten until 3.2.3 */
11788        trigger_event == CE_DIGGING_X);
11789 #endif
11790
11791     if (change->can_change_or_has_action &&
11792         change->has_event[trigger_event] &&
11793         change->trigger_side & trigger_side &&
11794         change->trigger_player & trigger_player &&
11795         (!check_trigger_element ||
11796          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11797     {
11798       change->actual_trigger_element = trigger_element;
11799       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11800       change->actual_trigger_player_bits = trigger_player;
11801       change->actual_trigger_side = trigger_side;
11802       change->actual_trigger_ce_value = CustomValue[x][y];
11803       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11804
11805       /* special case: trigger element not at (x,y) position for some events */
11806       if (check_trigger_element)
11807       {
11808         static struct
11809         {
11810           int dx, dy;
11811         } move_xy[] =
11812           {
11813             {  0,  0 },
11814             { -1,  0 },
11815             { +1,  0 },
11816             {  0,  0 },
11817             {  0, -1 },
11818             {  0,  0 }, { 0, 0 }, { 0, 0 },
11819             {  0, +1 }
11820           };
11821
11822         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11823         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11824
11825         change->actual_trigger_ce_value = CustomValue[xx][yy];
11826         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11827       }
11828
11829       if (change->can_change && !change_done)
11830       {
11831         ChangeDelay[x][y] = 1;
11832         ChangeEvent[x][y] = trigger_event;
11833
11834         HandleElementChange(x, y, p);
11835
11836         change_done = TRUE;
11837       }
11838 #if USE_NEW_DELAYED_ACTION
11839       else if (change->has_action)
11840       {
11841         ExecuteCustomElementAction(x, y, element, p);
11842         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11843       }
11844 #else
11845       if (change->has_action)
11846       {
11847         ExecuteCustomElementAction(x, y, element, p);
11848         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11849       }
11850 #endif
11851     }
11852   }
11853
11854   RECURSION_LOOP_DETECTION_END();
11855
11856   return change_done;
11857 }
11858
11859 static void PlayPlayerSound(struct PlayerInfo *player)
11860 {
11861   int jx = player->jx, jy = player->jy;
11862   int sound_element = player->artwork_element;
11863   int last_action = player->last_action_waiting;
11864   int action = player->action_waiting;
11865
11866   if (player->is_waiting)
11867   {
11868     if (action != last_action)
11869       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11870     else
11871       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11872   }
11873   else
11874   {
11875     if (action != last_action)
11876       StopSound(element_info[sound_element].sound[last_action]);
11877
11878     if (last_action == ACTION_SLEEPING)
11879       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11880   }
11881 }
11882
11883 static void PlayAllPlayersSound()
11884 {
11885   int i;
11886
11887   for (i = 0; i < MAX_PLAYERS; i++)
11888     if (stored_player[i].active)
11889       PlayPlayerSound(&stored_player[i]);
11890 }
11891
11892 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11893 {
11894   boolean last_waiting = player->is_waiting;
11895   int move_dir = player->MovDir;
11896
11897   player->dir_waiting = move_dir;
11898   player->last_action_waiting = player->action_waiting;
11899
11900   if (is_waiting)
11901   {
11902     if (!last_waiting)          /* not waiting -> waiting */
11903     {
11904       player->is_waiting = TRUE;
11905
11906       player->frame_counter_bored =
11907         FrameCounter +
11908         game.player_boring_delay_fixed +
11909         GetSimpleRandom(game.player_boring_delay_random);
11910       player->frame_counter_sleeping =
11911         FrameCounter +
11912         game.player_sleeping_delay_fixed +
11913         GetSimpleRandom(game.player_sleeping_delay_random);
11914
11915       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11916     }
11917
11918     if (game.player_sleeping_delay_fixed +
11919         game.player_sleeping_delay_random > 0 &&
11920         player->anim_delay_counter == 0 &&
11921         player->post_delay_counter == 0 &&
11922         FrameCounter >= player->frame_counter_sleeping)
11923       player->is_sleeping = TRUE;
11924     else if (game.player_boring_delay_fixed +
11925              game.player_boring_delay_random > 0 &&
11926              FrameCounter >= player->frame_counter_bored)
11927       player->is_bored = TRUE;
11928
11929     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11930                               player->is_bored ? ACTION_BORING :
11931                               ACTION_WAITING);
11932
11933     if (player->is_sleeping && player->use_murphy)
11934     {
11935       /* special case for sleeping Murphy when leaning against non-free tile */
11936
11937       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11938           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11939            !IS_MOVING(player->jx - 1, player->jy)))
11940         move_dir = MV_LEFT;
11941       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11942                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11943                 !IS_MOVING(player->jx + 1, player->jy)))
11944         move_dir = MV_RIGHT;
11945       else
11946         player->is_sleeping = FALSE;
11947
11948       player->dir_waiting = move_dir;
11949     }
11950
11951     if (player->is_sleeping)
11952     {
11953       if (player->num_special_action_sleeping > 0)
11954       {
11955         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11956         {
11957           int last_special_action = player->special_action_sleeping;
11958           int num_special_action = player->num_special_action_sleeping;
11959           int special_action =
11960             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11961              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11962              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11963              last_special_action + 1 : ACTION_SLEEPING);
11964           int special_graphic =
11965             el_act_dir2img(player->artwork_element, special_action, move_dir);
11966
11967           player->anim_delay_counter =
11968             graphic_info[special_graphic].anim_delay_fixed +
11969             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11970           player->post_delay_counter =
11971             graphic_info[special_graphic].post_delay_fixed +
11972             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11973
11974           player->special_action_sleeping = special_action;
11975         }
11976
11977         if (player->anim_delay_counter > 0)
11978         {
11979           player->action_waiting = player->special_action_sleeping;
11980           player->anim_delay_counter--;
11981         }
11982         else if (player->post_delay_counter > 0)
11983         {
11984           player->post_delay_counter--;
11985         }
11986       }
11987     }
11988     else if (player->is_bored)
11989     {
11990       if (player->num_special_action_bored > 0)
11991       {
11992         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11993         {
11994           int special_action =
11995             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11996           int special_graphic =
11997             el_act_dir2img(player->artwork_element, special_action, move_dir);
11998
11999           player->anim_delay_counter =
12000             graphic_info[special_graphic].anim_delay_fixed +
12001             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12002           player->post_delay_counter =
12003             graphic_info[special_graphic].post_delay_fixed +
12004             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12005
12006           player->special_action_bored = special_action;
12007         }
12008
12009         if (player->anim_delay_counter > 0)
12010         {
12011           player->action_waiting = player->special_action_bored;
12012           player->anim_delay_counter--;
12013         }
12014         else if (player->post_delay_counter > 0)
12015         {
12016           player->post_delay_counter--;
12017         }
12018       }
12019     }
12020   }
12021   else if (last_waiting)        /* waiting -> not waiting */
12022   {
12023     player->is_waiting = FALSE;
12024     player->is_bored = FALSE;
12025     player->is_sleeping = FALSE;
12026
12027     player->frame_counter_bored = -1;
12028     player->frame_counter_sleeping = -1;
12029
12030     player->anim_delay_counter = 0;
12031     player->post_delay_counter = 0;
12032
12033     player->dir_waiting = player->MovDir;
12034     player->action_waiting = ACTION_DEFAULT;
12035
12036     player->special_action_bored = ACTION_DEFAULT;
12037     player->special_action_sleeping = ACTION_DEFAULT;
12038   }
12039 }
12040
12041 static void CheckSingleStepMode(struct PlayerInfo *player)
12042 {
12043   if (tape.single_step && tape.recording && !tape.pausing)
12044   {
12045     /* as it is called "single step mode", just return to pause mode when the
12046        player stopped moving after one tile (or never starts moving at all) */
12047     if (!player->is_moving && !player->is_pushing)
12048     {
12049       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12050       SnapField(player, 0, 0);                  /* stop snapping */
12051     }
12052   }
12053 }
12054
12055 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12056 {
12057   int left      = player_action & JOY_LEFT;
12058   int right     = player_action & JOY_RIGHT;
12059   int up        = player_action & JOY_UP;
12060   int down      = player_action & JOY_DOWN;
12061   int button1   = player_action & JOY_BUTTON_1;
12062   int button2   = player_action & JOY_BUTTON_2;
12063   int dx        = (left ? -1 : right ? 1 : 0);
12064   int dy        = (up   ? -1 : down  ? 1 : 0);
12065
12066   if (!player->active || tape.pausing)
12067     return 0;
12068
12069   if (player_action)
12070   {
12071     if (button1)
12072       SnapField(player, dx, dy);
12073     else
12074     {
12075       if (button2)
12076         DropElement(player);
12077
12078       MovePlayer(player, dx, dy);
12079     }
12080
12081     CheckSingleStepMode(player);
12082
12083     SetPlayerWaiting(player, FALSE);
12084
12085     return player_action;
12086   }
12087   else
12088   {
12089     /* no actions for this player (no input at player's configured device) */
12090
12091     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12092     SnapField(player, 0, 0);
12093     CheckGravityMovementWhenNotMoving(player);
12094
12095     if (player->MovPos == 0)
12096       SetPlayerWaiting(player, TRUE);
12097
12098     if (player->MovPos == 0)    /* needed for tape.playing */
12099       player->is_moving = FALSE;
12100
12101     player->is_dropping = FALSE;
12102     player->is_dropping_pressed = FALSE;
12103     player->drop_pressed_delay = 0;
12104
12105     CheckSingleStepMode(player);
12106
12107     return 0;
12108   }
12109 }
12110
12111 static void CheckLevelTime()
12112 {
12113   int i;
12114
12115   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12116   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12117   {
12118     if (level.native_em_level->lev->home == 0)  /* all players at home */
12119     {
12120       PlayerWins(local_player);
12121
12122       AllPlayersGone = TRUE;
12123
12124       level.native_em_level->lev->home = -1;
12125     }
12126
12127     if (level.native_em_level->ply[0]->alive == 0 &&
12128         level.native_em_level->ply[1]->alive == 0 &&
12129         level.native_em_level->ply[2]->alive == 0 &&
12130         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12131       AllPlayersGone = TRUE;
12132   }
12133   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12134   {
12135     if (game_sp.LevelSolved &&
12136         !game_sp.GameOver)                              /* game won */
12137     {
12138       PlayerWins(local_player);
12139
12140       game_sp.GameOver = TRUE;
12141
12142       AllPlayersGone = TRUE;
12143     }
12144
12145     if (game_sp.GameOver)                               /* game lost */
12146       AllPlayersGone = TRUE;
12147   }
12148
12149   if (TimeFrames >= FRAMES_PER_SECOND)
12150   {
12151     TimeFrames = 0;
12152     TapeTime++;
12153
12154     for (i = 0; i < MAX_PLAYERS; i++)
12155     {
12156       struct PlayerInfo *player = &stored_player[i];
12157
12158       if (SHIELD_ON(player))
12159       {
12160         player->shield_normal_time_left--;
12161
12162         if (player->shield_deadly_time_left > 0)
12163           player->shield_deadly_time_left--;
12164       }
12165     }
12166
12167     if (!local_player->LevelSolved && !level.use_step_counter)
12168     {
12169       TimePlayed++;
12170
12171       if (TimeLeft > 0)
12172       {
12173         TimeLeft--;
12174
12175         if (TimeLeft <= 10 && setup.time_limit)
12176           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12177
12178 #if 1
12179         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12180            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12181
12182         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12183
12184         /* (already called by UpdateAndDisplayGameControlValues() below) */
12185         // DisplayGameControlValues();
12186 #else
12187         DrawGameValue_Time(TimeLeft);
12188 #endif
12189
12190         if (!TimeLeft && setup.time_limit)
12191         {
12192           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12193             level.native_em_level->lev->killed_out_of_time = TRUE;
12194           else
12195             for (i = 0; i < MAX_PLAYERS; i++)
12196               KillPlayer(&stored_player[i]);
12197         }
12198       }
12199 #if 1
12200       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12201       {
12202         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12203
12204         /* (already called by UpdateAndDisplayGameControlValues() below) */
12205         // DisplayGameControlValues();
12206       }
12207 #else
12208       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12209         DrawGameValue_Time(TimePlayed);
12210 #endif
12211
12212       level.native_em_level->lev->time =
12213         (game.no_time_limit ? TimePlayed : TimeLeft);
12214     }
12215
12216     if (tape.recording || tape.playing)
12217       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12218   }
12219
12220 #if 1
12221   UpdateAndDisplayGameControlValues();
12222 #else
12223   UpdateGameDoorValues();
12224   DrawGameDoorValues();
12225 #endif
12226 }
12227
12228 void AdvanceFrameAndPlayerCounters(int player_nr)
12229 {
12230   int i;
12231
12232   /* advance frame counters (global frame counter and time frame counter) */
12233   FrameCounter++;
12234   TimeFrames++;
12235
12236   /* advance player counters (counters for move delay, move animation etc.) */
12237   for (i = 0; i < MAX_PLAYERS; i++)
12238   {
12239     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12240     int move_delay_value = stored_player[i].move_delay_value;
12241     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12242
12243     if (!advance_player_counters)       /* not all players may be affected */
12244       continue;
12245
12246 #if USE_NEW_PLAYER_ANIM
12247     if (move_frames == 0)       /* less than one move per game frame */
12248     {
12249       int stepsize = TILEX / move_delay_value;
12250       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12251       int count = (stored_player[i].is_moving ?
12252                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12253
12254       if (count % delay == 0)
12255         move_frames = 1;
12256     }
12257 #endif
12258
12259     stored_player[i].Frame += move_frames;
12260
12261     if (stored_player[i].MovPos != 0)
12262       stored_player[i].StepFrame += move_frames;
12263
12264     if (stored_player[i].move_delay > 0)
12265       stored_player[i].move_delay--;
12266
12267     /* due to bugs in previous versions, counter must count up, not down */
12268     if (stored_player[i].push_delay != -1)
12269       stored_player[i].push_delay++;
12270
12271     if (stored_player[i].drop_delay > 0)
12272       stored_player[i].drop_delay--;
12273
12274     if (stored_player[i].is_dropping_pressed)
12275       stored_player[i].drop_pressed_delay++;
12276   }
12277 }
12278
12279 void StartGameActions(boolean init_network_game, boolean record_tape,
12280                       int random_seed)
12281 {
12282   unsigned int new_random_seed = InitRND(random_seed);
12283
12284   if (record_tape)
12285     TapeStartRecording(new_random_seed);
12286
12287 #if defined(NETWORK_AVALIABLE)
12288   if (init_network_game)
12289   {
12290     SendToServer_StartPlaying();
12291
12292     return;
12293   }
12294 #endif
12295
12296   InitGame();
12297 }
12298
12299 void GameActions()
12300 {
12301   static unsigned int game_frame_delay = 0;
12302   unsigned int game_frame_delay_value;
12303   byte *recorded_player_action;
12304   byte summarized_player_action = 0;
12305   byte tape_action[MAX_PLAYERS];
12306   int i;
12307
12308   /* detect endless loops, caused by custom element programming */
12309   if (recursion_loop_detected && recursion_loop_depth == 0)
12310   {
12311     char *message = getStringCat3("Internal Error! Element ",
12312                                   EL_NAME(recursion_loop_element),
12313                                   " caused endless loop! Quit the game?");
12314
12315     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12316           EL_NAME(recursion_loop_element));
12317
12318     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12319
12320     recursion_loop_detected = FALSE;    /* if game should be continued */
12321
12322     free(message);
12323
12324     return;
12325   }
12326
12327   if (game.restart_level)
12328     StartGameActions(options.network, setup.autorecord, level.random_seed);
12329
12330   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12331   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12332   {
12333     if (level.native_em_level->lev->home == 0)  /* all players at home */
12334     {
12335       PlayerWins(local_player);
12336
12337       AllPlayersGone = TRUE;
12338
12339       level.native_em_level->lev->home = -1;
12340     }
12341
12342     if (level.native_em_level->ply[0]->alive == 0 &&
12343         level.native_em_level->ply[1]->alive == 0 &&
12344         level.native_em_level->ply[2]->alive == 0 &&
12345         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12346       AllPlayersGone = TRUE;
12347   }
12348   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12349   {
12350     if (game_sp.LevelSolved &&
12351         !game_sp.GameOver)                              /* game won */
12352     {
12353       PlayerWins(local_player);
12354
12355       game_sp.GameOver = TRUE;
12356
12357       AllPlayersGone = TRUE;
12358     }
12359
12360     if (game_sp.GameOver)                               /* game lost */
12361       AllPlayersGone = TRUE;
12362   }
12363
12364   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12365     GameWon();
12366
12367   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12368     TapeStop();
12369
12370   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12371     return;
12372
12373   game_frame_delay_value =
12374     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12375
12376   if (tape.playing && tape.warp_forward && !tape.pausing)
12377     game_frame_delay_value = 0;
12378
12379   /* ---------- main game synchronization point ---------- */
12380
12381   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12382
12383   if (network_playing && !network_player_action_received)
12384   {
12385     /* try to get network player actions in time */
12386
12387 #if defined(NETWORK_AVALIABLE)
12388     /* last chance to get network player actions without main loop delay */
12389     HandleNetworking();
12390 #endif
12391
12392     /* game was quit by network peer */
12393     if (game_status != GAME_MODE_PLAYING)
12394       return;
12395
12396     if (!network_player_action_received)
12397       return;           /* failed to get network player actions in time */
12398
12399     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12400   }
12401
12402   if (tape.pausing)
12403     return;
12404
12405   /* at this point we know that we really continue executing the game */
12406
12407   network_player_action_received = FALSE;
12408
12409   /* when playing tape, read previously recorded player input from tape data */
12410   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12411
12412 #if 1
12413   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12414   if (tape.pausing)
12415     return;
12416 #endif
12417
12418   if (tape.set_centered_player)
12419   {
12420     game.centered_player_nr_next = tape.centered_player_nr_next;
12421     game.set_centered_player = TRUE;
12422   }
12423
12424   for (i = 0; i < MAX_PLAYERS; i++)
12425   {
12426     summarized_player_action |= stored_player[i].action;
12427
12428 #if 1
12429     if (!network_playing && (game.team_mode || tape.playing))
12430       stored_player[i].effective_action = stored_player[i].action;
12431 #else
12432     if (!network_playing)
12433       stored_player[i].effective_action = stored_player[i].action;
12434 #endif
12435   }
12436
12437 #if defined(NETWORK_AVALIABLE)
12438   if (network_playing)
12439     SendToServer_MovePlayer(summarized_player_action);
12440 #endif
12441
12442   if (!options.network && !game.team_mode)
12443     local_player->effective_action = summarized_player_action;
12444
12445   if (tape.recording &&
12446       setup.team_mode &&
12447       setup.input_on_focus &&
12448       game.centered_player_nr != -1)
12449   {
12450     for (i = 0; i < MAX_PLAYERS; i++)
12451       stored_player[i].effective_action =
12452         (i == game.centered_player_nr ? summarized_player_action : 0);
12453   }
12454
12455   if (recorded_player_action != NULL)
12456     for (i = 0; i < MAX_PLAYERS; i++)
12457       stored_player[i].effective_action = recorded_player_action[i];
12458
12459   for (i = 0; i < MAX_PLAYERS; i++)
12460   {
12461     tape_action[i] = stored_player[i].effective_action;
12462
12463 #if 1
12464     /* (this may happen in the RND game engine if a player was not present on
12465        the playfield on level start, but appeared later from a custom element */
12466     if (tape.recording &&
12467         setup.team_mode &&
12468         tape_action[i] &&
12469         !tape.player_participates[i])
12470       tape.player_participates[i] = TRUE;
12471 #else
12472     /* (this can only happen in the R'n'D game engine) */
12473     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12474       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12475 #endif
12476   }
12477
12478   /* only record actions from input devices, but not programmed actions */
12479   if (tape.recording)
12480     TapeRecordAction(tape_action);
12481
12482 #if USE_NEW_PLAYER_ASSIGNMENTS
12483 #if 1
12484   if (game.team_mode)
12485 #endif
12486   {
12487     byte mapped_action[MAX_PLAYERS];
12488
12489 #if DEBUG_PLAYER_ACTIONS
12490     printf(":::");
12491     for (i = 0; i < MAX_PLAYERS; i++)
12492       printf(" %d, ", stored_player[i].effective_action);
12493 #endif
12494
12495     for (i = 0; i < MAX_PLAYERS; i++)
12496       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12497
12498     for (i = 0; i < MAX_PLAYERS; i++)
12499       stored_player[i].effective_action = mapped_action[i];
12500
12501 #if DEBUG_PLAYER_ACTIONS
12502     printf(" =>");
12503     for (i = 0; i < MAX_PLAYERS; i++)
12504       printf(" %d, ", stored_player[i].effective_action);
12505     printf("\n");
12506 #endif
12507   }
12508 #if DEBUG_PLAYER_ACTIONS
12509   else
12510   {
12511     printf(":::");
12512     for (i = 0; i < MAX_PLAYERS; i++)
12513       printf(" %d, ", stored_player[i].effective_action);
12514     printf("\n");
12515   }
12516 #endif
12517 #endif
12518
12519 #if 0
12520   printf("::: summarized_player_action == %d\n",
12521          local_player->effective_action);
12522 #endif
12523
12524
12525
12526
12527 #if 0
12528 #if DEBUG_INIT_PLAYER
12529     if (options.debug)
12530     {
12531       printf("Player status (final):\n");
12532
12533       for (i = 0; i < MAX_PLAYERS; i++)
12534       {
12535         struct PlayerInfo *player = &stored_player[i];
12536
12537         printf("- player %d: present == %d, connected == %d, active == %d",
12538                i + 1,
12539                player->present,
12540                player->connected,
12541                player->active);
12542
12543         if (local_player == player)
12544           printf(" (local player)");
12545
12546         printf("\n");
12547       }
12548     }
12549 #endif
12550 #endif
12551
12552
12553
12554   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12555   {
12556     GameActions_EM_Main();
12557   }
12558   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12559   {
12560     GameActions_SP_Main();
12561   }
12562   else
12563   {
12564     GameActions_RND();
12565   }
12566 }
12567
12568 void GameActions_EM_Main()
12569 {
12570   byte effective_action[MAX_PLAYERS];
12571   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12572   int i;
12573
12574   for (i = 0; i < MAX_PLAYERS; i++)
12575     effective_action[i] = stored_player[i].effective_action;
12576
12577   GameActions_EM(effective_action, warp_mode);
12578
12579   CheckLevelTime();
12580
12581   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12582 }
12583
12584 void GameActions_SP_Main()
12585 {
12586   byte effective_action[MAX_PLAYERS];
12587   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12588   int i;
12589
12590   for (i = 0; i < MAX_PLAYERS; i++)
12591     effective_action[i] = stored_player[i].effective_action;
12592
12593   GameActions_SP(effective_action, warp_mode);
12594
12595   CheckLevelTime();
12596
12597   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12598 }
12599
12600 void GameActions_RND()
12601 {
12602   int magic_wall_x = 0, magic_wall_y = 0;
12603   int i, x, y, element, graphic;
12604
12605   InitPlayfieldScanModeVars();
12606
12607 #if USE_ONE_MORE_CHANGE_PER_FRAME
12608   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12609   {
12610     SCAN_PLAYFIELD(x, y)
12611     {
12612       ChangeCount[x][y] = 0;
12613       ChangeEvent[x][y] = -1;
12614     }
12615   }
12616 #endif
12617
12618   if (game.set_centered_player)
12619   {
12620     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12621
12622     /* switching to "all players" only possible if all players fit to screen */
12623     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12624     {
12625       game.centered_player_nr_next = game.centered_player_nr;
12626       game.set_centered_player = FALSE;
12627     }
12628
12629     /* do not switch focus to non-existing (or non-active) player */
12630     if (game.centered_player_nr_next >= 0 &&
12631         !stored_player[game.centered_player_nr_next].active)
12632     {
12633       game.centered_player_nr_next = game.centered_player_nr;
12634       game.set_centered_player = FALSE;
12635     }
12636   }
12637
12638   if (game.set_centered_player &&
12639       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12640   {
12641     int sx, sy;
12642
12643     if (game.centered_player_nr_next == -1)
12644     {
12645       setScreenCenteredToAllPlayers(&sx, &sy);
12646     }
12647     else
12648     {
12649       sx = stored_player[game.centered_player_nr_next].jx;
12650       sy = stored_player[game.centered_player_nr_next].jy;
12651     }
12652
12653     game.centered_player_nr = game.centered_player_nr_next;
12654     game.set_centered_player = FALSE;
12655
12656     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12657     DrawGameDoorValues();
12658   }
12659
12660   for (i = 0; i < MAX_PLAYERS; i++)
12661   {
12662     int actual_player_action = stored_player[i].effective_action;
12663
12664 #if 1
12665     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12666        - rnd_equinox_tetrachloride 048
12667        - rnd_equinox_tetrachloride_ii 096
12668        - rnd_emanuel_schmieg 002
12669        - doctor_sloan_ww 001, 020
12670     */
12671     if (stored_player[i].MovPos == 0)
12672       CheckGravityMovement(&stored_player[i]);
12673 #endif
12674
12675     /* overwrite programmed action with tape action */
12676     if (stored_player[i].programmed_action)
12677       actual_player_action = stored_player[i].programmed_action;
12678
12679     PlayerActions(&stored_player[i], actual_player_action);
12680
12681     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12682   }
12683
12684   ScrollScreen(NULL, SCROLL_GO_ON);
12685
12686   /* for backwards compatibility, the following code emulates a fixed bug that
12687      occured when pushing elements (causing elements that just made their last
12688      pushing step to already (if possible) make their first falling step in the
12689      same game frame, which is bad); this code is also needed to use the famous
12690      "spring push bug" which is used in older levels and might be wanted to be
12691      used also in newer levels, but in this case the buggy pushing code is only
12692      affecting the "spring" element and no other elements */
12693
12694   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12695   {
12696     for (i = 0; i < MAX_PLAYERS; i++)
12697     {
12698       struct PlayerInfo *player = &stored_player[i];
12699       int x = player->jx;
12700       int y = player->jy;
12701
12702       if (player->active && player->is_pushing && player->is_moving &&
12703           IS_MOVING(x, y) &&
12704           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12705            Feld[x][y] == EL_SPRING))
12706       {
12707         ContinueMoving(x, y);
12708
12709         /* continue moving after pushing (this is actually a bug) */
12710         if (!IS_MOVING(x, y))
12711           Stop[x][y] = FALSE;
12712       }
12713     }
12714   }
12715
12716 #if 0
12717   debug_print_timestamp(0, "start main loop profiling");
12718 #endif
12719
12720   SCAN_PLAYFIELD(x, y)
12721   {
12722     ChangeCount[x][y] = 0;
12723     ChangeEvent[x][y] = -1;
12724
12725     /* this must be handled before main playfield loop */
12726     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12727     {
12728       MovDelay[x][y]--;
12729       if (MovDelay[x][y] <= 0)
12730         RemoveField(x, y);
12731     }
12732
12733 #if USE_NEW_SNAP_DELAY
12734     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12735     {
12736       MovDelay[x][y]--;
12737       if (MovDelay[x][y] <= 0)
12738       {
12739         RemoveField(x, y);
12740         TEST_DrawLevelField(x, y);
12741
12742         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12743       }
12744     }
12745 #endif
12746
12747 #if DEBUG
12748     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12749     {
12750       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12751       printf("GameActions(): This should never happen!\n");
12752
12753       ChangePage[x][y] = -1;
12754     }
12755 #endif
12756
12757     Stop[x][y] = FALSE;
12758     if (WasJustMoving[x][y] > 0)
12759       WasJustMoving[x][y]--;
12760     if (WasJustFalling[x][y] > 0)
12761       WasJustFalling[x][y]--;
12762     if (CheckCollision[x][y] > 0)
12763       CheckCollision[x][y]--;
12764     if (CheckImpact[x][y] > 0)
12765       CheckImpact[x][y]--;
12766
12767     GfxFrame[x][y]++;
12768
12769     /* reset finished pushing action (not done in ContinueMoving() to allow
12770        continuous pushing animation for elements with zero push delay) */
12771     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12772     {
12773       ResetGfxAnimation(x, y);
12774       TEST_DrawLevelField(x, y);
12775     }
12776
12777 #if DEBUG
12778     if (IS_BLOCKED(x, y))
12779     {
12780       int oldx, oldy;
12781
12782       Blocked2Moving(x, y, &oldx, &oldy);
12783       if (!IS_MOVING(oldx, oldy))
12784       {
12785         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12786         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12787         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12788         printf("GameActions(): This should never happen!\n");
12789       }
12790     }
12791 #endif
12792   }
12793
12794 #if 0
12795   debug_print_timestamp(0, "- time for pre-main loop:");
12796 #endif
12797
12798 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12799   SCAN_PLAYFIELD(x, y)
12800   {
12801     element = Feld[x][y];
12802     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12803
12804 #if 1
12805     {
12806 #if 1
12807       int element2 = element;
12808       int graphic2 = graphic;
12809 #else
12810       int element2 = Feld[x][y];
12811       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12812 #endif
12813       int last_gfx_frame = GfxFrame[x][y];
12814
12815       if (graphic_info[graphic2].anim_global_sync)
12816         GfxFrame[x][y] = FrameCounter;
12817       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12818         GfxFrame[x][y] = CustomValue[x][y];
12819       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12820         GfxFrame[x][y] = element_info[element2].collect_score;
12821       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12822         GfxFrame[x][y] = ChangeDelay[x][y];
12823
12824       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12825         DrawLevelGraphicAnimation(x, y, graphic2);
12826     }
12827 #else
12828     ResetGfxFrame(x, y, TRUE);
12829 #endif
12830
12831 #if 1
12832     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12833         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12834       ResetRandomAnimationValue(x, y);
12835 #endif
12836
12837 #if 1
12838     SetRandomAnimationValue(x, y);
12839 #endif
12840
12841 #if 1
12842     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12843 #endif
12844   }
12845 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12846
12847 #if 0
12848   debug_print_timestamp(0, "- time for TEST loop:     -->");
12849 #endif
12850
12851   SCAN_PLAYFIELD(x, y)
12852   {
12853     element = Feld[x][y];
12854     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12855
12856     ResetGfxFrame(x, y, TRUE);
12857
12858     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12859         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12860       ResetRandomAnimationValue(x, y);
12861
12862     SetRandomAnimationValue(x, y);
12863
12864     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12865
12866     if (IS_INACTIVE(element))
12867     {
12868       if (IS_ANIMATED(graphic))
12869         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12870
12871       continue;
12872     }
12873
12874     /* this may take place after moving, so 'element' may have changed */
12875     if (IS_CHANGING(x, y) &&
12876         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12877     {
12878       int page = element_info[element].event_page_nr[CE_DELAY];
12879
12880 #if 1
12881       HandleElementChange(x, y, page);
12882 #else
12883       if (CAN_CHANGE(element))
12884         HandleElementChange(x, y, page);
12885
12886       if (HAS_ACTION(element))
12887         ExecuteCustomElementAction(x, y, element, page);
12888 #endif
12889
12890       element = Feld[x][y];
12891       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12892     }
12893
12894 #if 0   // ---------------------------------------------------------------------
12895
12896     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12897     {
12898       StartMoving(x, y);
12899
12900       element = Feld[x][y];
12901       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12902
12903       if (IS_ANIMATED(graphic) &&
12904           !IS_MOVING(x, y) &&
12905           !Stop[x][y])
12906         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12907
12908       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12909         TEST_DrawTwinkleOnField(x, y);
12910     }
12911     else if (IS_MOVING(x, y))
12912       ContinueMoving(x, y);
12913     else
12914     {
12915       switch (element)
12916       {
12917         case EL_ACID:
12918         case EL_EXIT_OPEN:
12919         case EL_EM_EXIT_OPEN:
12920         case EL_SP_EXIT_OPEN:
12921         case EL_STEEL_EXIT_OPEN:
12922         case EL_EM_STEEL_EXIT_OPEN:
12923         case EL_SP_TERMINAL:
12924         case EL_SP_TERMINAL_ACTIVE:
12925         case EL_EXTRA_TIME:
12926         case EL_SHIELD_NORMAL:
12927         case EL_SHIELD_DEADLY:
12928           if (IS_ANIMATED(graphic))
12929             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12930           break;
12931
12932         case EL_DYNAMITE_ACTIVE:
12933         case EL_EM_DYNAMITE_ACTIVE:
12934         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12935         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12936         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12937         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12938         case EL_SP_DISK_RED_ACTIVE:
12939           CheckDynamite(x, y);
12940           break;
12941
12942         case EL_AMOEBA_GROWING:
12943           AmoebeWaechst(x, y);
12944           break;
12945
12946         case EL_AMOEBA_SHRINKING:
12947           AmoebaDisappearing(x, y);
12948           break;
12949
12950 #if !USE_NEW_AMOEBA_CODE
12951         case EL_AMOEBA_WET:
12952         case EL_AMOEBA_DRY:
12953         case EL_AMOEBA_FULL:
12954         case EL_BD_AMOEBA:
12955         case EL_EMC_DRIPPER:
12956           AmoebeAbleger(x, y);
12957           break;
12958 #endif
12959
12960         case EL_GAME_OF_LIFE:
12961         case EL_BIOMAZE:
12962           Life(x, y);
12963           break;
12964
12965         case EL_EXIT_CLOSED:
12966           CheckExit(x, y);
12967           break;
12968
12969         case EL_EM_EXIT_CLOSED:
12970           CheckExitEM(x, y);
12971           break;
12972
12973         case EL_STEEL_EXIT_CLOSED:
12974           CheckExitSteel(x, y);
12975           break;
12976
12977         case EL_EM_STEEL_EXIT_CLOSED:
12978           CheckExitSteelEM(x, y);
12979           break;
12980
12981         case EL_SP_EXIT_CLOSED:
12982           CheckExitSP(x, y);
12983           break;
12984
12985         case EL_EXPANDABLE_WALL_GROWING:
12986         case EL_EXPANDABLE_STEELWALL_GROWING:
12987           MauerWaechst(x, y);
12988           break;
12989
12990         case EL_EXPANDABLE_WALL:
12991         case EL_EXPANDABLE_WALL_HORIZONTAL:
12992         case EL_EXPANDABLE_WALL_VERTICAL:
12993         case EL_EXPANDABLE_WALL_ANY:
12994         case EL_BD_EXPANDABLE_WALL:
12995           MauerAbleger(x, y);
12996           break;
12997
12998         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12999         case EL_EXPANDABLE_STEELWALL_VERTICAL:
13000         case EL_EXPANDABLE_STEELWALL_ANY:
13001           MauerAblegerStahl(x, y);
13002           break;
13003
13004         case EL_FLAMES:
13005           CheckForDragon(x, y);
13006           break;
13007
13008         case EL_EXPLOSION:
13009           break;
13010
13011         case EL_ELEMENT_SNAPPING:
13012         case EL_DIAGONAL_SHRINKING:
13013         case EL_DIAGONAL_GROWING:
13014         {
13015           graphic =
13016             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13017
13018           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13019           break;
13020         }
13021
13022         default:
13023           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13024             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13025           break;
13026       }
13027     }
13028
13029 #else   // ---------------------------------------------------------------------
13030
13031     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
13032     {
13033       StartMoving(x, y);
13034
13035       element = Feld[x][y];
13036       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13037
13038       if (IS_ANIMATED(graphic) &&
13039           !IS_MOVING(x, y) &&
13040           !Stop[x][y])
13041         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13042
13043       if (IS_GEM(element) || element == EL_SP_INFOTRON)
13044         TEST_DrawTwinkleOnField(x, y);
13045     }
13046     else if ((element == EL_ACID ||
13047               element == EL_EXIT_OPEN ||
13048               element == EL_EM_EXIT_OPEN ||
13049               element == EL_SP_EXIT_OPEN ||
13050               element == EL_STEEL_EXIT_OPEN ||
13051               element == EL_EM_STEEL_EXIT_OPEN ||
13052               element == EL_SP_TERMINAL ||
13053               element == EL_SP_TERMINAL_ACTIVE ||
13054               element == EL_EXTRA_TIME ||
13055               element == EL_SHIELD_NORMAL ||
13056               element == EL_SHIELD_DEADLY) &&
13057              IS_ANIMATED(graphic))
13058       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13059     else if (IS_MOVING(x, y))
13060       ContinueMoving(x, y);
13061     else if (IS_ACTIVE_BOMB(element))
13062       CheckDynamite(x, y);
13063     else if (element == EL_AMOEBA_GROWING)
13064       AmoebeWaechst(x, y);
13065     else if (element == EL_AMOEBA_SHRINKING)
13066       AmoebaDisappearing(x, y);
13067
13068 #if !USE_NEW_AMOEBA_CODE
13069     else if (IS_AMOEBALIVE(element))
13070       AmoebeAbleger(x, y);
13071 #endif
13072
13073     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13074       Life(x, y);
13075     else if (element == EL_EXIT_CLOSED)
13076       CheckExit(x, y);
13077     else if (element == EL_EM_EXIT_CLOSED)
13078       CheckExitEM(x, y);
13079     else if (element == EL_STEEL_EXIT_CLOSED)
13080       CheckExitSteel(x, y);
13081     else if (element == EL_EM_STEEL_EXIT_CLOSED)
13082       CheckExitSteelEM(x, y);
13083     else if (element == EL_SP_EXIT_CLOSED)
13084       CheckExitSP(x, y);
13085     else if (element == EL_EXPANDABLE_WALL_GROWING ||
13086              element == EL_EXPANDABLE_STEELWALL_GROWING)
13087       MauerWaechst(x, y);
13088     else if (element == EL_EXPANDABLE_WALL ||
13089              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13090              element == EL_EXPANDABLE_WALL_VERTICAL ||
13091              element == EL_EXPANDABLE_WALL_ANY ||
13092              element == EL_BD_EXPANDABLE_WALL)
13093       MauerAbleger(x, y);
13094     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13095              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13096              element == EL_EXPANDABLE_STEELWALL_ANY)
13097       MauerAblegerStahl(x, y);
13098     else if (element == EL_FLAMES)
13099       CheckForDragon(x, y);
13100     else if (element == EL_EXPLOSION)
13101       ; /* drawing of correct explosion animation is handled separately */
13102     else if (element == EL_ELEMENT_SNAPPING ||
13103              element == EL_DIAGONAL_SHRINKING ||
13104              element == EL_DIAGONAL_GROWING)
13105     {
13106       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13107
13108       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13109     }
13110     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13111       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13112
13113 #endif  // ---------------------------------------------------------------------
13114
13115     if (IS_BELT_ACTIVE(element))
13116       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13117
13118     if (game.magic_wall_active)
13119     {
13120       int jx = local_player->jx, jy = local_player->jy;
13121
13122       /* play the element sound at the position nearest to the player */
13123       if ((element == EL_MAGIC_WALL_FULL ||
13124            element == EL_MAGIC_WALL_ACTIVE ||
13125            element == EL_MAGIC_WALL_EMPTYING ||
13126            element == EL_BD_MAGIC_WALL_FULL ||
13127            element == EL_BD_MAGIC_WALL_ACTIVE ||
13128            element == EL_BD_MAGIC_WALL_EMPTYING ||
13129            element == EL_DC_MAGIC_WALL_FULL ||
13130            element == EL_DC_MAGIC_WALL_ACTIVE ||
13131            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13132           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13133       {
13134         magic_wall_x = x;
13135         magic_wall_y = y;
13136       }
13137     }
13138   }
13139
13140 #if 0
13141   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13142 #endif
13143
13144 #if USE_NEW_AMOEBA_CODE
13145   /* new experimental amoeba growth stuff */
13146   if (!(FrameCounter % 8))
13147   {
13148     static unsigned int random = 1684108901;
13149
13150     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13151     {
13152       x = RND(lev_fieldx);
13153       y = RND(lev_fieldy);
13154       element = Feld[x][y];
13155
13156       if (!IS_PLAYER(x,y) &&
13157           (element == EL_EMPTY ||
13158            CAN_GROW_INTO(element) ||
13159            element == EL_QUICKSAND_EMPTY ||
13160            element == EL_QUICKSAND_FAST_EMPTY ||
13161            element == EL_ACID_SPLASH_LEFT ||
13162            element == EL_ACID_SPLASH_RIGHT))
13163       {
13164         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13165             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13166             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13167             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13168           Feld[x][y] = EL_AMOEBA_DROP;
13169       }
13170
13171       random = random * 129 + 1;
13172     }
13173   }
13174 #endif
13175
13176 #if 0
13177   if (game.explosions_delayed)
13178 #endif
13179   {
13180     game.explosions_delayed = FALSE;
13181
13182     SCAN_PLAYFIELD(x, y)
13183     {
13184       element = Feld[x][y];
13185
13186       if (ExplodeField[x][y])
13187         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13188       else if (element == EL_EXPLOSION)
13189         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13190
13191       ExplodeField[x][y] = EX_TYPE_NONE;
13192     }
13193
13194     game.explosions_delayed = TRUE;
13195   }
13196
13197   if (game.magic_wall_active)
13198   {
13199     if (!(game.magic_wall_time_left % 4))
13200     {
13201       int element = Feld[magic_wall_x][magic_wall_y];
13202
13203       if (element == EL_BD_MAGIC_WALL_FULL ||
13204           element == EL_BD_MAGIC_WALL_ACTIVE ||
13205           element == EL_BD_MAGIC_WALL_EMPTYING)
13206         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13207       else if (element == EL_DC_MAGIC_WALL_FULL ||
13208                element == EL_DC_MAGIC_WALL_ACTIVE ||
13209                element == EL_DC_MAGIC_WALL_EMPTYING)
13210         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13211       else
13212         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13213     }
13214
13215     if (game.magic_wall_time_left > 0)
13216     {
13217       game.magic_wall_time_left--;
13218
13219       if (!game.magic_wall_time_left)
13220       {
13221         SCAN_PLAYFIELD(x, y)
13222         {
13223           element = Feld[x][y];
13224
13225           if (element == EL_MAGIC_WALL_ACTIVE ||
13226               element == EL_MAGIC_WALL_FULL)
13227           {
13228             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13229             TEST_DrawLevelField(x, y);
13230           }
13231           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13232                    element == EL_BD_MAGIC_WALL_FULL)
13233           {
13234             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13235             TEST_DrawLevelField(x, y);
13236           }
13237           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13238                    element == EL_DC_MAGIC_WALL_FULL)
13239           {
13240             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13241             TEST_DrawLevelField(x, y);
13242           }
13243         }
13244
13245         game.magic_wall_active = FALSE;
13246       }
13247     }
13248   }
13249
13250   if (game.light_time_left > 0)
13251   {
13252     game.light_time_left--;
13253
13254     if (game.light_time_left == 0)
13255       RedrawAllLightSwitchesAndInvisibleElements();
13256   }
13257
13258   if (game.timegate_time_left > 0)
13259   {
13260     game.timegate_time_left--;
13261
13262     if (game.timegate_time_left == 0)
13263       CloseAllOpenTimegates();
13264   }
13265
13266   if (game.lenses_time_left > 0)
13267   {
13268     game.lenses_time_left--;
13269
13270     if (game.lenses_time_left == 0)
13271       RedrawAllInvisibleElementsForLenses();
13272   }
13273
13274   if (game.magnify_time_left > 0)
13275   {
13276     game.magnify_time_left--;
13277
13278     if (game.magnify_time_left == 0)
13279       RedrawAllInvisibleElementsForMagnifier();
13280   }
13281
13282   for (i = 0; i < MAX_PLAYERS; i++)
13283   {
13284     struct PlayerInfo *player = &stored_player[i];
13285
13286     if (SHIELD_ON(player))
13287     {
13288       if (player->shield_deadly_time_left)
13289         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13290       else if (player->shield_normal_time_left)
13291         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13292     }
13293   }
13294
13295 #if USE_DELAYED_GFX_REDRAW
13296   SCAN_PLAYFIELD(x, y)
13297   {
13298 #if 1
13299     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13300 #else
13301     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13302         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13303 #endif
13304     {
13305       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13306          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13307
13308       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13309         DrawLevelField(x, y);
13310
13311       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13312         DrawLevelFieldCrumbled(x, y);
13313
13314       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13315         DrawLevelFieldCrumbledNeighbours(x, y);
13316
13317       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13318         DrawTwinkleOnField(x, y);
13319     }
13320
13321     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13322   }
13323 #endif
13324
13325   CheckLevelTime();
13326
13327   DrawAllPlayers();
13328   PlayAllPlayersSound();
13329
13330   if (options.debug)                    /* calculate frames per second */
13331   {
13332     static unsigned int fps_counter = 0;
13333     static int fps_frames = 0;
13334     unsigned int fps_delay_ms = Counter() - fps_counter;
13335
13336     fps_frames++;
13337
13338     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13339     {
13340       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13341
13342       fps_frames = 0;
13343       fps_counter = Counter();
13344     }
13345
13346     redraw_mask |= REDRAW_FPS;
13347   }
13348
13349   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13350
13351   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13352   {
13353     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13354
13355     local_player->show_envelope = 0;
13356   }
13357
13358 #if 0
13359   debug_print_timestamp(0, "stop main loop profiling ");
13360   printf("----------------------------------------------------------\n");
13361 #endif
13362
13363   /* use random number generator in every frame to make it less predictable */
13364   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13365     RND(1);
13366 }
13367
13368 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13369 {
13370   int min_x = x, min_y = y, max_x = x, max_y = y;
13371   int i;
13372
13373   for (i = 0; i < MAX_PLAYERS; i++)
13374   {
13375     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13376
13377     if (!stored_player[i].active || &stored_player[i] == player)
13378       continue;
13379
13380     min_x = MIN(min_x, jx);
13381     min_y = MIN(min_y, jy);
13382     max_x = MAX(max_x, jx);
13383     max_y = MAX(max_y, jy);
13384   }
13385
13386   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13387 }
13388
13389 static boolean AllPlayersInVisibleScreen()
13390 {
13391   int i;
13392
13393   for (i = 0; i < MAX_PLAYERS; i++)
13394   {
13395     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13396
13397     if (!stored_player[i].active)
13398       continue;
13399
13400     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13401       return FALSE;
13402   }
13403
13404   return TRUE;
13405 }
13406
13407 void ScrollLevel(int dx, int dy)
13408 {
13409 #if 0
13410   /* (directly solved in BlitBitmap() now) */
13411   static Bitmap *bitmap_db_field2 = NULL;
13412   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13413   int x, y;
13414 #else
13415   int x, y;
13416 #endif
13417
13418 #if 0
13419   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13420   /* only horizontal XOR vertical scroll direction allowed */
13421   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13422     return;
13423 #endif
13424
13425 #if 0
13426   /* (directly solved in BlitBitmap() now) */
13427   if (bitmap_db_field2 == NULL)
13428     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13429
13430   /* needed when blitting directly to same bitmap -- should not be needed with
13431      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13432   BlitBitmap(drawto_field, bitmap_db_field2,
13433              FX + TILEX * (dx == -1) - softscroll_offset,
13434              FY + TILEY * (dy == -1) - softscroll_offset,
13435              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13436              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13437              FX + TILEX * (dx == 1) - softscroll_offset,
13438              FY + TILEY * (dy == 1) - softscroll_offset);
13439   BlitBitmap(bitmap_db_field2, drawto_field,
13440              FX + TILEX * (dx == 1) - softscroll_offset,
13441              FY + TILEY * (dy == 1) - softscroll_offset,
13442              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13443              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13444              FX + TILEX * (dx == 1) - softscroll_offset,
13445              FY + TILEY * (dy == 1) - softscroll_offset);
13446
13447 #else
13448
13449 #if 0
13450   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13451   int xsize = (BX2 - BX1 + 1);
13452   int ysize = (BY2 - BY1 + 1);
13453   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13454   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13455   int step  = (start < end ? +1 : -1);
13456
13457   for (i = start; i != end; i += step)
13458   {
13459     BlitBitmap(drawto_field, drawto_field,
13460                FX + TILEX * (dx != 0 ? i + step : 0),
13461                FY + TILEY * (dy != 0 ? i + step : 0),
13462                TILEX * (dx != 0 ? 1 : xsize),
13463                TILEY * (dy != 0 ? 1 : ysize),
13464                FX + TILEX * (dx != 0 ? i : 0),
13465                FY + TILEY * (dy != 0 ? i : 0));
13466   }
13467
13468 #else
13469
13470 #if NEW_TILESIZE
13471 #if NEW_SCROLL
13472   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13473 #else
13474   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13475 #endif
13476 #else
13477 #if NEW_SCROLL
13478   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13479 #else
13480   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13481 #endif
13482 #endif
13483
13484 #if NEW_TILESIZE
13485   BlitBitmap(drawto_field, drawto_field,
13486              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13487              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13488              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13489              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13490              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13491              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13492 #else
13493   BlitBitmap(drawto_field, drawto_field,
13494              FX + TILEX * (dx == -1) - softscroll_offset,
13495              FY + TILEY * (dy == -1) - softscroll_offset,
13496              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13497              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13498              FX + TILEX * (dx == 1) - softscroll_offset,
13499              FY + TILEY * (dy == 1) - softscroll_offset);
13500 #endif
13501
13502 #endif
13503 #endif
13504
13505   if (dx != 0)
13506   {
13507     x = (dx == 1 ? BX1 : BX2);
13508     for (y = BY1; y <= BY2; y++)
13509       DrawScreenField(x, y);
13510   }
13511
13512   if (dy != 0)
13513   {
13514     y = (dy == 1 ? BY1 : BY2);
13515     for (x = BX1; x <= BX2; x++)
13516       DrawScreenField(x, y);
13517   }
13518
13519   redraw_mask |= REDRAW_FIELD;
13520 }
13521
13522 static boolean canFallDown(struct PlayerInfo *player)
13523 {
13524   int jx = player->jx, jy = player->jy;
13525
13526   return (IN_LEV_FIELD(jx, jy + 1) &&
13527           (IS_FREE(jx, jy + 1) ||
13528            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13529           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13530           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13531 }
13532
13533 static boolean canPassField(int x, int y, int move_dir)
13534 {
13535   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13536   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13537   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13538   int nextx = x + dx;
13539   int nexty = y + dy;
13540   int element = Feld[x][y];
13541
13542   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13543           !CAN_MOVE(element) &&
13544           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13545           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13546           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13547 }
13548
13549 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13550 {
13551   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13552   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13553   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13554   int newx = x + dx;
13555   int newy = y + dy;
13556
13557   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13558           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13559           (IS_DIGGABLE(Feld[newx][newy]) ||
13560            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13561            canPassField(newx, newy, move_dir)));
13562 }
13563
13564 static void CheckGravityMovement(struct PlayerInfo *player)
13565 {
13566 #if USE_PLAYER_GRAVITY
13567   if (player->gravity && !player->programmed_action)
13568 #else
13569   if (game.gravity && !player->programmed_action)
13570 #endif
13571   {
13572     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13573     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13574     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13575     int jx = player->jx, jy = player->jy;
13576     boolean player_is_moving_to_valid_field =
13577       (!player_is_snapping &&
13578        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13579         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13580     boolean player_can_fall_down = canFallDown(player);
13581
13582     if (player_can_fall_down &&
13583         !player_is_moving_to_valid_field)
13584       player->programmed_action = MV_DOWN;
13585   }
13586 }
13587
13588 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13589 {
13590   return CheckGravityMovement(player);
13591
13592 #if USE_PLAYER_GRAVITY
13593   if (player->gravity && !player->programmed_action)
13594 #else
13595   if (game.gravity && !player->programmed_action)
13596 #endif
13597   {
13598     int jx = player->jx, jy = player->jy;
13599     boolean field_under_player_is_free =
13600       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13601     boolean player_is_standing_on_valid_field =
13602       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13603        (IS_WALKABLE(Feld[jx][jy]) &&
13604         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13605
13606     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13607       player->programmed_action = MV_DOWN;
13608   }
13609 }
13610
13611 /*
13612   MovePlayerOneStep()
13613   -----------------------------------------------------------------------------
13614   dx, dy:               direction (non-diagonal) to try to move the player to
13615   real_dx, real_dy:     direction as read from input device (can be diagonal)
13616 */
13617
13618 boolean MovePlayerOneStep(struct PlayerInfo *player,
13619                           int dx, int dy, int real_dx, int real_dy)
13620 {
13621   int jx = player->jx, jy = player->jy;
13622   int new_jx = jx + dx, new_jy = jy + dy;
13623 #if !USE_FIXED_DONT_RUN_INTO
13624   int element;
13625 #endif
13626   int can_move;
13627   boolean player_can_move = !player->cannot_move;
13628
13629   if (!player->active || (!dx && !dy))
13630     return MP_NO_ACTION;
13631
13632   player->MovDir = (dx < 0 ? MV_LEFT :
13633                     dx > 0 ? MV_RIGHT :
13634                     dy < 0 ? MV_UP :
13635                     dy > 0 ? MV_DOWN :  MV_NONE);
13636
13637   if (!IN_LEV_FIELD(new_jx, new_jy))
13638     return MP_NO_ACTION;
13639
13640   if (!player_can_move)
13641   {
13642     if (player->MovPos == 0)
13643     {
13644       player->is_moving = FALSE;
13645       player->is_digging = FALSE;
13646       player->is_collecting = FALSE;
13647       player->is_snapping = FALSE;
13648       player->is_pushing = FALSE;
13649     }
13650   }
13651
13652 #if 1
13653   if (!options.network && game.centered_player_nr == -1 &&
13654       !AllPlayersInSight(player, new_jx, new_jy))
13655     return MP_NO_ACTION;
13656 #else
13657   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13658     return MP_NO_ACTION;
13659 #endif
13660
13661 #if !USE_FIXED_DONT_RUN_INTO
13662   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13663
13664   /* (moved to DigField()) */
13665   if (player_can_move && DONT_RUN_INTO(element))
13666   {
13667     if (element == EL_ACID && dx == 0 && dy == 1)
13668     {
13669       SplashAcid(new_jx, new_jy);
13670       Feld[jx][jy] = EL_PLAYER_1;
13671       InitMovingField(jx, jy, MV_DOWN);
13672       Store[jx][jy] = EL_ACID;
13673       ContinueMoving(jx, jy);
13674       BuryPlayer(player);
13675     }
13676     else
13677       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13678
13679     return MP_MOVING;
13680   }
13681 #endif
13682
13683   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13684   if (can_move != MP_MOVING)
13685     return can_move;
13686
13687   /* check if DigField() has caused relocation of the player */
13688   if (player->jx != jx || player->jy != jy)
13689     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13690
13691   StorePlayer[jx][jy] = 0;
13692   player->last_jx = jx;
13693   player->last_jy = jy;
13694   player->jx = new_jx;
13695   player->jy = new_jy;
13696   StorePlayer[new_jx][new_jy] = player->element_nr;
13697
13698   if (player->move_delay_value_next != -1)
13699   {
13700     player->move_delay_value = player->move_delay_value_next;
13701     player->move_delay_value_next = -1;
13702   }
13703
13704   player->MovPos =
13705     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13706
13707   player->step_counter++;
13708
13709   PlayerVisit[jx][jy] = FrameCounter;
13710
13711 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13712   player->is_moving = TRUE;
13713 #endif
13714
13715 #if 1
13716   /* should better be called in MovePlayer(), but this breaks some tapes */
13717   ScrollPlayer(player, SCROLL_INIT);
13718 #endif
13719
13720   return MP_MOVING;
13721 }
13722
13723 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13724 {
13725   int jx = player->jx, jy = player->jy;
13726   int old_jx = jx, old_jy = jy;
13727   int moved = MP_NO_ACTION;
13728
13729   if (!player->active)
13730     return FALSE;
13731
13732   if (!dx && !dy)
13733   {
13734     if (player->MovPos == 0)
13735     {
13736       player->is_moving = FALSE;
13737       player->is_digging = FALSE;
13738       player->is_collecting = FALSE;
13739       player->is_snapping = FALSE;
13740       player->is_pushing = FALSE;
13741     }
13742
13743     return FALSE;
13744   }
13745
13746   if (player->move_delay > 0)
13747     return FALSE;
13748
13749   player->move_delay = -1;              /* set to "uninitialized" value */
13750
13751   /* store if player is automatically moved to next field */
13752   player->is_auto_moving = (player->programmed_action != MV_NONE);
13753
13754   /* remove the last programmed player action */
13755   player->programmed_action = 0;
13756
13757   if (player->MovPos)
13758   {
13759     /* should only happen if pre-1.2 tape recordings are played */
13760     /* this is only for backward compatibility */
13761
13762     int original_move_delay_value = player->move_delay_value;
13763
13764 #if DEBUG
13765     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13766            tape.counter);
13767 #endif
13768
13769     /* scroll remaining steps with finest movement resolution */
13770     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13771
13772     while (player->MovPos)
13773     {
13774       ScrollPlayer(player, SCROLL_GO_ON);
13775       ScrollScreen(NULL, SCROLL_GO_ON);
13776
13777       AdvanceFrameAndPlayerCounters(player->index_nr);
13778
13779       DrawAllPlayers();
13780       BackToFront();
13781     }
13782
13783     player->move_delay_value = original_move_delay_value;
13784   }
13785
13786   player->is_active = FALSE;
13787
13788   if (player->last_move_dir & MV_HORIZONTAL)
13789   {
13790     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13791       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13792   }
13793   else
13794   {
13795     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13796       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13797   }
13798
13799 #if USE_FIXED_BORDER_RUNNING_GFX
13800   if (!moved && !player->is_active)
13801   {
13802     player->is_moving = FALSE;
13803     player->is_digging = FALSE;
13804     player->is_collecting = FALSE;
13805     player->is_snapping = FALSE;
13806     player->is_pushing = FALSE;
13807   }
13808 #endif
13809
13810   jx = player->jx;
13811   jy = player->jy;
13812
13813 #if 1
13814   if (moved & MP_MOVING && !ScreenMovPos &&
13815       (player->index_nr == game.centered_player_nr ||
13816        game.centered_player_nr == -1))
13817 #else
13818   if (moved & MP_MOVING && !ScreenMovPos &&
13819       (player == local_player || !options.network))
13820 #endif
13821   {
13822     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13823     int offset = game.scroll_delay_value;
13824
13825     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13826     {
13827       /* actual player has left the screen -- scroll in that direction */
13828       if (jx != old_jx)         /* player has moved horizontally */
13829         scroll_x += (jx - old_jx);
13830       else                      /* player has moved vertically */
13831         scroll_y += (jy - old_jy);
13832     }
13833     else
13834     {
13835       if (jx != old_jx)         /* player has moved horizontally */
13836       {
13837         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13838             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13839           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13840
13841         /* don't scroll over playfield boundaries */
13842         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13843           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13844
13845         /* don't scroll more than one field at a time */
13846         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13847
13848         /* don't scroll against the player's moving direction */
13849         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13850             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13851           scroll_x = old_scroll_x;
13852       }
13853       else                      /* player has moved vertically */
13854       {
13855         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13856             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13857           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13858
13859         /* don't scroll over playfield boundaries */
13860         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13861           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13862
13863         /* don't scroll more than one field at a time */
13864         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13865
13866         /* don't scroll against the player's moving direction */
13867         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13868             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13869           scroll_y = old_scroll_y;
13870       }
13871     }
13872
13873     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13874     {
13875 #if 1
13876       if (!options.network && game.centered_player_nr == -1 &&
13877           !AllPlayersInVisibleScreen())
13878       {
13879         scroll_x = old_scroll_x;
13880         scroll_y = old_scroll_y;
13881       }
13882       else
13883 #else
13884       if (!options.network && !AllPlayersInVisibleScreen())
13885       {
13886         scroll_x = old_scroll_x;
13887         scroll_y = old_scroll_y;
13888       }
13889       else
13890 #endif
13891       {
13892         ScrollScreen(player, SCROLL_INIT);
13893         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13894       }
13895     }
13896   }
13897
13898   player->StepFrame = 0;
13899
13900   if (moved & MP_MOVING)
13901   {
13902     if (old_jx != jx && old_jy == jy)
13903       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13904     else if (old_jx == jx && old_jy != jy)
13905       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13906
13907     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13908
13909     player->last_move_dir = player->MovDir;
13910     player->is_moving = TRUE;
13911     player->is_snapping = FALSE;
13912     player->is_switching = FALSE;
13913     player->is_dropping = FALSE;
13914     player->is_dropping_pressed = FALSE;
13915     player->drop_pressed_delay = 0;
13916
13917 #if 0
13918     /* should better be called here than above, but this breaks some tapes */
13919     ScrollPlayer(player, SCROLL_INIT);
13920 #endif
13921   }
13922   else
13923   {
13924     CheckGravityMovementWhenNotMoving(player);
13925
13926     player->is_moving = FALSE;
13927
13928     /* at this point, the player is allowed to move, but cannot move right now
13929        (e.g. because of something blocking the way) -- ensure that the player
13930        is also allowed to move in the next frame (in old versions before 3.1.1,
13931        the player was forced to wait again for eight frames before next try) */
13932
13933     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13934       player->move_delay = 0;   /* allow direct movement in the next frame */
13935   }
13936
13937   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13938     player->move_delay = player->move_delay_value;
13939
13940   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13941   {
13942     TestIfPlayerTouchesBadThing(jx, jy);
13943     TestIfPlayerTouchesCustomElement(jx, jy);
13944   }
13945
13946   if (!player->active)
13947     RemovePlayer(player);
13948
13949   return moved;
13950 }
13951
13952 void ScrollPlayer(struct PlayerInfo *player, int mode)
13953 {
13954   int jx = player->jx, jy = player->jy;
13955   int last_jx = player->last_jx, last_jy = player->last_jy;
13956   int move_stepsize = TILEX / player->move_delay_value;
13957
13958 #if USE_NEW_PLAYER_SPEED
13959   if (!player->active)
13960     return;
13961
13962   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13963     return;
13964 #else
13965   if (!player->active || player->MovPos == 0)
13966     return;
13967 #endif
13968
13969   if (mode == SCROLL_INIT)
13970   {
13971     player->actual_frame_counter = FrameCounter;
13972     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13973
13974     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13975         Feld[last_jx][last_jy] == EL_EMPTY)
13976     {
13977       int last_field_block_delay = 0;   /* start with no blocking at all */
13978       int block_delay_adjustment = player->block_delay_adjustment;
13979
13980       /* if player blocks last field, add delay for exactly one move */
13981       if (player->block_last_field)
13982       {
13983         last_field_block_delay += player->move_delay_value;
13984
13985         /* when blocking enabled, prevent moving up despite gravity */
13986 #if USE_PLAYER_GRAVITY
13987         if (player->gravity && player->MovDir == MV_UP)
13988           block_delay_adjustment = -1;
13989 #else
13990         if (game.gravity && player->MovDir == MV_UP)
13991           block_delay_adjustment = -1;
13992 #endif
13993       }
13994
13995       /* add block delay adjustment (also possible when not blocking) */
13996       last_field_block_delay += block_delay_adjustment;
13997
13998       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13999       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
14000     }
14001
14002 #if USE_NEW_PLAYER_SPEED
14003     if (player->MovPos != 0)    /* player has not yet reached destination */
14004       return;
14005 #else
14006     return;
14007 #endif
14008   }
14009   else if (!FrameReached(&player->actual_frame_counter, 1))
14010     return;
14011
14012 #if USE_NEW_PLAYER_SPEED
14013   if (player->MovPos != 0)
14014   {
14015     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14016     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14017
14018     /* before DrawPlayer() to draw correct player graphic for this case */
14019     if (player->MovPos == 0)
14020       CheckGravityMovement(player);
14021   }
14022 #else
14023   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
14024   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
14025
14026   /* before DrawPlayer() to draw correct player graphic for this case */
14027   if (player->MovPos == 0)
14028     CheckGravityMovement(player);
14029 #endif
14030
14031   if (player->MovPos == 0)      /* player reached destination field */
14032   {
14033     if (player->move_delay_reset_counter > 0)
14034     {
14035       player->move_delay_reset_counter--;
14036
14037       if (player->move_delay_reset_counter == 0)
14038       {
14039         /* continue with normal speed after quickly moving through gate */
14040         HALVE_PLAYER_SPEED(player);
14041
14042         /* be able to make the next move without delay */
14043         player->move_delay = 0;
14044       }
14045     }
14046
14047     player->last_jx = jx;
14048     player->last_jy = jy;
14049
14050     if (Feld[jx][jy] == EL_EXIT_OPEN ||
14051         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14052 #if 1
14053         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14054 #endif
14055         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14056         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14057 #if 1
14058         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14059 #endif
14060         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14061         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
14062     {
14063       DrawPlayer(player);       /* needed here only to cleanup last field */
14064       RemovePlayer(player);
14065
14066       if (local_player->friends_still_needed == 0 ||
14067           IS_SP_ELEMENT(Feld[jx][jy]))
14068         PlayerWins(player);
14069     }
14070
14071     /* this breaks one level: "machine", level 000 */
14072     {
14073       int move_direction = player->MovDir;
14074       int enter_side = MV_DIR_OPPOSITE(move_direction);
14075       int leave_side = move_direction;
14076       int old_jx = last_jx;
14077       int old_jy = last_jy;
14078       int old_element = Feld[old_jx][old_jy];
14079       int new_element = Feld[jx][jy];
14080
14081       if (IS_CUSTOM_ELEMENT(old_element))
14082         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14083                                    CE_LEFT_BY_PLAYER,
14084                                    player->index_bit, leave_side);
14085
14086       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14087                                           CE_PLAYER_LEAVES_X,
14088                                           player->index_bit, leave_side);
14089
14090       if (IS_CUSTOM_ELEMENT(new_element))
14091         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14092                                    player->index_bit, enter_side);
14093
14094       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14095                                           CE_PLAYER_ENTERS_X,
14096                                           player->index_bit, enter_side);
14097
14098 #if USE_FIX_CE_ACTION_WITH_PLAYER
14099       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14100                                         CE_MOVE_OF_X, move_direction);
14101 #else
14102       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14103                                         CE_MOVE_OF_X, move_direction);
14104 #endif
14105     }
14106
14107     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14108     {
14109       TestIfPlayerTouchesBadThing(jx, jy);
14110       TestIfPlayerTouchesCustomElement(jx, jy);
14111
14112       /* needed because pushed element has not yet reached its destination,
14113          so it would trigger a change event at its previous field location */
14114       if (!player->is_pushing)
14115         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14116
14117       if (!player->active)
14118         RemovePlayer(player);
14119     }
14120
14121     if (!local_player->LevelSolved && level.use_step_counter)
14122     {
14123       int i;
14124
14125       TimePlayed++;
14126
14127       if (TimeLeft > 0)
14128       {
14129         TimeLeft--;
14130
14131         if (TimeLeft <= 10 && setup.time_limit)
14132           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14133
14134 #if 1
14135         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14136
14137         DisplayGameControlValues();
14138 #else
14139         DrawGameValue_Time(TimeLeft);
14140 #endif
14141
14142         if (!TimeLeft && setup.time_limit)
14143           for (i = 0; i < MAX_PLAYERS; i++)
14144             KillPlayer(&stored_player[i]);
14145       }
14146 #if 1
14147       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14148       {
14149         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14150
14151         DisplayGameControlValues();
14152       }
14153 #else
14154       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14155         DrawGameValue_Time(TimePlayed);
14156 #endif
14157     }
14158
14159     if (tape.single_step && tape.recording && !tape.pausing &&
14160         !player->programmed_action)
14161       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14162   }
14163 }
14164
14165 void ScrollScreen(struct PlayerInfo *player, int mode)
14166 {
14167   static unsigned int screen_frame_counter = 0;
14168
14169   if (mode == SCROLL_INIT)
14170   {
14171     /* set scrolling step size according to actual player's moving speed */
14172     ScrollStepSize = TILEX / player->move_delay_value;
14173
14174     screen_frame_counter = FrameCounter;
14175     ScreenMovDir = player->MovDir;
14176     ScreenMovPos = player->MovPos;
14177     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14178     return;
14179   }
14180   else if (!FrameReached(&screen_frame_counter, 1))
14181     return;
14182
14183   if (ScreenMovPos)
14184   {
14185     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14186     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14187     redraw_mask |= REDRAW_FIELD;
14188   }
14189   else
14190     ScreenMovDir = MV_NONE;
14191 }
14192
14193 void TestIfPlayerTouchesCustomElement(int x, int y)
14194 {
14195   static int xy[4][2] =
14196   {
14197     { 0, -1 },
14198     { -1, 0 },
14199     { +1, 0 },
14200     { 0, +1 }
14201   };
14202   static int trigger_sides[4][2] =
14203   {
14204     /* center side       border side */
14205     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14206     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14207     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14208     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14209   };
14210   static int touch_dir[4] =
14211   {
14212     MV_LEFT | MV_RIGHT,
14213     MV_UP   | MV_DOWN,
14214     MV_UP   | MV_DOWN,
14215     MV_LEFT | MV_RIGHT
14216   };
14217   int center_element = Feld[x][y];      /* should always be non-moving! */
14218   int i;
14219
14220   for (i = 0; i < NUM_DIRECTIONS; i++)
14221   {
14222     int xx = x + xy[i][0];
14223     int yy = y + xy[i][1];
14224     int center_side = trigger_sides[i][0];
14225     int border_side = trigger_sides[i][1];
14226     int border_element;
14227
14228     if (!IN_LEV_FIELD(xx, yy))
14229       continue;
14230
14231     if (IS_PLAYER(x, y))                /* player found at center element */
14232     {
14233       struct PlayerInfo *player = PLAYERINFO(x, y);
14234
14235       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14236         border_element = Feld[xx][yy];          /* may be moving! */
14237       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14238         border_element = Feld[xx][yy];
14239       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14240         border_element = MovingOrBlocked2Element(xx, yy);
14241       else
14242         continue;               /* center and border element do not touch */
14243
14244       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14245                                  player->index_bit, border_side);
14246       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14247                                           CE_PLAYER_TOUCHES_X,
14248                                           player->index_bit, border_side);
14249
14250 #if USE_FIX_CE_ACTION_WITH_PLAYER
14251       {
14252         /* use player element that is initially defined in the level playfield,
14253            not the player element that corresponds to the runtime player number
14254            (example: a level that contains EL_PLAYER_3 as the only player would
14255            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14256         int player_element = PLAYERINFO(x, y)->initial_element;
14257
14258         CheckElementChangeBySide(xx, yy, border_element, player_element,
14259                                  CE_TOUCHING_X, border_side);
14260       }
14261 #endif
14262     }
14263     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14264     {
14265       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14266
14267       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14268       {
14269         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14270           continue;             /* center and border element do not touch */
14271       }
14272
14273       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14274                                  player->index_bit, center_side);
14275       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14276                                           CE_PLAYER_TOUCHES_X,
14277                                           player->index_bit, center_side);
14278
14279 #if USE_FIX_CE_ACTION_WITH_PLAYER
14280       {
14281         /* use player element that is initially defined in the level playfield,
14282            not the player element that corresponds to the runtime player number
14283            (example: a level that contains EL_PLAYER_3 as the only player would
14284            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14285         int player_element = PLAYERINFO(xx, yy)->initial_element;
14286
14287         CheckElementChangeBySide(x, y, center_element, player_element,
14288                                  CE_TOUCHING_X, center_side);
14289       }
14290 #endif
14291
14292       break;
14293     }
14294   }
14295 }
14296
14297 #if USE_ELEMENT_TOUCHING_BUGFIX
14298
14299 void TestIfElementTouchesCustomElement(int x, int y)
14300 {
14301   static int xy[4][2] =
14302   {
14303     { 0, -1 },
14304     { -1, 0 },
14305     { +1, 0 },
14306     { 0, +1 }
14307   };
14308   static int trigger_sides[4][2] =
14309   {
14310     /* center side      border side */
14311     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14312     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14313     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14314     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14315   };
14316   static int touch_dir[4] =
14317   {
14318     MV_LEFT | MV_RIGHT,
14319     MV_UP   | MV_DOWN,
14320     MV_UP   | MV_DOWN,
14321     MV_LEFT | MV_RIGHT
14322   };
14323   boolean change_center_element = FALSE;
14324   int center_element = Feld[x][y];      /* should always be non-moving! */
14325   int border_element_old[NUM_DIRECTIONS];
14326   int i;
14327
14328   for (i = 0; i < NUM_DIRECTIONS; i++)
14329   {
14330     int xx = x + xy[i][0];
14331     int yy = y + xy[i][1];
14332     int border_element;
14333
14334     border_element_old[i] = -1;
14335
14336     if (!IN_LEV_FIELD(xx, yy))
14337       continue;
14338
14339     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14340       border_element = Feld[xx][yy];    /* may be moving! */
14341     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14342       border_element = Feld[xx][yy];
14343     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14344       border_element = MovingOrBlocked2Element(xx, yy);
14345     else
14346       continue;                 /* center and border element do not touch */
14347
14348     border_element_old[i] = border_element;
14349   }
14350
14351   for (i = 0; i < NUM_DIRECTIONS; i++)
14352   {
14353     int xx = x + xy[i][0];
14354     int yy = y + xy[i][1];
14355     int center_side = trigger_sides[i][0];
14356     int border_element = border_element_old[i];
14357
14358     if (border_element == -1)
14359       continue;
14360
14361     /* check for change of border element */
14362     CheckElementChangeBySide(xx, yy, border_element, center_element,
14363                              CE_TOUCHING_X, center_side);
14364
14365     /* (center element cannot be player, so we dont have to check this here) */
14366   }
14367
14368   for (i = 0; i < NUM_DIRECTIONS; i++)
14369   {
14370     int xx = x + xy[i][0];
14371     int yy = y + xy[i][1];
14372     int border_side = trigger_sides[i][1];
14373     int border_element = border_element_old[i];
14374
14375     if (border_element == -1)
14376       continue;
14377
14378     /* check for change of center element (but change it only once) */
14379     if (!change_center_element)
14380       change_center_element =
14381         CheckElementChangeBySide(x, y, center_element, border_element,
14382                                  CE_TOUCHING_X, border_side);
14383
14384 #if USE_FIX_CE_ACTION_WITH_PLAYER
14385     if (IS_PLAYER(xx, yy))
14386     {
14387       /* use player element that is initially defined in the level playfield,
14388          not the player element that corresponds to the runtime player number
14389          (example: a level that contains EL_PLAYER_3 as the only player would
14390          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14391       int player_element = PLAYERINFO(xx, yy)->initial_element;
14392
14393       CheckElementChangeBySide(x, y, center_element, player_element,
14394                                CE_TOUCHING_X, border_side);
14395     }
14396 #endif
14397   }
14398 }
14399
14400 #else
14401
14402 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14403 {
14404   static int xy[4][2] =
14405   {
14406     { 0, -1 },
14407     { -1, 0 },
14408     { +1, 0 },
14409     { 0, +1 }
14410   };
14411   static int trigger_sides[4][2] =
14412   {
14413     /* center side      border side */
14414     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14415     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14416     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14417     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14418   };
14419   static int touch_dir[4] =
14420   {
14421     MV_LEFT | MV_RIGHT,
14422     MV_UP   | MV_DOWN,
14423     MV_UP   | MV_DOWN,
14424     MV_LEFT | MV_RIGHT
14425   };
14426   boolean change_center_element = FALSE;
14427   int center_element = Feld[x][y];      /* should always be non-moving! */
14428   int i;
14429
14430   for (i = 0; i < NUM_DIRECTIONS; i++)
14431   {
14432     int xx = x + xy[i][0];
14433     int yy = y + xy[i][1];
14434     int center_side = trigger_sides[i][0];
14435     int border_side = trigger_sides[i][1];
14436     int border_element;
14437
14438     if (!IN_LEV_FIELD(xx, yy))
14439       continue;
14440
14441     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14442       border_element = Feld[xx][yy];    /* may be moving! */
14443     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14444       border_element = Feld[xx][yy];
14445     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14446       border_element = MovingOrBlocked2Element(xx, yy);
14447     else
14448       continue;                 /* center and border element do not touch */
14449
14450     /* check for change of center element (but change it only once) */
14451     if (!change_center_element)
14452       change_center_element =
14453         CheckElementChangeBySide(x, y, center_element, border_element,
14454                                  CE_TOUCHING_X, border_side);
14455
14456     /* check for change of border element */
14457     CheckElementChangeBySide(xx, yy, border_element, center_element,
14458                              CE_TOUCHING_X, center_side);
14459   }
14460 }
14461
14462 #endif
14463
14464 void TestIfElementHitsCustomElement(int x, int y, int direction)
14465 {
14466   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14467   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14468   int hitx = x + dx, hity = y + dy;
14469   int hitting_element = Feld[x][y];
14470   int touched_element;
14471
14472   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14473     return;
14474
14475   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14476                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14477
14478   if (IN_LEV_FIELD(hitx, hity))
14479   {
14480     int opposite_direction = MV_DIR_OPPOSITE(direction);
14481     int hitting_side = direction;
14482     int touched_side = opposite_direction;
14483     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14484                           MovDir[hitx][hity] != direction ||
14485                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14486
14487     object_hit = TRUE;
14488
14489     if (object_hit)
14490     {
14491       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14492                                CE_HITTING_X, touched_side);
14493
14494       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14495                                CE_HIT_BY_X, hitting_side);
14496
14497       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14498                                CE_HIT_BY_SOMETHING, opposite_direction);
14499
14500 #if USE_FIX_CE_ACTION_WITH_PLAYER
14501       if (IS_PLAYER(hitx, hity))
14502       {
14503         /* use player element that is initially defined in the level playfield,
14504            not the player element that corresponds to the runtime player number
14505            (example: a level that contains EL_PLAYER_3 as the only player would
14506            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14507         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14508
14509         CheckElementChangeBySide(x, y, hitting_element, player_element,
14510                                  CE_HITTING_X, touched_side);
14511       }
14512 #endif
14513     }
14514   }
14515
14516   /* "hitting something" is also true when hitting the playfield border */
14517   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14518                            CE_HITTING_SOMETHING, direction);
14519 }
14520
14521 #if 0
14522 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14523 {
14524   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14525   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14526   int hitx = x + dx, hity = y + dy;
14527   int hitting_element = Feld[x][y];
14528   int touched_element;
14529 #if 0
14530   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14531                         !IS_FREE(hitx, hity) &&
14532                         (!IS_MOVING(hitx, hity) ||
14533                          MovDir[hitx][hity] != direction ||
14534                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14535 #endif
14536
14537   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14538     return;
14539
14540 #if 0
14541   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14542     return;
14543 #endif
14544
14545   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14546                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14547
14548   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14549                            EP_CAN_SMASH_EVERYTHING, direction);
14550
14551   if (IN_LEV_FIELD(hitx, hity))
14552   {
14553     int opposite_direction = MV_DIR_OPPOSITE(direction);
14554     int hitting_side = direction;
14555     int touched_side = opposite_direction;
14556 #if 0
14557     int touched_element = MovingOrBlocked2Element(hitx, hity);
14558 #endif
14559 #if 1
14560     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14561                           MovDir[hitx][hity] != direction ||
14562                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14563
14564     object_hit = TRUE;
14565 #endif
14566
14567     if (object_hit)
14568     {
14569       int i;
14570
14571       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14572                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14573
14574       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14575                                CE_OTHER_IS_SMASHING, touched_side);
14576
14577       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14578                                CE_OTHER_GETS_SMASHED, hitting_side);
14579     }
14580   }
14581 }
14582 #endif
14583
14584 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14585 {
14586   int i, kill_x = -1, kill_y = -1;
14587
14588   int bad_element = -1;
14589   static int test_xy[4][2] =
14590   {
14591     { 0, -1 },
14592     { -1, 0 },
14593     { +1, 0 },
14594     { 0, +1 }
14595   };
14596   static int test_dir[4] =
14597   {
14598     MV_UP,
14599     MV_LEFT,
14600     MV_RIGHT,
14601     MV_DOWN
14602   };
14603
14604   for (i = 0; i < NUM_DIRECTIONS; i++)
14605   {
14606     int test_x, test_y, test_move_dir, test_element;
14607
14608     test_x = good_x + test_xy[i][0];
14609     test_y = good_y + test_xy[i][1];
14610
14611     if (!IN_LEV_FIELD(test_x, test_y))
14612       continue;
14613
14614     test_move_dir =
14615       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14616
14617     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14618
14619     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14620        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14621     */
14622     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14623         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14624     {
14625       kill_x = test_x;
14626       kill_y = test_y;
14627       bad_element = test_element;
14628
14629       break;
14630     }
14631   }
14632
14633   if (kill_x != -1 || kill_y != -1)
14634   {
14635     if (IS_PLAYER(good_x, good_y))
14636     {
14637       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14638
14639       if (player->shield_deadly_time_left > 0 &&
14640           !IS_INDESTRUCTIBLE(bad_element))
14641         Bang(kill_x, kill_y);
14642       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14643         KillPlayer(player);
14644     }
14645     else
14646       Bang(good_x, good_y);
14647   }
14648 }
14649
14650 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14651 {
14652   int i, kill_x = -1, kill_y = -1;
14653   int bad_element = Feld[bad_x][bad_y];
14654   static int test_xy[4][2] =
14655   {
14656     { 0, -1 },
14657     { -1, 0 },
14658     { +1, 0 },
14659     { 0, +1 }
14660   };
14661   static int touch_dir[4] =
14662   {
14663     MV_LEFT | MV_RIGHT,
14664     MV_UP   | MV_DOWN,
14665     MV_UP   | MV_DOWN,
14666     MV_LEFT | MV_RIGHT
14667   };
14668   static int test_dir[4] =
14669   {
14670     MV_UP,
14671     MV_LEFT,
14672     MV_RIGHT,
14673     MV_DOWN
14674   };
14675
14676   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14677     return;
14678
14679   for (i = 0; i < NUM_DIRECTIONS; i++)
14680   {
14681     int test_x, test_y, test_move_dir, test_element;
14682
14683     test_x = bad_x + test_xy[i][0];
14684     test_y = bad_y + test_xy[i][1];
14685
14686     if (!IN_LEV_FIELD(test_x, test_y))
14687       continue;
14688
14689     test_move_dir =
14690       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14691
14692     test_element = Feld[test_x][test_y];
14693
14694     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14695        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14696     */
14697     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14698         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14699     {
14700       /* good thing is player or penguin that does not move away */
14701       if (IS_PLAYER(test_x, test_y))
14702       {
14703         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14704
14705         if (bad_element == EL_ROBOT && player->is_moving)
14706           continue;     /* robot does not kill player if he is moving */
14707
14708         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14709         {
14710           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14711             continue;           /* center and border element do not touch */
14712         }
14713
14714         kill_x = test_x;
14715         kill_y = test_y;
14716
14717         break;
14718       }
14719       else if (test_element == EL_PENGUIN)
14720       {
14721         kill_x = test_x;
14722         kill_y = test_y;
14723
14724         break;
14725       }
14726     }
14727   }
14728
14729   if (kill_x != -1 || kill_y != -1)
14730   {
14731     if (IS_PLAYER(kill_x, kill_y))
14732     {
14733       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14734
14735       if (player->shield_deadly_time_left > 0 &&
14736           !IS_INDESTRUCTIBLE(bad_element))
14737         Bang(bad_x, bad_y);
14738       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14739         KillPlayer(player);
14740     }
14741     else
14742       Bang(kill_x, kill_y);
14743   }
14744 }
14745
14746 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14747 {
14748   int bad_element = Feld[bad_x][bad_y];
14749   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14750   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14751   int test_x = bad_x + dx, test_y = bad_y + dy;
14752   int test_move_dir, test_element;
14753   int kill_x = -1, kill_y = -1;
14754
14755   if (!IN_LEV_FIELD(test_x, test_y))
14756     return;
14757
14758   test_move_dir =
14759     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14760
14761   test_element = Feld[test_x][test_y];
14762
14763   if (test_move_dir != bad_move_dir)
14764   {
14765     /* good thing can be player or penguin that does not move away */
14766     if (IS_PLAYER(test_x, test_y))
14767     {
14768       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14769
14770       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14771          player as being hit when he is moving towards the bad thing, because
14772          the "get hit by" condition would be lost after the player stops) */
14773       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14774         return;         /* player moves away from bad thing */
14775
14776       kill_x = test_x;
14777       kill_y = test_y;
14778     }
14779     else if (test_element == EL_PENGUIN)
14780     {
14781       kill_x = test_x;
14782       kill_y = test_y;
14783     }
14784   }
14785
14786   if (kill_x != -1 || kill_y != -1)
14787   {
14788     if (IS_PLAYER(kill_x, kill_y))
14789     {
14790       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14791
14792       if (player->shield_deadly_time_left > 0 &&
14793           !IS_INDESTRUCTIBLE(bad_element))
14794         Bang(bad_x, bad_y);
14795       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14796         KillPlayer(player);
14797     }
14798     else
14799       Bang(kill_x, kill_y);
14800   }
14801 }
14802
14803 void TestIfPlayerTouchesBadThing(int x, int y)
14804 {
14805   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14806 }
14807
14808 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14809 {
14810   TestIfGoodThingHitsBadThing(x, y, move_dir);
14811 }
14812
14813 void TestIfBadThingTouchesPlayer(int x, int y)
14814 {
14815   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14816 }
14817
14818 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14819 {
14820   TestIfBadThingHitsGoodThing(x, y, move_dir);
14821 }
14822
14823 void TestIfFriendTouchesBadThing(int x, int y)
14824 {
14825   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14826 }
14827
14828 void TestIfBadThingTouchesFriend(int x, int y)
14829 {
14830   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14831 }
14832
14833 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14834 {
14835   int i, kill_x = bad_x, kill_y = bad_y;
14836   static int xy[4][2] =
14837   {
14838     { 0, -1 },
14839     { -1, 0 },
14840     { +1, 0 },
14841     { 0, +1 }
14842   };
14843
14844   for (i = 0; i < NUM_DIRECTIONS; i++)
14845   {
14846     int x, y, element;
14847
14848     x = bad_x + xy[i][0];
14849     y = bad_y + xy[i][1];
14850     if (!IN_LEV_FIELD(x, y))
14851       continue;
14852
14853     element = Feld[x][y];
14854     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14855         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14856     {
14857       kill_x = x;
14858       kill_y = y;
14859       break;
14860     }
14861   }
14862
14863   if (kill_x != bad_x || kill_y != bad_y)
14864     Bang(bad_x, bad_y);
14865 }
14866
14867 void KillPlayer(struct PlayerInfo *player)
14868 {
14869   int jx = player->jx, jy = player->jy;
14870
14871   if (!player->active)
14872     return;
14873
14874 #if 0
14875   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14876          player->killed, player->active, player->reanimated);
14877 #endif
14878
14879   /* the following code was introduced to prevent an infinite loop when calling
14880      -> Bang()
14881      -> CheckTriggeredElementChangeExt()
14882      -> ExecuteCustomElementAction()
14883      -> KillPlayer()
14884      -> (infinitely repeating the above sequence of function calls)
14885      which occurs when killing the player while having a CE with the setting
14886      "kill player X when explosion of <player X>"; the solution using a new
14887      field "player->killed" was chosen for backwards compatibility, although
14888      clever use of the fields "player->active" etc. would probably also work */
14889 #if 1
14890   if (player->killed)
14891     return;
14892 #endif
14893
14894   player->killed = TRUE;
14895
14896   /* remove accessible field at the player's position */
14897   Feld[jx][jy] = EL_EMPTY;
14898
14899   /* deactivate shield (else Bang()/Explode() would not work right) */
14900   player->shield_normal_time_left = 0;
14901   player->shield_deadly_time_left = 0;
14902
14903 #if 0
14904   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14905          player->killed, player->active, player->reanimated);
14906 #endif
14907
14908   Bang(jx, jy);
14909
14910 #if 0
14911   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14912          player->killed, player->active, player->reanimated);
14913 #endif
14914
14915 #if USE_PLAYER_REANIMATION
14916 #if 1
14917   if (player->reanimated)       /* killed player may have been reanimated */
14918     player->killed = player->reanimated = FALSE;
14919   else
14920     BuryPlayer(player);
14921 #else
14922   if (player->killed)           /* player may have been reanimated */
14923     BuryPlayer(player);
14924 #endif
14925 #else
14926   BuryPlayer(player);
14927 #endif
14928 }
14929
14930 static void KillPlayerUnlessEnemyProtected(int x, int y)
14931 {
14932   if (!PLAYER_ENEMY_PROTECTED(x, y))
14933     KillPlayer(PLAYERINFO(x, y));
14934 }
14935
14936 static void KillPlayerUnlessExplosionProtected(int x, int y)
14937 {
14938   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14939     KillPlayer(PLAYERINFO(x, y));
14940 }
14941
14942 void BuryPlayer(struct PlayerInfo *player)
14943 {
14944   int jx = player->jx, jy = player->jy;
14945
14946   if (!player->active)
14947     return;
14948
14949   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14950   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14951
14952   player->GameOver = TRUE;
14953   RemovePlayer(player);
14954 }
14955
14956 void RemovePlayer(struct PlayerInfo *player)
14957 {
14958   int jx = player->jx, jy = player->jy;
14959   int i, found = FALSE;
14960
14961   player->present = FALSE;
14962   player->active = FALSE;
14963
14964   if (!ExplodeField[jx][jy])
14965     StorePlayer[jx][jy] = 0;
14966
14967   if (player->is_moving)
14968     TEST_DrawLevelField(player->last_jx, player->last_jy);
14969
14970   for (i = 0; i < MAX_PLAYERS; i++)
14971     if (stored_player[i].active)
14972       found = TRUE;
14973
14974   if (!found)
14975     AllPlayersGone = TRUE;
14976
14977   ExitX = ZX = jx;
14978   ExitY = ZY = jy;
14979 }
14980
14981 #if USE_NEW_SNAP_DELAY
14982 static void setFieldForSnapping(int x, int y, int element, int direction)
14983 {
14984   struct ElementInfo *ei = &element_info[element];
14985   int direction_bit = MV_DIR_TO_BIT(direction);
14986   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14987   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14988                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14989
14990   Feld[x][y] = EL_ELEMENT_SNAPPING;
14991   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14992
14993   ResetGfxAnimation(x, y);
14994
14995   GfxElement[x][y] = element;
14996   GfxAction[x][y] = action;
14997   GfxDir[x][y] = direction;
14998   GfxFrame[x][y] = -1;
14999 }
15000 #endif
15001
15002 /*
15003   =============================================================================
15004   checkDiagonalPushing()
15005   -----------------------------------------------------------------------------
15006   check if diagonal input device direction results in pushing of object
15007   (by checking if the alternative direction is walkable, diggable, ...)
15008   =============================================================================
15009 */
15010
15011 static boolean checkDiagonalPushing(struct PlayerInfo *player,
15012                                     int x, int y, int real_dx, int real_dy)
15013 {
15014   int jx, jy, dx, dy, xx, yy;
15015
15016   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
15017     return TRUE;
15018
15019   /* diagonal direction: check alternative direction */
15020   jx = player->jx;
15021   jy = player->jy;
15022   dx = x - jx;
15023   dy = y - jy;
15024   xx = jx + (dx == 0 ? real_dx : 0);
15025   yy = jy + (dy == 0 ? real_dy : 0);
15026
15027   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
15028 }
15029
15030 /*
15031   =============================================================================
15032   DigField()
15033   -----------------------------------------------------------------------------
15034   x, y:                 field next to player (non-diagonal) to try to dig to
15035   real_dx, real_dy:     direction as read from input device (can be diagonal)
15036   =============================================================================
15037 */
15038
15039 static int DigField(struct PlayerInfo *player,
15040                     int oldx, int oldy, int x, int y,
15041                     int real_dx, int real_dy, int mode)
15042 {
15043   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15044   boolean player_was_pushing = player->is_pushing;
15045   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15046   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15047   int jx = oldx, jy = oldy;
15048   int dx = x - jx, dy = y - jy;
15049   int nextx = x + dx, nexty = y + dy;
15050   int move_direction = (dx == -1 ? MV_LEFT  :
15051                         dx == +1 ? MV_RIGHT :
15052                         dy == -1 ? MV_UP    :
15053                         dy == +1 ? MV_DOWN  : MV_NONE);
15054   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15055   int dig_side = MV_DIR_OPPOSITE(move_direction);
15056   int old_element = Feld[jx][jy];
15057 #if USE_FIXED_DONT_RUN_INTO
15058   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15059 #else
15060   int element;
15061 #endif
15062   int collect_count;
15063
15064   if (is_player)                /* function can also be called by EL_PENGUIN */
15065   {
15066     if (player->MovPos == 0)
15067     {
15068       player->is_digging = FALSE;
15069       player->is_collecting = FALSE;
15070     }
15071
15072     if (player->MovPos == 0)    /* last pushing move finished */
15073       player->is_pushing = FALSE;
15074
15075     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
15076     {
15077       player->is_switching = FALSE;
15078       player->push_delay = -1;
15079
15080       return MP_NO_ACTION;
15081     }
15082   }
15083
15084 #if !USE_FIXED_DONT_RUN_INTO
15085   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15086     return MP_NO_ACTION;
15087 #endif
15088
15089   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15090     old_element = Back[jx][jy];
15091
15092   /* in case of element dropped at player position, check background */
15093   else if (Back[jx][jy] != EL_EMPTY &&
15094            game.engine_version >= VERSION_IDENT(2,2,0,0))
15095     old_element = Back[jx][jy];
15096
15097   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15098     return MP_NO_ACTION;        /* field has no opening in this direction */
15099
15100   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15101     return MP_NO_ACTION;        /* field has no opening in this direction */
15102
15103 #if USE_FIXED_DONT_RUN_INTO
15104   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15105   {
15106     SplashAcid(x, y);
15107
15108     Feld[jx][jy] = player->artwork_element;
15109     InitMovingField(jx, jy, MV_DOWN);
15110     Store[jx][jy] = EL_ACID;
15111     ContinueMoving(jx, jy);
15112     BuryPlayer(player);
15113
15114     return MP_DONT_RUN_INTO;
15115   }
15116 #endif
15117
15118 #if USE_FIXED_DONT_RUN_INTO
15119   if (player_can_move && DONT_RUN_INTO(element))
15120   {
15121     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15122
15123     return MP_DONT_RUN_INTO;
15124   }
15125 #endif
15126
15127 #if USE_FIXED_DONT_RUN_INTO
15128   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15129     return MP_NO_ACTION;
15130 #endif
15131
15132 #if !USE_FIXED_DONT_RUN_INTO
15133   element = Feld[x][y];
15134 #endif
15135
15136   collect_count = element_info[element].collect_count_initial;
15137
15138   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15139     return MP_NO_ACTION;
15140
15141   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15142     player_can_move = player_can_move_or_snap;
15143
15144   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15145       game.engine_version >= VERSION_IDENT(2,2,0,0))
15146   {
15147     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15148                                player->index_bit, dig_side);
15149     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15150                                         player->index_bit, dig_side);
15151
15152     if (element == EL_DC_LANDMINE)
15153       Bang(x, y);
15154
15155     if (Feld[x][y] != element)          /* field changed by snapping */
15156       return MP_ACTION;
15157
15158     return MP_NO_ACTION;
15159   }
15160
15161 #if USE_PLAYER_GRAVITY
15162   if (player->gravity && is_player && !player->is_auto_moving &&
15163       canFallDown(player) && move_direction != MV_DOWN &&
15164       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15165     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15166 #else
15167   if (game.gravity && is_player && !player->is_auto_moving &&
15168       canFallDown(player) && move_direction != MV_DOWN &&
15169       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15170     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15171 #endif
15172
15173   if (player_can_move &&
15174       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15175   {
15176     int sound_element = SND_ELEMENT(element);
15177     int sound_action = ACTION_WALKING;
15178
15179     if (IS_RND_GATE(element))
15180     {
15181       if (!player->key[RND_GATE_NR(element)])
15182         return MP_NO_ACTION;
15183     }
15184     else if (IS_RND_GATE_GRAY(element))
15185     {
15186       if (!player->key[RND_GATE_GRAY_NR(element)])
15187         return MP_NO_ACTION;
15188     }
15189     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15190     {
15191       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15192         return MP_NO_ACTION;
15193     }
15194     else if (element == EL_EXIT_OPEN ||
15195              element == EL_EM_EXIT_OPEN ||
15196 #if 1
15197              element == EL_EM_EXIT_OPENING ||
15198 #endif
15199              element == EL_STEEL_EXIT_OPEN ||
15200              element == EL_EM_STEEL_EXIT_OPEN ||
15201 #if 1
15202              element == EL_EM_STEEL_EXIT_OPENING ||
15203 #endif
15204              element == EL_SP_EXIT_OPEN ||
15205              element == EL_SP_EXIT_OPENING)
15206     {
15207       sound_action = ACTION_PASSING;    /* player is passing exit */
15208     }
15209     else if (element == EL_EMPTY)
15210     {
15211       sound_action = ACTION_MOVING;             /* nothing to walk on */
15212     }
15213
15214     /* play sound from background or player, whatever is available */
15215     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15216       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15217     else
15218       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15219   }
15220   else if (player_can_move &&
15221            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15222   {
15223     if (!ACCESS_FROM(element, opposite_direction))
15224       return MP_NO_ACTION;      /* field not accessible from this direction */
15225
15226     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15227       return MP_NO_ACTION;
15228
15229     if (IS_EM_GATE(element))
15230     {
15231       if (!player->key[EM_GATE_NR(element)])
15232         return MP_NO_ACTION;
15233     }
15234     else if (IS_EM_GATE_GRAY(element))
15235     {
15236       if (!player->key[EM_GATE_GRAY_NR(element)])
15237         return MP_NO_ACTION;
15238     }
15239     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15240     {
15241       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15242         return MP_NO_ACTION;
15243     }
15244     else if (IS_EMC_GATE(element))
15245     {
15246       if (!player->key[EMC_GATE_NR(element)])
15247         return MP_NO_ACTION;
15248     }
15249     else if (IS_EMC_GATE_GRAY(element))
15250     {
15251       if (!player->key[EMC_GATE_GRAY_NR(element)])
15252         return MP_NO_ACTION;
15253     }
15254     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15255     {
15256       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15257         return MP_NO_ACTION;
15258     }
15259     else if (element == EL_DC_GATE_WHITE ||
15260              element == EL_DC_GATE_WHITE_GRAY ||
15261              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15262     {
15263       if (player->num_white_keys == 0)
15264         return MP_NO_ACTION;
15265
15266       player->num_white_keys--;
15267     }
15268     else if (IS_SP_PORT(element))
15269     {
15270       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15271           element == EL_SP_GRAVITY_PORT_RIGHT ||
15272           element == EL_SP_GRAVITY_PORT_UP ||
15273           element == EL_SP_GRAVITY_PORT_DOWN)
15274 #if USE_PLAYER_GRAVITY
15275         player->gravity = !player->gravity;
15276 #else
15277         game.gravity = !game.gravity;
15278 #endif
15279       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15280                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15281                element == EL_SP_GRAVITY_ON_PORT_UP ||
15282                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15283 #if USE_PLAYER_GRAVITY
15284         player->gravity = TRUE;
15285 #else
15286         game.gravity = TRUE;
15287 #endif
15288       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15289                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15290                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15291                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15292 #if USE_PLAYER_GRAVITY
15293         player->gravity = FALSE;
15294 #else
15295         game.gravity = FALSE;
15296 #endif
15297     }
15298
15299     /* automatically move to the next field with double speed */
15300     player->programmed_action = move_direction;
15301
15302     if (player->move_delay_reset_counter == 0)
15303     {
15304       player->move_delay_reset_counter = 2;     /* two double speed steps */
15305
15306       DOUBLE_PLAYER_SPEED(player);
15307     }
15308
15309     PlayLevelSoundAction(x, y, ACTION_PASSING);
15310   }
15311   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15312   {
15313     RemoveField(x, y);
15314
15315     if (mode != DF_SNAP)
15316     {
15317       GfxElement[x][y] = GFX_ELEMENT(element);
15318       player->is_digging = TRUE;
15319     }
15320
15321     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15322
15323     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15324                                         player->index_bit, dig_side);
15325
15326     if (mode == DF_SNAP)
15327     {
15328 #if USE_NEW_SNAP_DELAY
15329       if (level.block_snap_field)
15330         setFieldForSnapping(x, y, element, move_direction);
15331       else
15332         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15333 #else
15334       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15335 #endif
15336
15337       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15338                                           player->index_bit, dig_side);
15339     }
15340   }
15341   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15342   {
15343     RemoveField(x, y);
15344
15345     if (is_player && mode != DF_SNAP)
15346     {
15347       GfxElement[x][y] = element;
15348       player->is_collecting = TRUE;
15349     }
15350
15351     if (element == EL_SPEED_PILL)
15352     {
15353       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15354     }
15355     else if (element == EL_EXTRA_TIME && level.time > 0)
15356     {
15357       TimeLeft += level.extra_time;
15358
15359 #if 1
15360       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15361
15362       DisplayGameControlValues();
15363 #else
15364       DrawGameValue_Time(TimeLeft);
15365 #endif
15366     }
15367     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15368     {
15369       player->shield_normal_time_left += level.shield_normal_time;
15370       if (element == EL_SHIELD_DEADLY)
15371         player->shield_deadly_time_left += level.shield_deadly_time;
15372     }
15373     else if (element == EL_DYNAMITE ||
15374              element == EL_EM_DYNAMITE ||
15375              element == EL_SP_DISK_RED)
15376     {
15377       if (player->inventory_size < MAX_INVENTORY_SIZE)
15378         player->inventory_element[player->inventory_size++] = element;
15379
15380       DrawGameDoorValues();
15381     }
15382     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15383     {
15384       player->dynabomb_count++;
15385       player->dynabombs_left++;
15386     }
15387     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15388     {
15389       player->dynabomb_size++;
15390     }
15391     else if (element == EL_DYNABOMB_INCREASE_POWER)
15392     {
15393       player->dynabomb_xl = TRUE;
15394     }
15395     else if (IS_KEY(element))
15396     {
15397       player->key[KEY_NR(element)] = TRUE;
15398
15399       DrawGameDoorValues();
15400     }
15401     else if (element == EL_DC_KEY_WHITE)
15402     {
15403       player->num_white_keys++;
15404
15405       /* display white keys? */
15406       /* DrawGameDoorValues(); */
15407     }
15408     else if (IS_ENVELOPE(element))
15409     {
15410       player->show_envelope = element;
15411     }
15412     else if (element == EL_EMC_LENSES)
15413     {
15414       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15415
15416       RedrawAllInvisibleElementsForLenses();
15417     }
15418     else if (element == EL_EMC_MAGNIFIER)
15419     {
15420       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15421
15422       RedrawAllInvisibleElementsForMagnifier();
15423     }
15424     else if (IS_DROPPABLE(element) ||
15425              IS_THROWABLE(element))     /* can be collected and dropped */
15426     {
15427       int i;
15428
15429       if (collect_count == 0)
15430         player->inventory_infinite_element = element;
15431       else
15432         for (i = 0; i < collect_count; i++)
15433           if (player->inventory_size < MAX_INVENTORY_SIZE)
15434             player->inventory_element[player->inventory_size++] = element;
15435
15436       DrawGameDoorValues();
15437     }
15438     else if (collect_count > 0)
15439     {
15440       local_player->gems_still_needed -= collect_count;
15441       if (local_player->gems_still_needed < 0)
15442         local_player->gems_still_needed = 0;
15443
15444 #if 1
15445       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15446
15447       DisplayGameControlValues();
15448 #else
15449       DrawGameValue_Emeralds(local_player->gems_still_needed);
15450 #endif
15451     }
15452
15453     RaiseScoreElement(element);
15454     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15455
15456     if (is_player)
15457       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15458                                           player->index_bit, dig_side);
15459
15460     if (mode == DF_SNAP)
15461     {
15462 #if USE_NEW_SNAP_DELAY
15463       if (level.block_snap_field)
15464         setFieldForSnapping(x, y, element, move_direction);
15465       else
15466         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15467 #else
15468       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15469 #endif
15470
15471       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15472                                           player->index_bit, dig_side);
15473     }
15474   }
15475   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15476   {
15477     if (mode == DF_SNAP && element != EL_BD_ROCK)
15478       return MP_NO_ACTION;
15479
15480     if (CAN_FALL(element) && dy)
15481       return MP_NO_ACTION;
15482
15483     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15484         !(element == EL_SPRING && level.use_spring_bug))
15485       return MP_NO_ACTION;
15486
15487     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15488         ((move_direction & MV_VERTICAL &&
15489           ((element_info[element].move_pattern & MV_LEFT &&
15490             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15491            (element_info[element].move_pattern & MV_RIGHT &&
15492             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15493          (move_direction & MV_HORIZONTAL &&
15494           ((element_info[element].move_pattern & MV_UP &&
15495             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15496            (element_info[element].move_pattern & MV_DOWN &&
15497             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15498       return MP_NO_ACTION;
15499
15500     /* do not push elements already moving away faster than player */
15501     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15502         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15503       return MP_NO_ACTION;
15504
15505     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15506     {
15507       if (player->push_delay_value == -1 || !player_was_pushing)
15508         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15509     }
15510     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15511     {
15512       if (player->push_delay_value == -1)
15513         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15514     }
15515     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15516     {
15517       if (!player->is_pushing)
15518         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15519     }
15520
15521     player->is_pushing = TRUE;
15522     player->is_active = TRUE;
15523
15524     if (!(IN_LEV_FIELD(nextx, nexty) &&
15525           (IS_FREE(nextx, nexty) ||
15526            (IS_SB_ELEMENT(element) &&
15527             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15528            (IS_CUSTOM_ELEMENT(element) &&
15529             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15530       return MP_NO_ACTION;
15531
15532     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15533       return MP_NO_ACTION;
15534
15535     if (player->push_delay == -1)       /* new pushing; restart delay */
15536       player->push_delay = 0;
15537
15538     if (player->push_delay < player->push_delay_value &&
15539         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15540         element != EL_SPRING && element != EL_BALLOON)
15541     {
15542       /* make sure that there is no move delay before next try to push */
15543       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15544         player->move_delay = 0;
15545
15546       return MP_NO_ACTION;
15547     }
15548
15549     if (IS_CUSTOM_ELEMENT(element) &&
15550         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15551     {
15552       if (!DigFieldByCE(nextx, nexty, element))
15553         return MP_NO_ACTION;
15554     }
15555
15556     if (IS_SB_ELEMENT(element))
15557     {
15558       if (element == EL_SOKOBAN_FIELD_FULL)
15559       {
15560         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15561         local_player->sokobanfields_still_needed++;
15562       }
15563
15564       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15565       {
15566         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15567         local_player->sokobanfields_still_needed--;
15568       }
15569
15570       Feld[x][y] = EL_SOKOBAN_OBJECT;
15571
15572       if (Back[x][y] == Back[nextx][nexty])
15573         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15574       else if (Back[x][y] != 0)
15575         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15576                                     ACTION_EMPTYING);
15577       else
15578         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15579                                     ACTION_FILLING);
15580
15581 #if 1
15582       if (local_player->sokobanfields_still_needed == 0 &&
15583           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15584 #else
15585       if (local_player->sokobanfields_still_needed == 0 &&
15586           game.emulation == EMU_SOKOBAN)
15587 #endif
15588       {
15589         PlayerWins(player);
15590
15591         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15592       }
15593     }
15594     else
15595       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15596
15597     InitMovingField(x, y, move_direction);
15598     GfxAction[x][y] = ACTION_PUSHING;
15599
15600     if (mode == DF_SNAP)
15601       ContinueMoving(x, y);
15602     else
15603       MovPos[x][y] = (dx != 0 ? dx : dy);
15604
15605     Pushed[x][y] = TRUE;
15606     Pushed[nextx][nexty] = TRUE;
15607
15608     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15609       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15610     else
15611       player->push_delay_value = -1;    /* get new value later */
15612
15613     /* check for element change _after_ element has been pushed */
15614     if (game.use_change_when_pushing_bug)
15615     {
15616       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15617                                  player->index_bit, dig_side);
15618       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15619                                           player->index_bit, dig_side);
15620     }
15621   }
15622   else if (IS_SWITCHABLE(element))
15623   {
15624     if (PLAYER_SWITCHING(player, x, y))
15625     {
15626       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15627                                           player->index_bit, dig_side);
15628
15629       return MP_ACTION;
15630     }
15631
15632     player->is_switching = TRUE;
15633     player->switch_x = x;
15634     player->switch_y = y;
15635
15636     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15637
15638     if (element == EL_ROBOT_WHEEL)
15639     {
15640       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15641       ZX = x;
15642       ZY = y;
15643
15644       game.robot_wheel_active = TRUE;
15645
15646       TEST_DrawLevelField(x, y);
15647     }
15648     else if (element == EL_SP_TERMINAL)
15649     {
15650       int xx, yy;
15651
15652       SCAN_PLAYFIELD(xx, yy)
15653       {
15654         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15655           Bang(xx, yy);
15656         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15657           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15658       }
15659     }
15660     else if (IS_BELT_SWITCH(element))
15661     {
15662       ToggleBeltSwitch(x, y);
15663     }
15664     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15665              element == EL_SWITCHGATE_SWITCH_DOWN ||
15666              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15667              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15668     {
15669       ToggleSwitchgateSwitch(x, y);
15670     }
15671     else if (element == EL_LIGHT_SWITCH ||
15672              element == EL_LIGHT_SWITCH_ACTIVE)
15673     {
15674       ToggleLightSwitch(x, y);
15675     }
15676     else if (element == EL_TIMEGATE_SWITCH ||
15677              element == EL_DC_TIMEGATE_SWITCH)
15678     {
15679       ActivateTimegateSwitch(x, y);
15680     }
15681     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15682              element == EL_BALLOON_SWITCH_RIGHT ||
15683              element == EL_BALLOON_SWITCH_UP    ||
15684              element == EL_BALLOON_SWITCH_DOWN  ||
15685              element == EL_BALLOON_SWITCH_NONE  ||
15686              element == EL_BALLOON_SWITCH_ANY)
15687     {
15688       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15689                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15690                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15691                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15692                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15693                              move_direction);
15694     }
15695     else if (element == EL_LAMP)
15696     {
15697       Feld[x][y] = EL_LAMP_ACTIVE;
15698       local_player->lights_still_needed--;
15699
15700       ResetGfxAnimation(x, y);
15701       TEST_DrawLevelField(x, y);
15702     }
15703     else if (element == EL_TIME_ORB_FULL)
15704     {
15705       Feld[x][y] = EL_TIME_ORB_EMPTY;
15706
15707       if (level.time > 0 || level.use_time_orb_bug)
15708       {
15709         TimeLeft += level.time_orb_time;
15710         game.no_time_limit = FALSE;
15711
15712 #if 1
15713         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15714
15715         DisplayGameControlValues();
15716 #else
15717         DrawGameValue_Time(TimeLeft);
15718 #endif
15719       }
15720
15721       ResetGfxAnimation(x, y);
15722       TEST_DrawLevelField(x, y);
15723     }
15724     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15725              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15726     {
15727       int xx, yy;
15728
15729       game.ball_state = !game.ball_state;
15730
15731       SCAN_PLAYFIELD(xx, yy)
15732       {
15733         int e = Feld[xx][yy];
15734
15735         if (game.ball_state)
15736         {
15737           if (e == EL_EMC_MAGIC_BALL)
15738             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15739           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15740             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15741         }
15742         else
15743         {
15744           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15745             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15746           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15747             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15748         }
15749       }
15750     }
15751
15752     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15753                                         player->index_bit, dig_side);
15754
15755     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15756                                         player->index_bit, dig_side);
15757
15758     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15759                                         player->index_bit, dig_side);
15760
15761     return MP_ACTION;
15762   }
15763   else
15764   {
15765     if (!PLAYER_SWITCHING(player, x, y))
15766     {
15767       player->is_switching = TRUE;
15768       player->switch_x = x;
15769       player->switch_y = y;
15770
15771       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15772                                  player->index_bit, dig_side);
15773       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15774                                           player->index_bit, dig_side);
15775
15776       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15777                                  player->index_bit, dig_side);
15778       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15779                                           player->index_bit, dig_side);
15780     }
15781
15782     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15783                                player->index_bit, dig_side);
15784     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15785                                         player->index_bit, dig_side);
15786
15787     return MP_NO_ACTION;
15788   }
15789
15790   player->push_delay = -1;
15791
15792   if (is_player)                /* function can also be called by EL_PENGUIN */
15793   {
15794     if (Feld[x][y] != element)          /* really digged/collected something */
15795     {
15796       player->is_collecting = !player->is_digging;
15797       player->is_active = TRUE;
15798     }
15799   }
15800
15801   return MP_MOVING;
15802 }
15803
15804 static boolean DigFieldByCE(int x, int y, int digging_element)
15805 {
15806   int element = Feld[x][y];
15807
15808   if (!IS_FREE(x, y))
15809   {
15810     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15811                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15812                   ACTION_BREAKING);
15813
15814     /* no element can dig solid indestructible elements */
15815     if (IS_INDESTRUCTIBLE(element) &&
15816         !IS_DIGGABLE(element) &&
15817         !IS_COLLECTIBLE(element))
15818       return FALSE;
15819
15820     if (AmoebaNr[x][y] &&
15821         (element == EL_AMOEBA_FULL ||
15822          element == EL_BD_AMOEBA ||
15823          element == EL_AMOEBA_GROWING))
15824     {
15825       AmoebaCnt[AmoebaNr[x][y]]--;
15826       AmoebaCnt2[AmoebaNr[x][y]]--;
15827     }
15828
15829     if (IS_MOVING(x, y))
15830       RemoveMovingField(x, y);
15831     else
15832     {
15833       RemoveField(x, y);
15834       TEST_DrawLevelField(x, y);
15835     }
15836
15837     /* if digged element was about to explode, prevent the explosion */
15838     ExplodeField[x][y] = EX_TYPE_NONE;
15839
15840     PlayLevelSoundAction(x, y, action);
15841   }
15842
15843   Store[x][y] = EL_EMPTY;
15844
15845 #if 1
15846   /* this makes it possible to leave the removed element again */
15847   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15848     Store[x][y] = element;
15849 #else
15850   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15851   {
15852     int move_leave_element = element_info[digging_element].move_leave_element;
15853
15854     /* this makes it possible to leave the removed element again */
15855     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15856                    element : move_leave_element);
15857   }
15858 #endif
15859
15860   return TRUE;
15861 }
15862
15863 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15864 {
15865   int jx = player->jx, jy = player->jy;
15866   int x = jx + dx, y = jy + dy;
15867   int snap_direction = (dx == -1 ? MV_LEFT  :
15868                         dx == +1 ? MV_RIGHT :
15869                         dy == -1 ? MV_UP    :
15870                         dy == +1 ? MV_DOWN  : MV_NONE);
15871   boolean can_continue_snapping = (level.continuous_snapping &&
15872                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15873
15874   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15875     return FALSE;
15876
15877   if (!player->active || !IN_LEV_FIELD(x, y))
15878     return FALSE;
15879
15880   if (dx && dy)
15881     return FALSE;
15882
15883   if (!dx && !dy)
15884   {
15885     if (player->MovPos == 0)
15886       player->is_pushing = FALSE;
15887
15888     player->is_snapping = FALSE;
15889
15890     if (player->MovPos == 0)
15891     {
15892       player->is_moving = FALSE;
15893       player->is_digging = FALSE;
15894       player->is_collecting = FALSE;
15895     }
15896
15897     return FALSE;
15898   }
15899
15900 #if USE_NEW_CONTINUOUS_SNAPPING
15901   /* prevent snapping with already pressed snap key when not allowed */
15902   if (player->is_snapping && !can_continue_snapping)
15903     return FALSE;
15904 #else
15905   if (player->is_snapping)
15906     return FALSE;
15907 #endif
15908
15909   player->MovDir = snap_direction;
15910
15911   if (player->MovPos == 0)
15912   {
15913     player->is_moving = FALSE;
15914     player->is_digging = FALSE;
15915     player->is_collecting = FALSE;
15916   }
15917
15918   player->is_dropping = FALSE;
15919   player->is_dropping_pressed = FALSE;
15920   player->drop_pressed_delay = 0;
15921
15922   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15923     return FALSE;
15924
15925   player->is_snapping = TRUE;
15926   player->is_active = TRUE;
15927
15928   if (player->MovPos == 0)
15929   {
15930     player->is_moving = FALSE;
15931     player->is_digging = FALSE;
15932     player->is_collecting = FALSE;
15933   }
15934
15935   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15936     TEST_DrawLevelField(player->last_jx, player->last_jy);
15937
15938   TEST_DrawLevelField(x, y);
15939
15940   return TRUE;
15941 }
15942
15943 static boolean DropElement(struct PlayerInfo *player)
15944 {
15945   int old_element, new_element;
15946   int dropx = player->jx, dropy = player->jy;
15947   int drop_direction = player->MovDir;
15948   int drop_side = drop_direction;
15949 #if 1
15950   int drop_element = get_next_dropped_element(player);
15951 #else
15952   int drop_element = (player->inventory_size > 0 ?
15953                       player->inventory_element[player->inventory_size - 1] :
15954                       player->inventory_infinite_element != EL_UNDEFINED ?
15955                       player->inventory_infinite_element :
15956                       player->dynabombs_left > 0 ?
15957                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15958                       EL_UNDEFINED);
15959 #endif
15960
15961   player->is_dropping_pressed = TRUE;
15962
15963   /* do not drop an element on top of another element; when holding drop key
15964      pressed without moving, dropped element must move away before the next
15965      element can be dropped (this is especially important if the next element
15966      is dynamite, which can be placed on background for historical reasons) */
15967   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15968     return MP_ACTION;
15969
15970   if (IS_THROWABLE(drop_element))
15971   {
15972     dropx += GET_DX_FROM_DIR(drop_direction);
15973     dropy += GET_DY_FROM_DIR(drop_direction);
15974
15975     if (!IN_LEV_FIELD(dropx, dropy))
15976       return FALSE;
15977   }
15978
15979   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15980   new_element = drop_element;           /* default: no change when dropping */
15981
15982   /* check if player is active, not moving and ready to drop */
15983   if (!player->active || player->MovPos || player->drop_delay > 0)
15984     return FALSE;
15985
15986   /* check if player has anything that can be dropped */
15987   if (new_element == EL_UNDEFINED)
15988     return FALSE;
15989
15990   /* check if drop key was pressed long enough for EM style dynamite */
15991   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15992     return FALSE;
15993
15994   /* check if anything can be dropped at the current position */
15995   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15996     return FALSE;
15997
15998   /* collected custom elements can only be dropped on empty fields */
15999   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
16000     return FALSE;
16001
16002   if (old_element != EL_EMPTY)
16003     Back[dropx][dropy] = old_element;   /* store old element on this field */
16004
16005   ResetGfxAnimation(dropx, dropy);
16006   ResetRandomAnimationValue(dropx, dropy);
16007
16008   if (player->inventory_size > 0 ||
16009       player->inventory_infinite_element != EL_UNDEFINED)
16010   {
16011     if (player->inventory_size > 0)
16012     {
16013       player->inventory_size--;
16014
16015       DrawGameDoorValues();
16016
16017       if (new_element == EL_DYNAMITE)
16018         new_element = EL_DYNAMITE_ACTIVE;
16019       else if (new_element == EL_EM_DYNAMITE)
16020         new_element = EL_EM_DYNAMITE_ACTIVE;
16021       else if (new_element == EL_SP_DISK_RED)
16022         new_element = EL_SP_DISK_RED_ACTIVE;
16023     }
16024
16025     Feld[dropx][dropy] = new_element;
16026
16027     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16028       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16029                           el2img(Feld[dropx][dropy]), 0);
16030
16031     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16032
16033     /* needed if previous element just changed to "empty" in the last frame */
16034     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16035
16036     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16037                                player->index_bit, drop_side);
16038     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16039                                         CE_PLAYER_DROPS_X,
16040                                         player->index_bit, drop_side);
16041
16042     TestIfElementTouchesCustomElement(dropx, dropy);
16043   }
16044   else          /* player is dropping a dyna bomb */
16045   {
16046     player->dynabombs_left--;
16047
16048     Feld[dropx][dropy] = new_element;
16049
16050     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16051       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16052                           el2img(Feld[dropx][dropy]), 0);
16053
16054     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16055   }
16056
16057   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16058     InitField_WithBug1(dropx, dropy, FALSE);
16059
16060   new_element = Feld[dropx][dropy];     /* element might have changed */
16061
16062   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16063       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16064   {
16065 #if 0
16066     int move_direction;
16067     int nextx, nexty;
16068 #endif
16069
16070     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16071       MovDir[dropx][dropy] = drop_direction;
16072
16073 #if 0
16074     move_direction = MovDir[dropx][dropy];
16075     nextx = dropx + GET_DX_FROM_DIR(move_direction);
16076     nexty = dropy + GET_DY_FROM_DIR(move_direction);
16077 #endif
16078
16079     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16080
16081 #if USE_FIX_IMPACT_COLLISION
16082     /* do not cause impact style collision by dropping elements that can fall */
16083     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16084 #else
16085     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16086 #endif
16087   }
16088
16089   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16090   player->is_dropping = TRUE;
16091
16092   player->drop_pressed_delay = 0;
16093   player->is_dropping_pressed = FALSE;
16094
16095   player->drop_x = dropx;
16096   player->drop_y = dropy;
16097
16098   return TRUE;
16099 }
16100
16101 /* ------------------------------------------------------------------------- */
16102 /* game sound playing functions                                              */
16103 /* ------------------------------------------------------------------------- */
16104
16105 static int *loop_sound_frame = NULL;
16106 static int *loop_sound_volume = NULL;
16107
16108 void InitPlayLevelSound()
16109 {
16110   int num_sounds = getSoundListSize();
16111
16112   checked_free(loop_sound_frame);
16113   checked_free(loop_sound_volume);
16114
16115   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16116   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16117 }
16118
16119 static void PlayLevelSound(int x, int y, int nr)
16120 {
16121   int sx = SCREENX(x), sy = SCREENY(y);
16122   int volume, stereo_position;
16123   int max_distance = 8;
16124   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16125
16126   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16127       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16128     return;
16129
16130   if (!IN_LEV_FIELD(x, y) ||
16131       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16132       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16133     return;
16134
16135   volume = SOUND_MAX_VOLUME;
16136
16137   if (!IN_SCR_FIELD(sx, sy))
16138   {
16139     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16140     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16141
16142     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16143   }
16144
16145   stereo_position = (SOUND_MAX_LEFT +
16146                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16147                      (SCR_FIELDX + 2 * max_distance));
16148
16149   if (IS_LOOP_SOUND(nr))
16150   {
16151     /* This assures that quieter loop sounds do not overwrite louder ones,
16152        while restarting sound volume comparison with each new game frame. */
16153
16154     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16155       return;
16156
16157     loop_sound_volume[nr] = volume;
16158     loop_sound_frame[nr] = FrameCounter;
16159   }
16160
16161   PlaySoundExt(nr, volume, stereo_position, type);
16162 }
16163
16164 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16165 {
16166   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16167                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16168                  y < LEVELY(BY1) ? LEVELY(BY1) :
16169                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16170                  sound_action);
16171 }
16172
16173 static void PlayLevelSoundAction(int x, int y, int action)
16174 {
16175   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16176 }
16177
16178 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16179 {
16180   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16181
16182   if (sound_effect != SND_UNDEFINED)
16183     PlayLevelSound(x, y, sound_effect);
16184 }
16185
16186 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16187                                               int action)
16188 {
16189   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16190
16191   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16192     PlayLevelSound(x, y, sound_effect);
16193 }
16194
16195 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16196 {
16197   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16198
16199   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16200     PlayLevelSound(x, y, sound_effect);
16201 }
16202
16203 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16204 {
16205   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16206
16207   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16208     StopSound(sound_effect);
16209 }
16210
16211 static void PlayLevelMusic()
16212 {
16213   if (levelset.music[level_nr] != MUS_UNDEFINED)
16214     PlayMusic(levelset.music[level_nr]);        /* from config file */
16215   else
16216     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16217 }
16218
16219 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16220 {
16221   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16222   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16223   int x = xx - 1 - offset;
16224   int y = yy - 1 - offset;
16225
16226   switch (sample)
16227   {
16228     case SAMPLE_blank:
16229       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16230       break;
16231
16232     case SAMPLE_roll:
16233       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16234       break;
16235
16236     case SAMPLE_stone:
16237       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16238       break;
16239
16240     case SAMPLE_nut:
16241       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16242       break;
16243
16244     case SAMPLE_crack:
16245       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16246       break;
16247
16248     case SAMPLE_bug:
16249       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16250       break;
16251
16252     case SAMPLE_tank:
16253       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16254       break;
16255
16256     case SAMPLE_android_clone:
16257       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16258       break;
16259
16260     case SAMPLE_android_move:
16261       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16262       break;
16263
16264     case SAMPLE_spring:
16265       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16266       break;
16267
16268     case SAMPLE_slurp:
16269       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16270       break;
16271
16272     case SAMPLE_eater:
16273       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16274       break;
16275
16276     case SAMPLE_eater_eat:
16277       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16278       break;
16279
16280     case SAMPLE_alien:
16281       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16282       break;
16283
16284     case SAMPLE_collect:
16285       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16286       break;
16287
16288     case SAMPLE_diamond:
16289       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16290       break;
16291
16292     case SAMPLE_squash:
16293       /* !!! CHECK THIS !!! */
16294 #if 1
16295       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16296 #else
16297       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16298 #endif
16299       break;
16300
16301     case SAMPLE_wonderfall:
16302       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16303       break;
16304
16305     case SAMPLE_drip:
16306       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16307       break;
16308
16309     case SAMPLE_push:
16310       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16311       break;
16312
16313     case SAMPLE_dirt:
16314       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16315       break;
16316
16317     case SAMPLE_acid:
16318       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16319       break;
16320
16321     case SAMPLE_ball:
16322       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16323       break;
16324
16325     case SAMPLE_grow:
16326       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16327       break;
16328
16329     case SAMPLE_wonder:
16330       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16331       break;
16332
16333     case SAMPLE_door:
16334       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16335       break;
16336
16337     case SAMPLE_exit_open:
16338       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16339       break;
16340
16341     case SAMPLE_exit_leave:
16342       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16343       break;
16344
16345     case SAMPLE_dynamite:
16346       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16347       break;
16348
16349     case SAMPLE_tick:
16350       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16351       break;
16352
16353     case SAMPLE_press:
16354       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16355       break;
16356
16357     case SAMPLE_wheel:
16358       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16359       break;
16360
16361     case SAMPLE_boom:
16362       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16363       break;
16364
16365     case SAMPLE_die:
16366       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16367       break;
16368
16369     case SAMPLE_time:
16370       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16371       break;
16372
16373     default:
16374       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16375       break;
16376   }
16377 }
16378
16379 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16380 {
16381   int element = map_element_SP_to_RND(element_sp);
16382   int action = map_action_SP_to_RND(action_sp);
16383   int offset = (setup.sp_show_border_elements ? 0 : 1);
16384   int x = xx - offset;
16385   int y = yy - offset;
16386
16387 #if 0
16388   printf("::: %d -> %d\n", element_sp, action_sp);
16389 #endif
16390
16391   PlayLevelSoundElementAction(x, y, element, action);
16392 }
16393
16394 void RaiseScore(int value)
16395 {
16396   local_player->score += value;
16397
16398 #if 1
16399   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16400
16401   DisplayGameControlValues();
16402 #else
16403   DrawGameValue_Score(local_player->score);
16404 #endif
16405 }
16406
16407 void RaiseScoreElement(int element)
16408 {
16409   switch (element)
16410   {
16411     case EL_EMERALD:
16412     case EL_BD_DIAMOND:
16413     case EL_EMERALD_YELLOW:
16414     case EL_EMERALD_RED:
16415     case EL_EMERALD_PURPLE:
16416     case EL_SP_INFOTRON:
16417       RaiseScore(level.score[SC_EMERALD]);
16418       break;
16419     case EL_DIAMOND:
16420       RaiseScore(level.score[SC_DIAMOND]);
16421       break;
16422     case EL_CRYSTAL:
16423       RaiseScore(level.score[SC_CRYSTAL]);
16424       break;
16425     case EL_PEARL:
16426       RaiseScore(level.score[SC_PEARL]);
16427       break;
16428     case EL_BUG:
16429     case EL_BD_BUTTERFLY:
16430     case EL_SP_ELECTRON:
16431       RaiseScore(level.score[SC_BUG]);
16432       break;
16433     case EL_SPACESHIP:
16434     case EL_BD_FIREFLY:
16435     case EL_SP_SNIKSNAK:
16436       RaiseScore(level.score[SC_SPACESHIP]);
16437       break;
16438     case EL_YAMYAM:
16439     case EL_DARK_YAMYAM:
16440       RaiseScore(level.score[SC_YAMYAM]);
16441       break;
16442     case EL_ROBOT:
16443       RaiseScore(level.score[SC_ROBOT]);
16444       break;
16445     case EL_PACMAN:
16446       RaiseScore(level.score[SC_PACMAN]);
16447       break;
16448     case EL_NUT:
16449       RaiseScore(level.score[SC_NUT]);
16450       break;
16451     case EL_DYNAMITE:
16452     case EL_EM_DYNAMITE:
16453     case EL_SP_DISK_RED:
16454     case EL_DYNABOMB_INCREASE_NUMBER:
16455     case EL_DYNABOMB_INCREASE_SIZE:
16456     case EL_DYNABOMB_INCREASE_POWER:
16457       RaiseScore(level.score[SC_DYNAMITE]);
16458       break;
16459     case EL_SHIELD_NORMAL:
16460     case EL_SHIELD_DEADLY:
16461       RaiseScore(level.score[SC_SHIELD]);
16462       break;
16463     case EL_EXTRA_TIME:
16464       RaiseScore(level.extra_time_score);
16465       break;
16466     case EL_KEY_1:
16467     case EL_KEY_2:
16468     case EL_KEY_3:
16469     case EL_KEY_4:
16470     case EL_EM_KEY_1:
16471     case EL_EM_KEY_2:
16472     case EL_EM_KEY_3:
16473     case EL_EM_KEY_4:
16474     case EL_EMC_KEY_5:
16475     case EL_EMC_KEY_6:
16476     case EL_EMC_KEY_7:
16477     case EL_EMC_KEY_8:
16478     case EL_DC_KEY_WHITE:
16479       RaiseScore(level.score[SC_KEY]);
16480       break;
16481     default:
16482       RaiseScore(element_info[element].collect_score);
16483       break;
16484   }
16485 }
16486
16487 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16488 {
16489   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16490   {
16491 #if 1
16492     /* closing door required in case of envelope style request dialogs */
16493     if (!skip_request)
16494       CloseDoor(DOOR_CLOSE_1);
16495 #endif
16496
16497 #if defined(NETWORK_AVALIABLE)
16498     if (options.network)
16499       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16500     else
16501 #endif
16502     {
16503       if (quick_quit)
16504       {
16505 #if 1
16506
16507 #if 1
16508         FadeSkipNextFadeIn();
16509 #else
16510         fading = fading_none;
16511 #endif
16512
16513 #else
16514         OpenDoor(DOOR_CLOSE_1);
16515 #endif
16516
16517         game_status = GAME_MODE_MAIN;
16518
16519 #if 1
16520         DrawAndFadeInMainMenu(REDRAW_FIELD);
16521 #else
16522         DrawMainMenu();
16523 #endif
16524       }
16525       else
16526       {
16527 #if 0
16528         FadeOut(REDRAW_FIELD);
16529 #endif
16530
16531         game_status = GAME_MODE_MAIN;
16532
16533         DrawAndFadeInMainMenu(REDRAW_FIELD);
16534       }
16535     }
16536   }
16537   else          /* continue playing the game */
16538   {
16539     if (tape.playing && tape.deactivate_display)
16540       TapeDeactivateDisplayOff(TRUE);
16541
16542     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16543
16544     if (tape.playing && tape.deactivate_display)
16545       TapeDeactivateDisplayOn();
16546   }
16547 }
16548
16549 void RequestQuitGame(boolean ask_if_really_quit)
16550 {
16551   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16552   boolean skip_request = AllPlayersGone || quick_quit;
16553
16554   RequestQuitGameExt(skip_request, quick_quit,
16555                      "Do you really want to quit the game?");
16556 }
16557
16558
16559 /* ------------------------------------------------------------------------- */
16560 /* random generator functions                                                */
16561 /* ------------------------------------------------------------------------- */
16562
16563 unsigned int InitEngineRandom_RND(int seed)
16564 {
16565   game.num_random_calls = 0;
16566
16567 #if 0
16568   unsigned int rnd_seed = InitEngineRandom(seed);
16569
16570   printf("::: START RND: %d\n", rnd_seed);
16571
16572   return rnd_seed;
16573 #else
16574
16575   return InitEngineRandom(seed);
16576
16577 #endif
16578
16579 }
16580
16581 unsigned int RND(int max)
16582 {
16583   if (max > 0)
16584   {
16585     game.num_random_calls++;
16586
16587     return GetEngineRandom(max);
16588   }
16589
16590   return 0;
16591 }
16592
16593
16594 /* ------------------------------------------------------------------------- */
16595 /* game engine snapshot handling functions                                   */
16596 /* ------------------------------------------------------------------------- */
16597
16598 struct EngineSnapshotInfo
16599 {
16600   /* runtime values for custom element collect score */
16601   int collect_score[NUM_CUSTOM_ELEMENTS];
16602
16603   /* runtime values for group element choice position */
16604   int choice_pos[NUM_GROUP_ELEMENTS];
16605
16606   /* runtime values for belt position animations */
16607   int belt_graphic[4][NUM_BELT_PARTS];
16608   int belt_anim_mode[4][NUM_BELT_PARTS];
16609 };
16610
16611 static struct EngineSnapshotInfo engine_snapshot_rnd;
16612 static char *snapshot_level_identifier = NULL;
16613 static int snapshot_level_nr = -1;
16614
16615 static void SaveEngineSnapshotValues_RND()
16616 {
16617   static int belt_base_active_element[4] =
16618   {
16619     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16620     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16621     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16622     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16623   };
16624   int i, j;
16625
16626   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16627   {
16628     int element = EL_CUSTOM_START + i;
16629
16630     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16631   }
16632
16633   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16634   {
16635     int element = EL_GROUP_START + i;
16636
16637     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16638   }
16639
16640   for (i = 0; i < 4; i++)
16641   {
16642     for (j = 0; j < NUM_BELT_PARTS; j++)
16643     {
16644       int element = belt_base_active_element[i] + j;
16645       int graphic = el2img(element);
16646       int anim_mode = graphic_info[graphic].anim_mode;
16647
16648       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16649       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16650     }
16651   }
16652 }
16653
16654 static void LoadEngineSnapshotValues_RND()
16655 {
16656   unsigned int num_random_calls = game.num_random_calls;
16657   int i, j;
16658
16659   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16660   {
16661     int element = EL_CUSTOM_START + i;
16662
16663     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16664   }
16665
16666   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16667   {
16668     int element = EL_GROUP_START + i;
16669
16670     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16671   }
16672
16673   for (i = 0; i < 4; i++)
16674   {
16675     for (j = 0; j < NUM_BELT_PARTS; j++)
16676     {
16677       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16678       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16679
16680       graphic_info[graphic].anim_mode = anim_mode;
16681     }
16682   }
16683
16684   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16685   {
16686     InitRND(tape.random_seed);
16687     for (i = 0; i < num_random_calls; i++)
16688       RND(1);
16689   }
16690
16691   if (game.num_random_calls != num_random_calls)
16692   {
16693     Error(ERR_INFO, "number of random calls out of sync");
16694     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16695     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16696     Error(ERR_EXIT, "this should not happen -- please debug");
16697   }
16698 }
16699
16700 void SaveEngineSnapshot()
16701 {
16702   /* do not save snapshots from editor */
16703   if (level_editor_test_game)
16704     return;
16705
16706   /* free previous snapshot buffers, if needed */
16707   FreeEngineSnapshotBuffers();
16708
16709   /* copy some special values to a structure better suited for the snapshot */
16710
16711   SaveEngineSnapshotValues_RND();
16712   SaveEngineSnapshotValues_EM();
16713   SaveEngineSnapshotValues_SP();
16714
16715   /* save values stored in special snapshot structure */
16716
16717   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16718   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16719   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16720
16721   /* save further RND engine values */
16722
16723   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16724   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16725   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16726
16727   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16728   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16729   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16730   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16731
16732   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16733   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16734   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16735   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16736   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16737
16738   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16739   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16740   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16741
16742   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16743
16744   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16745
16746   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16747   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16748
16749   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16750   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16751   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16752   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16753   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16754   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16755   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16756   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16757   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16758   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16759   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16760   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16761   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16762   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16763   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16764   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16765   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16766   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16767
16768   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16769   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16770
16771   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16772   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16773   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16774
16775   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16776   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16777
16778   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16779   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16780   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16781   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16782   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16783
16784   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16785   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16786
16787   /* save level identification information */
16788
16789   setString(&snapshot_level_identifier, leveldir_current->identifier);
16790   snapshot_level_nr = level_nr;
16791
16792 #if 0
16793   ListNode *node = engine_snapshot_list_rnd;
16794   int num_bytes = 0;
16795
16796   while (node != NULL)
16797   {
16798     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16799
16800     node = node->next;
16801   }
16802
16803   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16804 #endif
16805 }
16806
16807 void LoadEngineSnapshot()
16808 {
16809   /* restore generically stored snapshot buffers */
16810
16811   LoadEngineSnapshotBuffers();
16812
16813   /* restore special values from snapshot structure */
16814
16815   LoadEngineSnapshotValues_RND();
16816   LoadEngineSnapshotValues_EM();
16817   LoadEngineSnapshotValues_SP();
16818 }
16819
16820 boolean CheckEngineSnapshot()
16821 {
16822   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16823           snapshot_level_nr == level_nr);
16824 }
16825
16826
16827 /* ---------- new game button stuff ---------------------------------------- */
16828
16829 static struct
16830 {
16831   int graphic;
16832   struct Rect *pos;
16833   int gadget_id;
16834   char *infotext;
16835 } gamebutton_info[NUM_GAME_BUTTONS] =
16836 {
16837   {
16838     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16839     GAME_CTRL_ID_STOP,                  "stop game"
16840   },
16841   {
16842     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16843     GAME_CTRL_ID_PAUSE,                 "pause game"
16844   },
16845   {
16846     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16847     GAME_CTRL_ID_PLAY,                  "play game"
16848   },
16849   {
16850     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16851     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16852   },
16853   {
16854     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16855     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16856   },
16857   {
16858     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16859     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16860   },
16861   {
16862     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
16863     GAME_CTRL_ID_SAVE,                  "save game"
16864   },
16865   {
16866     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
16867     GAME_CTRL_ID_LOAD,                  "load game"
16868   }
16869 };
16870
16871 void CreateGameButtons()
16872 {
16873   int i;
16874
16875   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16876   {
16877     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16878     struct Rect *pos = gamebutton_info[i].pos;
16879     struct GadgetInfo *gi;
16880     int button_type;
16881     boolean checked;
16882     unsigned int event_mask;
16883     int base_x = (tape.show_game_buttons ? VX : DX);
16884     int base_y = (tape.show_game_buttons ? VY : DY);
16885     int gd_x   = gfx->src_x;
16886     int gd_y   = gfx->src_y;
16887     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16888     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16889     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16890     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16891     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16892     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16893     int id = i;
16894
16895     if (gfx->bitmap == NULL)
16896     {
16897       game_gadget[id] = NULL;
16898
16899       continue;
16900     }
16901
16902     if (id == GAME_CTRL_ID_STOP ||
16903         id == GAME_CTRL_ID_PAUSE ||
16904         id == GAME_CTRL_ID_PLAY ||
16905         id == GAME_CTRL_ID_SAVE ||
16906         id == GAME_CTRL_ID_LOAD)
16907     {
16908       button_type = GD_TYPE_NORMAL_BUTTON;
16909       checked = FALSE;
16910       event_mask = GD_EVENT_RELEASED;
16911     }
16912     else
16913     {
16914       button_type = GD_TYPE_CHECK_BUTTON;
16915       checked =
16916         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16917          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16918          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16919       event_mask = GD_EVENT_PRESSED;
16920     }
16921
16922     gi = CreateGadget(GDI_CUSTOM_ID, id,
16923                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16924                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
16925                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
16926                       GDI_WIDTH, gfx->width,
16927                       GDI_HEIGHT, gfx->height,
16928                       GDI_TYPE, button_type,
16929                       GDI_STATE, GD_BUTTON_UNPRESSED,
16930                       GDI_CHECKED, checked,
16931                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16932                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16933                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16934                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16935                       GDI_DIRECT_DRAW, FALSE,
16936                       GDI_EVENT_MASK, event_mask,
16937                       GDI_CALLBACK_ACTION, HandleGameButtons,
16938                       GDI_END);
16939
16940     if (gi == NULL)
16941       Error(ERR_EXIT, "cannot create gadget");
16942
16943     game_gadget[id] = gi;
16944   }
16945 }
16946
16947 void FreeGameButtons()
16948 {
16949   int i;
16950
16951   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16952     FreeGadget(game_gadget[i]);
16953 }
16954
16955 void MapGameButtons()
16956 {
16957   int i;
16958
16959   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16960     MapGadget(game_gadget[i]);
16961 }
16962
16963 void UnmapGameButtons()
16964 {
16965   int i;
16966
16967   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16968     UnmapGadget(game_gadget[i]);
16969 }
16970
16971 void RedrawGameButtons()
16972 {
16973   int i;
16974
16975   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16976     RedrawGadget(game_gadget[i]);
16977 }
16978
16979 static void HandleGameButtonsExt(int id)
16980 {
16981   boolean handle_game_buttons =
16982     (game_status == GAME_MODE_PLAYING ||
16983      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16984
16985   if (!handle_game_buttons)
16986     return;
16987
16988   switch (id)
16989   {
16990     case GAME_CTRL_ID_STOP:
16991       if (game_status == GAME_MODE_MAIN)
16992         break;
16993
16994       if (tape.playing)
16995         TapeStop();
16996       else
16997         RequestQuitGame(TRUE);
16998
16999       break;
17000
17001     case GAME_CTRL_ID_PAUSE:
17002       if (options.network && game_status == GAME_MODE_PLAYING)
17003       {
17004 #if defined(NETWORK_AVALIABLE)
17005         if (tape.pausing)
17006           SendToServer_ContinuePlaying();
17007         else
17008           SendToServer_PausePlaying();
17009 #endif
17010       }
17011       else
17012         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17013       break;
17014
17015     case GAME_CTRL_ID_PLAY:
17016       if (game_status == GAME_MODE_MAIN)
17017       {
17018         StartGameActions(options.network, setup.autorecord, level.random_seed);
17019       }
17020       else if (tape.pausing)
17021       {
17022 #if defined(NETWORK_AVALIABLE)
17023         if (options.network)
17024           SendToServer_ContinuePlaying();
17025         else
17026 #endif
17027         {
17028           tape.pausing = FALSE;
17029           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
17030         }
17031       }
17032       break;
17033
17034     case SOUND_CTRL_ID_MUSIC:
17035       if (setup.sound_music)
17036       { 
17037         setup.sound_music = FALSE;
17038
17039         FadeMusic();
17040       }
17041       else if (audio.music_available)
17042       { 
17043         setup.sound = setup.sound_music = TRUE;
17044
17045         SetAudioMode(setup.sound);
17046
17047         PlayLevelMusic();
17048       }
17049       break;
17050
17051     case SOUND_CTRL_ID_LOOPS:
17052       if (setup.sound_loops)
17053         setup.sound_loops = FALSE;
17054       else if (audio.loops_available)
17055       {
17056         setup.sound = setup.sound_loops = TRUE;
17057
17058         SetAudioMode(setup.sound);
17059       }
17060       break;
17061
17062     case SOUND_CTRL_ID_SIMPLE:
17063       if (setup.sound_simple)
17064         setup.sound_simple = FALSE;
17065       else if (audio.sound_available)
17066       {
17067         setup.sound = setup.sound_simple = TRUE;
17068
17069         SetAudioMode(setup.sound);
17070       }
17071       break;
17072
17073     case GAME_CTRL_ID_SAVE:
17074       TapeQuickSave();
17075       break;
17076
17077     case GAME_CTRL_ID_LOAD:
17078       TapeQuickLoad();
17079       break;
17080
17081     default:
17082       break;
17083   }
17084 }
17085
17086 static void HandleGameButtons(struct GadgetInfo *gi)
17087 {
17088   HandleGameButtonsExt(gi->custom_id);
17089 }
17090
17091 void HandleSoundButtonKeys(Key key)
17092 {
17093 #if 1
17094   if (key == setup.shortcut.sound_simple)
17095     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17096   else if (key == setup.shortcut.sound_loops)
17097     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17098   else if (key == setup.shortcut.sound_music)
17099     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17100 #else
17101   if (key == setup.shortcut.sound_simple)
17102     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17103   else if (key == setup.shortcut.sound_loops)
17104     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17105   else if (key == setup.shortcut.sound_music)
17106     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17107 #endif
17108 }