rnd-20140218-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
1018 #define NUM_GAME_BUTTONS                6
1019
1020
1021 /* forward declaration for internal use */
1022
1023 static void CreateField(int, int, int);
1024
1025 static void ResetGfxAnimation(int, int);
1026
1027 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1028 static void AdvanceFrameAndPlayerCounters(int);
1029
1030 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1031 static boolean MovePlayer(struct PlayerInfo *, int, int);
1032 static void ScrollPlayer(struct PlayerInfo *, int);
1033 static void ScrollScreen(struct PlayerInfo *, int);
1034
1035 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1036 static boolean DigFieldByCE(int, int, int);
1037 static boolean SnapField(struct PlayerInfo *, int, int);
1038 static boolean DropElement(struct PlayerInfo *);
1039
1040 static void InitBeltMovement(void);
1041 static void CloseAllOpenTimegates(void);
1042 static void CheckGravityMovement(struct PlayerInfo *);
1043 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1044 static void KillPlayerUnlessEnemyProtected(int, int);
1045 static void KillPlayerUnlessExplosionProtected(int, int);
1046
1047 static void TestIfPlayerTouchesCustomElement(int, int);
1048 static void TestIfElementTouchesCustomElement(int, int);
1049 static void TestIfElementHitsCustomElement(int, int, int);
1050 #if 0
1051 static void TestIfElementSmashesCustomElement(int, int, int);
1052 #endif
1053
1054 static void HandleElementChange(int, int, int);
1055 static void ExecuteCustomElementAction(int, int, int, int);
1056 static boolean ChangeElement(int, int, int, int);
1057
1058 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1059 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1060         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1061 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1062         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1063 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1064         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1065 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1066         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1067
1068 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1069 #define CheckElementChange(x, y, e, te, ev)                             \
1070         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1071 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1072         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1073 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1074         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1075
1076 static void PlayLevelSound(int, int, int);
1077 static void PlayLevelSoundNearest(int, int, int);
1078 static void PlayLevelSoundAction(int, int, int);
1079 static void PlayLevelSoundElementAction(int, int, int, int);
1080 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1081 static void PlayLevelSoundActionIfLoop(int, int, int);
1082 static void StopLevelSoundActionIfLoop(int, int, int);
1083 static void PlayLevelMusic();
1084
1085 static void MapGameButtons();
1086 static void HandleGameButtons(struct GadgetInfo *);
1087
1088 int AmoebeNachbarNr(int, int);
1089 void AmoebeUmwandeln(int, int);
1090 void ContinueMoving(int, int);
1091 void Bang(int, int);
1092 void InitMovDir(int, int);
1093 void InitAmoebaNr(int, int);
1094 int NewHiScore(void);
1095
1096 void TestIfGoodThingHitsBadThing(int, int, int);
1097 void TestIfBadThingHitsGoodThing(int, int, int);
1098 void TestIfPlayerTouchesBadThing(int, int);
1099 void TestIfPlayerRunsIntoBadThing(int, int, int);
1100 void TestIfBadThingTouchesPlayer(int, int);
1101 void TestIfBadThingRunsIntoPlayer(int, int, int);
1102 void TestIfFriendTouchesBadThing(int, int);
1103 void TestIfBadThingTouchesFriend(int, int);
1104 void TestIfBadThingTouchesOtherBadThing(int, int);
1105 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1106
1107 void KillPlayer(struct PlayerInfo *);
1108 void BuryPlayer(struct PlayerInfo *);
1109 void RemovePlayer(struct PlayerInfo *);
1110
1111 static int getInvisibleActiveFromInvisibleElement(int);
1112 static int getInvisibleFromInvisibleActiveElement(int);
1113
1114 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1115
1116 /* for detection of endless loops, caused by custom element programming */
1117 /* (using maximal playfield width x 10 is just a rough approximation) */
1118 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1119
1120 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1121 {                                                                       \
1122   if (recursion_loop_detected)                                          \
1123     return (rc);                                                        \
1124                                                                         \
1125   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1126   {                                                                     \
1127     recursion_loop_detected = TRUE;                                     \
1128     recursion_loop_element = (e);                                       \
1129   }                                                                     \
1130                                                                         \
1131   recursion_loop_depth++;                                               \
1132 }
1133
1134 #define RECURSION_LOOP_DETECTION_END()                                  \
1135 {                                                                       \
1136   recursion_loop_depth--;                                               \
1137 }
1138
1139 static int recursion_loop_depth;
1140 static boolean recursion_loop_detected;
1141 static boolean recursion_loop_element;
1142
1143 static int map_player_action[MAX_PLAYERS];
1144
1145
1146 /* ------------------------------------------------------------------------- */
1147 /* definition of elements that automatically change to other elements after  */
1148 /* a specified time, eventually calling a function when changing             */
1149 /* ------------------------------------------------------------------------- */
1150
1151 /* forward declaration for changer functions */
1152 static void InitBuggyBase(int, int);
1153 static void WarnBuggyBase(int, int);
1154
1155 static void InitTrap(int, int);
1156 static void ActivateTrap(int, int);
1157 static void ChangeActiveTrap(int, int);
1158
1159 static void InitRobotWheel(int, int);
1160 static void RunRobotWheel(int, int);
1161 static void StopRobotWheel(int, int);
1162
1163 static void InitTimegateWheel(int, int);
1164 static void RunTimegateWheel(int, int);
1165
1166 static void InitMagicBallDelay(int, int);
1167 static void ActivateMagicBall(int, int);
1168
1169 struct ChangingElementInfo
1170 {
1171   int element;
1172   int target_element;
1173   int change_delay;
1174   void (*pre_change_function)(int x, int y);
1175   void (*change_function)(int x, int y);
1176   void (*post_change_function)(int x, int y);
1177 };
1178
1179 static struct ChangingElementInfo change_delay_list[] =
1180 {
1181   {
1182     EL_NUT_BREAKING,
1183     EL_EMERALD,
1184     6,
1185     NULL,
1186     NULL,
1187     NULL
1188   },
1189   {
1190     EL_PEARL_BREAKING,
1191     EL_EMPTY,
1192     8,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_EXIT_OPENING,
1199     EL_EXIT_OPEN,
1200     29,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EXIT_CLOSING,
1207     EL_EXIT_CLOSED,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_STEEL_EXIT_OPENING,
1215     EL_STEEL_EXIT_OPEN,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_STEEL_EXIT_CLOSING,
1223     EL_STEEL_EXIT_CLOSED,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_EM_EXIT_OPENING,
1231     EL_EM_EXIT_OPEN,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_EM_EXIT_CLOSING,
1239 #if 1
1240     EL_EMPTY,
1241 #else
1242     EL_EM_EXIT_CLOSED,
1243 #endif
1244     29,
1245     NULL,
1246     NULL,
1247     NULL
1248   },
1249   {
1250     EL_EM_STEEL_EXIT_OPENING,
1251     EL_EM_STEEL_EXIT_OPEN,
1252     29,
1253     NULL,
1254     NULL,
1255     NULL
1256   },
1257   {
1258     EL_EM_STEEL_EXIT_CLOSING,
1259 #if 1
1260     EL_STEELWALL,
1261 #else
1262     EL_EM_STEEL_EXIT_CLOSED,
1263 #endif
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_OPENING,
1271     EL_SP_EXIT_OPEN,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SP_EXIT_CLOSING,
1279     EL_SP_EXIT_CLOSED,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_OPENING,
1287     EL_SWITCHGATE_OPEN,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_SWITCHGATE_CLOSING,
1295     EL_SWITCHGATE_CLOSED,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_OPENING,
1303     EL_TIMEGATE_OPEN,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309   {
1310     EL_TIMEGATE_CLOSING,
1311     EL_TIMEGATE_CLOSED,
1312     29,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317
1318   {
1319     EL_ACID_SPLASH_LEFT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_ACID_SPLASH_RIGHT,
1328     EL_EMPTY,
1329     8,
1330     NULL,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE,
1336     EL_SP_BUGGY_BASE_ACTIVATING,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVATING,
1344     EL_SP_BUGGY_BASE_ACTIVE,
1345     0,
1346     InitBuggyBase,
1347     NULL,
1348     NULL
1349   },
1350   {
1351     EL_SP_BUGGY_BASE_ACTIVE,
1352     EL_SP_BUGGY_BASE,
1353     0,
1354     InitBuggyBase,
1355     WarnBuggyBase,
1356     NULL
1357   },
1358   {
1359     EL_TRAP,
1360     EL_TRAP_ACTIVE,
1361     0,
1362     InitTrap,
1363     NULL,
1364     ActivateTrap
1365   },
1366   {
1367     EL_TRAP_ACTIVE,
1368     EL_TRAP,
1369     31,
1370     NULL,
1371     ChangeActiveTrap,
1372     NULL
1373   },
1374   {
1375     EL_ROBOT_WHEEL_ACTIVE,
1376     EL_ROBOT_WHEEL,
1377     0,
1378     InitRobotWheel,
1379     RunRobotWheel,
1380     StopRobotWheel
1381   },
1382   {
1383     EL_TIMEGATE_SWITCH_ACTIVE,
1384     EL_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1392     EL_DC_TIMEGATE_SWITCH,
1393     0,
1394     InitTimegateWheel,
1395     RunTimegateWheel,
1396     NULL
1397   },
1398   {
1399     EL_EMC_MAGIC_BALL_ACTIVE,
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     0,
1402     InitMagicBallDelay,
1403     NULL,
1404     ActivateMagicBall
1405   },
1406   {
1407     EL_EMC_SPRING_BUMPER_ACTIVE,
1408     EL_EMC_SPRING_BUMPER,
1409     8,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_SHRINKING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL
1421   },
1422   {
1423     EL_DIAGONAL_GROWING,
1424     EL_UNDEFINED,
1425     0,
1426     NULL,
1427     NULL,
1428     NULL,
1429   },
1430
1431   {
1432     EL_UNDEFINED,
1433     EL_UNDEFINED,
1434     -1,
1435     NULL,
1436     NULL,
1437     NULL
1438   }
1439 };
1440
1441 struct
1442 {
1443   int element;
1444   int push_delay_fixed, push_delay_random;
1445 }
1446 push_delay_list[] =
1447 {
1448   { EL_SPRING,                  0, 0 },
1449   { EL_BALLOON,                 0, 0 },
1450
1451   { EL_SOKOBAN_OBJECT,          2, 0 },
1452   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1453   { EL_SATELLITE,               2, 0 },
1454   { EL_SP_DISK_YELLOW,          2, 0 },
1455
1456   { EL_UNDEFINED,               0, 0 },
1457 };
1458
1459 struct
1460 {
1461   int element;
1462   int move_stepsize;
1463 }
1464 move_stepsize_list[] =
1465 {
1466   { EL_AMOEBA_DROP,             2 },
1467   { EL_AMOEBA_DROPPING,         2 },
1468   { EL_QUICKSAND_FILLING,       1 },
1469   { EL_QUICKSAND_EMPTYING,      1 },
1470   { EL_QUICKSAND_FAST_FILLING,  2 },
1471   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1472   { EL_MAGIC_WALL_FILLING,      2 },
1473   { EL_MAGIC_WALL_EMPTYING,     2 },
1474   { EL_BD_MAGIC_WALL_FILLING,   2 },
1475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1476   { EL_DC_MAGIC_WALL_FILLING,   2 },
1477   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1478
1479   { EL_UNDEFINED,               0 },
1480 };
1481
1482 struct
1483 {
1484   int element;
1485   int count;
1486 }
1487 collect_count_list[] =
1488 {
1489   { EL_EMERALD,                 1 },
1490   { EL_BD_DIAMOND,              1 },
1491   { EL_EMERALD_YELLOW,          1 },
1492   { EL_EMERALD_RED,             1 },
1493   { EL_EMERALD_PURPLE,          1 },
1494   { EL_DIAMOND,                 3 },
1495   { EL_SP_INFOTRON,             1 },
1496   { EL_PEARL,                   5 },
1497   { EL_CRYSTAL,                 8 },
1498
1499   { EL_UNDEFINED,               0 },
1500 };
1501
1502 struct
1503 {
1504   int element;
1505   int direction;
1506 }
1507 access_direction_list[] =
1508 {
1509   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1512   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1513   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1514   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1515   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1516   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1517   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1518   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1519   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1520
1521   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1522   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1523   { EL_SP_PORT_UP,                                                   MV_DOWN },
1524   { EL_SP_PORT_DOWN,                                         MV_UP           },
1525   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1526   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1527   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1529   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1530   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1531   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1532   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1533   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1534   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1535   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1536   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1537   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1538   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1539   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1540
1541   { EL_UNDEFINED,                       MV_NONE                              }
1542 };
1543
1544 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1545
1546 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1547 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1548 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1549                                  IS_JUST_CHANGING(x, y))
1550
1551 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1552
1553 /* static variables for playfield scan mode (scanning forward or backward) */
1554 static int playfield_scan_start_x = 0;
1555 static int playfield_scan_start_y = 0;
1556 static int playfield_scan_delta_x = 1;
1557 static int playfield_scan_delta_y = 1;
1558
1559 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1560                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1561                                      (y) += playfield_scan_delta_y)     \
1562                                 for ((x) = playfield_scan_start_x;      \
1563                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1564                                      (x) += playfield_scan_delta_x)
1565
1566 #ifdef DEBUG
1567 void DEBUG_SetMaximumDynamite()
1568 {
1569   int i;
1570
1571   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1572     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1573       local_player->inventory_element[local_player->inventory_size++] =
1574         EL_DYNAMITE;
1575 }
1576 #endif
1577
1578 static void InitPlayfieldScanModeVars()
1579 {
1580   if (game.use_reverse_scan_direction)
1581   {
1582     playfield_scan_start_x = lev_fieldx - 1;
1583     playfield_scan_start_y = lev_fieldy - 1;
1584
1585     playfield_scan_delta_x = -1;
1586     playfield_scan_delta_y = -1;
1587   }
1588   else
1589   {
1590     playfield_scan_start_x = 0;
1591     playfield_scan_start_y = 0;
1592
1593     playfield_scan_delta_x = 1;
1594     playfield_scan_delta_y = 1;
1595   }
1596 }
1597
1598 static void InitPlayfieldScanMode(int mode)
1599 {
1600   game.use_reverse_scan_direction =
1601     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1602
1603   InitPlayfieldScanModeVars();
1604 }
1605
1606 static int get_move_delay_from_stepsize(int move_stepsize)
1607 {
1608   move_stepsize =
1609     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1610
1611   /* make sure that stepsize value is always a power of 2 */
1612   move_stepsize = (1 << log_2(move_stepsize));
1613
1614   return TILEX / move_stepsize;
1615 }
1616
1617 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1618                                boolean init_game)
1619 {
1620   int player_nr = player->index_nr;
1621   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1622   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1623
1624   /* do no immediately change move delay -- the player might just be moving */
1625   player->move_delay_value_next = move_delay;
1626
1627   /* information if player can move must be set separately */
1628   player->cannot_move = cannot_move;
1629
1630   if (init_game)
1631   {
1632     player->move_delay       = game.initial_move_delay[player_nr];
1633     player->move_delay_value = game.initial_move_delay_value[player_nr];
1634
1635     player->move_delay_value_next = -1;
1636
1637     player->move_delay_reset_counter = 0;
1638   }
1639 }
1640
1641 void GetPlayerConfig()
1642 {
1643   GameFrameDelay = setup.game_frame_delay;
1644
1645   if (!audio.sound_available)
1646     setup.sound_simple = FALSE;
1647
1648   if (!audio.loops_available)
1649     setup.sound_loops = FALSE;
1650
1651   if (!audio.music_available)
1652     setup.sound_music = FALSE;
1653
1654   if (!video.fullscreen_available)
1655     setup.fullscreen = FALSE;
1656
1657   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1658
1659   SetAudioMode(setup.sound);
1660   InitJoysticks();
1661 }
1662
1663 int GetElementFromGroupElement(int element)
1664 {
1665   if (IS_GROUP_ELEMENT(element))
1666   {
1667     struct ElementGroupInfo *group = element_info[element].group;
1668     int last_anim_random_frame = gfx.anim_random_frame;
1669     int element_pos;
1670
1671     if (group->choice_mode == ANIM_RANDOM)
1672       gfx.anim_random_frame = RND(group->num_elements_resolved);
1673
1674     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1675                                     group->choice_mode, 0,
1676                                     group->choice_pos);
1677
1678     if (group->choice_mode == ANIM_RANDOM)
1679       gfx.anim_random_frame = last_anim_random_frame;
1680
1681     group->choice_pos++;
1682
1683     element = group->element_resolved[element_pos];
1684   }
1685
1686   return element;
1687 }
1688
1689 static void InitPlayerField(int x, int y, int element, boolean init_game)
1690 {
1691   if (element == EL_SP_MURPHY)
1692   {
1693     if (init_game)
1694     {
1695       if (stored_player[0].present)
1696       {
1697         Feld[x][y] = EL_SP_MURPHY_CLONE;
1698
1699         return;
1700       }
1701       else
1702       {
1703         stored_player[0].initial_element = element;
1704         stored_player[0].use_murphy = TRUE;
1705
1706         if (!level.use_artwork_element[0])
1707           stored_player[0].artwork_element = EL_SP_MURPHY;
1708       }
1709
1710       Feld[x][y] = EL_PLAYER_1;
1711     }
1712   }
1713
1714   if (init_game)
1715   {
1716     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1717     int jx = player->jx, jy = player->jy;
1718
1719     player->present = TRUE;
1720
1721     player->block_last_field = (element == EL_SP_MURPHY ?
1722                                 level.sp_block_last_field :
1723                                 level.block_last_field);
1724
1725     /* ---------- initialize player's last field block delay --------------- */
1726
1727     /* always start with reliable default value (no adjustment needed) */
1728     player->block_delay_adjustment = 0;
1729
1730     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1731     if (player->block_last_field && element == EL_SP_MURPHY)
1732       player->block_delay_adjustment = 1;
1733
1734     /* special case 2: in game engines before 3.1.1, blocking was different */
1735     if (game.use_block_last_field_bug)
1736       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1737
1738     if (!options.network || player->connected)
1739     {
1740       player->active = TRUE;
1741
1742       /* remove potentially duplicate players */
1743       if (StorePlayer[jx][jy] == Feld[x][y])
1744         StorePlayer[jx][jy] = 0;
1745
1746       StorePlayer[x][y] = Feld[x][y];
1747
1748 #if DEBUG_INIT_PLAYER
1749       if (options.debug)
1750       {
1751         printf("- player element %d activated", player->element_nr);
1752         printf(" (local player is %d and currently %s)\n",
1753                local_player->element_nr,
1754                local_player->active ? "active" : "not active");
1755       }
1756     }
1757 #endif
1758
1759     Feld[x][y] = EL_EMPTY;
1760
1761     player->jx = player->last_jx = x;
1762     player->jy = player->last_jy = y;
1763   }
1764
1765 #if USE_PLAYER_REANIMATION
1766   if (!init_game)
1767   {
1768     int player_nr = GET_PLAYER_NR(element);
1769     struct PlayerInfo *player = &stored_player[player_nr];
1770
1771     if (player->active && player->killed)
1772       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1773   }
1774 #endif
1775 }
1776
1777 static void InitField(int x, int y, boolean init_game)
1778 {
1779   int element = Feld[x][y];
1780
1781   switch (element)
1782   {
1783     case EL_SP_MURPHY:
1784     case EL_PLAYER_1:
1785     case EL_PLAYER_2:
1786     case EL_PLAYER_3:
1787     case EL_PLAYER_4:
1788       InitPlayerField(x, y, element, init_game);
1789       break;
1790
1791     case EL_SOKOBAN_FIELD_PLAYER:
1792       element = Feld[x][y] = EL_PLAYER_1;
1793       InitField(x, y, init_game);
1794
1795       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1796       InitField(x, y, init_game);
1797       break;
1798
1799     case EL_SOKOBAN_FIELD_EMPTY:
1800       local_player->sokobanfields_still_needed++;
1801       break;
1802
1803     case EL_STONEBLOCK:
1804       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1805         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1806       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1807         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1808       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1809         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1810       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1811         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1812       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1813         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1814       break;
1815
1816     case EL_BUG:
1817     case EL_BUG_RIGHT:
1818     case EL_BUG_UP:
1819     case EL_BUG_LEFT:
1820     case EL_BUG_DOWN:
1821     case EL_SPACESHIP:
1822     case EL_SPACESHIP_RIGHT:
1823     case EL_SPACESHIP_UP:
1824     case EL_SPACESHIP_LEFT:
1825     case EL_SPACESHIP_DOWN:
1826     case EL_BD_BUTTERFLY:
1827     case EL_BD_BUTTERFLY_RIGHT:
1828     case EL_BD_BUTTERFLY_UP:
1829     case EL_BD_BUTTERFLY_LEFT:
1830     case EL_BD_BUTTERFLY_DOWN:
1831     case EL_BD_FIREFLY:
1832     case EL_BD_FIREFLY_RIGHT:
1833     case EL_BD_FIREFLY_UP:
1834     case EL_BD_FIREFLY_LEFT:
1835     case EL_BD_FIREFLY_DOWN:
1836     case EL_PACMAN_RIGHT:
1837     case EL_PACMAN_UP:
1838     case EL_PACMAN_LEFT:
1839     case EL_PACMAN_DOWN:
1840     case EL_YAMYAM:
1841     case EL_YAMYAM_LEFT:
1842     case EL_YAMYAM_RIGHT:
1843     case EL_YAMYAM_UP:
1844     case EL_YAMYAM_DOWN:
1845     case EL_DARK_YAMYAM:
1846     case EL_ROBOT:
1847     case EL_PACMAN:
1848     case EL_SP_SNIKSNAK:
1849     case EL_SP_ELECTRON:
1850     case EL_MOLE:
1851     case EL_MOLE_LEFT:
1852     case EL_MOLE_RIGHT:
1853     case EL_MOLE_UP:
1854     case EL_MOLE_DOWN:
1855       InitMovDir(x, y);
1856       break;
1857
1858     case EL_AMOEBA_FULL:
1859     case EL_BD_AMOEBA:
1860       InitAmoebaNr(x, y);
1861       break;
1862
1863     case EL_AMOEBA_DROP:
1864       if (y == lev_fieldy - 1)
1865       {
1866         Feld[x][y] = EL_AMOEBA_GROWING;
1867         Store[x][y] = EL_AMOEBA_WET;
1868       }
1869       break;
1870
1871     case EL_DYNAMITE_ACTIVE:
1872     case EL_SP_DISK_RED_ACTIVE:
1873     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1874     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1875     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1876     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1877       MovDelay[x][y] = 96;
1878       break;
1879
1880     case EL_EM_DYNAMITE_ACTIVE:
1881       MovDelay[x][y] = 32;
1882       break;
1883
1884     case EL_LAMP:
1885       local_player->lights_still_needed++;
1886       break;
1887
1888     case EL_PENGUIN:
1889       local_player->friends_still_needed++;
1890       break;
1891
1892     case EL_PIG:
1893     case EL_DRAGON:
1894       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1895       break;
1896
1897     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1898     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1899     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1900     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1901     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1902     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1903     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1904     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1905     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1906     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1907     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1908     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1909       if (init_game)
1910       {
1911         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1912         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1913         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1914
1915         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1916         {
1917           game.belt_dir[belt_nr] = belt_dir;
1918           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1919         }
1920         else    /* more than one switch -- set it like the first switch */
1921         {
1922           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1923         }
1924       }
1925       break;
1926
1927 #if !USE_BOTH_SWITCHGATE_SWITCHES
1928     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1929       if (init_game)
1930         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1931       break;
1932
1933     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1934       if (init_game)
1935         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1936       break;
1937 #endif
1938
1939     case EL_LIGHT_SWITCH_ACTIVE:
1940       if (init_game)
1941         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1942       break;
1943
1944     case EL_INVISIBLE_STEELWALL:
1945     case EL_INVISIBLE_WALL:
1946     case EL_INVISIBLE_SAND:
1947       if (game.light_time_left > 0 ||
1948           game.lenses_time_left > 0)
1949         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1950       break;
1951
1952     case EL_EMC_MAGIC_BALL:
1953       if (game.ball_state)
1954         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1955       break;
1956
1957     case EL_EMC_MAGIC_BALL_SWITCH:
1958       if (game.ball_state)
1959         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1960       break;
1961
1962     case EL_TRIGGER_PLAYER:
1963     case EL_TRIGGER_ELEMENT:
1964     case EL_TRIGGER_CE_VALUE:
1965     case EL_TRIGGER_CE_SCORE:
1966     case EL_SELF:
1967     case EL_ANY_ELEMENT:
1968     case EL_CURRENT_CE_VALUE:
1969     case EL_CURRENT_CE_SCORE:
1970     case EL_PREV_CE_1:
1971     case EL_PREV_CE_2:
1972     case EL_PREV_CE_3:
1973     case EL_PREV_CE_4:
1974     case EL_PREV_CE_5:
1975     case EL_PREV_CE_6:
1976     case EL_PREV_CE_7:
1977     case EL_PREV_CE_8:
1978     case EL_NEXT_CE_1:
1979     case EL_NEXT_CE_2:
1980     case EL_NEXT_CE_3:
1981     case EL_NEXT_CE_4:
1982     case EL_NEXT_CE_5:
1983     case EL_NEXT_CE_6:
1984     case EL_NEXT_CE_7:
1985     case EL_NEXT_CE_8:
1986       /* reference elements should not be used on the playfield */
1987       Feld[x][y] = EL_EMPTY;
1988       break;
1989
1990     default:
1991       if (IS_CUSTOM_ELEMENT(element))
1992       {
1993         if (CAN_MOVE(element))
1994           InitMovDir(x, y);
1995
1996 #if USE_NEW_CUSTOM_VALUE
1997         if (!element_info[element].use_last_ce_value || init_game)
1998           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1999 #endif
2000       }
2001       else if (IS_GROUP_ELEMENT(element))
2002       {
2003         Feld[x][y] = GetElementFromGroupElement(element);
2004
2005         InitField(x, y, init_game);
2006       }
2007
2008       break;
2009   }
2010
2011   if (!init_game)
2012     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2013 }
2014
2015 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2016 {
2017   InitField(x, y, init_game);
2018
2019   /* not needed to call InitMovDir() -- already done by InitField()! */
2020   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2021       CAN_MOVE(Feld[x][y]))
2022     InitMovDir(x, y);
2023 }
2024
2025 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2026 {
2027   int old_element = Feld[x][y];
2028
2029   InitField(x, y, init_game);
2030
2031   /* not needed to call InitMovDir() -- already done by InitField()! */
2032   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2033       CAN_MOVE(old_element) &&
2034       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2035     InitMovDir(x, y);
2036
2037   /* this case is in fact a combination of not less than three bugs:
2038      first, it calls InitMovDir() for elements that can move, although this is
2039      already done by InitField(); then, it checks the element that was at this
2040      field _before_ the call to InitField() (which can change it); lastly, it
2041      was not called for "mole with direction" elements, which were treated as
2042      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2043   */
2044 }
2045
2046 static int get_key_element_from_nr(int key_nr)
2047 {
2048   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2049                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2050                           EL_EM_KEY_1 : EL_KEY_1);
2051
2052   return key_base_element + key_nr;
2053 }
2054
2055 static int get_next_dropped_element(struct PlayerInfo *player)
2056 {
2057   return (player->inventory_size > 0 ?
2058           player->inventory_element[player->inventory_size - 1] :
2059           player->inventory_infinite_element != EL_UNDEFINED ?
2060           player->inventory_infinite_element :
2061           player->dynabombs_left > 0 ?
2062           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2063           EL_UNDEFINED);
2064 }
2065
2066 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2067 {
2068   /* pos >= 0: get element from bottom of the stack;
2069      pos <  0: get element from top of the stack */
2070
2071   if (pos < 0)
2072   {
2073     int min_inventory_size = -pos;
2074     int inventory_pos = player->inventory_size - min_inventory_size;
2075     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2076
2077     return (player->inventory_size >= min_inventory_size ?
2078             player->inventory_element[inventory_pos] :
2079             player->inventory_infinite_element != EL_UNDEFINED ?
2080             player->inventory_infinite_element :
2081             player->dynabombs_left >= min_dynabombs_left ?
2082             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2083             EL_UNDEFINED);
2084   }
2085   else
2086   {
2087     int min_dynabombs_left = pos + 1;
2088     int min_inventory_size = pos + 1 - player->dynabombs_left;
2089     int inventory_pos = pos - player->dynabombs_left;
2090
2091     return (player->inventory_infinite_element != EL_UNDEFINED ?
2092             player->inventory_infinite_element :
2093             player->dynabombs_left >= min_dynabombs_left ?
2094             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2095             player->inventory_size >= min_inventory_size ?
2096             player->inventory_element[inventory_pos] :
2097             EL_UNDEFINED);
2098   }
2099 }
2100
2101 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2102 {
2103   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2104   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2105   int compare_result;
2106
2107   if (gpo1->sort_priority != gpo2->sort_priority)
2108     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2109   else
2110     compare_result = gpo1->nr - gpo2->nr;
2111
2112   return compare_result;
2113 }
2114
2115 void InitGameControlValues()
2116 {
2117   int i;
2118
2119   for (i = 0; game_panel_controls[i].nr != -1; i++)
2120   {
2121     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2122     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2123     struct TextPosInfo *pos = gpc->pos;
2124     int nr = gpc->nr;
2125     int type = gpc->type;
2126
2127     if (nr != i)
2128     {
2129       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2130       Error(ERR_EXIT, "this should not happen -- please debug");
2131     }
2132
2133     /* force update of game controls after initialization */
2134     gpc->value = gpc->last_value = -1;
2135     gpc->frame = gpc->last_frame = -1;
2136     gpc->gfx_frame = -1;
2137
2138     /* determine panel value width for later calculation of alignment */
2139     if (type == TYPE_INTEGER || type == TYPE_STRING)
2140     {
2141       pos->width = pos->size * getFontWidth(pos->font);
2142       pos->height = getFontHeight(pos->font);
2143     }
2144     else if (type == TYPE_ELEMENT)
2145     {
2146       pos->width = pos->size;
2147       pos->height = pos->size;
2148     }
2149
2150     /* fill structure for game panel draw order */
2151     gpo->nr = gpc->nr;
2152     gpo->sort_priority = pos->sort_priority;
2153   }
2154
2155   /* sort game panel controls according to sort_priority and control number */
2156   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2157         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2158 }
2159
2160 void UpdatePlayfieldElementCount()
2161 {
2162   boolean use_element_count = FALSE;
2163   int i, j, x, y;
2164
2165   /* first check if it is needed at all to calculate playfield element count */
2166   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2167     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2168       use_element_count = TRUE;
2169
2170   if (!use_element_count)
2171     return;
2172
2173   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2174     element_info[i].element_count = 0;
2175
2176   SCAN_PLAYFIELD(x, y)
2177   {
2178     element_info[Feld[x][y]].element_count++;
2179   }
2180
2181   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2182     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2183       if (IS_IN_GROUP(j, i))
2184         element_info[EL_GROUP_START + i].element_count +=
2185           element_info[j].element_count;
2186 }
2187
2188 void UpdateGameControlValues()
2189 {
2190   int i, k;
2191   int time = (local_player->LevelSolved ?
2192               local_player->LevelSolved_CountingTime :
2193               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194               level.native_em_level->lev->time :
2195               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196               level.native_sp_level->game_sp->time_played :
2197               game.no_time_limit ? TimePlayed : TimeLeft);
2198   int score = (local_player->LevelSolved ?
2199                local_player->LevelSolved_CountingScore :
2200                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201                level.native_em_level->lev->score :
2202                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203                level.native_sp_level->game_sp->score :
2204                local_player->score);
2205   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2206               level.native_em_level->lev->required :
2207               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2208               level.native_sp_level->game_sp->infotrons_still_needed :
2209               local_player->gems_still_needed);
2210   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2211                      level.native_em_level->lev->required > 0 :
2212                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2213                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2214                      local_player->gems_still_needed > 0 ||
2215                      local_player->sokobanfields_still_needed > 0 ||
2216                      local_player->lights_still_needed > 0);
2217
2218   UpdatePlayfieldElementCount();
2219
2220   /* update game panel control values */
2221
2222   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2223   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2224
2225   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2226   for (i = 0; i < MAX_NUM_KEYS; i++)
2227     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2228   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2229   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2230
2231   if (game.centered_player_nr == -1)
2232   {
2233     for (i = 0; i < MAX_PLAYERS; i++)
2234     {
2235       /* only one player in Supaplex game engine */
2236       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2237         break;
2238
2239       for (k = 0; k < MAX_NUM_KEYS; k++)
2240       {
2241         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2242         {
2243           if (level.native_em_level->ply[i]->keys & (1 << k))
2244             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2245               get_key_element_from_nr(k);
2246         }
2247         else if (stored_player[i].key[k])
2248           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249             get_key_element_from_nr(k);
2250       }
2251
2252       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2253         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2254           level.native_em_level->ply[i]->dynamite;
2255       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2256         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257           level.native_sp_level->game_sp->red_disk_count;
2258       else
2259         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2260           stored_player[i].inventory_size;
2261
2262       if (stored_player[i].num_white_keys > 0)
2263         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2264           EL_DC_KEY_WHITE;
2265
2266       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2267         stored_player[i].num_white_keys;
2268     }
2269   }
2270   else
2271   {
2272     int player_nr = game.centered_player_nr;
2273
2274     for (k = 0; k < MAX_NUM_KEYS; k++)
2275     {
2276       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277       {
2278         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2279           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280             get_key_element_from_nr(k);
2281       }
2282       else if (stored_player[player_nr].key[k])
2283         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284           get_key_element_from_nr(k);
2285     }
2286
2287     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289         level.native_em_level->ply[player_nr]->dynamite;
2290     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2291       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292         level.native_sp_level->game_sp->red_disk_count;
2293     else
2294       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2295         stored_player[player_nr].inventory_size;
2296
2297     if (stored_player[player_nr].num_white_keys > 0)
2298       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2299
2300     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2301       stored_player[player_nr].num_white_keys;
2302   }
2303
2304   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2305   {
2306     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2307       get_inventory_element_from_pos(local_player, i);
2308     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2309       get_inventory_element_from_pos(local_player, -i - 1);
2310   }
2311
2312   game_panel_controls[GAME_PANEL_SCORE].value = score;
2313   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2314
2315   game_panel_controls[GAME_PANEL_TIME].value = time;
2316
2317   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2318   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2319   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2320
2321   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2322
2323   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2324     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2325      EL_EMPTY);
2326   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2327     local_player->shield_normal_time_left;
2328   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2329     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2330      EL_EMPTY);
2331   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2332     local_player->shield_deadly_time_left;
2333
2334   game_panel_controls[GAME_PANEL_EXIT].value =
2335     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2336
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2339   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2340     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2341      EL_EMC_MAGIC_BALL_SWITCH);
2342
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2344     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2345   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2346     game.light_time_left;
2347
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2349     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2350   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2351     game.timegate_time_left;
2352
2353   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2354     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2355
2356   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2357     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2358   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2359     game.lenses_time_left;
2360
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2362     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2364     game.magnify_time_left;
2365
2366   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2367     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2368      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2369      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2370      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2371      EL_BALLOON_SWITCH_NONE);
2372
2373   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2374     local_player->dynabomb_count;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2376     local_player->dynabomb_size;
2377   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2378     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2379
2380   game_panel_controls[GAME_PANEL_PENGUINS].value =
2381     local_player->friends_still_needed;
2382
2383   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2384     local_player->sokobanfields_still_needed;
2385   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2386     local_player->sokobanfields_still_needed;
2387
2388   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2389     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2390
2391   for (i = 0; i < NUM_BELTS; i++)
2392   {
2393     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2394       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2395        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2396     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2397       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398   }
2399
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2401     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2402   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2403     game.magic_wall_time_left;
2404
2405 #if USE_PLAYER_GRAVITY
2406   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2407     local_player->gravity;
2408 #else
2409   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2410 #endif
2411
2412   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2413     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2414
2415   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2416     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2417       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2418        game.panel.element[i].id : EL_UNDEFINED);
2419
2420   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2421     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2422       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2423        element_info[game.panel.element_count[i].id].element_count : 0);
2424
2425   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2426     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2427       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2428        element_info[game.panel.ce_score[i].id].collect_score : 0);
2429
2430   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2431     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2432       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2433        element_info[game.panel.ce_score_element[i].id].collect_score :
2434        EL_UNDEFINED);
2435
2436   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2437   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2438   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2439
2440   /* update game panel control frames */
2441
2442   for (i = 0; game_panel_controls[i].nr != -1; i++)
2443   {
2444     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2445
2446     if (gpc->type == TYPE_ELEMENT)
2447     {
2448       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2449       {
2450         int last_anim_random_frame = gfx.anim_random_frame;
2451         int element = gpc->value;
2452         int graphic = el2panelimg(element);
2453
2454         if (gpc->value != gpc->last_value)
2455         {
2456           gpc->gfx_frame = 0;
2457           gpc->gfx_random = INIT_GFX_RANDOM();
2458         }
2459         else
2460         {
2461           gpc->gfx_frame++;
2462
2463           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2464               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2465             gpc->gfx_random = INIT_GFX_RANDOM();
2466         }
2467
2468         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2469           gfx.anim_random_frame = gpc->gfx_random;
2470
2471         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2472           gpc->gfx_frame = element_info[element].collect_score;
2473
2474         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2475                                               gpc->gfx_frame);
2476
2477         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2478           gfx.anim_random_frame = last_anim_random_frame;
2479       }
2480     }
2481   }
2482 }
2483
2484 void DisplayGameControlValues()
2485 {
2486   boolean redraw_panel = FALSE;
2487   int i;
2488
2489   for (i = 0; game_panel_controls[i].nr != -1; i++)
2490   {
2491     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2492
2493     if (PANEL_DEACTIVATED(gpc->pos))
2494       continue;
2495
2496     if (gpc->value == gpc->last_value &&
2497         gpc->frame == gpc->last_frame)
2498       continue;
2499
2500     redraw_panel = TRUE;
2501   }
2502
2503   if (!redraw_panel)
2504     return;
2505
2506   /* copy default game door content to main double buffer */
2507 #if 1
2508   /* !!! CHECK AGAIN !!! */
2509   SetPanelBackground();
2510   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2511   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2512 #else
2513   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2514              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2515 #endif
2516
2517   /* redraw game control buttons */
2518 #if 1
2519   RedrawGameButtons();
2520 #else
2521   UnmapGameButtons();
2522   MapGameButtons();
2523 #endif
2524
2525   game_status = GAME_MODE_PSEUDO_PANEL;
2526
2527 #if 1
2528   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2529 #else
2530   for (i = 0; game_panel_controls[i].nr != -1; i++)
2531 #endif
2532   {
2533 #if 1
2534     int nr = game_panel_order[i].nr;
2535     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2536 #else
2537     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2538     int nr = gpc->nr;
2539 #endif
2540     struct TextPosInfo *pos = gpc->pos;
2541     int type = gpc->type;
2542     int value = gpc->value;
2543     int frame = gpc->frame;
2544 #if 0
2545     int last_value = gpc->last_value;
2546     int last_frame = gpc->last_frame;
2547 #endif
2548     int size = pos->size;
2549     int font = pos->font;
2550     boolean draw_masked = pos->draw_masked;
2551     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2552
2553     if (PANEL_DEACTIVATED(pos))
2554       continue;
2555
2556 #if 0
2557     if (value == last_value && frame == last_frame)
2558       continue;
2559 #endif
2560
2561     gpc->last_value = value;
2562     gpc->last_frame = frame;
2563
2564 #if 0
2565     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2566 #endif
2567
2568     if (type == TYPE_INTEGER)
2569     {
2570       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2571           nr == GAME_PANEL_TIME)
2572       {
2573         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2574
2575         if (use_dynamic_size)           /* use dynamic number of digits */
2576         {
2577           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2578           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2579           int size2 = size1 + 1;
2580           int font1 = pos->font;
2581           int font2 = pos->font_alt;
2582
2583           size = (value < value_change ? size1 : size2);
2584           font = (value < value_change ? font1 : font2);
2585
2586 #if 0
2587           /* clear background if value just changed its size (dynamic digits) */
2588           if ((last_value < value_change) != (value < value_change))
2589           {
2590             int width1 = size1 * getFontWidth(font1);
2591             int width2 = size2 * getFontWidth(font2);
2592             int max_width = MAX(width1, width2);
2593             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2594
2595             pos->width = max_width;
2596
2597             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2598                                        max_width, max_height);
2599           }
2600 #endif
2601         }
2602       }
2603
2604 #if 1
2605       /* correct text size if "digits" is zero or less */
2606       if (size <= 0)
2607         size = strlen(int2str(value, size));
2608
2609       /* dynamically correct text alignment */
2610       pos->width = size * getFontWidth(font);
2611 #endif
2612
2613       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2614                   int2str(value, size), font, mask_mode);
2615     }
2616     else if (type == TYPE_ELEMENT)
2617     {
2618       int element, graphic;
2619       Bitmap *src_bitmap;
2620       int src_x, src_y;
2621       int width, height;
2622       int dst_x = PANEL_XPOS(pos);
2623       int dst_y = PANEL_YPOS(pos);
2624
2625 #if 1
2626       if (value != EL_UNDEFINED && value != EL_EMPTY)
2627       {
2628         element = value;
2629         graphic = el2panelimg(value);
2630
2631         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2632
2633 #if 1
2634         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2635           size = TILESIZE;
2636 #endif
2637
2638         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2639                               &src_x, &src_y);
2640
2641         width  = graphic_info[graphic].width  * size / TILESIZE;
2642         height = graphic_info[graphic].height * size / TILESIZE;
2643
2644         if (draw_masked)
2645         {
2646           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2647                         dst_x - src_x, dst_y - src_y);
2648           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2649                            dst_x, dst_y);
2650         }
2651         else
2652         {
2653           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2654                      dst_x, dst_y);
2655         }
2656       }
2657 #else
2658       if (value == EL_UNDEFINED || value == EL_EMPTY)
2659       {
2660         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2661         graphic = el2panelimg(element);
2662
2663         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2664         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2665         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2666       }
2667       else
2668       {
2669         element = value;
2670         graphic = el2panelimg(value);
2671
2672         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2673       }
2674
2675       width  = graphic_info[graphic].width  * size / TILESIZE;
2676       height = graphic_info[graphic].height * size / TILESIZE;
2677
2678       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2679 #endif
2680     }
2681     else if (type == TYPE_STRING)
2682     {
2683       boolean active = (value != 0);
2684       char *state_normal = "off";
2685       char *state_active = "on";
2686       char *state = (active ? state_active : state_normal);
2687       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2688                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2689                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2690                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2691
2692       if (nr == GAME_PANEL_GRAVITY_STATE)
2693       {
2694         int font1 = pos->font;          /* (used for normal state) */
2695         int font2 = pos->font_alt;      /* (used for active state) */
2696 #if 0
2697         int size1 = strlen(state_normal);
2698         int size2 = strlen(state_active);
2699         int width1 = size1 * getFontWidth(font1);
2700         int width2 = size2 * getFontWidth(font2);
2701         int max_width = MAX(width1, width2);
2702         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2703
2704         pos->width = max_width;
2705
2706         /* clear background for values that may have changed its size */
2707         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2708                                    max_width, max_height);
2709 #endif
2710
2711         font = (active ? font2 : font1);
2712       }
2713
2714       if (s != NULL)
2715       {
2716         char *s_cut;
2717
2718 #if 1
2719         if (size <= 0)
2720         {
2721           /* don't truncate output if "chars" is zero or less */
2722           size = strlen(s);
2723
2724           /* dynamically correct text alignment */
2725           pos->width = size * getFontWidth(font);
2726         }
2727 #endif
2728
2729         s_cut = getStringCopyN(s, size);
2730
2731         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2732                     s_cut, font, mask_mode);
2733
2734         free(s_cut);
2735       }
2736     }
2737
2738     redraw_mask |= REDRAW_DOOR_1;
2739   }
2740
2741   game_status = GAME_MODE_PLAYING;
2742 }
2743
2744 void UpdateAndDisplayGameControlValues()
2745 {
2746   if (tape.warp_forward)
2747     return;
2748
2749   UpdateGameControlValues();
2750   DisplayGameControlValues();
2751 }
2752
2753 void DrawGameValue_Emeralds(int value)
2754 {
2755   struct TextPosInfo *pos = &game.panel.gems;
2756   int font_nr = pos->font;
2757   int font_width = getFontWidth(font_nr);
2758   int chars = pos->size;
2759
2760 #if 1
2761   return;       /* !!! USE NEW STUFF !!! */
2762 #endif
2763
2764   if (PANEL_DEACTIVATED(pos))
2765     return;
2766
2767   pos->width = chars * font_width;
2768
2769   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2770 }
2771
2772 void DrawGameValue_Dynamite(int value)
2773 {
2774   struct TextPosInfo *pos = &game.panel.inventory_count;
2775   int font_nr = pos->font;
2776   int font_width = getFontWidth(font_nr);
2777   int chars = pos->size;
2778
2779 #if 1
2780   return;       /* !!! USE NEW STUFF !!! */
2781 #endif
2782
2783   if (PANEL_DEACTIVATED(pos))
2784     return;
2785
2786   pos->width = chars * font_width;
2787
2788   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2789 }
2790
2791 void DrawGameValue_Score(int value)
2792 {
2793   struct TextPosInfo *pos = &game.panel.score;
2794   int font_nr = pos->font;
2795   int font_width = getFontWidth(font_nr);
2796   int chars = pos->size;
2797
2798 #if 1
2799   return;       /* !!! USE NEW STUFF !!! */
2800 #endif
2801
2802   if (PANEL_DEACTIVATED(pos))
2803     return;
2804
2805   pos->width = chars * font_width;
2806
2807   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2808 }
2809
2810 void DrawGameValue_Time(int value)
2811 {
2812   struct TextPosInfo *pos = &game.panel.time;
2813   static int last_value = -1;
2814   int chars1 = 3;
2815   int chars2 = 4;
2816   int chars = pos->size;
2817   int font1_nr = pos->font;
2818   int font2_nr = pos->font_alt;
2819   int font_nr = font1_nr;
2820   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2821
2822 #if 1
2823   return;       /* !!! USE NEW STUFF !!! */
2824 #endif
2825
2826   if (PANEL_DEACTIVATED(pos))
2827     return;
2828
2829   if (use_dynamic_chars)                /* use dynamic number of chars */
2830   {
2831     chars   = (value < 1000 ? chars1   : chars2);
2832     font_nr = (value < 1000 ? font1_nr : font2_nr);
2833   }
2834
2835   /* clear background if value just changed its size (dynamic chars only) */
2836   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2837   {
2838     int width1 = chars1 * getFontWidth(font1_nr);
2839     int width2 = chars2 * getFontWidth(font2_nr);
2840     int max_width = MAX(width1, width2);
2841     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2842
2843     pos->width = max_width;
2844
2845     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2846                                max_width, max_height);
2847   }
2848
2849   pos->width = chars * getFontWidth(font_nr);
2850
2851   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2852
2853   last_value = value;
2854 }
2855
2856 void DrawGameValue_Level(int value)
2857 {
2858   struct TextPosInfo *pos = &game.panel.level_number;
2859   int chars1 = 2;
2860   int chars2 = 3;
2861   int chars = pos->size;
2862   int font1_nr = pos->font;
2863   int font2_nr = pos->font_alt;
2864   int font_nr = font1_nr;
2865   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2866
2867 #if 1
2868   return;       /* !!! USE NEW STUFF !!! */
2869 #endif
2870
2871   if (PANEL_DEACTIVATED(pos))
2872     return;
2873
2874   if (use_dynamic_chars)                /* use dynamic number of chars */
2875   {
2876     chars   = (level_nr < 100 ? chars1   : chars2);
2877     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2878   }
2879
2880   pos->width = chars * getFontWidth(font_nr);
2881
2882   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2883 }
2884
2885 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2886 {
2887   int i;
2888
2889 #if 1
2890   return;       /* !!! USE NEW STUFF !!! */
2891 #endif
2892
2893   for (i = 0; i < MAX_NUM_KEYS; i++)
2894   {
2895     struct TextPosInfo *pos = &game.panel.key[i];
2896     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2897     int src_y = DOOR_GFX_PAGEY1 + 123;
2898     int dst_x = PANEL_XPOS(pos);
2899     int dst_y = PANEL_YPOS(pos);
2900
2901     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2902                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2903                    EL_KEY_1) + i;
2904     int graphic = el2edimg(element);
2905
2906     if (PANEL_DEACTIVATED(pos))
2907       continue;
2908
2909 #if 0
2910     /* masked blit with tiles from half-size scaled bitmap does not work yet
2911        (no mask bitmap created for these sizes after loading and scaling) --
2912        solution: load without creating mask, scale, then create final mask */
2913
2914     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2915                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2916
2917     if (key[i])
2918     {
2919       Bitmap *src_bitmap;
2920       int src_x, src_y;
2921
2922       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2923
2924       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2925                     dst_x - src_x, dst_y - src_y);
2926       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2927                        dst_x, dst_y);
2928     }
2929 #else
2930     if (key[i])
2931       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2932     else
2933       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2934                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2935 #endif
2936   }
2937 }
2938
2939 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2940                        int key_bits)
2941 {
2942   int key[MAX_NUM_KEYS];
2943   int i;
2944
2945   /* prevent EM engine from updating time/score values parallel to GameWon() */
2946   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2947       local_player->LevelSolved)
2948     return;
2949
2950   for (i = 0; i < MAX_NUM_KEYS; i++)
2951     key[i] = key_bits & (1 << i);
2952
2953   DrawGameValue_Level(level_nr);
2954
2955   DrawGameValue_Emeralds(emeralds);
2956   DrawGameValue_Dynamite(dynamite);
2957   DrawGameValue_Score(score);
2958   DrawGameValue_Time(time);
2959
2960   DrawGameValue_Keys(key);
2961 }
2962
2963 void UpdateGameDoorValues()
2964 {
2965   UpdateGameControlValues();
2966 }
2967
2968 void DrawGameDoorValues()
2969 {
2970   DisplayGameControlValues();
2971 }
2972
2973 void DrawGameDoorValues_OLD()
2974 {
2975   int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2976   int dynamite_value = 0;
2977   int score_value = (local_player->LevelSolved ? local_player->score_final :
2978                      local_player->score);
2979   int gems_value = local_player->gems_still_needed;
2980   int key_bits = 0;
2981   int i, j;
2982
2983   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2984   {
2985     DrawGameDoorValues_EM();
2986
2987     return;
2988   }
2989
2990   if (game.centered_player_nr == -1)
2991   {
2992     for (i = 0; i < MAX_PLAYERS; i++)
2993     {
2994       for (j = 0; j < MAX_NUM_KEYS; j++)
2995         if (stored_player[i].key[j])
2996           key_bits |= (1 << j);
2997
2998       dynamite_value += stored_player[i].inventory_size;
2999     }
3000   }
3001   else
3002   {
3003     int player_nr = game.centered_player_nr;
3004
3005     for (i = 0; i < MAX_NUM_KEYS; i++)
3006       if (stored_player[player_nr].key[i])
3007         key_bits |= (1 << i);
3008
3009     dynamite_value = stored_player[player_nr].inventory_size;
3010   }
3011
3012   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3013                     key_bits);
3014 }
3015
3016
3017 /*
3018   =============================================================================
3019   InitGameEngine()
3020   -----------------------------------------------------------------------------
3021   initialize game engine due to level / tape version number
3022   =============================================================================
3023 */
3024
3025 static void InitGameEngine()
3026 {
3027   int i, j, k, l, x, y;
3028
3029   /* set game engine from tape file when re-playing, else from level file */
3030   game.engine_version = (tape.playing ? tape.engine_version :
3031                          level.game_version);
3032
3033   /* set single or multi-player game mode (needed for re-playing tapes) */
3034   game.team_mode = setup.team_mode;
3035
3036   if (tape.playing)
3037   {
3038     int num_players = 0;
3039
3040     for (i = 0; i < MAX_PLAYERS; i++)
3041       if (tape.player_participates[i])
3042         num_players++;
3043
3044     /* multi-player tapes contain input data for more than one player */
3045     game.team_mode = (num_players > 1);
3046   }
3047
3048   /* ---------------------------------------------------------------------- */
3049   /* set flags for bugs and changes according to active game engine version */
3050   /* ---------------------------------------------------------------------- */
3051
3052   /*
3053     Summary of bugfix/change:
3054     Fixed handling for custom elements that change when pushed by the player.
3055
3056     Fixed/changed in version:
3057     3.1.0
3058
3059     Description:
3060     Before 3.1.0, custom elements that "change when pushing" changed directly
3061     after the player started pushing them (until then handled in "DigField()").
3062     Since 3.1.0, these custom elements are not changed until the "pushing"
3063     move of the element is finished (now handled in "ContinueMoving()").
3064
3065     Affected levels/tapes:
3066     The first condition is generally needed for all levels/tapes before version
3067     3.1.0, which might use the old behaviour before it was changed; known tapes
3068     that are affected are some tapes from the level set "Walpurgis Gardens" by
3069     Jamie Cullen.
3070     The second condition is an exception from the above case and is needed for
3071     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3072     above (including some development versions of 3.1.0), but before it was
3073     known that this change would break tapes like the above and was fixed in
3074     3.1.1, so that the changed behaviour was active although the engine version
3075     while recording maybe was before 3.1.0. There is at least one tape that is
3076     affected by this exception, which is the tape for the one-level set "Bug
3077     Machine" by Juergen Bonhagen.
3078   */
3079
3080   game.use_change_when_pushing_bug =
3081     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3082      !(tape.playing &&
3083        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3084        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3085
3086   /*
3087     Summary of bugfix/change:
3088     Fixed handling for blocking the field the player leaves when moving.
3089
3090     Fixed/changed in version:
3091     3.1.1
3092
3093     Description:
3094     Before 3.1.1, when "block last field when moving" was enabled, the field
3095     the player is leaving when moving was blocked for the time of the move,
3096     and was directly unblocked afterwards. This resulted in the last field
3097     being blocked for exactly one less than the number of frames of one player
3098     move. Additionally, even when blocking was disabled, the last field was
3099     blocked for exactly one frame.
3100     Since 3.1.1, due to changes in player movement handling, the last field
3101     is not blocked at all when blocking is disabled. When blocking is enabled,
3102     the last field is blocked for exactly the number of frames of one player
3103     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3104     last field is blocked for exactly one more than the number of frames of
3105     one player move.
3106
3107     Affected levels/tapes:
3108     (!!! yet to be determined -- probably many !!!)
3109   */
3110
3111   game.use_block_last_field_bug =
3112     (game.engine_version < VERSION_IDENT(3,1,1,0));
3113
3114   /*
3115     Summary of bugfix/change:
3116     Changed behaviour of CE changes with multiple changes per single frame.
3117
3118     Fixed/changed in version:
3119     3.2.0-6
3120
3121     Description:
3122     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3123     This resulted in race conditions where CEs seem to behave strange in some
3124     situations (where triggered CE changes were just skipped because there was
3125     already a CE change on that tile in the playfield in that engine frame).
3126     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3127     (The number of changes per frame must be limited in any case, because else
3128     it is easily possible to define CE changes that would result in an infinite
3129     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3130     should be set large enough so that it would only be reached in cases where
3131     the corresponding CE change conditions run into a loop. Therefore, it seems
3132     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3133     maximal number of change pages for custom elements.)
3134
3135     Affected levels/tapes:
3136     Probably many.
3137   */
3138
3139 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3140   game.max_num_changes_per_frame = 1;
3141 #else
3142   game.max_num_changes_per_frame =
3143     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3144 #endif
3145
3146   /* ---------------------------------------------------------------------- */
3147
3148   /* default scan direction: scan playfield from top/left to bottom/right */
3149   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3150
3151   /* dynamically adjust element properties according to game engine version */
3152   InitElementPropertiesEngine(game.engine_version);
3153
3154 #if 0
3155   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3156   printf("          tape version == %06d [%s] [file: %06d]\n",
3157          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3158          tape.file_version);
3159   printf("       => game.engine_version == %06d\n", game.engine_version);
3160 #endif
3161
3162   /* ---------- initialize player's initial move delay --------------------- */
3163
3164   /* dynamically adjust player properties according to level information */
3165   for (i = 0; i < MAX_PLAYERS; i++)
3166     game.initial_move_delay_value[i] =
3167       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3168
3169   /* dynamically adjust player properties according to game engine version */
3170   for (i = 0; i < MAX_PLAYERS; i++)
3171     game.initial_move_delay[i] =
3172       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3173        game.initial_move_delay_value[i] : 0);
3174
3175   /* ---------- initialize player's initial push delay --------------------- */
3176
3177   /* dynamically adjust player properties according to game engine version */
3178   game.initial_push_delay_value =
3179     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3180
3181   /* ---------- initialize changing elements ------------------------------- */
3182
3183   /* initialize changing elements information */
3184   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3185   {
3186     struct ElementInfo *ei = &element_info[i];
3187
3188     /* this pointer might have been changed in the level editor */
3189     ei->change = &ei->change_page[0];
3190
3191     if (!IS_CUSTOM_ELEMENT(i))
3192     {
3193       ei->change->target_element = EL_EMPTY_SPACE;
3194       ei->change->delay_fixed = 0;
3195       ei->change->delay_random = 0;
3196       ei->change->delay_frames = 1;
3197     }
3198
3199     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3200     {
3201       ei->has_change_event[j] = FALSE;
3202
3203       ei->event_page_nr[j] = 0;
3204       ei->event_page[j] = &ei->change_page[0];
3205     }
3206   }
3207
3208   /* add changing elements from pre-defined list */
3209   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3210   {
3211     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3212     struct ElementInfo *ei = &element_info[ch_delay->element];
3213
3214     ei->change->target_element       = ch_delay->target_element;
3215     ei->change->delay_fixed          = ch_delay->change_delay;
3216
3217     ei->change->pre_change_function  = ch_delay->pre_change_function;
3218     ei->change->change_function      = ch_delay->change_function;
3219     ei->change->post_change_function = ch_delay->post_change_function;
3220
3221     ei->change->can_change = TRUE;
3222     ei->change->can_change_or_has_action = TRUE;
3223
3224     ei->has_change_event[CE_DELAY] = TRUE;
3225
3226     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3227     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3228   }
3229
3230   /* ---------- initialize internal run-time variables --------------------- */
3231
3232   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3233   {
3234     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3235
3236     for (j = 0; j < ei->num_change_pages; j++)
3237     {
3238       ei->change_page[j].can_change_or_has_action =
3239         (ei->change_page[j].can_change |
3240          ei->change_page[j].has_action);
3241     }
3242   }
3243
3244   /* add change events from custom element configuration */
3245   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3246   {
3247     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3248
3249     for (j = 0; j < ei->num_change_pages; j++)
3250     {
3251       if (!ei->change_page[j].can_change_or_has_action)
3252         continue;
3253
3254       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3255       {
3256         /* only add event page for the first page found with this event */
3257         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3258         {
3259           ei->has_change_event[k] = TRUE;
3260
3261           ei->event_page_nr[k] = j;
3262           ei->event_page[k] = &ei->change_page[j];
3263         }
3264       }
3265     }
3266   }
3267
3268 #if 1
3269   /* ---------- initialize reference elements in change conditions --------- */
3270
3271   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3272   {
3273     int element = EL_CUSTOM_START + i;
3274     struct ElementInfo *ei = &element_info[element];
3275
3276     for (j = 0; j < ei->num_change_pages; j++)
3277     {
3278       int trigger_element = ei->change_page[j].initial_trigger_element;
3279
3280       if (trigger_element >= EL_PREV_CE_8 &&
3281           trigger_element <= EL_NEXT_CE_8)
3282         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3283
3284       ei->change_page[j].trigger_element = trigger_element;
3285     }
3286   }
3287 #endif
3288
3289   /* ---------- initialize run-time trigger player and element ------------- */
3290
3291   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3292   {
3293     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3294
3295     for (j = 0; j < ei->num_change_pages; j++)
3296     {
3297       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3298       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3299       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3300       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3301       ei->change_page[j].actual_trigger_ce_value = 0;
3302       ei->change_page[j].actual_trigger_ce_score = 0;
3303     }
3304   }
3305
3306   /* ---------- initialize trigger events ---------------------------------- */
3307
3308   /* initialize trigger events information */
3309   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3310     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3311       trigger_events[i][j] = FALSE;
3312
3313   /* add trigger events from element change event properties */
3314   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3315   {
3316     struct ElementInfo *ei = &element_info[i];
3317
3318     for (j = 0; j < ei->num_change_pages; j++)
3319     {
3320       if (!ei->change_page[j].can_change_or_has_action)
3321         continue;
3322
3323       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3324       {
3325         int trigger_element = ei->change_page[j].trigger_element;
3326
3327         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3328         {
3329           if (ei->change_page[j].has_event[k])
3330           {
3331             if (IS_GROUP_ELEMENT(trigger_element))
3332             {
3333               struct ElementGroupInfo *group =
3334                 element_info[trigger_element].group;
3335
3336               for (l = 0; l < group->num_elements_resolved; l++)
3337                 trigger_events[group->element_resolved[l]][k] = TRUE;
3338             }
3339             else if (trigger_element == EL_ANY_ELEMENT)
3340               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3341                 trigger_events[l][k] = TRUE;
3342             else
3343               trigger_events[trigger_element][k] = TRUE;
3344           }
3345         }
3346       }
3347     }
3348   }
3349
3350   /* ---------- initialize push delay -------------------------------------- */
3351
3352   /* initialize push delay values to default */
3353   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3354   {
3355     if (!IS_CUSTOM_ELEMENT(i))
3356     {
3357       /* set default push delay values (corrected since version 3.0.7-1) */
3358       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3359       {
3360         element_info[i].push_delay_fixed = 2;
3361         element_info[i].push_delay_random = 8;
3362       }
3363       else
3364       {
3365         element_info[i].push_delay_fixed = 8;
3366         element_info[i].push_delay_random = 8;
3367       }
3368     }
3369   }
3370
3371   /* set push delay value for certain elements from pre-defined list */
3372   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3373   {
3374     int e = push_delay_list[i].element;
3375
3376     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3377     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3378   }
3379
3380   /* set push delay value for Supaplex elements for newer engine versions */
3381   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3382   {
3383     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3384     {
3385       if (IS_SP_ELEMENT(i))
3386       {
3387         /* set SP push delay to just enough to push under a falling zonk */
3388         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3389
3390         element_info[i].push_delay_fixed  = delay;
3391         element_info[i].push_delay_random = 0;
3392       }
3393     }
3394   }
3395
3396   /* ---------- initialize move stepsize ----------------------------------- */
3397
3398   /* initialize move stepsize values to default */
3399   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3400     if (!IS_CUSTOM_ELEMENT(i))
3401       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3402
3403   /* set move stepsize value for certain elements from pre-defined list */
3404   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3405   {
3406     int e = move_stepsize_list[i].element;
3407
3408     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3409   }
3410
3411   /* ---------- initialize collect score ----------------------------------- */
3412
3413   /* initialize collect score values for custom elements from initial value */
3414   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3415     if (IS_CUSTOM_ELEMENT(i))
3416       element_info[i].collect_score = element_info[i].collect_score_initial;
3417
3418   /* ---------- initialize collect count ----------------------------------- */
3419
3420   /* initialize collect count values for non-custom elements */
3421   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3422     if (!IS_CUSTOM_ELEMENT(i))
3423       element_info[i].collect_count_initial = 0;
3424
3425   /* add collect count values for all elements from pre-defined list */
3426   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3427     element_info[collect_count_list[i].element].collect_count_initial =
3428       collect_count_list[i].count;
3429
3430   /* ---------- initialize access direction -------------------------------- */
3431
3432   /* initialize access direction values to default (access from every side) */
3433   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3434     if (!IS_CUSTOM_ELEMENT(i))
3435       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3436
3437   /* set access direction value for certain elements from pre-defined list */
3438   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3439     element_info[access_direction_list[i].element].access_direction =
3440       access_direction_list[i].direction;
3441
3442   /* ---------- initialize explosion content ------------------------------- */
3443   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3444   {
3445     if (IS_CUSTOM_ELEMENT(i))
3446       continue;
3447
3448     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3449     {
3450       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3451
3452       element_info[i].content.e[x][y] =
3453         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3454          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3455          i == EL_PLAYER_3 ? EL_EMERALD :
3456          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3457          i == EL_MOLE ? EL_EMERALD_RED :
3458          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3459          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3460          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3461          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3462          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3463          i == EL_WALL_EMERALD ? EL_EMERALD :
3464          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3465          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3466          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3467          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3468          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3469          i == EL_WALL_PEARL ? EL_PEARL :
3470          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3471          EL_EMPTY);
3472     }
3473   }
3474
3475   /* ---------- initialize recursion detection ------------------------------ */
3476   recursion_loop_depth = 0;
3477   recursion_loop_detected = FALSE;
3478   recursion_loop_element = EL_UNDEFINED;
3479
3480   /* ---------- initialize graphics engine ---------------------------------- */
3481   game.scroll_delay_value =
3482     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3483      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3484   game.scroll_delay_value =
3485     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3486 }
3487
3488 int get_num_special_action(int element, int action_first, int action_last)
3489 {
3490   int num_special_action = 0;
3491   int i, j;
3492
3493   for (i = action_first; i <= action_last; i++)
3494   {
3495     boolean found = FALSE;
3496
3497     for (j = 0; j < NUM_DIRECTIONS; j++)
3498       if (el_act_dir2img(element, i, j) !=
3499           el_act_dir2img(element, ACTION_DEFAULT, j))
3500         found = TRUE;
3501
3502     if (found)
3503       num_special_action++;
3504     else
3505       break;
3506   }
3507
3508   return num_special_action;
3509 }
3510
3511
3512 /*
3513   =============================================================================
3514   InitGame()
3515   -----------------------------------------------------------------------------
3516   initialize and start new game
3517   =============================================================================
3518 */
3519
3520 void InitGame()
3521 {
3522   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3523   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3524   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3525 #if 0
3526   boolean do_fading = (game_status == GAME_MODE_MAIN);
3527 #endif
3528 #if 1
3529   int initial_move_dir = MV_DOWN;
3530 #else
3531   int initial_move_dir = MV_NONE;
3532 #endif
3533   int i, j, x, y;
3534
3535   game_status = GAME_MODE_PLAYING;
3536
3537 #if 1
3538   /* needed if different viewport properties defined for playing */
3539   ChangeViewportPropertiesIfNeeded();
3540 #endif
3541
3542 #if 1
3543   DrawCompleteVideoDisplay();
3544 #endif
3545
3546   InitGameEngine();
3547   InitGameControlValues();
3548
3549   /* don't play tapes over network */
3550   network_playing = (options.network && !tape.playing);
3551
3552   for (i = 0; i < MAX_PLAYERS; i++)
3553   {
3554     struct PlayerInfo *player = &stored_player[i];
3555
3556     player->index_nr = i;
3557     player->index_bit = (1 << i);
3558     player->element_nr = EL_PLAYER_1 + i;
3559
3560     player->present = FALSE;
3561     player->active = FALSE;
3562     player->mapped = FALSE;
3563
3564     player->killed = FALSE;
3565     player->reanimated = FALSE;
3566
3567     player->action = 0;
3568     player->effective_action = 0;
3569     player->programmed_action = 0;
3570
3571     player->score = 0;
3572     player->score_final = 0;
3573
3574     player->gems_still_needed = level.gems_needed;
3575     player->sokobanfields_still_needed = 0;
3576     player->lights_still_needed = 0;
3577     player->friends_still_needed = 0;
3578
3579     for (j = 0; j < MAX_NUM_KEYS; j++)
3580       player->key[j] = FALSE;
3581
3582     player->num_white_keys = 0;
3583
3584     player->dynabomb_count = 0;
3585     player->dynabomb_size = 1;
3586     player->dynabombs_left = 0;
3587     player->dynabomb_xl = FALSE;
3588
3589     player->MovDir = initial_move_dir;
3590     player->MovPos = 0;
3591     player->GfxPos = 0;
3592     player->GfxDir = initial_move_dir;
3593     player->GfxAction = ACTION_DEFAULT;
3594     player->Frame = 0;
3595     player->StepFrame = 0;
3596
3597     player->initial_element = player->element_nr;
3598     player->artwork_element =
3599       (level.use_artwork_element[i] ? level.artwork_element[i] :
3600        player->element_nr);
3601     player->use_murphy = FALSE;
3602
3603     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3604     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3605
3606     player->gravity = level.initial_player_gravity[i];
3607
3608     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3609
3610     player->actual_frame_counter = 0;
3611
3612     player->step_counter = 0;
3613
3614     player->last_move_dir = initial_move_dir;
3615
3616     player->is_active = FALSE;
3617
3618     player->is_waiting = FALSE;
3619     player->is_moving = FALSE;
3620     player->is_auto_moving = FALSE;
3621     player->is_digging = FALSE;
3622     player->is_snapping = FALSE;
3623     player->is_collecting = FALSE;
3624     player->is_pushing = FALSE;
3625     player->is_switching = FALSE;
3626     player->is_dropping = FALSE;
3627     player->is_dropping_pressed = FALSE;
3628
3629     player->is_bored = FALSE;
3630     player->is_sleeping = FALSE;
3631
3632     player->frame_counter_bored = -1;
3633     player->frame_counter_sleeping = -1;
3634
3635     player->anim_delay_counter = 0;
3636     player->post_delay_counter = 0;
3637
3638     player->dir_waiting = initial_move_dir;
3639     player->action_waiting = ACTION_DEFAULT;
3640     player->last_action_waiting = ACTION_DEFAULT;
3641     player->special_action_bored = ACTION_DEFAULT;
3642     player->special_action_sleeping = ACTION_DEFAULT;
3643
3644     player->switch_x = -1;
3645     player->switch_y = -1;
3646
3647     player->drop_x = -1;
3648     player->drop_y = -1;
3649
3650     player->show_envelope = 0;
3651
3652     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3653
3654     player->push_delay       = -1;      /* initialized when pushing starts */
3655     player->push_delay_value = game.initial_push_delay_value;
3656
3657     player->drop_delay = 0;
3658     player->drop_pressed_delay = 0;
3659
3660     player->last_jx = -1;
3661     player->last_jy = -1;
3662     player->jx = -1;
3663     player->jy = -1;
3664
3665     player->shield_normal_time_left = 0;
3666     player->shield_deadly_time_left = 0;
3667
3668     player->inventory_infinite_element = EL_UNDEFINED;
3669     player->inventory_size = 0;
3670
3671     if (level.use_initial_inventory[i])
3672     {
3673       for (j = 0; j < level.initial_inventory_size[i]; j++)
3674       {
3675         int element = level.initial_inventory_content[i][j];
3676         int collect_count = element_info[element].collect_count_initial;
3677         int k;
3678
3679         if (!IS_CUSTOM_ELEMENT(element))
3680           collect_count = 1;
3681
3682         if (collect_count == 0)
3683           player->inventory_infinite_element = element;
3684         else
3685           for (k = 0; k < collect_count; k++)
3686             if (player->inventory_size < MAX_INVENTORY_SIZE)
3687               player->inventory_element[player->inventory_size++] = element;
3688       }
3689     }
3690
3691     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3692     SnapField(player, 0, 0);
3693
3694     player->LevelSolved = FALSE;
3695     player->GameOver = FALSE;
3696
3697     player->LevelSolved_GameWon = FALSE;
3698     player->LevelSolved_GameEnd = FALSE;
3699     player->LevelSolved_PanelOff = FALSE;
3700     player->LevelSolved_SaveTape = FALSE;
3701     player->LevelSolved_SaveScore = FALSE;
3702     player->LevelSolved_CountingTime = 0;
3703     player->LevelSolved_CountingScore = 0;
3704
3705     map_player_action[i] = i;
3706   }
3707
3708   network_player_action_received = FALSE;
3709
3710 #if defined(NETWORK_AVALIABLE)
3711   /* initial null action */
3712   if (network_playing)
3713     SendToServer_MovePlayer(MV_NONE);
3714 #endif
3715
3716   ZX = ZY = -1;
3717   ExitX = ExitY = -1;
3718
3719   FrameCounter = 0;
3720   TimeFrames = 0;
3721   TimePlayed = 0;
3722   TimeLeft = level.time;
3723   TapeTime = 0;
3724
3725   ScreenMovDir = MV_NONE;
3726   ScreenMovPos = 0;
3727   ScreenGfxPos = 0;
3728
3729   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3730
3731   AllPlayersGone = FALSE;
3732
3733   game.no_time_limit = (level.time == 0);
3734
3735   game.yamyam_content_nr = 0;
3736   game.robot_wheel_active = FALSE;
3737   game.magic_wall_active = FALSE;
3738   game.magic_wall_time_left = 0;
3739   game.light_time_left = 0;
3740   game.timegate_time_left = 0;
3741   game.switchgate_pos = 0;
3742   game.wind_direction = level.wind_direction_initial;
3743
3744 #if !USE_PLAYER_GRAVITY
3745   game.gravity = FALSE;
3746   game.explosions_delayed = TRUE;
3747 #endif
3748
3749   game.lenses_time_left = 0;
3750   game.magnify_time_left = 0;
3751
3752   game.ball_state = level.ball_state_initial;
3753   game.ball_content_nr = 0;
3754
3755   game.envelope_active = FALSE;
3756
3757   /* set focus to local player for network games, else to all players */
3758   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3759   game.centered_player_nr_next = game.centered_player_nr;
3760   game.set_centered_player = FALSE;
3761
3762   if (network_playing && tape.recording)
3763   {
3764     /* store client dependent player focus when recording network games */
3765     tape.centered_player_nr_next = game.centered_player_nr_next;
3766     tape.set_centered_player = TRUE;
3767   }
3768
3769   for (i = 0; i < NUM_BELTS; i++)
3770   {
3771     game.belt_dir[i] = MV_NONE;
3772     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3773   }
3774
3775   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3776     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3777
3778 #if DEBUG_INIT_PLAYER
3779   if (options.debug)
3780   {
3781     printf("Player status at level initialization:\n");
3782   }
3783 #endif
3784
3785   SCAN_PLAYFIELD(x, y)
3786   {
3787     Feld[x][y] = level.field[x][y];
3788     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3789     ChangeDelay[x][y] = 0;
3790     ChangePage[x][y] = -1;
3791 #if USE_NEW_CUSTOM_VALUE
3792     CustomValue[x][y] = 0;              /* initialized in InitField() */
3793 #endif
3794     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3795     AmoebaNr[x][y] = 0;
3796     WasJustMoving[x][y] = 0;
3797     WasJustFalling[x][y] = 0;
3798     CheckCollision[x][y] = 0;
3799     CheckImpact[x][y] = 0;
3800     Stop[x][y] = FALSE;
3801     Pushed[x][y] = FALSE;
3802
3803     ChangeCount[x][y] = 0;
3804     ChangeEvent[x][y] = -1;
3805
3806     ExplodePhase[x][y] = 0;
3807     ExplodeDelay[x][y] = 0;
3808     ExplodeField[x][y] = EX_TYPE_NONE;
3809
3810     RunnerVisit[x][y] = 0;
3811     PlayerVisit[x][y] = 0;
3812
3813     GfxFrame[x][y] = 0;
3814     GfxRandom[x][y] = INIT_GFX_RANDOM();
3815     GfxElement[x][y] = EL_UNDEFINED;
3816     GfxAction[x][y] = ACTION_DEFAULT;
3817     GfxDir[x][y] = MV_NONE;
3818     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3819   }
3820
3821   SCAN_PLAYFIELD(x, y)
3822   {
3823     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3824       emulate_bd = FALSE;
3825     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3826       emulate_sb = FALSE;
3827     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3828       emulate_sp = FALSE;
3829
3830     InitField(x, y, TRUE);
3831
3832     ResetGfxAnimation(x, y);
3833   }
3834
3835   InitBeltMovement();
3836
3837   for (i = 0; i < MAX_PLAYERS; i++)
3838   {
3839     struct PlayerInfo *player = &stored_player[i];
3840
3841     /* set number of special actions for bored and sleeping animation */
3842     player->num_special_action_bored =
3843       get_num_special_action(player->artwork_element,
3844                              ACTION_BORING_1, ACTION_BORING_LAST);
3845     player->num_special_action_sleeping =
3846       get_num_special_action(player->artwork_element,
3847                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3848   }
3849
3850   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3851                     emulate_sb ? EMU_SOKOBAN :
3852                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3853
3854 #if USE_NEW_ALL_SLIPPERY
3855   /* initialize type of slippery elements */
3856   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3857   {
3858     if (!IS_CUSTOM_ELEMENT(i))
3859     {
3860       /* default: elements slip down either to the left or right randomly */
3861       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3862
3863       /* SP style elements prefer to slip down on the left side */
3864       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3865         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3866
3867       /* BD style elements prefer to slip down on the left side */
3868       if (game.emulation == EMU_BOULDERDASH)
3869         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3870     }
3871   }
3872 #endif
3873
3874   /* initialize explosion and ignition delay */
3875   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3876   {
3877     if (!IS_CUSTOM_ELEMENT(i))
3878     {
3879       int num_phase = 8;
3880       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3881                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3882                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3883       int last_phase = (num_phase + 1) * delay;
3884       int half_phase = (num_phase / 2) * delay;
3885
3886       element_info[i].explosion_delay = last_phase - 1;
3887       element_info[i].ignition_delay = half_phase;
3888
3889       if (i == EL_BLACK_ORB)
3890         element_info[i].ignition_delay = 1;
3891     }
3892
3893 #if 0
3894     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3895       element_info[i].explosion_delay = 1;
3896
3897     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3898       element_info[i].ignition_delay = 1;
3899 #endif
3900   }
3901
3902   /* correct non-moving belts to start moving left */
3903   for (i = 0; i < NUM_BELTS; i++)
3904     if (game.belt_dir[i] == MV_NONE)
3905       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3906
3907 #if USE_NEW_PLAYER_ASSIGNMENTS
3908   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3909   /* choose default local player */
3910   local_player = &stored_player[0];
3911
3912   for (i = 0; i < MAX_PLAYERS; i++)
3913     stored_player[i].connected = FALSE;
3914
3915   local_player->connected = TRUE;
3916   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3917
3918 #if 0
3919   printf("::: TEAM MODE: %d\n", game.team_mode);
3920 #endif
3921
3922   if (tape.playing)
3923   {
3924 #if 1
3925     for (i = 0; i < MAX_PLAYERS; i++)
3926       stored_player[i].connected = tape.player_participates[i];
3927 #else
3928     /* try to guess locally connected team mode players (needed for correct
3929        assignment of player figures from level to locally playing players) */
3930
3931     for (i = 0; i < MAX_PLAYERS; i++)
3932       if (tape.player_participates[i])
3933         stored_player[i].connected = TRUE;
3934 #endif
3935   }
3936   else if (game.team_mode && !options.network)
3937   {
3938     /* try to guess locally connected team mode players (needed for correct
3939        assignment of player figures from level to locally playing players) */
3940
3941     for (i = 0; i < MAX_PLAYERS; i++)
3942       if (setup.input[i].use_joystick ||
3943           setup.input[i].key.left != KSYM_UNDEFINED)
3944         stored_player[i].connected = TRUE;
3945   }
3946
3947 #if DEBUG_INIT_PLAYER
3948   if (options.debug)
3949   {
3950     printf("Player status after level initialization:\n");
3951
3952     for (i = 0; i < MAX_PLAYERS; i++)
3953     {
3954       struct PlayerInfo *player = &stored_player[i];
3955
3956       printf("- player %d: present == %d, connected == %d, active == %d",
3957              i + 1,
3958              player->present,
3959              player->connected,
3960              player->active);
3961
3962       if (local_player == player)
3963         printf(" (local player)");
3964
3965       printf("\n");
3966     }
3967   }
3968 #endif
3969
3970 #if DEBUG_INIT_PLAYER
3971   if (options.debug)
3972     printf("Reassigning players ...\n");
3973 #endif
3974
3975   /* check if any connected player was not found in playfield */
3976   for (i = 0; i < MAX_PLAYERS; i++)
3977   {
3978     struct PlayerInfo *player = &stored_player[i];
3979
3980     if (player->connected && !player->present)
3981     {
3982       struct PlayerInfo *field_player = NULL;
3983
3984 #if DEBUG_INIT_PLAYER
3985       if (options.debug)
3986         printf("- looking for field player for player %d ...\n", i + 1);
3987 #endif
3988
3989       /* assign first free player found that is present in the playfield */
3990
3991 #if 1
3992       /* first try: look for unmapped playfield player that is not connected */
3993       for (j = 0; j < MAX_PLAYERS; j++)
3994         if (field_player == NULL &&
3995             stored_player[j].present &&
3996             !stored_player[j].mapped &&
3997             !stored_player[j].connected)
3998           field_player = &stored_player[j];
3999
4000       /* second try: look for *any* unmapped playfield player */
4001       for (j = 0; j < MAX_PLAYERS; j++)
4002         if (field_player == NULL &&
4003             stored_player[j].present &&
4004             !stored_player[j].mapped)
4005           field_player = &stored_player[j];
4006 #else
4007       /* first try: look for unmapped playfield player that is not connected */
4008       if (field_player == NULL)
4009         for (j = 0; j < MAX_PLAYERS; j++)
4010           if (stored_player[j].present &&
4011               !stored_player[j].mapped &&
4012               !stored_player[j].connected)
4013             field_player = &stored_player[j];
4014
4015       /* second try: look for *any* unmapped playfield player */
4016       if (field_player == NULL)
4017         for (j = 0; j < MAX_PLAYERS; j++)
4018           if (stored_player[j].present &&
4019               !stored_player[j].mapped)
4020             field_player = &stored_player[j];
4021 #endif
4022
4023       if (field_player != NULL)
4024       {
4025         int jx = field_player->jx, jy = field_player->jy;
4026
4027 #if DEBUG_INIT_PLAYER
4028         if (options.debug)
4029           printf("- found player %d\n", field_player->index_nr + 1);
4030 #endif
4031
4032         player->present = FALSE;
4033         player->active = FALSE;
4034
4035         field_player->present = TRUE;
4036         field_player->active = TRUE;
4037
4038         /*
4039         player->initial_element = field_player->initial_element;
4040         player->artwork_element = field_player->artwork_element;
4041
4042         player->block_last_field       = field_player->block_last_field;
4043         player->block_delay_adjustment = field_player->block_delay_adjustment;
4044         */
4045
4046         StorePlayer[jx][jy] = field_player->element_nr;
4047
4048         field_player->jx = field_player->last_jx = jx;
4049         field_player->jy = field_player->last_jy = jy;
4050
4051         if (local_player == player)
4052           local_player = field_player;
4053
4054         map_player_action[field_player->index_nr] = i;
4055
4056         field_player->mapped = TRUE;
4057
4058 #if DEBUG_INIT_PLAYER
4059         if (options.debug)
4060           printf("- map_player_action[%d] == %d\n",
4061                  field_player->index_nr + 1, i + 1);
4062 #endif
4063       }
4064     }
4065
4066     if (player->connected && player->present)
4067       player->mapped = TRUE;
4068   }
4069
4070 #if DEBUG_INIT_PLAYER
4071   if (options.debug)
4072   {
4073     printf("Player status after player assignment (first stage):\n");
4074
4075     for (i = 0; i < MAX_PLAYERS; i++)
4076     {
4077       struct PlayerInfo *player = &stored_player[i];
4078
4079       printf("- player %d: present == %d, connected == %d, active == %d",
4080              i + 1,
4081              player->present,
4082              player->connected,
4083              player->active);
4084
4085       if (local_player == player)
4086         printf(" (local player)");
4087
4088       printf("\n");
4089     }
4090   }
4091 #endif
4092
4093 #else
4094
4095   /* check if any connected player was not found in playfield */
4096   for (i = 0; i < MAX_PLAYERS; i++)
4097   {
4098     struct PlayerInfo *player = &stored_player[i];
4099
4100     if (player->connected && !player->present)
4101     {
4102       for (j = 0; j < MAX_PLAYERS; j++)
4103       {
4104         struct PlayerInfo *field_player = &stored_player[j];
4105         int jx = field_player->jx, jy = field_player->jy;
4106
4107         /* assign first free player found that is present in the playfield */
4108         if (field_player->present && !field_player->connected)
4109         {
4110           player->present = TRUE;
4111           player->active = TRUE;
4112
4113           field_player->present = FALSE;
4114           field_player->active = FALSE;
4115
4116           player->initial_element = field_player->initial_element;
4117           player->artwork_element = field_player->artwork_element;
4118
4119           player->block_last_field       = field_player->block_last_field;
4120           player->block_delay_adjustment = field_player->block_delay_adjustment;
4121
4122           StorePlayer[jx][jy] = player->element_nr;
4123
4124           player->jx = player->last_jx = jx;
4125           player->jy = player->last_jy = jy;
4126
4127           break;
4128         }
4129       }
4130     }
4131   }
4132 #endif
4133
4134 #if 0
4135   printf("::: local_player->present == %d\n", local_player->present);
4136 #endif
4137
4138   if (tape.playing)
4139   {
4140     /* when playing a tape, eliminate all players who do not participate */
4141
4142 #if USE_NEW_PLAYER_ASSIGNMENTS
4143
4144 #if 1
4145     if (!game.team_mode)
4146 #endif
4147
4148     for (i = 0; i < MAX_PLAYERS; i++)
4149     {
4150       if (stored_player[i].active &&
4151           !tape.player_participates[map_player_action[i]])
4152       {
4153         struct PlayerInfo *player = &stored_player[i];
4154         int jx = player->jx, jy = player->jy;
4155
4156 #if DEBUG_INIT_PLAYER
4157         if (options.debug)
4158           printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4159 #endif
4160
4161         player->active = FALSE;
4162         StorePlayer[jx][jy] = 0;
4163         Feld[jx][jy] = EL_EMPTY;
4164       }
4165     }
4166
4167 #else
4168
4169     for (i = 0; i < MAX_PLAYERS; i++)
4170     {
4171       if (stored_player[i].active &&
4172           !tape.player_participates[i])
4173       {
4174         struct PlayerInfo *player = &stored_player[i];
4175         int jx = player->jx, jy = player->jy;
4176
4177         player->active = FALSE;
4178         StorePlayer[jx][jy] = 0;
4179         Feld[jx][jy] = EL_EMPTY;
4180       }
4181     }
4182 #endif
4183   }
4184   else if (!options.network && !game.team_mode)         /* && !tape.playing */
4185   {
4186     /* when in single player mode, eliminate all but the first active player */
4187
4188     for (i = 0; i < MAX_PLAYERS; i++)
4189     {
4190       if (stored_player[i].active)
4191       {
4192         for (j = i + 1; j < MAX_PLAYERS; j++)
4193         {
4194           if (stored_player[j].active)
4195           {
4196             struct PlayerInfo *player = &stored_player[j];
4197             int jx = player->jx, jy = player->jy;
4198
4199             player->active = FALSE;
4200             player->present = FALSE;
4201
4202             StorePlayer[jx][jy] = 0;
4203             Feld[jx][jy] = EL_EMPTY;
4204           }
4205         }
4206       }
4207     }
4208   }
4209
4210   /* when recording the game, store which players take part in the game */
4211   if (tape.recording)
4212   {
4213 #if USE_NEW_PLAYER_ASSIGNMENTS
4214     for (i = 0; i < MAX_PLAYERS; i++)
4215       if (stored_player[i].connected)
4216         tape.player_participates[i] = TRUE;
4217 #else
4218     for (i = 0; i < MAX_PLAYERS; i++)
4219       if (stored_player[i].active)
4220         tape.player_participates[i] = TRUE;
4221 #endif
4222   }
4223
4224 #if DEBUG_INIT_PLAYER
4225   if (options.debug)
4226   {
4227     printf("Player status after player assignment (final stage):\n");
4228
4229     for (i = 0; i < MAX_PLAYERS; i++)
4230     {
4231       struct PlayerInfo *player = &stored_player[i];
4232
4233       printf("- player %d: present == %d, connected == %d, active == %d",
4234              i + 1,
4235              player->present,
4236              player->connected,
4237              player->active);
4238
4239       if (local_player == player)
4240         printf(" (local player)");
4241
4242       printf("\n");
4243     }
4244   }
4245 #endif
4246
4247   if (BorderElement == EL_EMPTY)
4248   {
4249     SBX_Left = 0;
4250     SBX_Right = lev_fieldx - SCR_FIELDX;
4251     SBY_Upper = 0;
4252     SBY_Lower = lev_fieldy - SCR_FIELDY;
4253   }
4254   else
4255   {
4256     SBX_Left = -1;
4257     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4258     SBY_Upper = -1;
4259     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4260   }
4261
4262 #if NEW_TILESIZE
4263
4264   if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4265     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4266
4267   if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4268     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4269
4270   if (EVEN(SCR_FIELDX))
4271     SBX_Left--;
4272   if (EVEN(SCR_FIELDY))
4273     SBY_Upper--;
4274
4275 #else
4276
4277   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4278     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4279
4280   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4281     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4282 #endif
4283
4284   /* if local player not found, look for custom element that might create
4285      the player (make some assumptions about the right custom element) */
4286   if (!local_player->present)
4287   {
4288     int start_x = 0, start_y = 0;
4289     int found_rating = 0;
4290     int found_element = EL_UNDEFINED;
4291     int player_nr = local_player->index_nr;
4292
4293     SCAN_PLAYFIELD(x, y)
4294     {
4295       int element = Feld[x][y];
4296       int content;
4297       int xx, yy;
4298       boolean is_player;
4299
4300       if (level.use_start_element[player_nr] &&
4301           level.start_element[player_nr] == element &&
4302           found_rating < 4)
4303       {
4304         start_x = x;
4305         start_y = y;
4306
4307         found_rating = 4;
4308         found_element = element;
4309       }
4310
4311       if (!IS_CUSTOM_ELEMENT(element))
4312         continue;
4313
4314       if (CAN_CHANGE(element))
4315       {
4316         for (i = 0; i < element_info[element].num_change_pages; i++)
4317         {
4318           /* check for player created from custom element as single target */
4319           content = element_info[element].change_page[i].target_element;
4320           is_player = ELEM_IS_PLAYER(content);
4321
4322           if (is_player && (found_rating < 3 ||
4323                             (found_rating == 3 && element < found_element)))
4324           {
4325             start_x = x;
4326             start_y = y;
4327
4328             found_rating = 3;
4329             found_element = element;
4330           }
4331         }
4332       }
4333
4334       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4335       {
4336         /* check for player created from custom element as explosion content */
4337         content = element_info[element].content.e[xx][yy];
4338         is_player = ELEM_IS_PLAYER(content);
4339
4340         if (is_player && (found_rating < 2 ||
4341                           (found_rating == 2 && element < found_element)))
4342         {
4343           start_x = x + xx - 1;
4344           start_y = y + yy - 1;
4345
4346           found_rating = 2;
4347           found_element = element;
4348         }
4349
4350         if (!CAN_CHANGE(element))
4351           continue;
4352
4353         for (i = 0; i < element_info[element].num_change_pages; i++)
4354         {
4355           /* check for player created from custom element as extended target */
4356           content =
4357             element_info[element].change_page[i].target_content.e[xx][yy];
4358
4359           is_player = ELEM_IS_PLAYER(content);
4360
4361           if (is_player && (found_rating < 1 ||
4362                             (found_rating == 1 && element < found_element)))
4363           {
4364             start_x = x + xx - 1;
4365             start_y = y + yy - 1;
4366
4367             found_rating = 1;
4368             found_element = element;
4369           }
4370         }
4371       }
4372     }
4373
4374     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4375                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4376                 start_x - MIDPOSX);
4377
4378     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4379                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4380                 start_y - MIDPOSY);
4381   }
4382   else
4383   {
4384     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4385                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4386                 local_player->jx - MIDPOSX);
4387
4388     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4389                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4390                 local_player->jy - MIDPOSY);
4391   }
4392
4393 #if 0
4394   printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4395 #endif
4396
4397 #if 0
4398   /* do not use PLAYING mask for fading out from main screen */
4399   game_status = GAME_MODE_MAIN;
4400 #endif
4401
4402   StopAnimation();
4403
4404   if (!game.restart_level)
4405     CloseDoor(DOOR_CLOSE_1);
4406
4407 #if 1
4408   if (level_editor_test_game)
4409     FadeSkipNextFadeIn();
4410   else
4411     FadeSetEnterScreen();
4412 #else
4413   if (level_editor_test_game)
4414     fading = fading_none;
4415   else
4416     fading = menu.destination;
4417 #endif
4418
4419 #if 1
4420   FadeOut(REDRAW_FIELD);
4421 #else
4422   if (do_fading)
4423     FadeOut(REDRAW_FIELD);
4424 #endif
4425
4426 #if 0
4427   game_status = GAME_MODE_PLAYING;
4428 #endif
4429
4430   /* !!! FIX THIS (START) !!! */
4431   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4432   {
4433     InitGameEngine_EM();
4434
4435     /* blit playfield from scroll buffer to normal back buffer for fading in */
4436     BlitScreenToBitmap_EM(backbuffer);
4437   }
4438   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4439   {
4440     InitGameEngine_SP();
4441
4442     /* blit playfield from scroll buffer to normal back buffer for fading in */
4443     BlitScreenToBitmap_SP(backbuffer);
4444   }
4445   else
4446   {
4447     DrawLevel();
4448     DrawAllPlayers();
4449
4450     /* after drawing the level, correct some elements */
4451     if (game.timegate_time_left == 0)
4452       CloseAllOpenTimegates();
4453
4454 #if NEW_TILESIZE
4455     BlitScreenToBitmap(backbuffer);
4456 #else
4457     /* blit playfield from scroll buffer to normal back buffer for fading in */
4458     if (setup.soft_scrolling)
4459       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4460 #endif
4461
4462     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4463   }
4464   /* !!! FIX THIS (END) !!! */
4465
4466 #if 1
4467   FadeIn(REDRAW_FIELD);
4468 #else
4469   if (do_fading)
4470     FadeIn(REDRAW_FIELD);
4471
4472   BackToFront();
4473 #endif
4474
4475   if (!game.restart_level)
4476   {
4477     /* copy default game door content to main double buffer */
4478 #if 1
4479 #if 1
4480     /* !!! CHECK AGAIN !!! */
4481     SetPanelBackground();
4482     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4483     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4484 #else
4485     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4486
4487     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4488     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4489     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4490                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4491 #endif
4492 #else
4493     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4494                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4495 #endif
4496   }
4497
4498   SetPanelBackground();
4499   SetDrawBackgroundMask(REDRAW_DOOR_1);
4500
4501 #if 1
4502   UpdateAndDisplayGameControlValues();
4503 #else
4504   UpdateGameDoorValues();
4505   DrawGameDoorValues();
4506 #endif
4507
4508   if (!game.restart_level)
4509   {
4510     UnmapGameButtons();
4511     UnmapTapeButtons();
4512     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4513     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4514     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4515     MapGameButtons();
4516     MapTapeButtons();
4517
4518     /* copy actual game door content to door double buffer for OpenDoor() */
4519     BlitBitmap(drawto, bitmap_db_door,
4520                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4521
4522     OpenDoor(DOOR_OPEN_ALL);
4523
4524     PlaySound(SND_GAME_STARTING);
4525
4526     if (setup.sound_music)
4527       PlayLevelMusic();
4528
4529     KeyboardAutoRepeatOffUnlessAutoplay();
4530
4531 #if DEBUG_INIT_PLAYER
4532     if (options.debug)
4533     {
4534       printf("Player status (final):\n");
4535
4536       for (i = 0; i < MAX_PLAYERS; i++)
4537       {
4538         struct PlayerInfo *player = &stored_player[i];
4539
4540         printf("- player %d: present == %d, connected == %d, active == %d",
4541                i + 1,
4542                player->present,
4543                player->connected,
4544                player->active);
4545
4546         if (local_player == player)
4547           printf(" (local player)");
4548
4549         printf("\n");
4550       }
4551     }
4552 #endif
4553   }
4554
4555 #if 1
4556   UnmapAllGadgets();
4557
4558   MapGameButtons();
4559   MapTapeButtons();
4560 #endif
4561
4562   if (!game.restart_level && !tape.playing)
4563   {
4564     LevelStats_incPlayed(level_nr);
4565
4566     SaveLevelSetup_SeriesInfo();
4567
4568 #if 0
4569     printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4570 #endif
4571   }
4572
4573   game.restart_level = FALSE;
4574 }
4575
4576 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4577 {
4578   /* this is used for non-R'n'D game engines to update certain engine values */
4579
4580   /* needed to determine if sounds are played within the visible screen area */
4581   scroll_x = actual_scroll_x;
4582   scroll_y = actual_scroll_y;
4583 }
4584
4585 void InitMovDir(int x, int y)
4586 {
4587   int i, element = Feld[x][y];
4588   static int xy[4][2] =
4589   {
4590     {  0, +1 },
4591     { +1,  0 },
4592     {  0, -1 },
4593     { -1,  0 }
4594   };
4595   static int direction[3][4] =
4596   {
4597     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4598     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4599     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4600   };
4601
4602   switch (element)
4603   {
4604     case EL_BUG_RIGHT:
4605     case EL_BUG_UP:
4606     case EL_BUG_LEFT:
4607     case EL_BUG_DOWN:
4608       Feld[x][y] = EL_BUG;
4609       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4610       break;
4611
4612     case EL_SPACESHIP_RIGHT:
4613     case EL_SPACESHIP_UP:
4614     case EL_SPACESHIP_LEFT:
4615     case EL_SPACESHIP_DOWN:
4616       Feld[x][y] = EL_SPACESHIP;
4617       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4618       break;
4619
4620     case EL_BD_BUTTERFLY_RIGHT:
4621     case EL_BD_BUTTERFLY_UP:
4622     case EL_BD_BUTTERFLY_LEFT:
4623     case EL_BD_BUTTERFLY_DOWN:
4624       Feld[x][y] = EL_BD_BUTTERFLY;
4625       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4626       break;
4627
4628     case EL_BD_FIREFLY_RIGHT:
4629     case EL_BD_FIREFLY_UP:
4630     case EL_BD_FIREFLY_LEFT:
4631     case EL_BD_FIREFLY_DOWN:
4632       Feld[x][y] = EL_BD_FIREFLY;
4633       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4634       break;
4635
4636     case EL_PACMAN_RIGHT:
4637     case EL_PACMAN_UP:
4638     case EL_PACMAN_LEFT:
4639     case EL_PACMAN_DOWN:
4640       Feld[x][y] = EL_PACMAN;
4641       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4642       break;
4643
4644     case EL_YAMYAM_LEFT:
4645     case EL_YAMYAM_RIGHT:
4646     case EL_YAMYAM_UP:
4647     case EL_YAMYAM_DOWN:
4648       Feld[x][y] = EL_YAMYAM;
4649       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4650       break;
4651
4652     case EL_SP_SNIKSNAK:
4653       MovDir[x][y] = MV_UP;
4654       break;
4655
4656     case EL_SP_ELECTRON:
4657       MovDir[x][y] = MV_LEFT;
4658       break;
4659
4660     case EL_MOLE_LEFT:
4661     case EL_MOLE_RIGHT:
4662     case EL_MOLE_UP:
4663     case EL_MOLE_DOWN:
4664       Feld[x][y] = EL_MOLE;
4665       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4666       break;
4667
4668     default:
4669       if (IS_CUSTOM_ELEMENT(element))
4670       {
4671         struct ElementInfo *ei = &element_info[element];
4672         int move_direction_initial = ei->move_direction_initial;
4673         int move_pattern = ei->move_pattern;
4674
4675         if (move_direction_initial == MV_START_PREVIOUS)
4676         {
4677           if (MovDir[x][y] != MV_NONE)
4678             return;
4679
4680           move_direction_initial = MV_START_AUTOMATIC;
4681         }
4682
4683         if (move_direction_initial == MV_START_RANDOM)
4684           MovDir[x][y] = 1 << RND(4);
4685         else if (move_direction_initial & MV_ANY_DIRECTION)
4686           MovDir[x][y] = move_direction_initial;
4687         else if (move_pattern == MV_ALL_DIRECTIONS ||
4688                  move_pattern == MV_TURNING_LEFT ||
4689                  move_pattern == MV_TURNING_RIGHT ||
4690                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4691                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4692                  move_pattern == MV_TURNING_RANDOM)
4693           MovDir[x][y] = 1 << RND(4);
4694         else if (move_pattern == MV_HORIZONTAL)
4695           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4696         else if (move_pattern == MV_VERTICAL)
4697           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4698         else if (move_pattern & MV_ANY_DIRECTION)
4699           MovDir[x][y] = element_info[element].move_pattern;
4700         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4701                  move_pattern == MV_ALONG_RIGHT_SIDE)
4702         {
4703           /* use random direction as default start direction */
4704           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4705             MovDir[x][y] = 1 << RND(4);
4706
4707           for (i = 0; i < NUM_DIRECTIONS; i++)
4708           {
4709             int x1 = x + xy[i][0];
4710             int y1 = y + xy[i][1];
4711
4712             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4713             {
4714               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4715                 MovDir[x][y] = direction[0][i];
4716               else
4717                 MovDir[x][y] = direction[1][i];
4718
4719               break;
4720             }
4721           }
4722         }                
4723       }
4724       else
4725       {
4726         MovDir[x][y] = 1 << RND(4);
4727
4728         if (element != EL_BUG &&
4729             element != EL_SPACESHIP &&
4730             element != EL_BD_BUTTERFLY &&
4731             element != EL_BD_FIREFLY)
4732           break;
4733
4734         for (i = 0; i < NUM_DIRECTIONS; i++)
4735         {
4736           int x1 = x + xy[i][0];
4737           int y1 = y + xy[i][1];
4738
4739           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4740           {
4741             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4742             {
4743               MovDir[x][y] = direction[0][i];
4744               break;
4745             }
4746             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4747                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4748             {
4749               MovDir[x][y] = direction[1][i];
4750               break;
4751             }
4752           }
4753         }
4754       }
4755       break;
4756   }
4757
4758   GfxDir[x][y] = MovDir[x][y];
4759 }
4760
4761 void InitAmoebaNr(int x, int y)
4762 {
4763   int i;
4764   int group_nr = AmoebeNachbarNr(x, y);
4765
4766   if (group_nr == 0)
4767   {
4768     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4769     {
4770       if (AmoebaCnt[i] == 0)
4771       {
4772         group_nr = i;
4773         break;
4774       }
4775     }
4776   }
4777
4778   AmoebaNr[x][y] = group_nr;
4779   AmoebaCnt[group_nr]++;
4780   AmoebaCnt2[group_nr]++;
4781 }
4782
4783 static void PlayerWins(struct PlayerInfo *player)
4784 {
4785   player->LevelSolved = TRUE;
4786   player->GameOver = TRUE;
4787
4788   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4789                          level.native_em_level->lev->score : player->score);
4790
4791   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4792                                       TimeLeft);
4793   player->LevelSolved_CountingScore = player->score_final;
4794 }
4795
4796 void GameWon()
4797 {
4798   static int time, time_final;
4799   static int score, score_final;
4800   static int game_over_delay_1 = 0;
4801   static int game_over_delay_2 = 0;
4802   int game_over_delay_value_1 = 50;
4803   int game_over_delay_value_2 = 50;
4804
4805   if (!local_player->LevelSolved_GameWon)
4806   {
4807     int i;
4808
4809     /* do not start end game actions before the player stops moving (to exit) */
4810     if (local_player->MovPos)
4811       return;
4812
4813     local_player->LevelSolved_GameWon = TRUE;
4814     local_player->LevelSolved_SaveTape = tape.recording;
4815     local_player->LevelSolved_SaveScore = !tape.playing;
4816
4817     if (!tape.playing)
4818     {
4819       LevelStats_incSolved(level_nr);
4820
4821       SaveLevelSetup_SeriesInfo();
4822
4823 #if 0
4824       printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4825 #endif
4826     }
4827
4828     if (tape.auto_play)         /* tape might already be stopped here */
4829       tape.auto_play_level_solved = TRUE;
4830
4831 #if 1
4832     TapeStop();
4833 #endif
4834
4835     game_over_delay_1 = game_over_delay_value_1;
4836     game_over_delay_2 = game_over_delay_value_2;
4837
4838     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4839     score = score_final = local_player->score_final;
4840
4841     if (TimeLeft > 0)
4842     {
4843       time_final = 0;
4844       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4845     }
4846     else if (game.no_time_limit && TimePlayed < 999)
4847     {
4848       time_final = 999;
4849       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4850     }
4851
4852     local_player->score_final = score_final;
4853
4854     if (level_editor_test_game)
4855     {
4856       time = time_final;
4857       score = score_final;
4858
4859 #if 1
4860       local_player->LevelSolved_CountingTime = time;
4861       local_player->LevelSolved_CountingScore = score;
4862
4863       game_panel_controls[GAME_PANEL_TIME].value = time;
4864       game_panel_controls[GAME_PANEL_SCORE].value = score;
4865
4866       DisplayGameControlValues();
4867 #else
4868       DrawGameValue_Time(time);
4869       DrawGameValue_Score(score);
4870 #endif
4871     }
4872
4873     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4874     {
4875       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4876       {
4877         /* close exit door after last player */
4878         if ((AllPlayersGone &&
4879              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4880               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4881               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4882             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4883             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4884         {
4885           int element = Feld[ExitX][ExitY];
4886
4887 #if 0
4888           if (element == EL_EM_EXIT_OPEN ||
4889               element == EL_EM_STEEL_EXIT_OPEN)
4890           {
4891             Bang(ExitX, ExitY);
4892           }
4893           else
4894 #endif
4895           {
4896             Feld[ExitX][ExitY] =
4897               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4898                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4899                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4900                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4901                EL_EM_STEEL_EXIT_CLOSING);
4902
4903             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4904           }
4905         }
4906
4907         /* player disappears */
4908         DrawLevelField(ExitX, ExitY);
4909       }
4910
4911       for (i = 0; i < MAX_PLAYERS; i++)
4912       {
4913         struct PlayerInfo *player = &stored_player[i];
4914
4915         if (player->present)
4916         {
4917           RemovePlayer(player);
4918
4919           /* player disappears */
4920           DrawLevelField(player->jx, player->jy);
4921         }
4922       }
4923     }
4924
4925     PlaySound(SND_GAME_WINNING);
4926   }
4927
4928   if (game_over_delay_1 > 0)
4929   {
4930     game_over_delay_1--;
4931
4932     return;
4933   }
4934
4935   if (time != time_final)
4936   {
4937     int time_to_go = ABS(time_final - time);
4938     int time_count_dir = (time < time_final ? +1 : -1);
4939     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4940
4941     time  += time_count_steps * time_count_dir;
4942     score += time_count_steps * level.score[SC_TIME_BONUS];
4943
4944 #if 1
4945     local_player->LevelSolved_CountingTime = time;
4946     local_player->LevelSolved_CountingScore = score;
4947
4948     game_panel_controls[GAME_PANEL_TIME].value = time;
4949     game_panel_controls[GAME_PANEL_SCORE].value = score;
4950
4951     DisplayGameControlValues();
4952 #else
4953     DrawGameValue_Time(time);
4954     DrawGameValue_Score(score);
4955 #endif
4956
4957     if (time == time_final)
4958       StopSound(SND_GAME_LEVELTIME_BONUS);
4959     else if (setup.sound_loops)
4960       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4961     else
4962       PlaySound(SND_GAME_LEVELTIME_BONUS);
4963
4964     return;
4965   }
4966
4967   local_player->LevelSolved_PanelOff = TRUE;
4968
4969   if (game_over_delay_2 > 0)
4970   {
4971     game_over_delay_2--;
4972
4973     return;
4974   }
4975
4976 #if 1
4977   GameEnd();
4978 #endif
4979 }
4980
4981 void GameEnd()
4982 {
4983   int hi_pos;
4984   boolean raise_level = FALSE;
4985
4986   local_player->LevelSolved_GameEnd = TRUE;
4987
4988   CloseDoor(DOOR_CLOSE_1);
4989
4990   if (local_player->LevelSolved_SaveTape)
4991   {
4992 #if 0
4993     TapeStop();
4994 #endif
4995
4996 #if 1
4997     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4998 #else
4999     SaveTape(tape.level_nr);            /* ask to save tape */
5000 #endif
5001   }
5002
5003   if (level_editor_test_game)
5004   {
5005     game_status = GAME_MODE_MAIN;
5006
5007 #if 1
5008     DrawAndFadeInMainMenu(REDRAW_FIELD);
5009 #else
5010     DrawMainMenu();
5011 #endif
5012
5013     return;
5014   }
5015
5016   if (!local_player->LevelSolved_SaveScore)
5017   {
5018 #if 1
5019     FadeOut(REDRAW_FIELD);
5020 #endif
5021
5022     game_status = GAME_MODE_MAIN;
5023
5024     DrawAndFadeInMainMenu(REDRAW_FIELD);
5025
5026     return;
5027   }
5028
5029   if (level_nr == leveldir_current->handicap_level)
5030   {
5031     leveldir_current->handicap_level++;
5032
5033     SaveLevelSetup_SeriesInfo();
5034   }
5035
5036   if (level_nr < leveldir_current->last_level)
5037     raise_level = TRUE;                 /* advance to next level */
5038
5039   if ((hi_pos = NewHiScore()) >= 0) 
5040   {
5041     game_status = GAME_MODE_SCORES;
5042
5043     DrawHallOfFame(hi_pos);
5044
5045     if (raise_level)
5046     {
5047       level_nr++;
5048       TapeErase();
5049     }
5050   }
5051   else
5052   {
5053 #if 1
5054     FadeOut(REDRAW_FIELD);
5055 #endif
5056
5057     game_status = GAME_MODE_MAIN;
5058
5059     if (raise_level)
5060     {
5061       level_nr++;
5062       TapeErase();
5063     }
5064
5065     DrawAndFadeInMainMenu(REDRAW_FIELD);
5066   }
5067 }
5068
5069 int NewHiScore()
5070 {
5071   int k, l;
5072   int position = -1;
5073
5074   LoadScore(level_nr);
5075
5076   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5077       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5078     return -1;
5079
5080   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5081   {
5082     if (local_player->score_final > highscore[k].Score)
5083     {
5084       /* player has made it to the hall of fame */
5085
5086       if (k < MAX_SCORE_ENTRIES - 1)
5087       {
5088         int m = MAX_SCORE_ENTRIES - 1;
5089
5090 #ifdef ONE_PER_NAME
5091         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5092           if (strEqual(setup.player_name, highscore[l].Name))
5093             m = l;
5094         if (m == k)     /* player's new highscore overwrites his old one */
5095           goto put_into_list;
5096 #endif
5097
5098         for (l = m; l > k; l--)
5099         {
5100           strcpy(highscore[l].Name, highscore[l - 1].Name);
5101           highscore[l].Score = highscore[l - 1].Score;
5102         }
5103       }
5104
5105 #ifdef ONE_PER_NAME
5106       put_into_list:
5107 #endif
5108       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5109       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5110       highscore[k].Score = local_player->score_final; 
5111       position = k;
5112       break;
5113     }
5114
5115 #ifdef ONE_PER_NAME
5116     else if (!strncmp(setup.player_name, highscore[k].Name,
5117                       MAX_PLAYER_NAME_LEN))
5118       break;    /* player already there with a higher score */
5119 #endif
5120
5121   }
5122
5123   if (position >= 0) 
5124     SaveScore(level_nr);
5125
5126   return position;
5127 }
5128
5129 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5130 {
5131   int element = Feld[x][y];
5132   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5133   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5134   int horiz_move = (dx != 0);
5135   int sign = (horiz_move ? dx : dy);
5136   int step = sign * element_info[element].move_stepsize;
5137
5138   /* special values for move stepsize for spring and things on conveyor belt */
5139   if (horiz_move)
5140   {
5141     if (CAN_FALL(element) &&
5142         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5143       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5144     else if (element == EL_SPRING)
5145       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5146   }
5147
5148   return step;
5149 }
5150
5151 inline static int getElementMoveStepsize(int x, int y)
5152 {
5153   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5154 }
5155
5156 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5157 {
5158   if (player->GfxAction != action || player->GfxDir != dir)
5159   {
5160 #if 0
5161     printf("Player frame reset! (%d => %d, %d => %d)\n",
5162            player->GfxAction, action, player->GfxDir, dir);
5163 #endif
5164
5165     player->GfxAction = action;
5166     player->GfxDir = dir;
5167     player->Frame = 0;
5168     player->StepFrame = 0;
5169   }
5170 }
5171
5172 #if USE_GFX_RESET_GFX_ANIMATION
5173 static void ResetGfxFrame(int x, int y, boolean redraw)
5174 {
5175   int element = Feld[x][y];
5176   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5177   int last_gfx_frame = GfxFrame[x][y];
5178
5179   if (graphic_info[graphic].anim_global_sync)
5180     GfxFrame[x][y] = FrameCounter;
5181   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5182     GfxFrame[x][y] = CustomValue[x][y];
5183   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5184     GfxFrame[x][y] = element_info[element].collect_score;
5185   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5186     GfxFrame[x][y] = ChangeDelay[x][y];
5187
5188   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5189     DrawLevelGraphicAnimation(x, y, graphic);
5190 }
5191 #endif
5192
5193 static void ResetGfxAnimation(int x, int y)
5194 {
5195   GfxAction[x][y] = ACTION_DEFAULT;
5196   GfxDir[x][y] = MovDir[x][y];
5197   GfxFrame[x][y] = 0;
5198
5199 #if USE_GFX_RESET_GFX_ANIMATION
5200   ResetGfxFrame(x, y, FALSE);
5201 #endif
5202 }
5203
5204 static void ResetRandomAnimationValue(int x, int y)
5205 {
5206   GfxRandom[x][y] = INIT_GFX_RANDOM();
5207 }
5208
5209 void InitMovingField(int x, int y, int direction)
5210 {
5211   int element = Feld[x][y];
5212   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5213   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5214   int newx = x + dx;
5215   int newy = y + dy;
5216   boolean is_moving_before, is_moving_after;
5217 #if 0
5218   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5219 #endif
5220
5221   /* check if element was/is moving or being moved before/after mode change */
5222 #if 1
5223 #if 1
5224   is_moving_before = (WasJustMoving[x][y] != 0);
5225 #else
5226   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5227   is_moving_before = WasJustMoving[x][y];
5228 #endif
5229 #else
5230   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5231 #endif
5232   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5233
5234   /* reset animation only for moving elements which change direction of moving
5235      or which just started or stopped moving
5236      (else CEs with property "can move" / "not moving" are reset each frame) */
5237 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5238 #if 1
5239   if (is_moving_before != is_moving_after ||
5240       direction != MovDir[x][y])
5241     ResetGfxAnimation(x, y);
5242 #else
5243   if ((is_moving_before || is_moving_after) && !continues_moving)
5244     ResetGfxAnimation(x, y);
5245 #endif
5246 #else
5247   if (!continues_moving)
5248     ResetGfxAnimation(x, y);
5249 #endif
5250
5251   MovDir[x][y] = direction;
5252   GfxDir[x][y] = direction;
5253
5254 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5255   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5256                      direction == MV_DOWN && CAN_FALL(element) ?
5257                      ACTION_FALLING : ACTION_MOVING);
5258 #else
5259   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5260                      ACTION_FALLING : ACTION_MOVING);
5261 #endif
5262
5263   /* this is needed for CEs with property "can move" / "not moving" */
5264
5265   if (is_moving_after)
5266   {
5267     if (Feld[newx][newy] == EL_EMPTY)
5268       Feld[newx][newy] = EL_BLOCKED;
5269
5270     MovDir[newx][newy] = MovDir[x][y];
5271
5272 #if USE_NEW_CUSTOM_VALUE
5273     CustomValue[newx][newy] = CustomValue[x][y];
5274 #endif
5275
5276     GfxFrame[newx][newy] = GfxFrame[x][y];
5277     GfxRandom[newx][newy] = GfxRandom[x][y];
5278     GfxAction[newx][newy] = GfxAction[x][y];
5279     GfxDir[newx][newy] = GfxDir[x][y];
5280   }
5281 }
5282
5283 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5284 {
5285   int direction = MovDir[x][y];
5286   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5287   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5288
5289   *goes_to_x = newx;
5290   *goes_to_y = newy;
5291 }
5292
5293 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5294 {
5295   int oldx = x, oldy = y;
5296   int direction = MovDir[x][y];
5297
5298   if (direction == MV_LEFT)
5299     oldx++;
5300   else if (direction == MV_RIGHT)
5301     oldx--;
5302   else if (direction == MV_UP)
5303     oldy++;
5304   else if (direction == MV_DOWN)
5305     oldy--;
5306
5307   *comes_from_x = oldx;
5308   *comes_from_y = oldy;
5309 }
5310
5311 int MovingOrBlocked2Element(int x, int y)
5312 {
5313   int element = Feld[x][y];
5314
5315   if (element == EL_BLOCKED)
5316   {
5317     int oldx, oldy;
5318
5319     Blocked2Moving(x, y, &oldx, &oldy);
5320     return Feld[oldx][oldy];
5321   }
5322   else
5323     return element;
5324 }
5325
5326 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5327 {
5328   /* like MovingOrBlocked2Element(), but if element is moving
5329      and (x,y) is the field the moving element is just leaving,
5330      return EL_BLOCKED instead of the element value */
5331   int element = Feld[x][y];
5332
5333   if (IS_MOVING(x, y))
5334   {
5335     if (element == EL_BLOCKED)
5336     {
5337       int oldx, oldy;
5338
5339       Blocked2Moving(x, y, &oldx, &oldy);
5340       return Feld[oldx][oldy];
5341     }
5342     else
5343       return EL_BLOCKED;
5344   }
5345   else
5346     return element;
5347 }
5348
5349 static void RemoveField(int x, int y)
5350 {
5351   Feld[x][y] = EL_EMPTY;
5352
5353   MovPos[x][y] = 0;
5354   MovDir[x][y] = 0;
5355   MovDelay[x][y] = 0;
5356
5357 #if USE_NEW_CUSTOM_VALUE
5358   CustomValue[x][y] = 0;
5359 #endif
5360
5361   AmoebaNr[x][y] = 0;
5362   ChangeDelay[x][y] = 0;
5363   ChangePage[x][y] = -1;
5364   Pushed[x][y] = FALSE;
5365
5366 #if 0
5367   ExplodeField[x][y] = EX_TYPE_NONE;
5368 #endif
5369
5370   GfxElement[x][y] = EL_UNDEFINED;
5371   GfxAction[x][y] = ACTION_DEFAULT;
5372   GfxDir[x][y] = MV_NONE;
5373 #if 0
5374   /* !!! this would prevent the removed tile from being redrawn !!! */
5375   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5376 #endif
5377 }
5378
5379 void RemoveMovingField(int x, int y)
5380 {
5381   int oldx = x, oldy = y, newx = x, newy = y;
5382   int element = Feld[x][y];
5383   int next_element = EL_UNDEFINED;
5384
5385   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5386     return;
5387
5388   if (IS_MOVING(x, y))
5389   {
5390     Moving2Blocked(x, y, &newx, &newy);
5391
5392     if (Feld[newx][newy] != EL_BLOCKED)
5393     {
5394       /* element is moving, but target field is not free (blocked), but
5395          already occupied by something different (example: acid pool);
5396          in this case, only remove the moving field, but not the target */
5397
5398       RemoveField(oldx, oldy);
5399
5400       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5401
5402       TEST_DrawLevelField(oldx, oldy);
5403
5404       return;
5405     }
5406   }
5407   else if (element == EL_BLOCKED)
5408   {
5409     Blocked2Moving(x, y, &oldx, &oldy);
5410     if (!IS_MOVING(oldx, oldy))
5411       return;
5412   }
5413
5414   if (element == EL_BLOCKED &&
5415       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5416        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5417        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5418        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5419        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5420        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5421     next_element = get_next_element(Feld[oldx][oldy]);
5422
5423   RemoveField(oldx, oldy);
5424   RemoveField(newx, newy);
5425
5426   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5427
5428   if (next_element != EL_UNDEFINED)
5429     Feld[oldx][oldy] = next_element;
5430
5431   TEST_DrawLevelField(oldx, oldy);
5432   TEST_DrawLevelField(newx, newy);
5433 }
5434
5435 void DrawDynamite(int x, int y)
5436 {
5437   int sx = SCREENX(x), sy = SCREENY(y);
5438   int graphic = el2img(Feld[x][y]);
5439   int frame;
5440
5441   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5442     return;
5443
5444   if (IS_WALKABLE_INSIDE(Back[x][y]))
5445     return;
5446
5447   if (Back[x][y])
5448     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5449   else if (Store[x][y])
5450     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5451
5452   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5453
5454   if (Back[x][y] || Store[x][y])
5455     DrawGraphicThruMask(sx, sy, graphic, frame);
5456   else
5457     DrawGraphic(sx, sy, graphic, frame);
5458 }
5459
5460 void CheckDynamite(int x, int y)
5461 {
5462   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5463   {
5464     MovDelay[x][y]--;
5465
5466     if (MovDelay[x][y] != 0)
5467     {
5468       DrawDynamite(x, y);
5469       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5470
5471       return;
5472     }
5473   }
5474
5475   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5476
5477   Bang(x, y);
5478 }
5479
5480 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5481 {
5482   boolean num_checked_players = 0;
5483   int i;
5484
5485   for (i = 0; i < MAX_PLAYERS; i++)
5486   {
5487     if (stored_player[i].active)
5488     {
5489       int sx = stored_player[i].jx;
5490       int sy = stored_player[i].jy;
5491
5492       if (num_checked_players == 0)
5493       {
5494         *sx1 = *sx2 = sx;
5495         *sy1 = *sy2 = sy;
5496       }
5497       else
5498       {
5499         *sx1 = MIN(*sx1, sx);
5500         *sy1 = MIN(*sy1, sy);
5501         *sx2 = MAX(*sx2, sx);
5502         *sy2 = MAX(*sy2, sy);
5503       }
5504
5505       num_checked_players++;
5506     }
5507   }
5508 }
5509
5510 static boolean checkIfAllPlayersFitToScreen_RND()
5511 {
5512   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5513
5514   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5515
5516   return (sx2 - sx1 < SCR_FIELDX &&
5517           sy2 - sy1 < SCR_FIELDY);
5518 }
5519
5520 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5521 {
5522   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5523
5524   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5525
5526   *sx = (sx1 + sx2) / 2;
5527   *sy = (sy1 + sy2) / 2;
5528 }
5529
5530 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5531                         boolean center_screen, boolean quick_relocation)
5532 {
5533   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5534   boolean no_delay = (tape.warp_forward);
5535   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5536   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5537
5538   if (quick_relocation)
5539   {
5540     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5541     {
5542       if (!level.shifted_relocation || center_screen)
5543       {
5544         /* quick relocation (without scrolling), with centering of screen */
5545
5546         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5547                     x > SBX_Right + MIDPOSX ? SBX_Right :
5548                     x - MIDPOSX);
5549
5550         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5551                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5552                     y - MIDPOSY);
5553       }
5554       else
5555       {
5556         /* quick relocation (without scrolling), but do not center screen */
5557
5558         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5559                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5560                                old_x - MIDPOSX);
5561
5562         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5563                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5564                                old_y - MIDPOSY);
5565
5566         int offset_x = x + (scroll_x - center_scroll_x);
5567         int offset_y = y + (scroll_y - center_scroll_y);
5568
5569         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5570                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5571                     offset_x - MIDPOSX);
5572
5573         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5574                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5575                     offset_y - MIDPOSY);
5576       }
5577     }
5578     else
5579     {
5580 #if 1
5581       if (!level.shifted_relocation || center_screen)
5582       {
5583         /* quick relocation (without scrolling), with centering of screen */
5584
5585         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5586                     x > SBX_Right + MIDPOSX ? SBX_Right :
5587                     x - MIDPOSX);
5588
5589         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5590                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5591                     y - MIDPOSY);
5592       }
5593       else
5594       {
5595         /* quick relocation (without scrolling), but do not center screen */
5596
5597         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5598                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5599                                old_x - MIDPOSX);
5600
5601         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5602                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5603                                old_y - MIDPOSY);
5604
5605         int offset_x = x + (scroll_x - center_scroll_x);
5606         int offset_y = y + (scroll_y - center_scroll_y);
5607
5608         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5609                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5610                     offset_x - MIDPOSX);
5611
5612         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5613                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5614                     offset_y - MIDPOSY);
5615       }
5616 #else
5617       /* quick relocation (without scrolling), inside visible screen area */
5618
5619       int offset = game.scroll_delay_value;
5620
5621       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5622           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5623         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5624
5625       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5626           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5627         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5628
5629       /* don't scroll over playfield boundaries */
5630       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5631         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5632
5633       /* don't scroll over playfield boundaries */
5634       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5635         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5636 #endif
5637     }
5638
5639     RedrawPlayfield(TRUE, 0,0,0,0);
5640   }
5641   else
5642   {
5643 #if 1
5644     int scroll_xx, scroll_yy;
5645
5646     if (!level.shifted_relocation || center_screen)
5647     {
5648       /* visible relocation (with scrolling), with centering of screen */
5649
5650       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5651                    x > SBX_Right + MIDPOSX ? SBX_Right :
5652                    x - MIDPOSX);
5653
5654       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5655                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5656                    y - MIDPOSY);
5657     }
5658     else
5659     {
5660       /* visible relocation (with scrolling), but do not center screen */
5661
5662       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5663                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5664                              old_x - MIDPOSX);
5665
5666       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5667                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5668                              old_y - MIDPOSY);
5669
5670       int offset_x = x + (scroll_x - center_scroll_x);
5671       int offset_y = y + (scroll_y - center_scroll_y);
5672
5673       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5674                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5675                    offset_x - MIDPOSX);
5676
5677       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5678                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5679                    offset_y - MIDPOSY);
5680     }
5681
5682 #else
5683
5684     /* visible relocation (with scrolling), with centering of screen */
5685
5686     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5687                      x > SBX_Right + MIDPOSX ? SBX_Right :
5688                      x - MIDPOSX);
5689
5690     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5691                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5692                      y - MIDPOSY);
5693 #endif
5694
5695     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5696
5697     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5698     {
5699       int dx = 0, dy = 0;
5700       int fx = FX, fy = FY;
5701
5702       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5703       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5704
5705       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5706         break;
5707
5708       scroll_x -= dx;
5709       scroll_y -= dy;
5710
5711       fx += dx * TILEX / 2;
5712       fy += dy * TILEY / 2;
5713
5714       ScrollLevel(dx, dy);
5715       DrawAllPlayers();
5716
5717       /* scroll in two steps of half tile size to make things smoother */
5718       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5719       FlushDisplay();
5720       Delay(wait_delay_value);
5721
5722       /* scroll second step to align at full tile size */
5723       BackToFront();
5724       Delay(wait_delay_value);
5725     }
5726
5727     DrawAllPlayers();
5728     BackToFront();
5729     Delay(wait_delay_value);
5730   }
5731 }
5732
5733 void RelocatePlayer(int jx, int jy, int el_player_raw)
5734 {
5735   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5736   int player_nr = GET_PLAYER_NR(el_player);
5737   struct PlayerInfo *player = &stored_player[player_nr];
5738   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5739   boolean no_delay = (tape.warp_forward);
5740   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5741   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5742   int old_jx = player->jx;
5743   int old_jy = player->jy;
5744   int old_element = Feld[old_jx][old_jy];
5745   int element = Feld[jx][jy];
5746   boolean player_relocated = (old_jx != jx || old_jy != jy);
5747
5748   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5749   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5750   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5751   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5752   int leave_side_horiz = move_dir_horiz;
5753   int leave_side_vert  = move_dir_vert;
5754   int enter_side = enter_side_horiz | enter_side_vert;
5755   int leave_side = leave_side_horiz | leave_side_vert;
5756
5757   if (player->GameOver)         /* do not reanimate dead player */
5758     return;
5759
5760   if (!player_relocated)        /* no need to relocate the player */
5761     return;
5762
5763   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5764   {
5765     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5766     DrawLevelField(jx, jy);
5767   }
5768
5769   if (player->present)
5770   {
5771     while (player->MovPos)
5772     {
5773       ScrollPlayer(player, SCROLL_GO_ON);
5774       ScrollScreen(NULL, SCROLL_GO_ON);
5775
5776       AdvanceFrameAndPlayerCounters(player->index_nr);
5777
5778       DrawPlayer(player);
5779
5780       BackToFront();
5781       Delay(wait_delay_value);
5782     }
5783
5784     DrawPlayer(player);         /* needed here only to cleanup last field */
5785     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5786
5787     player->is_moving = FALSE;
5788   }
5789
5790   if (IS_CUSTOM_ELEMENT(old_element))
5791     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5792                                CE_LEFT_BY_PLAYER,
5793                                player->index_bit, leave_side);
5794
5795   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5796                                       CE_PLAYER_LEAVES_X,
5797                                       player->index_bit, leave_side);
5798
5799   Feld[jx][jy] = el_player;
5800   InitPlayerField(jx, jy, el_player, TRUE);
5801
5802   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5803      possible that the relocation target field did not contain a player element,
5804      but a walkable element, to which the new player was relocated -- in this
5805      case, restore that (already initialized!) element on the player field */
5806   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5807   {
5808     Feld[jx][jy] = element;     /* restore previously existing element */
5809 #if 0
5810     /* !!! do not initialize already initialized element a second time !!! */
5811     /* (this causes at least problems with "element creation" CE trigger for
5812        already existing elements, and existing Sokoban fields counted twice) */
5813     InitField(jx, jy, FALSE);
5814 #endif
5815   }
5816
5817   /* only visually relocate centered player */
5818   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5819                      FALSE, level.instant_relocation);
5820
5821   TestIfPlayerTouchesBadThing(jx, jy);
5822   TestIfPlayerTouchesCustomElement(jx, jy);
5823
5824   if (IS_CUSTOM_ELEMENT(element))
5825     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5826                                player->index_bit, enter_side);
5827
5828   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5829                                       player->index_bit, enter_side);
5830
5831 #if 1
5832   if (player->is_switching)
5833   {
5834     /* ensure that relocation while still switching an element does not cause
5835        a new element to be treated as also switched directly after relocation
5836        (this is important for teleporter switches that teleport the player to
5837        a place where another teleporter switch is in the same direction, which
5838        would then incorrectly be treated as immediately switched before the
5839        direction key that caused the switch was released) */
5840
5841     player->switch_x += jx - old_jx;
5842     player->switch_y += jy - old_jy;
5843   }
5844 #endif
5845 }
5846
5847 void Explode(int ex, int ey, int phase, int mode)
5848 {
5849   int x, y;
5850   int last_phase;
5851   int border_element;
5852
5853   /* !!! eliminate this variable !!! */
5854   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5855
5856   if (game.explosions_delayed)
5857   {
5858     ExplodeField[ex][ey] = mode;
5859     return;
5860   }
5861
5862   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5863   {
5864     int center_element = Feld[ex][ey];
5865     int artwork_element, explosion_element;     /* set these values later */
5866
5867 #if 0
5868     /* --- This is only really needed (and now handled) in "Impact()". --- */
5869     /* do not explode moving elements that left the explode field in time */
5870     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5871         center_element == EL_EMPTY &&
5872         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5873       return;
5874 #endif
5875
5876 #if 0
5877     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5878     if (mode == EX_TYPE_NORMAL ||
5879         mode == EX_TYPE_CENTER ||
5880         mode == EX_TYPE_CROSS)
5881       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5882 #endif
5883
5884     /* remove things displayed in background while burning dynamite */
5885     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5886       Back[ex][ey] = 0;
5887
5888     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5889     {
5890       /* put moving element to center field (and let it explode there) */
5891       center_element = MovingOrBlocked2Element(ex, ey);
5892       RemoveMovingField(ex, ey);
5893       Feld[ex][ey] = center_element;
5894     }
5895
5896     /* now "center_element" is finally determined -- set related values now */
5897     artwork_element = center_element;           /* for custom player artwork */
5898     explosion_element = center_element;         /* for custom player artwork */
5899
5900     if (IS_PLAYER(ex, ey))
5901     {
5902       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5903
5904       artwork_element = stored_player[player_nr].artwork_element;
5905
5906       if (level.use_explosion_element[player_nr])
5907       {
5908         explosion_element = level.explosion_element[player_nr];
5909         artwork_element = explosion_element;
5910       }
5911     }
5912
5913 #if 1
5914     if (mode == EX_TYPE_NORMAL ||
5915         mode == EX_TYPE_CENTER ||
5916         mode == EX_TYPE_CROSS)
5917       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5918 #endif
5919
5920     last_phase = element_info[explosion_element].explosion_delay + 1;
5921
5922     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5923     {
5924       int xx = x - ex + 1;
5925       int yy = y - ey + 1;
5926       int element;
5927
5928       if (!IN_LEV_FIELD(x, y) ||
5929           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5930           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5931         continue;
5932
5933       element = Feld[x][y];
5934
5935       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5936       {
5937         element = MovingOrBlocked2Element(x, y);
5938
5939         if (!IS_EXPLOSION_PROOF(element))
5940           RemoveMovingField(x, y);
5941       }
5942
5943       /* indestructible elements can only explode in center (but not flames) */
5944       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5945                                            mode == EX_TYPE_BORDER)) ||
5946           element == EL_FLAMES)
5947         continue;
5948
5949       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5950          behaviour, for example when touching a yamyam that explodes to rocks
5951          with active deadly shield, a rock is created under the player !!! */
5952       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5953 #if 0
5954       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5955           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5956            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5957 #else
5958       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5959 #endif
5960       {
5961         if (IS_ACTIVE_BOMB(element))
5962         {
5963           /* re-activate things under the bomb like gate or penguin */
5964           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5965           Back[x][y] = 0;
5966         }
5967
5968         continue;
5969       }
5970
5971       /* save walkable background elements while explosion on same tile */
5972       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5973           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5974         Back[x][y] = element;
5975
5976       /* ignite explodable elements reached by other explosion */
5977       if (element == EL_EXPLOSION)
5978         element = Store2[x][y];
5979
5980       if (AmoebaNr[x][y] &&
5981           (element == EL_AMOEBA_FULL ||
5982            element == EL_BD_AMOEBA ||
5983            element == EL_AMOEBA_GROWING))
5984       {
5985         AmoebaCnt[AmoebaNr[x][y]]--;
5986         AmoebaCnt2[AmoebaNr[x][y]]--;
5987       }
5988
5989       RemoveField(x, y);
5990
5991       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5992       {
5993         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5994
5995         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5996
5997         if (PLAYERINFO(ex, ey)->use_murphy)
5998           Store[x][y] = EL_EMPTY;
5999       }
6000
6001       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6002          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6003       else if (ELEM_IS_PLAYER(center_element))
6004         Store[x][y] = EL_EMPTY;
6005       else if (center_element == EL_YAMYAM)
6006         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6007       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6008         Store[x][y] = element_info[center_element].content.e[xx][yy];
6009 #if 1
6010       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6011          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6012          otherwise) -- FIX THIS !!! */
6013       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6014         Store[x][y] = element_info[element].content.e[1][1];
6015 #else
6016       else if (!CAN_EXPLODE(element))
6017         Store[x][y] = element_info[element].content.e[1][1];
6018 #endif
6019       else
6020         Store[x][y] = EL_EMPTY;
6021
6022       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6023           center_element == EL_AMOEBA_TO_DIAMOND)
6024         Store2[x][y] = element;
6025
6026       Feld[x][y] = EL_EXPLOSION;
6027       GfxElement[x][y] = artwork_element;
6028
6029       ExplodePhase[x][y] = 1;
6030       ExplodeDelay[x][y] = last_phase;
6031
6032       Stop[x][y] = TRUE;
6033     }
6034
6035     if (center_element == EL_YAMYAM)
6036       game.yamyam_content_nr =
6037         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6038
6039     return;
6040   }
6041
6042   if (Stop[ex][ey])
6043     return;
6044
6045   x = ex;
6046   y = ey;
6047
6048   if (phase == 1)
6049     GfxFrame[x][y] = 0;         /* restart explosion animation */
6050
6051   last_phase = ExplodeDelay[x][y];
6052
6053   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6054
6055 #ifdef DEBUG
6056
6057   /* activate this even in non-DEBUG version until cause for crash in
6058      getGraphicAnimationFrame() (see below) is found and eliminated */
6059
6060 #endif
6061 #if 1
6062
6063 #if 1
6064   /* this can happen if the player leaves an explosion just in time */
6065   if (GfxElement[x][y] == EL_UNDEFINED)
6066     GfxElement[x][y] = EL_EMPTY;
6067 #else
6068   if (GfxElement[x][y] == EL_UNDEFINED)
6069   {
6070     printf("\n\n");
6071     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6072     printf("Explode(): This should never happen!\n");
6073     printf("\n\n");
6074
6075     GfxElement[x][y] = EL_EMPTY;
6076   }
6077 #endif
6078
6079 #endif
6080
6081   border_element = Store2[x][y];
6082   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6083     border_element = StorePlayer[x][y];
6084
6085   if (phase == element_info[border_element].ignition_delay ||
6086       phase == last_phase)
6087   {
6088     boolean border_explosion = FALSE;
6089
6090     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6091         !PLAYER_EXPLOSION_PROTECTED(x, y))
6092     {
6093       KillPlayerUnlessExplosionProtected(x, y);
6094       border_explosion = TRUE;
6095     }
6096     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6097     {
6098       Feld[x][y] = Store2[x][y];
6099       Store2[x][y] = 0;
6100       Bang(x, y);
6101       border_explosion = TRUE;
6102     }
6103     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6104     {
6105       AmoebeUmwandeln(x, y);
6106       Store2[x][y] = 0;
6107       border_explosion = TRUE;
6108     }
6109
6110     /* if an element just explodes due to another explosion (chain-reaction),
6111        do not immediately end the new explosion when it was the last frame of
6112        the explosion (as it would be done in the following "if"-statement!) */
6113     if (border_explosion && phase == last_phase)
6114       return;
6115   }
6116
6117   if (phase == last_phase)
6118   {
6119     int element;
6120
6121     element = Feld[x][y] = Store[x][y];
6122     Store[x][y] = Store2[x][y] = 0;
6123     GfxElement[x][y] = EL_UNDEFINED;
6124
6125     /* player can escape from explosions and might therefore be still alive */
6126     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6127         element <= EL_PLAYER_IS_EXPLODING_4)
6128     {
6129       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6130       int explosion_element = EL_PLAYER_1 + player_nr;
6131       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6132       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6133
6134       if (level.use_explosion_element[player_nr])
6135         explosion_element = level.explosion_element[player_nr];
6136
6137       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6138                     element_info[explosion_element].content.e[xx][yy]);
6139     }
6140
6141     /* restore probably existing indestructible background element */
6142     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6143       element = Feld[x][y] = Back[x][y];
6144     Back[x][y] = 0;
6145
6146     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6147     GfxDir[x][y] = MV_NONE;
6148     ChangeDelay[x][y] = 0;
6149     ChangePage[x][y] = -1;
6150
6151 #if USE_NEW_CUSTOM_VALUE
6152     CustomValue[x][y] = 0;
6153 #endif
6154
6155     InitField_WithBug2(x, y, FALSE);
6156
6157     TEST_DrawLevelField(x, y);
6158
6159     TestIfElementTouchesCustomElement(x, y);
6160
6161     if (GFX_CRUMBLED(element))
6162       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6163
6164     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6165       StorePlayer[x][y] = 0;
6166
6167     if (ELEM_IS_PLAYER(element))
6168       RelocatePlayer(x, y, element);
6169   }
6170   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6171   {
6172     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6173     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6174
6175     if (phase == delay)
6176       TEST_DrawLevelFieldCrumbled(x, y);
6177
6178     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6179     {
6180       DrawLevelElement(x, y, Back[x][y]);
6181       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6182     }
6183     else if (IS_WALKABLE_UNDER(Back[x][y]))
6184     {
6185       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6186       DrawLevelElementThruMask(x, y, Back[x][y]);
6187     }
6188     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6189       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6190   }
6191 }
6192
6193 void DynaExplode(int ex, int ey)
6194 {
6195   int i, j;
6196   int dynabomb_element = Feld[ex][ey];
6197   int dynabomb_size = 1;
6198   boolean dynabomb_xl = FALSE;
6199   struct PlayerInfo *player;
6200   static int xy[4][2] =
6201   {
6202     { 0, -1 },
6203     { -1, 0 },
6204     { +1, 0 },
6205     { 0, +1 }
6206   };
6207
6208   if (IS_ACTIVE_BOMB(dynabomb_element))
6209   {
6210     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6211     dynabomb_size = player->dynabomb_size;
6212     dynabomb_xl = player->dynabomb_xl;
6213     player->dynabombs_left++;
6214   }
6215
6216   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6217
6218   for (i = 0; i < NUM_DIRECTIONS; i++)
6219   {
6220     for (j = 1; j <= dynabomb_size; j++)
6221     {
6222       int x = ex + j * xy[i][0];
6223       int y = ey + j * xy[i][1];
6224       int element;
6225
6226       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6227         break;
6228
6229       element = Feld[x][y];
6230
6231       /* do not restart explosions of fields with active bombs */
6232       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6233         continue;
6234
6235       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6236
6237       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6238           !IS_DIGGABLE(element) && !dynabomb_xl)
6239         break;
6240     }
6241   }
6242 }
6243
6244 void Bang(int x, int y)
6245 {
6246   int element = MovingOrBlocked2Element(x, y);
6247   int explosion_type = EX_TYPE_NORMAL;
6248
6249   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6250   {
6251     struct PlayerInfo *player = PLAYERINFO(x, y);
6252
6253 #if USE_FIX_CE_ACTION_WITH_PLAYER
6254     element = Feld[x][y] = player->initial_element;
6255 #else
6256     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6257                             player->element_nr);
6258 #endif
6259
6260     if (level.use_explosion_element[player->index_nr])
6261     {
6262       int explosion_element = level.explosion_element[player->index_nr];
6263
6264       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6265         explosion_type = EX_TYPE_CROSS;
6266       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6267         explosion_type = EX_TYPE_CENTER;
6268     }
6269   }
6270
6271   switch (element)
6272   {
6273     case EL_BUG:
6274     case EL_SPACESHIP:
6275     case EL_BD_BUTTERFLY:
6276     case EL_BD_FIREFLY:
6277     case EL_YAMYAM:
6278     case EL_DARK_YAMYAM:
6279     case EL_ROBOT:
6280     case EL_PACMAN:
6281     case EL_MOLE:
6282       RaiseScoreElement(element);
6283       break;
6284
6285     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6286     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6287     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6288     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6289     case EL_DYNABOMB_INCREASE_NUMBER:
6290     case EL_DYNABOMB_INCREASE_SIZE:
6291     case EL_DYNABOMB_INCREASE_POWER:
6292       explosion_type = EX_TYPE_DYNA;
6293       break;
6294
6295     case EL_DC_LANDMINE:
6296 #if 0
6297     case EL_EM_EXIT_OPEN:
6298     case EL_EM_STEEL_EXIT_OPEN:
6299 #endif
6300       explosion_type = EX_TYPE_CENTER;
6301       break;
6302
6303     case EL_PENGUIN:
6304     case EL_LAMP:
6305     case EL_LAMP_ACTIVE:
6306     case EL_AMOEBA_TO_DIAMOND:
6307       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6308         explosion_type = EX_TYPE_CENTER;
6309       break;
6310
6311     default:
6312       if (element_info[element].explosion_type == EXPLODES_CROSS)
6313         explosion_type = EX_TYPE_CROSS;
6314       else if (element_info[element].explosion_type == EXPLODES_1X1)
6315         explosion_type = EX_TYPE_CENTER;
6316       break;
6317   }
6318
6319   if (explosion_type == EX_TYPE_DYNA)
6320     DynaExplode(x, y);
6321   else
6322     Explode(x, y, EX_PHASE_START, explosion_type);
6323
6324   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6325 }
6326
6327 void SplashAcid(int x, int y)
6328 {
6329   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6330       (!IN_LEV_FIELD(x - 1, y - 2) ||
6331        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6332     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6333
6334   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6335       (!IN_LEV_FIELD(x + 1, y - 2) ||
6336        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6337     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6338
6339   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6340 }
6341
6342 static void InitBeltMovement()
6343 {
6344   static int belt_base_element[4] =
6345   {
6346     EL_CONVEYOR_BELT_1_LEFT,
6347     EL_CONVEYOR_BELT_2_LEFT,
6348     EL_CONVEYOR_BELT_3_LEFT,
6349     EL_CONVEYOR_BELT_4_LEFT
6350   };
6351   static int belt_base_active_element[4] =
6352   {
6353     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6354     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6355     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6356     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6357   };
6358
6359   int x, y, i, j;
6360
6361   /* set frame order for belt animation graphic according to belt direction */
6362   for (i = 0; i < NUM_BELTS; i++)
6363   {
6364     int belt_nr = i;
6365
6366     for (j = 0; j < NUM_BELT_PARTS; j++)
6367     {
6368       int element = belt_base_active_element[belt_nr] + j;
6369       int graphic_1 = el2img(element);
6370       int graphic_2 = el2panelimg(element);
6371
6372       if (game.belt_dir[i] == MV_LEFT)
6373       {
6374         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6375         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6376       }
6377       else
6378       {
6379         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6380         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6381       }
6382     }
6383   }
6384
6385   SCAN_PLAYFIELD(x, y)
6386   {
6387     int element = Feld[x][y];
6388
6389     for (i = 0; i < NUM_BELTS; i++)
6390     {
6391       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6392       {
6393         int e_belt_nr = getBeltNrFromBeltElement(element);
6394         int belt_nr = i;
6395
6396         if (e_belt_nr == belt_nr)
6397         {
6398           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6399
6400           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6401         }
6402       }
6403     }
6404   }
6405 }
6406
6407 static void ToggleBeltSwitch(int x, int y)
6408 {
6409   static int belt_base_element[4] =
6410   {
6411     EL_CONVEYOR_BELT_1_LEFT,
6412     EL_CONVEYOR_BELT_2_LEFT,
6413     EL_CONVEYOR_BELT_3_LEFT,
6414     EL_CONVEYOR_BELT_4_LEFT
6415   };
6416   static int belt_base_active_element[4] =
6417   {
6418     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6419     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6420     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6421     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6422   };
6423   static int belt_base_switch_element[4] =
6424   {
6425     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6426     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6427     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6428     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6429   };
6430   static int belt_move_dir[4] =
6431   {
6432     MV_LEFT,
6433     MV_NONE,
6434     MV_RIGHT,
6435     MV_NONE,
6436   };
6437
6438   int element = Feld[x][y];
6439   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6440   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6441   int belt_dir = belt_move_dir[belt_dir_nr];
6442   int xx, yy, i;
6443
6444   if (!IS_BELT_SWITCH(element))
6445     return;
6446
6447   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6448   game.belt_dir[belt_nr] = belt_dir;
6449
6450   if (belt_dir_nr == 3)
6451     belt_dir_nr = 1;
6452
6453   /* set frame order for belt animation graphic according to belt direction */
6454   for (i = 0; i < NUM_BELT_PARTS; i++)
6455   {
6456     int element = belt_base_active_element[belt_nr] + i;
6457     int graphic_1 = el2img(element);
6458     int graphic_2 = el2panelimg(element);
6459
6460     if (belt_dir == MV_LEFT)
6461     {
6462       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6463       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6464     }
6465     else
6466     {
6467       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6468       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6469     }
6470   }
6471
6472   SCAN_PLAYFIELD(xx, yy)
6473   {
6474     int element = Feld[xx][yy];
6475
6476     if (IS_BELT_SWITCH(element))
6477     {
6478       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6479
6480       if (e_belt_nr == belt_nr)
6481       {
6482         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6483         TEST_DrawLevelField(xx, yy);
6484       }
6485     }
6486     else if (IS_BELT(element) && belt_dir != MV_NONE)
6487     {
6488       int e_belt_nr = getBeltNrFromBeltElement(element);
6489
6490       if (e_belt_nr == belt_nr)
6491       {
6492         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6493
6494         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6495         TEST_DrawLevelField(xx, yy);
6496       }
6497     }
6498     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6499     {
6500       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6501
6502       if (e_belt_nr == belt_nr)
6503       {
6504         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6505
6506         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6507         TEST_DrawLevelField(xx, yy);
6508       }
6509     }
6510   }
6511 }
6512
6513 static void ToggleSwitchgateSwitch(int x, int y)
6514 {
6515   int xx, yy;
6516
6517   game.switchgate_pos = !game.switchgate_pos;
6518
6519   SCAN_PLAYFIELD(xx, yy)
6520   {
6521     int element = Feld[xx][yy];
6522
6523 #if !USE_BOTH_SWITCHGATE_SWITCHES
6524     if (element == EL_SWITCHGATE_SWITCH_UP ||
6525         element == EL_SWITCHGATE_SWITCH_DOWN)
6526     {
6527       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6528       TEST_DrawLevelField(xx, yy);
6529     }
6530     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6531              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6532     {
6533       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6534       TEST_DrawLevelField(xx, yy);
6535     }
6536 #else
6537     if (element == EL_SWITCHGATE_SWITCH_UP)
6538     {
6539       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6540       TEST_DrawLevelField(xx, yy);
6541     }
6542     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6543     {
6544       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6545       TEST_DrawLevelField(xx, yy);
6546     }
6547     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6548     {
6549       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6550       TEST_DrawLevelField(xx, yy);
6551     }
6552     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6553     {
6554       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6555       TEST_DrawLevelField(xx, yy);
6556     }
6557 #endif
6558     else if (element == EL_SWITCHGATE_OPEN ||
6559              element == EL_SWITCHGATE_OPENING)
6560     {
6561       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6562
6563       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6564     }
6565     else if (element == EL_SWITCHGATE_CLOSED ||
6566              element == EL_SWITCHGATE_CLOSING)
6567     {
6568       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6569
6570       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6571     }
6572   }
6573 }
6574
6575 static int getInvisibleActiveFromInvisibleElement(int element)
6576 {
6577   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6578           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6579           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6580           element);
6581 }
6582
6583 static int getInvisibleFromInvisibleActiveElement(int element)
6584 {
6585   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6586           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6587           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6588           element);
6589 }
6590
6591 static void RedrawAllLightSwitchesAndInvisibleElements()
6592 {
6593   int x, y;
6594
6595   SCAN_PLAYFIELD(x, y)
6596   {
6597     int element = Feld[x][y];
6598
6599     if (element == EL_LIGHT_SWITCH &&
6600         game.light_time_left > 0)
6601     {
6602       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6603       TEST_DrawLevelField(x, y);
6604     }
6605     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6606              game.light_time_left == 0)
6607     {
6608       Feld[x][y] = EL_LIGHT_SWITCH;
6609       TEST_DrawLevelField(x, y);
6610     }
6611     else if (element == EL_EMC_DRIPPER &&
6612              game.light_time_left > 0)
6613     {
6614       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6615       TEST_DrawLevelField(x, y);
6616     }
6617     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6618              game.light_time_left == 0)
6619     {
6620       Feld[x][y] = EL_EMC_DRIPPER;
6621       TEST_DrawLevelField(x, y);
6622     }
6623     else if (element == EL_INVISIBLE_STEELWALL ||
6624              element == EL_INVISIBLE_WALL ||
6625              element == EL_INVISIBLE_SAND)
6626     {
6627       if (game.light_time_left > 0)
6628         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6629
6630       TEST_DrawLevelField(x, y);
6631
6632       /* uncrumble neighbour fields, if needed */
6633       if (element == EL_INVISIBLE_SAND)
6634         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6635     }
6636     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6637              element == EL_INVISIBLE_WALL_ACTIVE ||
6638              element == EL_INVISIBLE_SAND_ACTIVE)
6639     {
6640       if (game.light_time_left == 0)
6641         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6642
6643       TEST_DrawLevelField(x, y);
6644
6645       /* re-crumble neighbour fields, if needed */
6646       if (element == EL_INVISIBLE_SAND)
6647         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6648     }
6649   }
6650 }
6651
6652 static void RedrawAllInvisibleElementsForLenses()
6653 {
6654   int x, y;
6655
6656   SCAN_PLAYFIELD(x, y)
6657   {
6658     int element = Feld[x][y];
6659
6660     if (element == EL_EMC_DRIPPER &&
6661         game.lenses_time_left > 0)
6662     {
6663       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6664       TEST_DrawLevelField(x, y);
6665     }
6666     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6667              game.lenses_time_left == 0)
6668     {
6669       Feld[x][y] = EL_EMC_DRIPPER;
6670       TEST_DrawLevelField(x, y);
6671     }
6672     else if (element == EL_INVISIBLE_STEELWALL ||
6673              element == EL_INVISIBLE_WALL ||
6674              element == EL_INVISIBLE_SAND)
6675     {
6676       if (game.lenses_time_left > 0)
6677         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6678
6679       TEST_DrawLevelField(x, y);
6680
6681       /* uncrumble neighbour fields, if needed */
6682       if (element == EL_INVISIBLE_SAND)
6683         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6684     }
6685     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6686              element == EL_INVISIBLE_WALL_ACTIVE ||
6687              element == EL_INVISIBLE_SAND_ACTIVE)
6688     {
6689       if (game.lenses_time_left == 0)
6690         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6691
6692       TEST_DrawLevelField(x, y);
6693
6694       /* re-crumble neighbour fields, if needed */
6695       if (element == EL_INVISIBLE_SAND)
6696         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6697     }
6698   }
6699 }
6700
6701 static void RedrawAllInvisibleElementsForMagnifier()
6702 {
6703   int x, y;
6704
6705   SCAN_PLAYFIELD(x, y)
6706   {
6707     int element = Feld[x][y];
6708
6709     if (element == EL_EMC_FAKE_GRASS &&
6710         game.magnify_time_left > 0)
6711     {
6712       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6713       TEST_DrawLevelField(x, y);
6714     }
6715     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6716              game.magnify_time_left == 0)
6717     {
6718       Feld[x][y] = EL_EMC_FAKE_GRASS;
6719       TEST_DrawLevelField(x, y);
6720     }
6721     else if (IS_GATE_GRAY(element) &&
6722              game.magnify_time_left > 0)
6723     {
6724       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6725                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6726                     IS_EM_GATE_GRAY(element) ?
6727                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6728                     IS_EMC_GATE_GRAY(element) ?
6729                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6730                     IS_DC_GATE_GRAY(element) ?
6731                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6732                     element);
6733       TEST_DrawLevelField(x, y);
6734     }
6735     else if (IS_GATE_GRAY_ACTIVE(element) &&
6736              game.magnify_time_left == 0)
6737     {
6738       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6739                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6740                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6741                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6742                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6743                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6744                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6745                     EL_DC_GATE_WHITE_GRAY :
6746                     element);
6747       TEST_DrawLevelField(x, y);
6748     }
6749   }
6750 }
6751
6752 static void ToggleLightSwitch(int x, int y)
6753 {
6754   int element = Feld[x][y];
6755
6756   game.light_time_left =
6757     (element == EL_LIGHT_SWITCH ?
6758      level.time_light * FRAMES_PER_SECOND : 0);
6759
6760   RedrawAllLightSwitchesAndInvisibleElements();
6761 }
6762
6763 static void ActivateTimegateSwitch(int x, int y)
6764 {
6765   int xx, yy;
6766
6767   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6768
6769   SCAN_PLAYFIELD(xx, yy)
6770   {
6771     int element = Feld[xx][yy];
6772
6773     if (element == EL_TIMEGATE_CLOSED ||
6774         element == EL_TIMEGATE_CLOSING)
6775     {
6776       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6777       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6778     }
6779
6780     /*
6781     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6782     {
6783       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6784       TEST_DrawLevelField(xx, yy);
6785     }
6786     */
6787
6788   }
6789
6790 #if 1
6791   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6792                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6793 #else
6794   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6795 #endif
6796 }
6797
6798 void Impact(int x, int y)
6799 {
6800   boolean last_line = (y == lev_fieldy - 1);
6801   boolean object_hit = FALSE;
6802   boolean impact = (last_line || object_hit);
6803   int element = Feld[x][y];
6804   int smashed = EL_STEELWALL;
6805
6806   if (!last_line)       /* check if element below was hit */
6807   {
6808     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6809       return;
6810
6811     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6812                                          MovDir[x][y + 1] != MV_DOWN ||
6813                                          MovPos[x][y + 1] <= TILEY / 2));
6814
6815     /* do not smash moving elements that left the smashed field in time */
6816     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6817         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6818       object_hit = FALSE;
6819
6820 #if USE_QUICKSAND_IMPACT_BUGFIX
6821     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6822     {
6823       RemoveMovingField(x, y + 1);
6824       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6825       Feld[x][y + 2] = EL_ROCK;
6826       TEST_DrawLevelField(x, y + 2);
6827
6828       object_hit = TRUE;
6829     }
6830
6831     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6832     {
6833       RemoveMovingField(x, y + 1);
6834       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6835       Feld[x][y + 2] = EL_ROCK;
6836       TEST_DrawLevelField(x, y + 2);
6837
6838       object_hit = TRUE;
6839     }
6840 #endif
6841
6842     if (object_hit)
6843       smashed = MovingOrBlocked2Element(x, y + 1);
6844
6845     impact = (last_line || object_hit);
6846   }
6847
6848   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6849   {
6850     SplashAcid(x, y + 1);
6851     return;
6852   }
6853
6854   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6855   /* only reset graphic animation if graphic really changes after impact */
6856   if (impact &&
6857       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6858   {
6859     ResetGfxAnimation(x, y);
6860     TEST_DrawLevelField(x, y);
6861   }
6862
6863   if (impact && CAN_EXPLODE_IMPACT(element))
6864   {
6865     Bang(x, y);
6866     return;
6867   }
6868   else if (impact && element == EL_PEARL &&
6869            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6870   {
6871     ResetGfxAnimation(x, y);
6872
6873     Feld[x][y] = EL_PEARL_BREAKING;
6874     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6875     return;
6876   }
6877   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6878   {
6879     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6880
6881     return;
6882   }
6883
6884   if (impact && element == EL_AMOEBA_DROP)
6885   {
6886     if (object_hit && IS_PLAYER(x, y + 1))
6887       KillPlayerUnlessEnemyProtected(x, y + 1);
6888     else if (object_hit && smashed == EL_PENGUIN)
6889       Bang(x, y + 1);
6890     else
6891     {
6892       Feld[x][y] = EL_AMOEBA_GROWING;
6893       Store[x][y] = EL_AMOEBA_WET;
6894
6895       ResetRandomAnimationValue(x, y);
6896     }
6897     return;
6898   }
6899
6900   if (object_hit)               /* check which object was hit */
6901   {
6902     if ((CAN_PASS_MAGIC_WALL(element) && 
6903          (smashed == EL_MAGIC_WALL ||
6904           smashed == EL_BD_MAGIC_WALL)) ||
6905         (CAN_PASS_DC_MAGIC_WALL(element) &&
6906          smashed == EL_DC_MAGIC_WALL))
6907     {
6908       int xx, yy;
6909       int activated_magic_wall =
6910         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6911          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6912          EL_DC_MAGIC_WALL_ACTIVE);
6913
6914       /* activate magic wall / mill */
6915       SCAN_PLAYFIELD(xx, yy)
6916       {
6917         if (Feld[xx][yy] == smashed)
6918           Feld[xx][yy] = activated_magic_wall;
6919       }
6920
6921       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6922       game.magic_wall_active = TRUE;
6923
6924       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6925                             SND_MAGIC_WALL_ACTIVATING :
6926                             smashed == EL_BD_MAGIC_WALL ?
6927                             SND_BD_MAGIC_WALL_ACTIVATING :
6928                             SND_DC_MAGIC_WALL_ACTIVATING));
6929     }
6930
6931     if (IS_PLAYER(x, y + 1))
6932     {
6933       if (CAN_SMASH_PLAYER(element))
6934       {
6935         KillPlayerUnlessEnemyProtected(x, y + 1);
6936         return;
6937       }
6938     }
6939     else if (smashed == EL_PENGUIN)
6940     {
6941       if (CAN_SMASH_PLAYER(element))
6942       {
6943         Bang(x, y + 1);
6944         return;
6945       }
6946     }
6947     else if (element == EL_BD_DIAMOND)
6948     {
6949       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6950       {
6951         Bang(x, y + 1);
6952         return;
6953       }
6954     }
6955     else if (((element == EL_SP_INFOTRON ||
6956                element == EL_SP_ZONK) &&
6957               (smashed == EL_SP_SNIKSNAK ||
6958                smashed == EL_SP_ELECTRON ||
6959                smashed == EL_SP_DISK_ORANGE)) ||
6960              (element == EL_SP_INFOTRON &&
6961               smashed == EL_SP_DISK_YELLOW))
6962     {
6963       Bang(x, y + 1);
6964       return;
6965     }
6966     else if (CAN_SMASH_EVERYTHING(element))
6967     {
6968       if (IS_CLASSIC_ENEMY(smashed) ||
6969           CAN_EXPLODE_SMASHED(smashed))
6970       {
6971         Bang(x, y + 1);
6972         return;
6973       }
6974       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6975       {
6976         if (smashed == EL_LAMP ||
6977             smashed == EL_LAMP_ACTIVE)
6978         {
6979           Bang(x, y + 1);
6980           return;
6981         }
6982         else if (smashed == EL_NUT)
6983         {
6984           Feld[x][y + 1] = EL_NUT_BREAKING;
6985           PlayLevelSound(x, y, SND_NUT_BREAKING);
6986           RaiseScoreElement(EL_NUT);
6987           return;
6988         }
6989         else if (smashed == EL_PEARL)
6990         {
6991           ResetGfxAnimation(x, y);
6992
6993           Feld[x][y + 1] = EL_PEARL_BREAKING;
6994           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6995           return;
6996         }
6997         else if (smashed == EL_DIAMOND)
6998         {
6999           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7000           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7001           return;
7002         }
7003         else if (IS_BELT_SWITCH(smashed))
7004         {
7005           ToggleBeltSwitch(x, y + 1);
7006         }
7007         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7008                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7009                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7010                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7011         {
7012           ToggleSwitchgateSwitch(x, y + 1);
7013         }
7014         else if (smashed == EL_LIGHT_SWITCH ||
7015                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7016         {
7017           ToggleLightSwitch(x, y + 1);
7018         }
7019         else
7020         {
7021 #if 0
7022           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7023 #endif
7024
7025           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7026
7027           CheckElementChangeBySide(x, y + 1, smashed, element,
7028                                    CE_SWITCHED, CH_SIDE_TOP);
7029           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7030                                             CH_SIDE_TOP);
7031         }
7032       }
7033       else
7034       {
7035         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7036       }
7037     }
7038   }
7039
7040   /* play sound of magic wall / mill */
7041   if (!last_line &&
7042       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7043        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7044        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7045   {
7046     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7047       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7048     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7049       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7050     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7051       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7052
7053     return;
7054   }
7055
7056   /* play sound of object that hits the ground */
7057   if (last_line || object_hit)
7058     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7059 }
7060
7061 inline static void TurnRoundExt(int x, int y)
7062 {
7063   static struct
7064   {
7065     int dx, dy;
7066   } move_xy[] =
7067   {
7068     {  0,  0 },
7069     { -1,  0 },
7070     { +1,  0 },
7071     {  0,  0 },
7072     {  0, -1 },
7073     {  0,  0 }, { 0, 0 }, { 0, 0 },
7074     {  0, +1 }
7075   };
7076   static struct
7077   {
7078     int left, right, back;
7079   } turn[] =
7080   {
7081     { 0,        0,              0        },
7082     { MV_DOWN,  MV_UP,          MV_RIGHT },
7083     { MV_UP,    MV_DOWN,        MV_LEFT  },
7084     { 0,        0,              0        },
7085     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7086     { 0,        0,              0        },
7087     { 0,        0,              0        },
7088     { 0,        0,              0        },
7089     { MV_RIGHT, MV_LEFT,        MV_UP    }
7090   };
7091
7092   int element = Feld[x][y];
7093   int move_pattern = element_info[element].move_pattern;
7094
7095   int old_move_dir = MovDir[x][y];
7096   int left_dir  = turn[old_move_dir].left;
7097   int right_dir = turn[old_move_dir].right;
7098   int back_dir  = turn[old_move_dir].back;
7099
7100   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7101   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7102   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7103   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7104
7105   int left_x  = x + left_dx,  left_y  = y + left_dy;
7106   int right_x = x + right_dx, right_y = y + right_dy;
7107   int move_x  = x + move_dx,  move_y  = y + move_dy;
7108
7109   int xx, yy;
7110
7111   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7112   {
7113     TestIfBadThingTouchesOtherBadThing(x, y);
7114
7115     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7116       MovDir[x][y] = right_dir;
7117     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7118       MovDir[x][y] = left_dir;
7119
7120     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7121       MovDelay[x][y] = 9;
7122     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7123       MovDelay[x][y] = 1;
7124   }
7125   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7126   {
7127     TestIfBadThingTouchesOtherBadThing(x, y);
7128
7129     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7130       MovDir[x][y] = left_dir;
7131     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7132       MovDir[x][y] = right_dir;
7133
7134     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7135       MovDelay[x][y] = 9;
7136     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7137       MovDelay[x][y] = 1;
7138   }
7139   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7140   {
7141     TestIfBadThingTouchesOtherBadThing(x, y);
7142
7143     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7144       MovDir[x][y] = left_dir;
7145     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7146       MovDir[x][y] = right_dir;
7147
7148     if (MovDir[x][y] != old_move_dir)
7149       MovDelay[x][y] = 9;
7150   }
7151   else if (element == EL_YAMYAM)
7152   {
7153     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7154     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7155
7156     if (can_turn_left && can_turn_right)
7157       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7158     else if (can_turn_left)
7159       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7160     else if (can_turn_right)
7161       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7162     else
7163       MovDir[x][y] = back_dir;
7164
7165     MovDelay[x][y] = 16 + 16 * RND(3);
7166   }
7167   else if (element == EL_DARK_YAMYAM)
7168   {
7169     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7170                                                          left_x, left_y);
7171     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7172                                                          right_x, right_y);
7173
7174     if (can_turn_left && can_turn_right)
7175       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7176     else if (can_turn_left)
7177       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7178     else if (can_turn_right)
7179       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7180     else
7181       MovDir[x][y] = back_dir;
7182
7183     MovDelay[x][y] = 16 + 16 * RND(3);
7184   }
7185   else if (element == EL_PACMAN)
7186   {
7187     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7188     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7189
7190     if (can_turn_left && can_turn_right)
7191       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7192     else if (can_turn_left)
7193       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7194     else if (can_turn_right)
7195       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7196     else
7197       MovDir[x][y] = back_dir;
7198
7199     MovDelay[x][y] = 6 + RND(40);
7200   }
7201   else if (element == EL_PIG)
7202   {
7203     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7204     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7205     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7206     boolean should_turn_left, should_turn_right, should_move_on;
7207     int rnd_value = 24;
7208     int rnd = RND(rnd_value);
7209
7210     should_turn_left = (can_turn_left &&
7211                         (!can_move_on ||
7212                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7213                                                    y + back_dy + left_dy)));
7214     should_turn_right = (can_turn_right &&
7215                          (!can_move_on ||
7216                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7217                                                     y + back_dy + right_dy)));
7218     should_move_on = (can_move_on &&
7219                       (!can_turn_left ||
7220                        !can_turn_right ||
7221                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7222                                                  y + move_dy + left_dy) ||
7223                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7224                                                  y + move_dy + right_dy)));
7225
7226     if (should_turn_left || should_turn_right || should_move_on)
7227     {
7228       if (should_turn_left && should_turn_right && should_move_on)
7229         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7230                         rnd < 2 * rnd_value / 3 ? right_dir :
7231                         old_move_dir);
7232       else if (should_turn_left && should_turn_right)
7233         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7234       else if (should_turn_left && should_move_on)
7235         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7236       else if (should_turn_right && should_move_on)
7237         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7238       else if (should_turn_left)
7239         MovDir[x][y] = left_dir;
7240       else if (should_turn_right)
7241         MovDir[x][y] = right_dir;
7242       else if (should_move_on)
7243         MovDir[x][y] = old_move_dir;
7244     }
7245     else if (can_move_on && rnd > rnd_value / 8)
7246       MovDir[x][y] = old_move_dir;
7247     else if (can_turn_left && can_turn_right)
7248       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7249     else if (can_turn_left && rnd > rnd_value / 8)
7250       MovDir[x][y] = left_dir;
7251     else if (can_turn_right && rnd > rnd_value/8)
7252       MovDir[x][y] = right_dir;
7253     else
7254       MovDir[x][y] = back_dir;
7255
7256     xx = x + move_xy[MovDir[x][y]].dx;
7257     yy = y + move_xy[MovDir[x][y]].dy;
7258
7259     if (!IN_LEV_FIELD(xx, yy) ||
7260         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7261       MovDir[x][y] = old_move_dir;
7262
7263     MovDelay[x][y] = 0;
7264   }
7265   else if (element == EL_DRAGON)
7266   {
7267     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7268     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7269     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7270     int rnd_value = 24;
7271     int rnd = RND(rnd_value);
7272
7273     if (can_move_on && rnd > rnd_value / 8)
7274       MovDir[x][y] = old_move_dir;
7275     else if (can_turn_left && can_turn_right)
7276       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7277     else if (can_turn_left && rnd > rnd_value / 8)
7278       MovDir[x][y] = left_dir;
7279     else if (can_turn_right && rnd > rnd_value / 8)
7280       MovDir[x][y] = right_dir;
7281     else
7282       MovDir[x][y] = back_dir;
7283
7284     xx = x + move_xy[MovDir[x][y]].dx;
7285     yy = y + move_xy[MovDir[x][y]].dy;
7286
7287     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7288       MovDir[x][y] = old_move_dir;
7289
7290     MovDelay[x][y] = 0;
7291   }
7292   else if (element == EL_MOLE)
7293   {
7294     boolean can_move_on =
7295       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7296                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7297                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7298     if (!can_move_on)
7299     {
7300       boolean can_turn_left =
7301         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7302                               IS_AMOEBOID(Feld[left_x][left_y])));
7303
7304       boolean can_turn_right =
7305         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7306                               IS_AMOEBOID(Feld[right_x][right_y])));
7307
7308       if (can_turn_left && can_turn_right)
7309         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7310       else if (can_turn_left)
7311         MovDir[x][y] = left_dir;
7312       else
7313         MovDir[x][y] = right_dir;
7314     }
7315
7316     if (MovDir[x][y] != old_move_dir)
7317       MovDelay[x][y] = 9;
7318   }
7319   else if (element == EL_BALLOON)
7320   {
7321     MovDir[x][y] = game.wind_direction;
7322     MovDelay[x][y] = 0;
7323   }
7324   else if (element == EL_SPRING)
7325   {
7326 #if USE_NEW_SPRING_BUMPER
7327     if (MovDir[x][y] & MV_HORIZONTAL)
7328     {
7329       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7330           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7331       {
7332         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7333         ResetGfxAnimation(move_x, move_y);
7334         TEST_DrawLevelField(move_x, move_y);
7335
7336         MovDir[x][y] = back_dir;
7337       }
7338       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7339                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7340         MovDir[x][y] = MV_NONE;
7341     }
7342 #else
7343     if (MovDir[x][y] & MV_HORIZONTAL &&
7344         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7345          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7346       MovDir[x][y] = MV_NONE;
7347 #endif
7348
7349     MovDelay[x][y] = 0;
7350   }
7351   else if (element == EL_ROBOT ||
7352            element == EL_SATELLITE ||
7353            element == EL_PENGUIN ||
7354            element == EL_EMC_ANDROID)
7355   {
7356     int attr_x = -1, attr_y = -1;
7357
7358     if (AllPlayersGone)
7359     {
7360       attr_x = ExitX;
7361       attr_y = ExitY;
7362     }
7363     else
7364     {
7365       int i;
7366
7367       for (i = 0; i < MAX_PLAYERS; i++)
7368       {
7369         struct PlayerInfo *player = &stored_player[i];
7370         int jx = player->jx, jy = player->jy;
7371
7372         if (!player->active)
7373           continue;
7374
7375         if (attr_x == -1 ||
7376             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7377         {
7378           attr_x = jx;
7379           attr_y = jy;
7380         }
7381       }
7382     }
7383
7384     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7385         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7386          game.engine_version < VERSION_IDENT(3,1,0,0)))
7387     {
7388       attr_x = ZX;
7389       attr_y = ZY;
7390     }
7391
7392     if (element == EL_PENGUIN)
7393     {
7394       int i;
7395       static int xy[4][2] =
7396       {
7397         { 0, -1 },
7398         { -1, 0 },
7399         { +1, 0 },
7400         { 0, +1 }
7401       };
7402
7403       for (i = 0; i < NUM_DIRECTIONS; i++)
7404       {
7405         int ex = x + xy[i][0];
7406         int ey = y + xy[i][1];
7407
7408         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7409                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7410                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7411                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7412         {
7413           attr_x = ex;
7414           attr_y = ey;
7415           break;
7416         }
7417       }
7418     }
7419
7420     MovDir[x][y] = MV_NONE;
7421     if (attr_x < x)
7422       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7423     else if (attr_x > x)
7424       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7425     if (attr_y < y)
7426       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7427     else if (attr_y > y)
7428       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7429
7430     if (element == EL_ROBOT)
7431     {
7432       int newx, newy;
7433
7434       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7435         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7436       Moving2Blocked(x, y, &newx, &newy);
7437
7438       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7439         MovDelay[x][y] = 8 + 8 * !RND(3);
7440       else
7441         MovDelay[x][y] = 16;
7442     }
7443     else if (element == EL_PENGUIN)
7444     {
7445       int newx, newy;
7446
7447       MovDelay[x][y] = 1;
7448
7449       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7450       {
7451         boolean first_horiz = RND(2);
7452         int new_move_dir = MovDir[x][y];
7453
7454         MovDir[x][y] =
7455           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7456         Moving2Blocked(x, y, &newx, &newy);
7457
7458         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7459           return;
7460
7461         MovDir[x][y] =
7462           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7463         Moving2Blocked(x, y, &newx, &newy);
7464
7465         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7466           return;
7467
7468         MovDir[x][y] = old_move_dir;
7469         return;
7470       }
7471     }
7472     else if (element == EL_SATELLITE)
7473     {
7474       int newx, newy;
7475
7476       MovDelay[x][y] = 1;
7477
7478       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7479       {
7480         boolean first_horiz = RND(2);
7481         int new_move_dir = MovDir[x][y];
7482
7483         MovDir[x][y] =
7484           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7485         Moving2Blocked(x, y, &newx, &newy);
7486
7487         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7488           return;
7489
7490         MovDir[x][y] =
7491           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7492         Moving2Blocked(x, y, &newx, &newy);
7493
7494         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7495           return;
7496
7497         MovDir[x][y] = old_move_dir;
7498         return;
7499       }
7500     }
7501     else if (element == EL_EMC_ANDROID)
7502     {
7503       static int check_pos[16] =
7504       {
7505         -1,             /*  0 => (invalid)          */
7506         7,              /*  1 => MV_LEFT            */
7507         3,              /*  2 => MV_RIGHT           */
7508         -1,             /*  3 => (invalid)          */
7509         1,              /*  4 =>            MV_UP   */
7510         0,              /*  5 => MV_LEFT  | MV_UP   */
7511         2,              /*  6 => MV_RIGHT | MV_UP   */
7512         -1,             /*  7 => (invalid)          */
7513         5,              /*  8 =>            MV_DOWN */
7514         6,              /*  9 => MV_LEFT  | MV_DOWN */
7515         4,              /* 10 => MV_RIGHT | MV_DOWN */
7516         -1,             /* 11 => (invalid)          */
7517         -1,             /* 12 => (invalid)          */
7518         -1,             /* 13 => (invalid)          */
7519         -1,             /* 14 => (invalid)          */
7520         -1,             /* 15 => (invalid)          */
7521       };
7522       static struct
7523       {
7524         int dx, dy;
7525         int dir;
7526       } check_xy[8] =
7527       {
7528         { -1, -1,       MV_LEFT  | MV_UP   },
7529         {  0, -1,                  MV_UP   },
7530         { +1, -1,       MV_RIGHT | MV_UP   },
7531         { +1,  0,       MV_RIGHT           },
7532         { +1, +1,       MV_RIGHT | MV_DOWN },
7533         {  0, +1,                  MV_DOWN },
7534         { -1, +1,       MV_LEFT  | MV_DOWN },
7535         { -1,  0,       MV_LEFT            },
7536       };
7537       int start_pos, check_order;
7538       boolean can_clone = FALSE;
7539       int i;
7540
7541       /* check if there is any free field around current position */
7542       for (i = 0; i < 8; i++)
7543       {
7544         int newx = x + check_xy[i].dx;
7545         int newy = y + check_xy[i].dy;
7546
7547         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7548         {
7549           can_clone = TRUE;
7550
7551           break;
7552         }
7553       }
7554
7555       if (can_clone)            /* randomly find an element to clone */
7556       {
7557         can_clone = FALSE;
7558
7559         start_pos = check_pos[RND(8)];
7560         check_order = (RND(2) ? -1 : +1);
7561
7562         for (i = 0; i < 8; i++)
7563         {
7564           int pos_raw = start_pos + i * check_order;
7565           int pos = (pos_raw + 8) % 8;
7566           int newx = x + check_xy[pos].dx;
7567           int newy = y + check_xy[pos].dy;
7568
7569           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7570           {
7571             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7572             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7573
7574             Store[x][y] = Feld[newx][newy];
7575
7576             can_clone = TRUE;
7577
7578             break;
7579           }
7580         }
7581       }
7582
7583       if (can_clone)            /* randomly find a direction to move */
7584       {
7585         can_clone = FALSE;
7586
7587         start_pos = check_pos[RND(8)];
7588         check_order = (RND(2) ? -1 : +1);
7589
7590         for (i = 0; i < 8; i++)
7591         {
7592           int pos_raw = start_pos + i * check_order;
7593           int pos = (pos_raw + 8) % 8;
7594           int newx = x + check_xy[pos].dx;
7595           int newy = y + check_xy[pos].dy;
7596           int new_move_dir = check_xy[pos].dir;
7597
7598           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7599           {
7600             MovDir[x][y] = new_move_dir;
7601             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7602
7603             can_clone = TRUE;
7604
7605             break;
7606           }
7607         }
7608       }
7609
7610       if (can_clone)            /* cloning and moving successful */
7611         return;
7612
7613       /* cannot clone -- try to move towards player */
7614
7615       start_pos = check_pos[MovDir[x][y] & 0x0f];
7616       check_order = (RND(2) ? -1 : +1);
7617
7618       for (i = 0; i < 3; i++)
7619       {
7620         /* first check start_pos, then previous/next or (next/previous) pos */
7621         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7622         int pos = (pos_raw + 8) % 8;
7623         int newx = x + check_xy[pos].dx;
7624         int newy = y + check_xy[pos].dy;
7625         int new_move_dir = check_xy[pos].dir;
7626
7627         if (IS_PLAYER(newx, newy))
7628           break;
7629
7630         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7631         {
7632           MovDir[x][y] = new_move_dir;
7633           MovDelay[x][y] = level.android_move_time * 8 + 1;
7634
7635           break;
7636         }
7637       }
7638     }
7639   }
7640   else if (move_pattern == MV_TURNING_LEFT ||
7641            move_pattern == MV_TURNING_RIGHT ||
7642            move_pattern == MV_TURNING_LEFT_RIGHT ||
7643            move_pattern == MV_TURNING_RIGHT_LEFT ||
7644            move_pattern == MV_TURNING_RANDOM ||
7645            move_pattern == MV_ALL_DIRECTIONS)
7646   {
7647     boolean can_turn_left =
7648       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7649     boolean can_turn_right =
7650       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7651
7652     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7653       return;
7654
7655     if (move_pattern == MV_TURNING_LEFT)
7656       MovDir[x][y] = left_dir;
7657     else if (move_pattern == MV_TURNING_RIGHT)
7658       MovDir[x][y] = right_dir;
7659     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7660       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7661     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7662       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7663     else if (move_pattern == MV_TURNING_RANDOM)
7664       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7665                       can_turn_right && !can_turn_left ? right_dir :
7666                       RND(2) ? left_dir : right_dir);
7667     else if (can_turn_left && can_turn_right)
7668       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7669     else if (can_turn_left)
7670       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7671     else if (can_turn_right)
7672       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7673     else
7674       MovDir[x][y] = back_dir;
7675
7676     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7677   }
7678   else if (move_pattern == MV_HORIZONTAL ||
7679            move_pattern == MV_VERTICAL)
7680   {
7681     if (move_pattern & old_move_dir)
7682       MovDir[x][y] = back_dir;
7683     else if (move_pattern == MV_HORIZONTAL)
7684       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7685     else if (move_pattern == MV_VERTICAL)
7686       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7687
7688     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7689   }
7690   else if (move_pattern & MV_ANY_DIRECTION)
7691   {
7692     MovDir[x][y] = move_pattern;
7693     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7694   }
7695   else if (move_pattern & MV_WIND_DIRECTION)
7696   {
7697     MovDir[x][y] = game.wind_direction;
7698     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7699   }
7700   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7701   {
7702     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7703       MovDir[x][y] = left_dir;
7704     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7705       MovDir[x][y] = right_dir;
7706
7707     if (MovDir[x][y] != old_move_dir)
7708       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7709   }
7710   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7711   {
7712     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7713       MovDir[x][y] = right_dir;
7714     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7715       MovDir[x][y] = left_dir;
7716
7717     if (MovDir[x][y] != old_move_dir)
7718       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7719   }
7720   else if (move_pattern == MV_TOWARDS_PLAYER ||
7721            move_pattern == MV_AWAY_FROM_PLAYER)
7722   {
7723     int attr_x = -1, attr_y = -1;
7724     int newx, newy;
7725     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7726
7727     if (AllPlayersGone)
7728     {
7729       attr_x = ExitX;
7730       attr_y = ExitY;
7731     }
7732     else
7733     {
7734       int i;
7735
7736       for (i = 0; i < MAX_PLAYERS; i++)
7737       {
7738         struct PlayerInfo *player = &stored_player[i];
7739         int jx = player->jx, jy = player->jy;
7740
7741         if (!player->active)
7742           continue;
7743
7744         if (attr_x == -1 ||
7745             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7746         {
7747           attr_x = jx;
7748           attr_y = jy;
7749         }
7750       }
7751     }
7752
7753     MovDir[x][y] = MV_NONE;
7754     if (attr_x < x)
7755       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7756     else if (attr_x > x)
7757       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7758     if (attr_y < y)
7759       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7760     else if (attr_y > y)
7761       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7762
7763     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7764
7765     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7766     {
7767       boolean first_horiz = RND(2);
7768       int new_move_dir = MovDir[x][y];
7769
7770       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7771       {
7772         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7773         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7774
7775         return;
7776       }
7777
7778       MovDir[x][y] =
7779         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7780       Moving2Blocked(x, y, &newx, &newy);
7781
7782       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7783         return;
7784
7785       MovDir[x][y] =
7786         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7787       Moving2Blocked(x, y, &newx, &newy);
7788
7789       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7790         return;
7791
7792       MovDir[x][y] = old_move_dir;
7793     }
7794   }
7795   else if (move_pattern == MV_WHEN_PUSHED ||
7796            move_pattern == MV_WHEN_DROPPED)
7797   {
7798     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7799       MovDir[x][y] = MV_NONE;
7800
7801     MovDelay[x][y] = 0;
7802   }
7803   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7804   {
7805     static int test_xy[7][2] =
7806     {
7807       { 0, -1 },
7808       { -1, 0 },
7809       { +1, 0 },
7810       { 0, +1 },
7811       { 0, -1 },
7812       { -1, 0 },
7813       { +1, 0 },
7814     };
7815     static int test_dir[7] =
7816     {
7817       MV_UP,
7818       MV_LEFT,
7819       MV_RIGHT,
7820       MV_DOWN,
7821       MV_UP,
7822       MV_LEFT,
7823       MV_RIGHT,
7824     };
7825     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7826     int move_preference = -1000000;     /* start with very low preference */
7827     int new_move_dir = MV_NONE;
7828     int start_test = RND(4);
7829     int i;
7830
7831     for (i = 0; i < NUM_DIRECTIONS; i++)
7832     {
7833       int move_dir = test_dir[start_test + i];
7834       int move_dir_preference;
7835
7836       xx = x + test_xy[start_test + i][0];
7837       yy = y + test_xy[start_test + i][1];
7838
7839       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7840           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7841       {
7842         new_move_dir = move_dir;
7843
7844         break;
7845       }
7846
7847       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7848         continue;
7849
7850       move_dir_preference = -1 * RunnerVisit[xx][yy];
7851       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7852         move_dir_preference = PlayerVisit[xx][yy];
7853
7854       if (move_dir_preference > move_preference)
7855       {
7856         /* prefer field that has not been visited for the longest time */
7857         move_preference = move_dir_preference;
7858         new_move_dir = move_dir;
7859       }
7860       else if (move_dir_preference == move_preference &&
7861                move_dir == old_move_dir)
7862       {
7863         /* prefer last direction when all directions are preferred equally */
7864         move_preference = move_dir_preference;
7865         new_move_dir = move_dir;
7866       }
7867     }
7868
7869     MovDir[x][y] = new_move_dir;
7870     if (old_move_dir != new_move_dir)
7871       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7872   }
7873 }
7874
7875 static void TurnRound(int x, int y)
7876 {
7877   int direction = MovDir[x][y];
7878
7879   TurnRoundExt(x, y);
7880
7881   GfxDir[x][y] = MovDir[x][y];
7882
7883   if (direction != MovDir[x][y])
7884     GfxFrame[x][y] = 0;
7885
7886   if (MovDelay[x][y])
7887     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7888
7889   ResetGfxFrame(x, y, FALSE);
7890 }
7891
7892 static boolean JustBeingPushed(int x, int y)
7893 {
7894   int i;
7895
7896   for (i = 0; i < MAX_PLAYERS; i++)
7897   {
7898     struct PlayerInfo *player = &stored_player[i];
7899
7900     if (player->active && player->is_pushing && player->MovPos)
7901     {
7902       int next_jx = player->jx + (player->jx - player->last_jx);
7903       int next_jy = player->jy + (player->jy - player->last_jy);
7904
7905       if (x == next_jx && y == next_jy)
7906         return TRUE;
7907     }
7908   }
7909
7910   return FALSE;
7911 }
7912
7913 void StartMoving(int x, int y)
7914 {
7915   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7916   int element = Feld[x][y];
7917
7918   if (Stop[x][y])
7919     return;
7920
7921   if (MovDelay[x][y] == 0)
7922     GfxAction[x][y] = ACTION_DEFAULT;
7923
7924   if (CAN_FALL(element) && y < lev_fieldy - 1)
7925   {
7926     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7927         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7928       if (JustBeingPushed(x, y))
7929         return;
7930
7931     if (element == EL_QUICKSAND_FULL)
7932     {
7933       if (IS_FREE(x, y + 1))
7934       {
7935         InitMovingField(x, y, MV_DOWN);
7936         started_moving = TRUE;
7937
7938         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7939 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7940         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7941           Store[x][y] = EL_ROCK;
7942 #else
7943         Store[x][y] = EL_ROCK;
7944 #endif
7945
7946         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7947       }
7948       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7949       {
7950         if (!MovDelay[x][y])
7951         {
7952           MovDelay[x][y] = TILEY + 1;
7953
7954           ResetGfxAnimation(x, y);
7955           ResetGfxAnimation(x, y + 1);
7956         }
7957
7958         if (MovDelay[x][y])
7959         {
7960           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7961           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7962
7963           MovDelay[x][y]--;
7964           if (MovDelay[x][y])
7965             return;
7966         }
7967
7968         Feld[x][y] = EL_QUICKSAND_EMPTY;
7969         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7970         Store[x][y + 1] = Store[x][y];
7971         Store[x][y] = 0;
7972
7973         PlayLevelSoundAction(x, y, ACTION_FILLING);
7974       }
7975       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7976       {
7977         if (!MovDelay[x][y])
7978         {
7979           MovDelay[x][y] = TILEY + 1;
7980
7981           ResetGfxAnimation(x, y);
7982           ResetGfxAnimation(x, y + 1);
7983         }
7984
7985         if (MovDelay[x][y])
7986         {
7987           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7988           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7989
7990           MovDelay[x][y]--;
7991           if (MovDelay[x][y])
7992             return;
7993         }
7994
7995         Feld[x][y] = EL_QUICKSAND_EMPTY;
7996         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7997         Store[x][y + 1] = Store[x][y];
7998         Store[x][y] = 0;
7999
8000         PlayLevelSoundAction(x, y, ACTION_FILLING);
8001       }
8002     }
8003     else if (element == EL_QUICKSAND_FAST_FULL)
8004     {
8005       if (IS_FREE(x, y + 1))
8006       {
8007         InitMovingField(x, y, MV_DOWN);
8008         started_moving = TRUE;
8009
8010         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8011 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8012         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8013           Store[x][y] = EL_ROCK;
8014 #else
8015         Store[x][y] = EL_ROCK;
8016 #endif
8017
8018         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8019       }
8020       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8021       {
8022         if (!MovDelay[x][y])
8023         {
8024           MovDelay[x][y] = TILEY + 1;
8025
8026           ResetGfxAnimation(x, y);
8027           ResetGfxAnimation(x, y + 1);
8028         }
8029
8030         if (MovDelay[x][y])
8031         {
8032           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8033           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8034
8035           MovDelay[x][y]--;
8036           if (MovDelay[x][y])
8037             return;
8038         }
8039
8040         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8041         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8042         Store[x][y + 1] = Store[x][y];
8043         Store[x][y] = 0;
8044
8045         PlayLevelSoundAction(x, y, ACTION_FILLING);
8046       }
8047       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8048       {
8049         if (!MovDelay[x][y])
8050         {
8051           MovDelay[x][y] = TILEY + 1;
8052
8053           ResetGfxAnimation(x, y);
8054           ResetGfxAnimation(x, y + 1);
8055         }
8056
8057         if (MovDelay[x][y])
8058         {
8059           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8060           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8061
8062           MovDelay[x][y]--;
8063           if (MovDelay[x][y])
8064             return;
8065         }
8066
8067         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8068         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8069         Store[x][y + 1] = Store[x][y];
8070         Store[x][y] = 0;
8071
8072         PlayLevelSoundAction(x, y, ACTION_FILLING);
8073       }
8074     }
8075     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8076              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8077     {
8078       InitMovingField(x, y, MV_DOWN);
8079       started_moving = TRUE;
8080
8081       Feld[x][y] = EL_QUICKSAND_FILLING;
8082       Store[x][y] = element;
8083
8084       PlayLevelSoundAction(x, y, ACTION_FILLING);
8085     }
8086     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8087              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8088     {
8089       InitMovingField(x, y, MV_DOWN);
8090       started_moving = TRUE;
8091
8092       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8093       Store[x][y] = element;
8094
8095       PlayLevelSoundAction(x, y, ACTION_FILLING);
8096     }
8097     else if (element == EL_MAGIC_WALL_FULL)
8098     {
8099       if (IS_FREE(x, y + 1))
8100       {
8101         InitMovingField(x, y, MV_DOWN);
8102         started_moving = TRUE;
8103
8104         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8105         Store[x][y] = EL_CHANGED(Store[x][y]);
8106       }
8107       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8108       {
8109         if (!MovDelay[x][y])
8110           MovDelay[x][y] = TILEY / 4 + 1;
8111
8112         if (MovDelay[x][y])
8113         {
8114           MovDelay[x][y]--;
8115           if (MovDelay[x][y])
8116             return;
8117         }
8118
8119         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8120         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8121         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8122         Store[x][y] = 0;
8123       }
8124     }
8125     else if (element == EL_BD_MAGIC_WALL_FULL)
8126     {
8127       if (IS_FREE(x, y + 1))
8128       {
8129         InitMovingField(x, y, MV_DOWN);
8130         started_moving = TRUE;
8131
8132         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8133         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8134       }
8135       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8136       {
8137         if (!MovDelay[x][y])
8138           MovDelay[x][y] = TILEY / 4 + 1;
8139
8140         if (MovDelay[x][y])
8141         {
8142           MovDelay[x][y]--;
8143           if (MovDelay[x][y])
8144             return;
8145         }
8146
8147         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8148         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8149         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8150         Store[x][y] = 0;
8151       }
8152     }
8153     else if (element == EL_DC_MAGIC_WALL_FULL)
8154     {
8155       if (IS_FREE(x, y + 1))
8156       {
8157         InitMovingField(x, y, MV_DOWN);
8158         started_moving = TRUE;
8159
8160         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8161         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8162       }
8163       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8164       {
8165         if (!MovDelay[x][y])
8166           MovDelay[x][y] = TILEY / 4 + 1;
8167
8168         if (MovDelay[x][y])
8169         {
8170           MovDelay[x][y]--;
8171           if (MovDelay[x][y])
8172             return;
8173         }
8174
8175         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8176         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8177         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8178         Store[x][y] = 0;
8179       }
8180     }
8181     else if ((CAN_PASS_MAGIC_WALL(element) &&
8182               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8183                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8184              (CAN_PASS_DC_MAGIC_WALL(element) &&
8185               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8186
8187     {
8188       InitMovingField(x, y, MV_DOWN);
8189       started_moving = TRUE;
8190
8191       Feld[x][y] =
8192         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8193          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8194          EL_DC_MAGIC_WALL_FILLING);
8195       Store[x][y] = element;
8196     }
8197     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8198     {
8199       SplashAcid(x, y + 1);
8200
8201       InitMovingField(x, y, MV_DOWN);
8202       started_moving = TRUE;
8203
8204       Store[x][y] = EL_ACID;
8205     }
8206     else if (
8207 #if USE_FIX_IMPACT_COLLISION
8208              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8209               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8210 #else
8211              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8212               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8213 #endif
8214              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8215               CAN_FALL(element) && WasJustFalling[x][y] &&
8216               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8217
8218              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8219               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8220               (Feld[x][y + 1] == EL_BLOCKED)))
8221     {
8222       /* this is needed for a special case not covered by calling "Impact()"
8223          from "ContinueMoving()": if an element moves to a tile directly below
8224          another element which was just falling on that tile (which was empty
8225          in the previous frame), the falling element above would just stop
8226          instead of smashing the element below (in previous version, the above
8227          element was just checked for "moving" instead of "falling", resulting
8228          in incorrect smashes caused by horizontal movement of the above
8229          element; also, the case of the player being the element to smash was
8230          simply not covered here... :-/ ) */
8231
8232       CheckCollision[x][y] = 0;
8233       CheckImpact[x][y] = 0;
8234
8235       Impact(x, y);
8236     }
8237     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8238     {
8239       if (MovDir[x][y] == MV_NONE)
8240       {
8241         InitMovingField(x, y, MV_DOWN);
8242         started_moving = TRUE;
8243       }
8244     }
8245     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8246     {
8247       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8248         MovDir[x][y] = MV_DOWN;
8249
8250       InitMovingField(x, y, MV_DOWN);
8251       started_moving = TRUE;
8252     }
8253     else if (element == EL_AMOEBA_DROP)
8254     {
8255       Feld[x][y] = EL_AMOEBA_GROWING;
8256       Store[x][y] = EL_AMOEBA_WET;
8257     }
8258     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8259               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8260              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8261              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8262     {
8263       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8264                                 (IS_FREE(x - 1, y + 1) ||
8265                                  Feld[x - 1][y + 1] == EL_ACID));
8266       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8267                                 (IS_FREE(x + 1, y + 1) ||
8268                                  Feld[x + 1][y + 1] == EL_ACID));
8269       boolean can_fall_any  = (can_fall_left || can_fall_right);
8270       boolean can_fall_both = (can_fall_left && can_fall_right);
8271       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8272
8273 #if USE_NEW_ALL_SLIPPERY
8274       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8275       {
8276         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8277           can_fall_right = FALSE;
8278         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8279           can_fall_left = FALSE;
8280         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8281           can_fall_right = FALSE;
8282         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8283           can_fall_left = FALSE;
8284
8285         can_fall_any  = (can_fall_left || can_fall_right);
8286         can_fall_both = FALSE;
8287       }
8288 #else
8289       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8290       {
8291         if (slippery_type == SLIPPERY_ONLY_LEFT)
8292           can_fall_right = FALSE;
8293         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8294           can_fall_left = FALSE;
8295         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8296           can_fall_right = FALSE;
8297         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8298           can_fall_left = FALSE;
8299
8300         can_fall_any  = (can_fall_left || can_fall_right);
8301         can_fall_both = (can_fall_left && can_fall_right);
8302       }
8303 #endif
8304
8305 #if USE_NEW_ALL_SLIPPERY
8306 #else
8307 #if USE_NEW_SP_SLIPPERY
8308       /* !!! better use the same properties as for custom elements here !!! */
8309       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8310                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8311       {
8312         can_fall_right = FALSE;         /* slip down on left side */
8313         can_fall_both = FALSE;
8314       }
8315 #endif
8316 #endif
8317
8318 #if USE_NEW_ALL_SLIPPERY
8319       if (can_fall_both)
8320       {
8321         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8322           can_fall_right = FALSE;       /* slip down on left side */
8323         else
8324           can_fall_left = !(can_fall_right = RND(2));
8325
8326         can_fall_both = FALSE;
8327       }
8328 #else
8329       if (can_fall_both)
8330       {
8331         if (game.emulation == EMU_BOULDERDASH ||
8332             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8333           can_fall_right = FALSE;       /* slip down on left side */
8334         else
8335           can_fall_left = !(can_fall_right = RND(2));
8336
8337         can_fall_both = FALSE;
8338       }
8339 #endif
8340
8341       if (can_fall_any)
8342       {
8343         /* if not determined otherwise, prefer left side for slipping down */
8344         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8345         started_moving = TRUE;
8346       }
8347     }
8348 #if 0
8349     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8350 #else
8351     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8352 #endif
8353     {
8354       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8355       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8356       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8357       int belt_dir = game.belt_dir[belt_nr];
8358
8359       if ((belt_dir == MV_LEFT  && left_is_free) ||
8360           (belt_dir == MV_RIGHT && right_is_free))
8361       {
8362         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8363
8364         InitMovingField(x, y, belt_dir);
8365         started_moving = TRUE;
8366
8367         Pushed[x][y] = TRUE;
8368         Pushed[nextx][y] = TRUE;
8369
8370         GfxAction[x][y] = ACTION_DEFAULT;
8371       }
8372       else
8373       {
8374         MovDir[x][y] = 0;       /* if element was moving, stop it */
8375       }
8376     }
8377   }
8378
8379   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8380 #if 0
8381   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8382 #else
8383   if (CAN_MOVE(element) && !started_moving)
8384 #endif
8385   {
8386     int move_pattern = element_info[element].move_pattern;
8387     int newx, newy;
8388
8389 #if 0
8390 #if DEBUG
8391     if (MovDir[x][y] == MV_NONE)
8392     {
8393       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8394              x, y, element, element_info[element].token_name);
8395       printf("StartMoving(): This should never happen!\n");
8396     }
8397 #endif
8398 #endif
8399
8400     Moving2Blocked(x, y, &newx, &newy);
8401
8402     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8403       return;
8404
8405     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8406         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8407     {
8408       WasJustMoving[x][y] = 0;
8409       CheckCollision[x][y] = 0;
8410
8411       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8412
8413       if (Feld[x][y] != element)        /* element has changed */
8414         return;
8415     }
8416
8417     if (!MovDelay[x][y])        /* start new movement phase */
8418     {
8419       /* all objects that can change their move direction after each step
8420          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8421
8422       if (element != EL_YAMYAM &&
8423           element != EL_DARK_YAMYAM &&
8424           element != EL_PACMAN &&
8425           !(move_pattern & MV_ANY_DIRECTION) &&
8426           move_pattern != MV_TURNING_LEFT &&
8427           move_pattern != MV_TURNING_RIGHT &&
8428           move_pattern != MV_TURNING_LEFT_RIGHT &&
8429           move_pattern != MV_TURNING_RIGHT_LEFT &&
8430           move_pattern != MV_TURNING_RANDOM)
8431       {
8432         TurnRound(x, y);
8433
8434         if (MovDelay[x][y] && (element == EL_BUG ||
8435                                element == EL_SPACESHIP ||
8436                                element == EL_SP_SNIKSNAK ||
8437                                element == EL_SP_ELECTRON ||
8438                                element == EL_MOLE))
8439           TEST_DrawLevelField(x, y);
8440       }
8441     }
8442
8443     if (MovDelay[x][y])         /* wait some time before next movement */
8444     {
8445       MovDelay[x][y]--;
8446
8447       if (element == EL_ROBOT ||
8448           element == EL_YAMYAM ||
8449           element == EL_DARK_YAMYAM)
8450       {
8451         DrawLevelElementAnimationIfNeeded(x, y, element);
8452         PlayLevelSoundAction(x, y, ACTION_WAITING);
8453       }
8454       else if (element == EL_SP_ELECTRON)
8455         DrawLevelElementAnimationIfNeeded(x, y, element);
8456       else if (element == EL_DRAGON)
8457       {
8458         int i;
8459         int dir = MovDir[x][y];
8460         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8461         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8462         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8463                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8464                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8465                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8466         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8467
8468         GfxAction[x][y] = ACTION_ATTACKING;
8469
8470         if (IS_PLAYER(x, y))
8471           DrawPlayerField(x, y);
8472         else
8473           TEST_DrawLevelField(x, y);
8474
8475         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8476
8477         for (i = 1; i <= 3; i++)
8478         {
8479           int xx = x + i * dx;
8480           int yy = y + i * dy;
8481           int sx = SCREENX(xx);
8482           int sy = SCREENY(yy);
8483           int flame_graphic = graphic + (i - 1);
8484
8485           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8486             break;
8487
8488           if (MovDelay[x][y])
8489           {
8490             int flamed = MovingOrBlocked2Element(xx, yy);
8491
8492             /* !!! */
8493 #if 0
8494             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8495               Bang(xx, yy);
8496             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8497               RemoveMovingField(xx, yy);
8498             else
8499               RemoveField(xx, yy);
8500 #else
8501             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8502               Bang(xx, yy);
8503             else
8504               RemoveMovingField(xx, yy);
8505 #endif
8506
8507             ChangeDelay[xx][yy] = 0;
8508
8509             Feld[xx][yy] = EL_FLAMES;
8510
8511             if (IN_SCR_FIELD(sx, sy))
8512             {
8513               TEST_DrawLevelFieldCrumbled(xx, yy);
8514               DrawGraphic(sx, sy, flame_graphic, frame);
8515             }
8516           }
8517           else
8518           {
8519             if (Feld[xx][yy] == EL_FLAMES)
8520               Feld[xx][yy] = EL_EMPTY;
8521             TEST_DrawLevelField(xx, yy);
8522           }
8523         }
8524       }
8525
8526       if (MovDelay[x][y])       /* element still has to wait some time */
8527       {
8528         PlayLevelSoundAction(x, y, ACTION_WAITING);
8529
8530         return;
8531       }
8532     }
8533
8534     /* now make next step */
8535
8536     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8537
8538     if (DONT_COLLIDE_WITH(element) &&
8539         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8540         !PLAYER_ENEMY_PROTECTED(newx, newy))
8541     {
8542       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8543
8544       return;
8545     }
8546
8547     else if (CAN_MOVE_INTO_ACID(element) &&
8548              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8549              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8550              (MovDir[x][y] == MV_DOWN ||
8551               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8552     {
8553       SplashAcid(newx, newy);
8554       Store[x][y] = EL_ACID;
8555     }
8556     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8557     {
8558       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8559           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8560           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8561           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8562       {
8563         RemoveField(x, y);
8564         TEST_DrawLevelField(x, y);
8565
8566         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8567         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8568           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8569
8570         local_player->friends_still_needed--;
8571         if (!local_player->friends_still_needed &&
8572             !local_player->GameOver && AllPlayersGone)
8573           PlayerWins(local_player);
8574
8575         return;
8576       }
8577       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8578       {
8579         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8580           TEST_DrawLevelField(newx, newy);
8581         else
8582           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8583       }
8584       else if (!IS_FREE(newx, newy))
8585       {
8586         GfxAction[x][y] = ACTION_WAITING;
8587
8588         if (IS_PLAYER(x, y))
8589           DrawPlayerField(x, y);
8590         else
8591           TEST_DrawLevelField(x, y);
8592
8593         return;
8594       }
8595     }
8596     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8597     {
8598       if (IS_FOOD_PIG(Feld[newx][newy]))
8599       {
8600         if (IS_MOVING(newx, newy))
8601           RemoveMovingField(newx, newy);
8602         else
8603         {
8604           Feld[newx][newy] = EL_EMPTY;
8605           TEST_DrawLevelField(newx, newy);
8606         }
8607
8608         PlayLevelSound(x, y, SND_PIG_DIGGING);
8609       }
8610       else if (!IS_FREE(newx, newy))
8611       {
8612         if (IS_PLAYER(x, y))
8613           DrawPlayerField(x, y);
8614         else
8615           TEST_DrawLevelField(x, y);
8616
8617         return;
8618       }
8619     }
8620     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8621     {
8622       if (Store[x][y] != EL_EMPTY)
8623       {
8624         boolean can_clone = FALSE;
8625         int xx, yy;
8626
8627         /* check if element to clone is still there */
8628         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8629         {
8630           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8631           {
8632             can_clone = TRUE;
8633
8634             break;
8635           }
8636         }
8637
8638         /* cannot clone or target field not free anymore -- do not clone */
8639         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8640           Store[x][y] = EL_EMPTY;
8641       }
8642
8643       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8644       {
8645         if (IS_MV_DIAGONAL(MovDir[x][y]))
8646         {
8647           int diagonal_move_dir = MovDir[x][y];
8648           int stored = Store[x][y];
8649           int change_delay = 8;
8650           int graphic;
8651
8652           /* android is moving diagonally */
8653
8654           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8655
8656           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8657           GfxElement[x][y] = EL_EMC_ANDROID;
8658           GfxAction[x][y] = ACTION_SHRINKING;
8659           GfxDir[x][y] = diagonal_move_dir;
8660           ChangeDelay[x][y] = change_delay;
8661
8662           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8663                                    GfxDir[x][y]);
8664
8665           DrawLevelGraphicAnimation(x, y, graphic);
8666           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8667
8668           if (Feld[newx][newy] == EL_ACID)
8669           {
8670             SplashAcid(newx, newy);
8671
8672             return;
8673           }
8674
8675           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8676
8677           Store[newx][newy] = EL_EMC_ANDROID;
8678           GfxElement[newx][newy] = EL_EMC_ANDROID;
8679           GfxAction[newx][newy] = ACTION_GROWING;
8680           GfxDir[newx][newy] = diagonal_move_dir;
8681           ChangeDelay[newx][newy] = change_delay;
8682
8683           graphic = el_act_dir2img(GfxElement[newx][newy],
8684                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8685
8686           DrawLevelGraphicAnimation(newx, newy, graphic);
8687           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8688
8689           return;
8690         }
8691         else
8692         {
8693           Feld[newx][newy] = EL_EMPTY;
8694           TEST_DrawLevelField(newx, newy);
8695
8696           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8697         }
8698       }
8699       else if (!IS_FREE(newx, newy))
8700       {
8701 #if 0
8702         if (IS_PLAYER(x, y))
8703           DrawPlayerField(x, y);
8704         else
8705           TEST_DrawLevelField(x, y);
8706 #endif
8707
8708         return;
8709       }
8710     }
8711     else if (IS_CUSTOM_ELEMENT(element) &&
8712              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8713     {
8714 #if 1
8715       if (!DigFieldByCE(newx, newy, element))
8716         return;
8717 #else
8718       int new_element = Feld[newx][newy];
8719
8720       if (!IS_FREE(newx, newy))
8721       {
8722         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8723                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8724                       ACTION_BREAKING);
8725
8726         /* no element can dig solid indestructible elements */
8727         if (IS_INDESTRUCTIBLE(new_element) &&
8728             !IS_DIGGABLE(new_element) &&
8729             !IS_COLLECTIBLE(new_element))
8730           return;
8731
8732         if (AmoebaNr[newx][newy] &&
8733             (new_element == EL_AMOEBA_FULL ||
8734              new_element == EL_BD_AMOEBA ||
8735              new_element == EL_AMOEBA_GROWING))
8736         {
8737           AmoebaCnt[AmoebaNr[newx][newy]]--;
8738           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8739         }
8740
8741         if (IS_MOVING(newx, newy))
8742           RemoveMovingField(newx, newy);
8743         else
8744         {
8745           RemoveField(newx, newy);
8746           TEST_DrawLevelField(newx, newy);
8747         }
8748
8749         /* if digged element was about to explode, prevent the explosion */
8750         ExplodeField[newx][newy] = EX_TYPE_NONE;
8751
8752         PlayLevelSoundAction(x, y, action);
8753       }
8754
8755       Store[newx][newy] = EL_EMPTY;
8756
8757 #if 1
8758       /* this makes it possible to leave the removed element again */
8759       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8760         Store[newx][newy] = new_element;
8761 #else
8762       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8763       {
8764         int move_leave_element = element_info[element].move_leave_element;
8765
8766         /* this makes it possible to leave the removed element again */
8767         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8768                              new_element : move_leave_element);
8769       }
8770 #endif
8771
8772 #endif
8773
8774       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8775       {
8776         RunnerVisit[x][y] = FrameCounter;
8777         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8778       }
8779     }
8780     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8781     {
8782       if (!IS_FREE(newx, newy))
8783       {
8784         if (IS_PLAYER(x, y))
8785           DrawPlayerField(x, y);
8786         else
8787           TEST_DrawLevelField(x, y);
8788
8789         return;
8790       }
8791       else
8792       {
8793         boolean wanna_flame = !RND(10);
8794         int dx = newx - x, dy = newy - y;
8795         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8796         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8797         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8798                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8799         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8800                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8801
8802         if ((wanna_flame ||
8803              IS_CLASSIC_ENEMY(element1) ||
8804              IS_CLASSIC_ENEMY(element2)) &&
8805             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8806             element1 != EL_FLAMES && element2 != EL_FLAMES)
8807         {
8808           ResetGfxAnimation(x, y);
8809           GfxAction[x][y] = ACTION_ATTACKING;
8810
8811           if (IS_PLAYER(x, y))
8812             DrawPlayerField(x, y);
8813           else
8814             TEST_DrawLevelField(x, y);
8815
8816           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8817
8818           MovDelay[x][y] = 50;
8819
8820           /* !!! */
8821 #if 0
8822           RemoveField(newx, newy);
8823 #endif
8824           Feld[newx][newy] = EL_FLAMES;
8825           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8826           {
8827 #if 0
8828             RemoveField(newx1, newy1);
8829 #endif
8830             Feld[newx1][newy1] = EL_FLAMES;
8831           }
8832           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8833           {
8834 #if 0
8835             RemoveField(newx2, newy2);
8836 #endif
8837             Feld[newx2][newy2] = EL_FLAMES;
8838           }
8839
8840           return;
8841         }
8842       }
8843     }
8844     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8845              Feld[newx][newy] == EL_DIAMOND)
8846     {
8847       if (IS_MOVING(newx, newy))
8848         RemoveMovingField(newx, newy);
8849       else
8850       {
8851         Feld[newx][newy] = EL_EMPTY;
8852         TEST_DrawLevelField(newx, newy);
8853       }
8854
8855       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8856     }
8857     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8858              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8859     {
8860       if (AmoebaNr[newx][newy])
8861       {
8862         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8863         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8864             Feld[newx][newy] == EL_BD_AMOEBA)
8865           AmoebaCnt[AmoebaNr[newx][newy]]--;
8866       }
8867
8868 #if 0
8869       /* !!! test !!! */
8870       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8871       {
8872         RemoveMovingField(newx, newy);
8873       }
8874 #else
8875       if (IS_MOVING(newx, newy))
8876       {
8877         RemoveMovingField(newx, newy);
8878       }
8879 #endif
8880       else
8881       {
8882         Feld[newx][newy] = EL_EMPTY;
8883         TEST_DrawLevelField(newx, newy);
8884       }
8885
8886       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8887     }
8888     else if ((element == EL_PACMAN || element == EL_MOLE)
8889              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8890     {
8891       if (AmoebaNr[newx][newy])
8892       {
8893         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8894         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8895             Feld[newx][newy] == EL_BD_AMOEBA)
8896           AmoebaCnt[AmoebaNr[newx][newy]]--;
8897       }
8898
8899       if (element == EL_MOLE)
8900       {
8901         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8902         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8903
8904         ResetGfxAnimation(x, y);
8905         GfxAction[x][y] = ACTION_DIGGING;
8906         TEST_DrawLevelField(x, y);
8907
8908         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8909
8910         return;                         /* wait for shrinking amoeba */
8911       }
8912       else      /* element == EL_PACMAN */
8913       {
8914         Feld[newx][newy] = EL_EMPTY;
8915         TEST_DrawLevelField(newx, newy);
8916         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8917       }
8918     }
8919     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8920              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8921               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8922     {
8923       /* wait for shrinking amoeba to completely disappear */
8924       return;
8925     }
8926     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8927     {
8928       /* object was running against a wall */
8929
8930       TurnRound(x, y);
8931
8932 #if 0
8933       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8934       if (move_pattern & MV_ANY_DIRECTION &&
8935           move_pattern == MovDir[x][y])
8936       {
8937         int blocking_element =
8938           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8939
8940         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8941                                  MovDir[x][y]);
8942
8943         element = Feld[x][y];   /* element might have changed */
8944       }
8945 #endif
8946
8947       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8948         DrawLevelElementAnimation(x, y, element);
8949
8950       if (DONT_TOUCH(element))
8951         TestIfBadThingTouchesPlayer(x, y);
8952
8953       return;
8954     }
8955
8956     InitMovingField(x, y, MovDir[x][y]);
8957
8958     PlayLevelSoundAction(x, y, ACTION_MOVING);
8959   }
8960
8961   if (MovDir[x][y])
8962     ContinueMoving(x, y);
8963 }
8964
8965 void ContinueMoving(int x, int y)
8966 {
8967   int element = Feld[x][y];
8968   struct ElementInfo *ei = &element_info[element];
8969   int direction = MovDir[x][y];
8970   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8971   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8972   int newx = x + dx, newy = y + dy;
8973   int stored = Store[x][y];
8974   int stored_new = Store[newx][newy];
8975   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8976   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8977   boolean last_line = (newy == lev_fieldy - 1);
8978
8979   MovPos[x][y] += getElementMoveStepsize(x, y);
8980
8981   if (pushed_by_player) /* special case: moving object pushed by player */
8982     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8983
8984   if (ABS(MovPos[x][y]) < TILEX)
8985   {
8986 #if 0
8987     int ee = Feld[x][y];
8988     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8989     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8990
8991     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8992            x, y, ABS(MovPos[x][y]),
8993            ee, gg, ff,
8994            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8995 #endif
8996
8997     TEST_DrawLevelField(x, y);
8998
8999     return;     /* element is still moving */
9000   }
9001
9002   /* element reached destination field */
9003
9004   Feld[x][y] = EL_EMPTY;
9005   Feld[newx][newy] = element;
9006   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9007
9008   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9009   {
9010     element = Feld[newx][newy] = EL_ACID;
9011   }
9012   else if (element == EL_MOLE)
9013   {
9014     Feld[x][y] = EL_SAND;
9015
9016     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9017   }
9018   else if (element == EL_QUICKSAND_FILLING)
9019   {
9020     element = Feld[newx][newy] = get_next_element(element);
9021     Store[newx][newy] = Store[x][y];
9022   }
9023   else if (element == EL_QUICKSAND_EMPTYING)
9024   {
9025     Feld[x][y] = get_next_element(element);
9026     element = Feld[newx][newy] = Store[x][y];
9027   }
9028   else if (element == EL_QUICKSAND_FAST_FILLING)
9029   {
9030     element = Feld[newx][newy] = get_next_element(element);
9031     Store[newx][newy] = Store[x][y];
9032   }
9033   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9034   {
9035     Feld[x][y] = get_next_element(element);
9036     element = Feld[newx][newy] = Store[x][y];
9037   }
9038   else if (element == EL_MAGIC_WALL_FILLING)
9039   {
9040     element = Feld[newx][newy] = get_next_element(element);
9041     if (!game.magic_wall_active)
9042       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9043     Store[newx][newy] = Store[x][y];
9044   }
9045   else if (element == EL_MAGIC_WALL_EMPTYING)
9046   {
9047     Feld[x][y] = get_next_element(element);
9048     if (!game.magic_wall_active)
9049       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9050     element = Feld[newx][newy] = Store[x][y];
9051
9052 #if USE_NEW_CUSTOM_VALUE
9053     InitField(newx, newy, FALSE);
9054 #endif
9055   }
9056   else if (element == EL_BD_MAGIC_WALL_FILLING)
9057   {
9058     element = Feld[newx][newy] = get_next_element(element);
9059     if (!game.magic_wall_active)
9060       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9061     Store[newx][newy] = Store[x][y];
9062   }
9063   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9064   {
9065     Feld[x][y] = get_next_element(element);
9066     if (!game.magic_wall_active)
9067       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9068     element = Feld[newx][newy] = Store[x][y];
9069
9070 #if USE_NEW_CUSTOM_VALUE
9071     InitField(newx, newy, FALSE);
9072 #endif
9073   }
9074   else if (element == EL_DC_MAGIC_WALL_FILLING)
9075   {
9076     element = Feld[newx][newy] = get_next_element(element);
9077     if (!game.magic_wall_active)
9078       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9079     Store[newx][newy] = Store[x][y];
9080   }
9081   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9082   {
9083     Feld[x][y] = get_next_element(element);
9084     if (!game.magic_wall_active)
9085       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9086     element = Feld[newx][newy] = Store[x][y];
9087
9088 #if USE_NEW_CUSTOM_VALUE
9089     InitField(newx, newy, FALSE);
9090 #endif
9091   }
9092   else if (element == EL_AMOEBA_DROPPING)
9093   {
9094     Feld[x][y] = get_next_element(element);
9095     element = Feld[newx][newy] = Store[x][y];
9096   }
9097   else if (element == EL_SOKOBAN_OBJECT)
9098   {
9099     if (Back[x][y])
9100       Feld[x][y] = Back[x][y];
9101
9102     if (Back[newx][newy])
9103       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9104
9105     Back[x][y] = Back[newx][newy] = 0;
9106   }
9107
9108   Store[x][y] = EL_EMPTY;
9109   MovPos[x][y] = 0;
9110   MovDir[x][y] = 0;
9111   MovDelay[x][y] = 0;
9112
9113   MovDelay[newx][newy] = 0;
9114
9115   if (CAN_CHANGE_OR_HAS_ACTION(element))
9116   {
9117     /* copy element change control values to new field */
9118     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9119     ChangePage[newx][newy]  = ChangePage[x][y];
9120     ChangeCount[newx][newy] = ChangeCount[x][y];
9121     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9122   }
9123
9124 #if USE_NEW_CUSTOM_VALUE
9125   CustomValue[newx][newy] = CustomValue[x][y];
9126 #endif
9127
9128   ChangeDelay[x][y] = 0;
9129   ChangePage[x][y] = -1;
9130   ChangeCount[x][y] = 0;
9131   ChangeEvent[x][y] = -1;
9132
9133 #if USE_NEW_CUSTOM_VALUE
9134   CustomValue[x][y] = 0;
9135 #endif
9136
9137   /* copy animation control values to new field */
9138   GfxFrame[newx][newy]  = GfxFrame[x][y];
9139   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9140   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9141   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9142
9143   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9144
9145   /* some elements can leave other elements behind after moving */
9146 #if 1
9147   if (ei->move_leave_element != EL_EMPTY &&
9148       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9149       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9150 #else
9151   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9152       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9153       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9154 #endif
9155   {
9156     int move_leave_element = ei->move_leave_element;
9157
9158 #if 1
9159 #if 1
9160     /* this makes it possible to leave the removed element again */
9161     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9162       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9163 #else
9164     /* this makes it possible to leave the removed element again */
9165     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9166       move_leave_element = stored;
9167 #endif
9168 #else
9169     /* this makes it possible to leave the removed element again */
9170     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9171         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9172       move_leave_element = stored;
9173 #endif
9174
9175     Feld[x][y] = move_leave_element;
9176
9177     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9178       MovDir[x][y] = direction;
9179
9180     InitField(x, y, FALSE);
9181
9182     if (GFX_CRUMBLED(Feld[x][y]))
9183       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9184
9185     if (ELEM_IS_PLAYER(move_leave_element))
9186       RelocatePlayer(x, y, move_leave_element);
9187   }
9188
9189   /* do this after checking for left-behind element */
9190   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9191
9192   if (!CAN_MOVE(element) ||
9193       (CAN_FALL(element) && direction == MV_DOWN &&
9194        (element == EL_SPRING ||
9195         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9196         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9197     GfxDir[x][y] = MovDir[newx][newy] = 0;
9198
9199   TEST_DrawLevelField(x, y);
9200   TEST_DrawLevelField(newx, newy);
9201
9202   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9203
9204   /* prevent pushed element from moving on in pushed direction */
9205   if (pushed_by_player && CAN_MOVE(element) &&
9206       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9207       !(element_info[element].move_pattern & direction))
9208     TurnRound(newx, newy);
9209
9210   /* prevent elements on conveyor belt from moving on in last direction */
9211   if (pushed_by_conveyor && CAN_FALL(element) &&
9212       direction & MV_HORIZONTAL)
9213     MovDir[newx][newy] = 0;
9214
9215   if (!pushed_by_player)
9216   {
9217     int nextx = newx + dx, nexty = newy + dy;
9218     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9219
9220     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9221
9222     if (CAN_FALL(element) && direction == MV_DOWN)
9223       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9224
9225     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9226       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9227
9228 #if USE_FIX_IMPACT_COLLISION
9229     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9230       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9231 #endif
9232   }
9233
9234   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9235   {
9236     TestIfBadThingTouchesPlayer(newx, newy);
9237     TestIfBadThingTouchesFriend(newx, newy);
9238
9239     if (!IS_CUSTOM_ELEMENT(element))
9240       TestIfBadThingTouchesOtherBadThing(newx, newy);
9241   }
9242   else if (element == EL_PENGUIN)
9243     TestIfFriendTouchesBadThing(newx, newy);
9244
9245   if (DONT_GET_HIT_BY(element))
9246   {
9247     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9248   }
9249
9250   /* give the player one last chance (one more frame) to move away */
9251   if (CAN_FALL(element) && direction == MV_DOWN &&
9252       (last_line || (!IS_FREE(x, newy + 1) &&
9253                      (!IS_PLAYER(x, newy + 1) ||
9254                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9255     Impact(x, newy);
9256
9257   if (pushed_by_player && !game.use_change_when_pushing_bug)
9258   {
9259     int push_side = MV_DIR_OPPOSITE(direction);
9260     struct PlayerInfo *player = PLAYERINFO(x, y);
9261
9262     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9263                                player->index_bit, push_side);
9264     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9265                                         player->index_bit, push_side);
9266   }
9267
9268   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9269     MovDelay[newx][newy] = 1;
9270
9271   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9272
9273   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9274
9275 #if 0
9276   if (ChangePage[newx][newy] != -1)             /* delayed change */
9277   {
9278     int page = ChangePage[newx][newy];
9279     struct ElementChangeInfo *change = &ei->change_page[page];
9280
9281     ChangePage[newx][newy] = -1;
9282
9283     if (change->can_change)
9284     {
9285       if (ChangeElement(newx, newy, element, page))
9286       {
9287         if (change->post_change_function)
9288           change->post_change_function(newx, newy);
9289       }
9290     }
9291
9292     if (change->has_action)
9293       ExecuteCustomElementAction(newx, newy, element, page);
9294   }
9295 #endif
9296
9297   TestIfElementHitsCustomElement(newx, newy, direction);
9298   TestIfPlayerTouchesCustomElement(newx, newy);
9299   TestIfElementTouchesCustomElement(newx, newy);
9300
9301   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9302       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9303     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9304                              MV_DIR_OPPOSITE(direction));
9305 }
9306
9307 int AmoebeNachbarNr(int ax, int ay)
9308 {
9309   int i;
9310   int element = Feld[ax][ay];
9311   int group_nr = 0;
9312   static int xy[4][2] =
9313   {
9314     { 0, -1 },
9315     { -1, 0 },
9316     { +1, 0 },
9317     { 0, +1 }
9318   };
9319
9320   for (i = 0; i < NUM_DIRECTIONS; i++)
9321   {
9322     int x = ax + xy[i][0];
9323     int y = ay + xy[i][1];
9324
9325     if (!IN_LEV_FIELD(x, y))
9326       continue;
9327
9328     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9329       group_nr = AmoebaNr[x][y];
9330   }
9331
9332   return group_nr;
9333 }
9334
9335 void AmoebenVereinigen(int ax, int ay)
9336 {
9337   int i, x, y, xx, yy;
9338   int new_group_nr = AmoebaNr[ax][ay];
9339   static int xy[4][2] =
9340   {
9341     { 0, -1 },
9342     { -1, 0 },
9343     { +1, 0 },
9344     { 0, +1 }
9345   };
9346
9347   if (new_group_nr == 0)
9348     return;
9349
9350   for (i = 0; i < NUM_DIRECTIONS; i++)
9351   {
9352     x = ax + xy[i][0];
9353     y = ay + xy[i][1];
9354
9355     if (!IN_LEV_FIELD(x, y))
9356       continue;
9357
9358     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9359          Feld[x][y] == EL_BD_AMOEBA ||
9360          Feld[x][y] == EL_AMOEBA_DEAD) &&
9361         AmoebaNr[x][y] != new_group_nr)
9362     {
9363       int old_group_nr = AmoebaNr[x][y];
9364
9365       if (old_group_nr == 0)
9366         return;
9367
9368       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9369       AmoebaCnt[old_group_nr] = 0;
9370       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9371       AmoebaCnt2[old_group_nr] = 0;
9372
9373       SCAN_PLAYFIELD(xx, yy)
9374       {
9375         if (AmoebaNr[xx][yy] == old_group_nr)
9376           AmoebaNr[xx][yy] = new_group_nr;
9377       }
9378     }
9379   }
9380 }
9381
9382 void AmoebeUmwandeln(int ax, int ay)
9383 {
9384   int i, x, y;
9385
9386   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9387   {
9388     int group_nr = AmoebaNr[ax][ay];
9389
9390 #ifdef DEBUG
9391     if (group_nr == 0)
9392     {
9393       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9394       printf("AmoebeUmwandeln(): This should never happen!\n");
9395       return;
9396     }
9397 #endif
9398
9399     SCAN_PLAYFIELD(x, y)
9400     {
9401       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9402       {
9403         AmoebaNr[x][y] = 0;
9404         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9405       }
9406     }
9407
9408     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9409                             SND_AMOEBA_TURNING_TO_GEM :
9410                             SND_AMOEBA_TURNING_TO_ROCK));
9411     Bang(ax, ay);
9412   }
9413   else
9414   {
9415     static int xy[4][2] =
9416     {
9417       { 0, -1 },
9418       { -1, 0 },
9419       { +1, 0 },
9420       { 0, +1 }
9421     };
9422
9423     for (i = 0; i < NUM_DIRECTIONS; i++)
9424     {
9425       x = ax + xy[i][0];
9426       y = ay + xy[i][1];
9427
9428       if (!IN_LEV_FIELD(x, y))
9429         continue;
9430
9431       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9432       {
9433         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9434                               SND_AMOEBA_TURNING_TO_GEM :
9435                               SND_AMOEBA_TURNING_TO_ROCK));
9436         Bang(x, y);
9437       }
9438     }
9439   }
9440 }
9441
9442 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9443 {
9444   int x, y;
9445   int group_nr = AmoebaNr[ax][ay];
9446   boolean done = FALSE;
9447
9448 #ifdef DEBUG
9449   if (group_nr == 0)
9450   {
9451     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9452     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9453     return;
9454   }
9455 #endif
9456
9457   SCAN_PLAYFIELD(x, y)
9458   {
9459     if (AmoebaNr[x][y] == group_nr &&
9460         (Feld[x][y] == EL_AMOEBA_DEAD ||
9461          Feld[x][y] == EL_BD_AMOEBA ||
9462          Feld[x][y] == EL_AMOEBA_GROWING))
9463     {
9464       AmoebaNr[x][y] = 0;
9465       Feld[x][y] = new_element;
9466       InitField(x, y, FALSE);
9467       TEST_DrawLevelField(x, y);
9468       done = TRUE;
9469     }
9470   }
9471
9472   if (done)
9473     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9474                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9475                             SND_BD_AMOEBA_TURNING_TO_GEM));
9476 }
9477
9478 void AmoebeWaechst(int x, int y)
9479 {
9480   static unsigned int sound_delay = 0;
9481   static unsigned int sound_delay_value = 0;
9482
9483   if (!MovDelay[x][y])          /* start new growing cycle */
9484   {
9485     MovDelay[x][y] = 7;
9486
9487     if (DelayReached(&sound_delay, sound_delay_value))
9488     {
9489       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9490       sound_delay_value = 30;
9491     }
9492   }
9493
9494   if (MovDelay[x][y])           /* wait some time before growing bigger */
9495   {
9496     MovDelay[x][y]--;
9497     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9498     {
9499       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9500                                            6 - MovDelay[x][y]);
9501
9502       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9503     }
9504
9505     if (!MovDelay[x][y])
9506     {
9507       Feld[x][y] = Store[x][y];
9508       Store[x][y] = 0;
9509       TEST_DrawLevelField(x, y);
9510     }
9511   }
9512 }
9513
9514 void AmoebaDisappearing(int x, int y)
9515 {
9516   static unsigned int sound_delay = 0;
9517   static unsigned int sound_delay_value = 0;
9518
9519   if (!MovDelay[x][y])          /* start new shrinking cycle */
9520   {
9521     MovDelay[x][y] = 7;
9522
9523     if (DelayReached(&sound_delay, sound_delay_value))
9524       sound_delay_value = 30;
9525   }
9526
9527   if (MovDelay[x][y])           /* wait some time before shrinking */
9528   {
9529     MovDelay[x][y]--;
9530     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9531     {
9532       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9533                                            6 - MovDelay[x][y]);
9534
9535       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9536     }
9537
9538     if (!MovDelay[x][y])
9539     {
9540       Feld[x][y] = EL_EMPTY;
9541       TEST_DrawLevelField(x, y);
9542
9543       /* don't let mole enter this field in this cycle;
9544          (give priority to objects falling to this field from above) */
9545       Stop[x][y] = TRUE;
9546     }
9547   }
9548 }
9549
9550 void AmoebeAbleger(int ax, int ay)
9551 {
9552   int i;
9553   int element = Feld[ax][ay];
9554   int graphic = el2img(element);
9555   int newax = ax, neway = ay;
9556   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9557   static int xy[4][2] =
9558   {
9559     { 0, -1 },
9560     { -1, 0 },
9561     { +1, 0 },
9562     { 0, +1 }
9563   };
9564
9565   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9566   {
9567     Feld[ax][ay] = EL_AMOEBA_DEAD;
9568     TEST_DrawLevelField(ax, ay);
9569     return;
9570   }
9571
9572   if (IS_ANIMATED(graphic))
9573     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9574
9575   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9576     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9577
9578   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9579   {
9580     MovDelay[ax][ay]--;
9581     if (MovDelay[ax][ay])
9582       return;
9583   }
9584
9585   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9586   {
9587     int start = RND(4);
9588     int x = ax + xy[start][0];
9589     int y = ay + xy[start][1];
9590
9591     if (!IN_LEV_FIELD(x, y))
9592       return;
9593
9594     if (IS_FREE(x, y) ||
9595         CAN_GROW_INTO(Feld[x][y]) ||
9596         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9597         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9598     {
9599       newax = x;
9600       neway = y;
9601     }
9602
9603     if (newax == ax && neway == ay)
9604       return;
9605   }
9606   else                          /* normal or "filled" (BD style) amoeba */
9607   {
9608     int start = RND(4);
9609     boolean waiting_for_player = FALSE;
9610
9611     for (i = 0; i < NUM_DIRECTIONS; i++)
9612     {
9613       int j = (start + i) % 4;
9614       int x = ax + xy[j][0];
9615       int y = ay + xy[j][1];
9616
9617       if (!IN_LEV_FIELD(x, y))
9618         continue;
9619
9620       if (IS_FREE(x, y) ||
9621           CAN_GROW_INTO(Feld[x][y]) ||
9622           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9623           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9624       {
9625         newax = x;
9626         neway = y;
9627         break;
9628       }
9629       else if (IS_PLAYER(x, y))
9630         waiting_for_player = TRUE;
9631     }
9632
9633     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9634     {
9635       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9636       {
9637         Feld[ax][ay] = EL_AMOEBA_DEAD;
9638         TEST_DrawLevelField(ax, ay);
9639         AmoebaCnt[AmoebaNr[ax][ay]]--;
9640
9641         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9642         {
9643           if (element == EL_AMOEBA_FULL)
9644             AmoebeUmwandeln(ax, ay);
9645           else if (element == EL_BD_AMOEBA)
9646             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9647         }
9648       }
9649       return;
9650     }
9651     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9652     {
9653       /* amoeba gets larger by growing in some direction */
9654
9655       int new_group_nr = AmoebaNr[ax][ay];
9656
9657 #ifdef DEBUG
9658   if (new_group_nr == 0)
9659   {
9660     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9661     printf("AmoebeAbleger(): This should never happen!\n");
9662     return;
9663   }
9664 #endif
9665
9666       AmoebaNr[newax][neway] = new_group_nr;
9667       AmoebaCnt[new_group_nr]++;
9668       AmoebaCnt2[new_group_nr]++;
9669
9670       /* if amoeba touches other amoeba(s) after growing, unify them */
9671       AmoebenVereinigen(newax, neway);
9672
9673       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9674       {
9675         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9676         return;
9677       }
9678     }
9679   }
9680
9681   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9682       (neway == lev_fieldy - 1 && newax != ax))
9683   {
9684     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9685     Store[newax][neway] = element;
9686   }
9687   else if (neway == ay || element == EL_EMC_DRIPPER)
9688   {
9689     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9690
9691     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9692   }
9693   else
9694   {
9695     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9696     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9697     Store[ax][ay] = EL_AMOEBA_DROP;
9698     ContinueMoving(ax, ay);
9699     return;
9700   }
9701
9702   TEST_DrawLevelField(newax, neway);
9703 }
9704
9705 void Life(int ax, int ay)
9706 {
9707   int x1, y1, x2, y2;
9708   int life_time = 40;
9709   int element = Feld[ax][ay];
9710   int graphic = el2img(element);
9711   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9712                          level.biomaze);
9713   boolean changed = FALSE;
9714
9715   if (IS_ANIMATED(graphic))
9716     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9717
9718   if (Stop[ax][ay])
9719     return;
9720
9721   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9722     MovDelay[ax][ay] = life_time;
9723
9724   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9725   {
9726     MovDelay[ax][ay]--;
9727     if (MovDelay[ax][ay])
9728       return;
9729   }
9730
9731   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9732   {
9733     int xx = ax+x1, yy = ay+y1;
9734     int nachbarn = 0;
9735
9736     if (!IN_LEV_FIELD(xx, yy))
9737       continue;
9738
9739     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9740     {
9741       int x = xx+x2, y = yy+y2;
9742
9743       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9744         continue;
9745
9746       if (((Feld[x][y] == element ||
9747             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9748            !Stop[x][y]) ||
9749           (IS_FREE(x, y) && Stop[x][y]))
9750         nachbarn++;
9751     }
9752
9753     if (xx == ax && yy == ay)           /* field in the middle */
9754     {
9755       if (nachbarn < life_parameter[0] ||
9756           nachbarn > life_parameter[1])
9757       {
9758         Feld[xx][yy] = EL_EMPTY;
9759         if (!Stop[xx][yy])
9760           TEST_DrawLevelField(xx, yy);
9761         Stop[xx][yy] = TRUE;
9762         changed = TRUE;
9763       }
9764     }
9765     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9766     {                                   /* free border field */
9767       if (nachbarn >= life_parameter[2] &&
9768           nachbarn <= life_parameter[3])
9769       {
9770         Feld[xx][yy] = element;
9771         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9772         if (!Stop[xx][yy])
9773           TEST_DrawLevelField(xx, yy);
9774         Stop[xx][yy] = TRUE;
9775         changed = TRUE;
9776       }
9777     }
9778   }
9779
9780   if (changed)
9781     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9782                    SND_GAME_OF_LIFE_GROWING);
9783 }
9784
9785 static void InitRobotWheel(int x, int y)
9786 {
9787   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9788 }
9789
9790 static void RunRobotWheel(int x, int y)
9791 {
9792   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9793 }
9794
9795 static void StopRobotWheel(int x, int y)
9796 {
9797   if (ZX == x && ZY == y)
9798   {
9799     ZX = ZY = -1;
9800
9801     game.robot_wheel_active = FALSE;
9802   }
9803 }
9804
9805 static void InitTimegateWheel(int x, int y)
9806 {
9807   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9808 }
9809
9810 static void RunTimegateWheel(int x, int y)
9811 {
9812   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9813 }
9814
9815 static void InitMagicBallDelay(int x, int y)
9816 {
9817 #if 1
9818   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9819 #else
9820   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9821 #endif
9822 }
9823
9824 static void ActivateMagicBall(int bx, int by)
9825 {
9826   int x, y;
9827
9828   if (level.ball_random)
9829   {
9830     int pos_border = RND(8);    /* select one of the eight border elements */
9831     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9832     int xx = pos_content % 3;
9833     int yy = pos_content / 3;
9834
9835     x = bx - 1 + xx;
9836     y = by - 1 + yy;
9837
9838     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9839       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9840   }
9841   else
9842   {
9843     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9844     {
9845       int xx = x - bx + 1;
9846       int yy = y - by + 1;
9847
9848       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9849         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9850     }
9851   }
9852
9853   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9854 }
9855
9856 void CheckExit(int x, int y)
9857 {
9858   if (local_player->gems_still_needed > 0 ||
9859       local_player->sokobanfields_still_needed > 0 ||
9860       local_player->lights_still_needed > 0)
9861   {
9862     int element = Feld[x][y];
9863     int graphic = el2img(element);
9864
9865     if (IS_ANIMATED(graphic))
9866       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9867
9868     return;
9869   }
9870
9871   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9872     return;
9873
9874   Feld[x][y] = EL_EXIT_OPENING;
9875
9876   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9877 }
9878
9879 void CheckExitEM(int x, int y)
9880 {
9881   if (local_player->gems_still_needed > 0 ||
9882       local_player->sokobanfields_still_needed > 0 ||
9883       local_player->lights_still_needed > 0)
9884   {
9885     int element = Feld[x][y];
9886     int graphic = el2img(element);
9887
9888     if (IS_ANIMATED(graphic))
9889       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9890
9891     return;
9892   }
9893
9894   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9895     return;
9896
9897   Feld[x][y] = EL_EM_EXIT_OPENING;
9898
9899   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9900 }
9901
9902 void CheckExitSteel(int x, int y)
9903 {
9904   if (local_player->gems_still_needed > 0 ||
9905       local_player->sokobanfields_still_needed > 0 ||
9906       local_player->lights_still_needed > 0)
9907   {
9908     int element = Feld[x][y];
9909     int graphic = el2img(element);
9910
9911     if (IS_ANIMATED(graphic))
9912       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9913
9914     return;
9915   }
9916
9917   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9918     return;
9919
9920   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9921
9922   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9923 }
9924
9925 void CheckExitSteelEM(int x, int y)
9926 {
9927   if (local_player->gems_still_needed > 0 ||
9928       local_player->sokobanfields_still_needed > 0 ||
9929       local_player->lights_still_needed > 0)
9930   {
9931     int element = Feld[x][y];
9932     int graphic = el2img(element);
9933
9934     if (IS_ANIMATED(graphic))
9935       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9936
9937     return;
9938   }
9939
9940   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9941     return;
9942
9943   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9944
9945   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9946 }
9947
9948 void CheckExitSP(int x, int y)
9949 {
9950   if (local_player->gems_still_needed > 0)
9951   {
9952     int element = Feld[x][y];
9953     int graphic = el2img(element);
9954
9955     if (IS_ANIMATED(graphic))
9956       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9957
9958     return;
9959   }
9960
9961   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9962     return;
9963
9964   Feld[x][y] = EL_SP_EXIT_OPENING;
9965
9966   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9967 }
9968
9969 static void CloseAllOpenTimegates()
9970 {
9971   int x, y;
9972
9973   SCAN_PLAYFIELD(x, y)
9974   {
9975     int element = Feld[x][y];
9976
9977     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9978     {
9979       Feld[x][y] = EL_TIMEGATE_CLOSING;
9980
9981       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9982     }
9983   }
9984 }
9985
9986 void DrawTwinkleOnField(int x, int y)
9987 {
9988   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9989     return;
9990
9991   if (Feld[x][y] == EL_BD_DIAMOND)
9992     return;
9993
9994   if (MovDelay[x][y] == 0)      /* next animation frame */
9995     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9996
9997   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9998   {
9999     MovDelay[x][y]--;
10000
10001     DrawLevelElementAnimation(x, y, Feld[x][y]);
10002
10003     if (MovDelay[x][y] != 0)
10004     {
10005       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10006                                            10 - MovDelay[x][y]);
10007
10008       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10009     }
10010   }
10011 }
10012
10013 void MauerWaechst(int x, int y)
10014 {
10015   int delay = 6;
10016
10017   if (!MovDelay[x][y])          /* next animation frame */
10018     MovDelay[x][y] = 3 * delay;
10019
10020   if (MovDelay[x][y])           /* wait some time before next frame */
10021   {
10022     MovDelay[x][y]--;
10023
10024     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10025     {
10026       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10027       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10028
10029       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10030     }
10031
10032     if (!MovDelay[x][y])
10033     {
10034       if (MovDir[x][y] == MV_LEFT)
10035       {
10036         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10037           TEST_DrawLevelField(x - 1, y);
10038       }
10039       else if (MovDir[x][y] == MV_RIGHT)
10040       {
10041         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10042           TEST_DrawLevelField(x + 1, y);
10043       }
10044       else if (MovDir[x][y] == MV_UP)
10045       {
10046         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10047           TEST_DrawLevelField(x, y - 1);
10048       }
10049       else
10050       {
10051         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10052           TEST_DrawLevelField(x, y + 1);
10053       }
10054
10055       Feld[x][y] = Store[x][y];
10056       Store[x][y] = 0;
10057       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10058       TEST_DrawLevelField(x, y);
10059     }
10060   }
10061 }
10062
10063 void MauerAbleger(int ax, int ay)
10064 {
10065   int element = Feld[ax][ay];
10066   int graphic = el2img(element);
10067   boolean oben_frei = FALSE, unten_frei = FALSE;
10068   boolean links_frei = FALSE, rechts_frei = FALSE;
10069   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10070   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10071   boolean new_wall = FALSE;
10072
10073   if (IS_ANIMATED(graphic))
10074     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10075
10076   if (!MovDelay[ax][ay])        /* start building new wall */
10077     MovDelay[ax][ay] = 6;
10078
10079   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10080   {
10081     MovDelay[ax][ay]--;
10082     if (MovDelay[ax][ay])
10083       return;
10084   }
10085
10086   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10087     oben_frei = TRUE;
10088   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10089     unten_frei = TRUE;
10090   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10091     links_frei = TRUE;
10092   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10093     rechts_frei = TRUE;
10094
10095   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10096       element == EL_EXPANDABLE_WALL_ANY)
10097   {
10098     if (oben_frei)
10099     {
10100       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10101       Store[ax][ay-1] = element;
10102       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10103       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10104         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10105                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10106       new_wall = TRUE;
10107     }
10108     if (unten_frei)
10109     {
10110       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10111       Store[ax][ay+1] = element;
10112       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10113       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10114         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10115                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10116       new_wall = TRUE;
10117     }
10118   }
10119
10120   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10121       element == EL_EXPANDABLE_WALL_ANY ||
10122       element == EL_EXPANDABLE_WALL ||
10123       element == EL_BD_EXPANDABLE_WALL)
10124   {
10125     if (links_frei)
10126     {
10127       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10128       Store[ax-1][ay] = element;
10129       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10130       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10131         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10132                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10133       new_wall = TRUE;
10134     }
10135
10136     if (rechts_frei)
10137     {
10138       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10139       Store[ax+1][ay] = element;
10140       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10141       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10142         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10143                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10144       new_wall = TRUE;
10145     }
10146   }
10147
10148   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10149     TEST_DrawLevelField(ax, ay);
10150
10151   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10152     oben_massiv = TRUE;
10153   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10154     unten_massiv = TRUE;
10155   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10156     links_massiv = TRUE;
10157   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10158     rechts_massiv = TRUE;
10159
10160   if (((oben_massiv && unten_massiv) ||
10161        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10162        element == EL_EXPANDABLE_WALL) &&
10163       ((links_massiv && rechts_massiv) ||
10164        element == EL_EXPANDABLE_WALL_VERTICAL))
10165     Feld[ax][ay] = EL_WALL;
10166
10167   if (new_wall)
10168     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10169 }
10170
10171 void MauerAblegerStahl(int ax, int ay)
10172 {
10173   int element = Feld[ax][ay];
10174   int graphic = el2img(element);
10175   boolean oben_frei = FALSE, unten_frei = FALSE;
10176   boolean links_frei = FALSE, rechts_frei = FALSE;
10177   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10178   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10179   boolean new_wall = FALSE;
10180
10181   if (IS_ANIMATED(graphic))
10182     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10183
10184   if (!MovDelay[ax][ay])        /* start building new wall */
10185     MovDelay[ax][ay] = 6;
10186
10187   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10188   {
10189     MovDelay[ax][ay]--;
10190     if (MovDelay[ax][ay])
10191       return;
10192   }
10193
10194   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10195     oben_frei = TRUE;
10196   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10197     unten_frei = TRUE;
10198   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10199     links_frei = TRUE;
10200   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10201     rechts_frei = TRUE;
10202
10203   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10204       element == EL_EXPANDABLE_STEELWALL_ANY)
10205   {
10206     if (oben_frei)
10207     {
10208       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10209       Store[ax][ay-1] = element;
10210       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10211       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10212         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10213                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10214       new_wall = TRUE;
10215     }
10216     if (unten_frei)
10217     {
10218       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10219       Store[ax][ay+1] = element;
10220       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10221       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10222         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10223                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10224       new_wall = TRUE;
10225     }
10226   }
10227
10228   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10229       element == EL_EXPANDABLE_STEELWALL_ANY)
10230   {
10231     if (links_frei)
10232     {
10233       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10234       Store[ax-1][ay] = element;
10235       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10236       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10237         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10238                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10239       new_wall = TRUE;
10240     }
10241
10242     if (rechts_frei)
10243     {
10244       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10245       Store[ax+1][ay] = element;
10246       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10247       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10248         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10249                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10250       new_wall = TRUE;
10251     }
10252   }
10253
10254   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10255     oben_massiv = TRUE;
10256   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10257     unten_massiv = TRUE;
10258   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10259     links_massiv = TRUE;
10260   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10261     rechts_massiv = TRUE;
10262
10263   if (((oben_massiv && unten_massiv) ||
10264        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10265       ((links_massiv && rechts_massiv) ||
10266        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10267     Feld[ax][ay] = EL_STEELWALL;
10268
10269   if (new_wall)
10270     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10271 }
10272
10273 void CheckForDragon(int x, int y)
10274 {
10275   int i, j;
10276   boolean dragon_found = FALSE;
10277   static int xy[4][2] =
10278   {
10279     { 0, -1 },
10280     { -1, 0 },
10281     { +1, 0 },
10282     { 0, +1 }
10283   };
10284
10285   for (i = 0; i < NUM_DIRECTIONS; i++)
10286   {
10287     for (j = 0; j < 4; j++)
10288     {
10289       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10290
10291       if (IN_LEV_FIELD(xx, yy) &&
10292           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10293       {
10294         if (Feld[xx][yy] == EL_DRAGON)
10295           dragon_found = TRUE;
10296       }
10297       else
10298         break;
10299     }
10300   }
10301
10302   if (!dragon_found)
10303   {
10304     for (i = 0; i < NUM_DIRECTIONS; i++)
10305     {
10306       for (j = 0; j < 3; j++)
10307       {
10308         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10309   
10310         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10311         {
10312           Feld[xx][yy] = EL_EMPTY;
10313           TEST_DrawLevelField(xx, yy);
10314         }
10315         else
10316           break;
10317       }
10318     }
10319   }
10320 }
10321
10322 static void InitBuggyBase(int x, int y)
10323 {
10324   int element = Feld[x][y];
10325   int activating_delay = FRAMES_PER_SECOND / 4;
10326
10327   ChangeDelay[x][y] =
10328     (element == EL_SP_BUGGY_BASE ?
10329      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10330      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10331      activating_delay :
10332      element == EL_SP_BUGGY_BASE_ACTIVE ?
10333      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10334 }
10335
10336 static void WarnBuggyBase(int x, int y)
10337 {
10338   int i;
10339   static int xy[4][2] =
10340   {
10341     { 0, -1 },
10342     { -1, 0 },
10343     { +1, 0 },
10344     { 0, +1 }
10345   };
10346
10347   for (i = 0; i < NUM_DIRECTIONS; i++)
10348   {
10349     int xx = x + xy[i][0];
10350     int yy = y + xy[i][1];
10351
10352     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10353     {
10354       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10355
10356       break;
10357     }
10358   }
10359 }
10360
10361 static void InitTrap(int x, int y)
10362 {
10363   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10364 }
10365
10366 static void ActivateTrap(int x, int y)
10367 {
10368   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10369 }
10370
10371 static void ChangeActiveTrap(int x, int y)
10372 {
10373   int graphic = IMG_TRAP_ACTIVE;
10374
10375   /* if new animation frame was drawn, correct crumbled sand border */
10376   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10377     TEST_DrawLevelFieldCrumbled(x, y);
10378 }
10379
10380 static int getSpecialActionElement(int element, int number, int base_element)
10381 {
10382   return (element != EL_EMPTY ? element :
10383           number != -1 ? base_element + number - 1 :
10384           EL_EMPTY);
10385 }
10386
10387 static int getModifiedActionNumber(int value_old, int operator, int operand,
10388                                    int value_min, int value_max)
10389 {
10390   int value_new = (operator == CA_MODE_SET      ? operand :
10391                    operator == CA_MODE_ADD      ? value_old + operand :
10392                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10393                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10394                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10395                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10396                    value_old);
10397
10398   return (value_new < value_min ? value_min :
10399           value_new > value_max ? value_max :
10400           value_new);
10401 }
10402
10403 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10404 {
10405   struct ElementInfo *ei = &element_info[element];
10406   struct ElementChangeInfo *change = &ei->change_page[page];
10407   int target_element = change->target_element;
10408   int action_type = change->action_type;
10409   int action_mode = change->action_mode;
10410   int action_arg = change->action_arg;
10411   int action_element = change->action_element;
10412   int i;
10413
10414   if (!change->has_action)
10415     return;
10416
10417   /* ---------- determine action paramater values -------------------------- */
10418
10419   int level_time_value =
10420     (level.time > 0 ? TimeLeft :
10421      TimePlayed);
10422
10423   int action_arg_element_raw =
10424     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10425      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10426      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10427      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10428      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10429      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10430      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10431      EL_EMPTY);
10432   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10433
10434 #if 0
10435   if (action_arg_element_raw == EL_GROUP_START)
10436     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10437 #endif
10438
10439   int action_arg_direction =
10440     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10441      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10442      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10443      change->actual_trigger_side :
10444      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10445      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10446      MV_NONE);
10447
10448   int action_arg_number_min =
10449     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10450      CA_ARG_MIN);
10451
10452   int action_arg_number_max =
10453     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10454      action_type == CA_SET_LEVEL_GEMS ? 999 :
10455      action_type == CA_SET_LEVEL_TIME ? 9999 :
10456      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10457      action_type == CA_SET_CE_VALUE ? 9999 :
10458      action_type == CA_SET_CE_SCORE ? 9999 :
10459      CA_ARG_MAX);
10460
10461   int action_arg_number_reset =
10462     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10463      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10464      action_type == CA_SET_LEVEL_TIME ? level.time :
10465      action_type == CA_SET_LEVEL_SCORE ? 0 :
10466 #if USE_NEW_CUSTOM_VALUE
10467      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10468 #else
10469      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10470 #endif
10471      action_type == CA_SET_CE_SCORE ? 0 :
10472      0);
10473
10474   int action_arg_number =
10475     (action_arg <= CA_ARG_MAX ? action_arg :
10476      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10477      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10478      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10479      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10480      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10481      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10482 #if USE_NEW_CUSTOM_VALUE
10483      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10484 #else
10485      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10486 #endif
10487      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10488      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10489      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10490      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10491      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10492      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10493      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10494      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10495      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10496      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10497      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10498      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10499      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10500      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10501      -1);
10502
10503   int action_arg_number_old =
10504     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10505      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10506      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10507      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10508      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10509      0);
10510
10511   int action_arg_number_new =
10512     getModifiedActionNumber(action_arg_number_old,
10513                             action_mode, action_arg_number,
10514                             action_arg_number_min, action_arg_number_max);
10515
10516 #if 1
10517   int trigger_player_bits =
10518     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10519      change->actual_trigger_player_bits : change->trigger_player);
10520 #else
10521   int trigger_player_bits =
10522     (change->actual_trigger_player >= EL_PLAYER_1 &&
10523      change->actual_trigger_player <= EL_PLAYER_4 ?
10524      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10525      PLAYER_BITS_ANY);
10526 #endif
10527
10528   int action_arg_player_bits =
10529     (action_arg >= CA_ARG_PLAYER_1 &&
10530      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10531      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10532      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10533      PLAYER_BITS_ANY);
10534
10535   /* ---------- execute action  -------------------------------------------- */
10536
10537   switch (action_type)
10538   {
10539     case CA_NO_ACTION:
10540     {
10541       return;
10542     }
10543
10544     /* ---------- level actions  ------------------------------------------- */
10545
10546     case CA_RESTART_LEVEL:
10547     {
10548       game.restart_level = TRUE;
10549
10550       break;
10551     }
10552
10553     case CA_SHOW_ENVELOPE:
10554     {
10555       int element = getSpecialActionElement(action_arg_element,
10556                                             action_arg_number, EL_ENVELOPE_1);
10557
10558       if (IS_ENVELOPE(element))
10559         local_player->show_envelope = element;
10560
10561       break;
10562     }
10563
10564     case CA_SET_LEVEL_TIME:
10565     {
10566       if (level.time > 0)       /* only modify limited time value */
10567       {
10568         TimeLeft = action_arg_number_new;
10569
10570 #if 1
10571         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10572
10573         DisplayGameControlValues();
10574 #else
10575         DrawGameValue_Time(TimeLeft);
10576 #endif
10577
10578         if (!TimeLeft && setup.time_limit)
10579           for (i = 0; i < MAX_PLAYERS; i++)
10580             KillPlayer(&stored_player[i]);
10581       }
10582
10583       break;
10584     }
10585
10586     case CA_SET_LEVEL_SCORE:
10587     {
10588       local_player->score = action_arg_number_new;
10589
10590 #if 1
10591       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10592
10593       DisplayGameControlValues();
10594 #else
10595       DrawGameValue_Score(local_player->score);
10596 #endif
10597
10598       break;
10599     }
10600
10601     case CA_SET_LEVEL_GEMS:
10602     {
10603       local_player->gems_still_needed = action_arg_number_new;
10604
10605 #if 1
10606       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10607
10608       DisplayGameControlValues();
10609 #else
10610       DrawGameValue_Emeralds(local_player->gems_still_needed);
10611 #endif
10612
10613       break;
10614     }
10615
10616 #if !USE_PLAYER_GRAVITY
10617     case CA_SET_LEVEL_GRAVITY:
10618     {
10619       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10620                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10621                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10622                       game.gravity);
10623       break;
10624     }
10625 #endif
10626
10627     case CA_SET_LEVEL_WIND:
10628     {
10629       game.wind_direction = action_arg_direction;
10630
10631       break;
10632     }
10633
10634     case CA_SET_LEVEL_RANDOM_SEED:
10635     {
10636 #if 1
10637       /* ensure that setting a new random seed while playing is predictable */
10638       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10639 #else
10640       InitRND(action_arg_number_new);
10641 #endif
10642
10643 #if 0
10644       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10645 #endif
10646
10647 #if 0
10648       {
10649         int i;
10650
10651         printf("::: ");
10652         for (i = 0; i < 9; i++)
10653           printf("%d, ", RND(2));
10654         printf("\n");
10655       }
10656 #endif
10657
10658       break;
10659     }
10660
10661     /* ---------- player actions  ------------------------------------------ */
10662
10663     case CA_MOVE_PLAYER:
10664     {
10665       /* automatically move to the next field in specified direction */
10666       for (i = 0; i < MAX_PLAYERS; i++)
10667         if (trigger_player_bits & (1 << i))
10668           stored_player[i].programmed_action = action_arg_direction;
10669
10670       break;
10671     }
10672
10673     case CA_EXIT_PLAYER:
10674     {
10675       for (i = 0; i < MAX_PLAYERS; i++)
10676         if (action_arg_player_bits & (1 << i))
10677           PlayerWins(&stored_player[i]);
10678
10679       break;
10680     }
10681
10682     case CA_KILL_PLAYER:
10683     {
10684       for (i = 0; i < MAX_PLAYERS; i++)
10685         if (action_arg_player_bits & (1 << i))
10686           KillPlayer(&stored_player[i]);
10687
10688       break;
10689     }
10690
10691     case CA_SET_PLAYER_KEYS:
10692     {
10693       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10694       int element = getSpecialActionElement(action_arg_element,
10695                                             action_arg_number, EL_KEY_1);
10696
10697       if (IS_KEY(element))
10698       {
10699         for (i = 0; i < MAX_PLAYERS; i++)
10700         {
10701           if (trigger_player_bits & (1 << i))
10702           {
10703             stored_player[i].key[KEY_NR(element)] = key_state;
10704
10705             DrawGameDoorValues();
10706           }
10707         }
10708       }
10709
10710       break;
10711     }
10712
10713     case CA_SET_PLAYER_SPEED:
10714     {
10715 #if 0
10716       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10717 #endif
10718
10719       for (i = 0; i < MAX_PLAYERS; i++)
10720       {
10721         if (trigger_player_bits & (1 << i))
10722         {
10723           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10724
10725           if (action_arg == CA_ARG_SPEED_FASTER &&
10726               stored_player[i].cannot_move)
10727           {
10728             action_arg_number = STEPSIZE_VERY_SLOW;
10729           }
10730           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10731                    action_arg == CA_ARG_SPEED_FASTER)
10732           {
10733             action_arg_number = 2;
10734             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10735                            CA_MODE_MULTIPLY);
10736           }
10737           else if (action_arg == CA_ARG_NUMBER_RESET)
10738           {
10739             action_arg_number = level.initial_player_stepsize[i];
10740           }
10741
10742           move_stepsize =
10743             getModifiedActionNumber(move_stepsize,
10744                                     action_mode,
10745                                     action_arg_number,
10746                                     action_arg_number_min,
10747                                     action_arg_number_max);
10748
10749           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10750         }
10751       }
10752
10753       break;
10754     }
10755
10756     case CA_SET_PLAYER_SHIELD:
10757     {
10758       for (i = 0; i < MAX_PLAYERS; i++)
10759       {
10760         if (trigger_player_bits & (1 << i))
10761         {
10762           if (action_arg == CA_ARG_SHIELD_OFF)
10763           {
10764             stored_player[i].shield_normal_time_left = 0;
10765             stored_player[i].shield_deadly_time_left = 0;
10766           }
10767           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10768           {
10769             stored_player[i].shield_normal_time_left = 999999;
10770           }
10771           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10772           {
10773             stored_player[i].shield_normal_time_left = 999999;
10774             stored_player[i].shield_deadly_time_left = 999999;
10775           }
10776         }
10777       }
10778
10779       break;
10780     }
10781
10782 #if USE_PLAYER_GRAVITY
10783     case CA_SET_PLAYER_GRAVITY:
10784     {
10785       for (i = 0; i < MAX_PLAYERS; i++)
10786       {
10787         if (trigger_player_bits & (1 << i))
10788         {
10789           stored_player[i].gravity =
10790             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10791              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10792              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10793              stored_player[i].gravity);
10794         }
10795       }
10796
10797       break;
10798     }
10799 #endif
10800
10801     case CA_SET_PLAYER_ARTWORK:
10802     {
10803       for (i = 0; i < MAX_PLAYERS; i++)
10804       {
10805         if (trigger_player_bits & (1 << i))
10806         {
10807           int artwork_element = action_arg_element;
10808
10809           if (action_arg == CA_ARG_ELEMENT_RESET)
10810             artwork_element =
10811               (level.use_artwork_element[i] ? level.artwork_element[i] :
10812                stored_player[i].element_nr);
10813
10814 #if USE_GFX_RESET_PLAYER_ARTWORK
10815           if (stored_player[i].artwork_element != artwork_element)
10816             stored_player[i].Frame = 0;
10817 #endif
10818
10819           stored_player[i].artwork_element = artwork_element;
10820
10821           SetPlayerWaiting(&stored_player[i], FALSE);
10822
10823           /* set number of special actions for bored and sleeping animation */
10824           stored_player[i].num_special_action_bored =
10825             get_num_special_action(artwork_element,
10826                                    ACTION_BORING_1, ACTION_BORING_LAST);
10827           stored_player[i].num_special_action_sleeping =
10828             get_num_special_action(artwork_element,
10829                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10830         }
10831       }
10832
10833       break;
10834     }
10835
10836     case CA_SET_PLAYER_INVENTORY:
10837     {
10838       for (i = 0; i < MAX_PLAYERS; i++)
10839       {
10840         struct PlayerInfo *player = &stored_player[i];
10841         int j, k;
10842
10843         if (trigger_player_bits & (1 << i))
10844         {
10845           int inventory_element = action_arg_element;
10846
10847           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10848               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10849               action_arg == CA_ARG_ELEMENT_ACTION)
10850           {
10851             int element = inventory_element;
10852             int collect_count = element_info[element].collect_count_initial;
10853
10854             if (!IS_CUSTOM_ELEMENT(element))
10855               collect_count = 1;
10856
10857             if (collect_count == 0)
10858               player->inventory_infinite_element = element;
10859             else
10860               for (k = 0; k < collect_count; k++)
10861                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10862                   player->inventory_element[player->inventory_size++] =
10863                     element;
10864           }
10865           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10866                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10867                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10868           {
10869             if (player->inventory_infinite_element != EL_UNDEFINED &&
10870                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10871                                      action_arg_element_raw))
10872               player->inventory_infinite_element = EL_UNDEFINED;
10873
10874             for (k = 0, j = 0; j < player->inventory_size; j++)
10875             {
10876               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10877                                         action_arg_element_raw))
10878                 player->inventory_element[k++] = player->inventory_element[j];
10879             }
10880
10881             player->inventory_size = k;
10882           }
10883           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10884           {
10885             if (player->inventory_size > 0)
10886             {
10887               for (j = 0; j < player->inventory_size - 1; j++)
10888                 player->inventory_element[j] = player->inventory_element[j + 1];
10889
10890               player->inventory_size--;
10891             }
10892           }
10893           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10894           {
10895             if (player->inventory_size > 0)
10896               player->inventory_size--;
10897           }
10898           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10899           {
10900             player->inventory_infinite_element = EL_UNDEFINED;
10901             player->inventory_size = 0;
10902           }
10903           else if (action_arg == CA_ARG_INVENTORY_RESET)
10904           {
10905             player->inventory_infinite_element = EL_UNDEFINED;
10906             player->inventory_size = 0;
10907
10908             if (level.use_initial_inventory[i])
10909             {
10910               for (j = 0; j < level.initial_inventory_size[i]; j++)
10911               {
10912                 int element = level.initial_inventory_content[i][j];
10913                 int collect_count = element_info[element].collect_count_initial;
10914
10915                 if (!IS_CUSTOM_ELEMENT(element))
10916                   collect_count = 1;
10917
10918                 if (collect_count == 0)
10919                   player->inventory_infinite_element = element;
10920                 else
10921                   for (k = 0; k < collect_count; k++)
10922                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10923                       player->inventory_element[player->inventory_size++] =
10924                         element;
10925               }
10926             }
10927           }
10928         }
10929       }
10930
10931       break;
10932     }
10933
10934     /* ---------- CE actions  ---------------------------------------------- */
10935
10936     case CA_SET_CE_VALUE:
10937     {
10938 #if USE_NEW_CUSTOM_VALUE
10939       int last_ce_value = CustomValue[x][y];
10940
10941       CustomValue[x][y] = action_arg_number_new;
10942
10943       if (CustomValue[x][y] != last_ce_value)
10944       {
10945         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10946         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10947
10948         if (CustomValue[x][y] == 0)
10949         {
10950           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10951           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10952         }
10953       }
10954 #endif
10955
10956       break;
10957     }
10958
10959     case CA_SET_CE_SCORE:
10960     {
10961 #if USE_NEW_CUSTOM_VALUE
10962       int last_ce_score = ei->collect_score;
10963
10964       ei->collect_score = action_arg_number_new;
10965
10966       if (ei->collect_score != last_ce_score)
10967       {
10968         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10969         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10970
10971         if (ei->collect_score == 0)
10972         {
10973           int xx, yy;
10974
10975           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10976           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10977
10978           /*
10979             This is a very special case that seems to be a mixture between
10980             CheckElementChange() and CheckTriggeredElementChange(): while
10981             the first one only affects single elements that are triggered
10982             directly, the second one affects multiple elements in the playfield
10983             that are triggered indirectly by another element. This is a third
10984             case: Changing the CE score always affects multiple identical CEs,
10985             so every affected CE must be checked, not only the single CE for
10986             which the CE score was changed in the first place (as every instance
10987             of that CE shares the same CE score, and therefore also can change)!
10988           */
10989           SCAN_PLAYFIELD(xx, yy)
10990           {
10991             if (Feld[xx][yy] == element)
10992               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10993                                  CE_SCORE_GETS_ZERO);
10994           }
10995         }
10996       }
10997 #endif
10998
10999       break;
11000     }
11001
11002     case CA_SET_CE_ARTWORK:
11003     {
11004       int artwork_element = action_arg_element;
11005       boolean reset_frame = FALSE;
11006       int xx, yy;
11007
11008       if (action_arg == CA_ARG_ELEMENT_RESET)
11009         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11010                            element);
11011
11012       if (ei->gfx_element != artwork_element)
11013         reset_frame = TRUE;
11014
11015       ei->gfx_element = artwork_element;
11016
11017       SCAN_PLAYFIELD(xx, yy)
11018       {
11019         if (Feld[xx][yy] == element)
11020         {
11021           if (reset_frame)
11022           {
11023             ResetGfxAnimation(xx, yy);
11024             ResetRandomAnimationValue(xx, yy);
11025           }
11026
11027           TEST_DrawLevelField(xx, yy);
11028         }
11029       }
11030
11031       break;
11032     }
11033
11034     /* ---------- engine actions  ------------------------------------------ */
11035
11036     case CA_SET_ENGINE_SCAN_MODE:
11037     {
11038       InitPlayfieldScanMode(action_arg);
11039
11040       break;
11041     }
11042
11043     default:
11044       break;
11045   }
11046 }
11047
11048 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11049 {
11050   int old_element = Feld[x][y];
11051   int new_element = GetElementFromGroupElement(element);
11052   int previous_move_direction = MovDir[x][y];
11053 #if USE_NEW_CUSTOM_VALUE
11054   int last_ce_value = CustomValue[x][y];
11055 #endif
11056   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11057   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11058   boolean add_player_onto_element = (new_element_is_player &&
11059 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11060                                      /* this breaks SnakeBite when a snake is
11061                                         halfway through a door that closes */
11062                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11063                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11064 #endif
11065                                      IS_WALKABLE(old_element));
11066
11067 #if 0
11068   /* check if element under the player changes from accessible to unaccessible
11069      (needed for special case of dropping element which then changes) */
11070   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11071       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11072   {
11073     Bang(x, y);
11074
11075     return;
11076   }
11077 #endif
11078
11079   if (!add_player_onto_element)
11080   {
11081     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11082       RemoveMovingField(x, y);
11083     else
11084       RemoveField(x, y);
11085
11086     Feld[x][y] = new_element;
11087
11088 #if !USE_GFX_RESET_GFX_ANIMATION
11089     ResetGfxAnimation(x, y);
11090     ResetRandomAnimationValue(x, y);
11091 #endif
11092
11093     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11094       MovDir[x][y] = previous_move_direction;
11095
11096 #if USE_NEW_CUSTOM_VALUE
11097     if (element_info[new_element].use_last_ce_value)
11098       CustomValue[x][y] = last_ce_value;
11099 #endif
11100
11101     InitField_WithBug1(x, y, FALSE);
11102
11103     new_element = Feld[x][y];   /* element may have changed */
11104
11105 #if USE_GFX_RESET_GFX_ANIMATION
11106     ResetGfxAnimation(x, y);
11107     ResetRandomAnimationValue(x, y);
11108 #endif
11109
11110     TEST_DrawLevelField(x, y);
11111
11112     if (GFX_CRUMBLED(new_element))
11113       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11114   }
11115
11116 #if 1
11117   /* check if element under the player changes from accessible to unaccessible
11118      (needed for special case of dropping element which then changes) */
11119   /* (must be checked after creating new element for walkable group elements) */
11120 #if USE_FIX_KILLED_BY_NON_WALKABLE
11121   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11122       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11123   {
11124     Bang(x, y);
11125
11126     return;
11127   }
11128 #else
11129   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11130       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11131   {
11132     Bang(x, y);
11133
11134     return;
11135   }
11136 #endif
11137 #endif
11138
11139   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11140   if (new_element_is_player)
11141     RelocatePlayer(x, y, new_element);
11142
11143   if (is_change)
11144     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11145
11146   TestIfBadThingTouchesPlayer(x, y);
11147   TestIfPlayerTouchesCustomElement(x, y);
11148   TestIfElementTouchesCustomElement(x, y);
11149 }
11150
11151 static void CreateField(int x, int y, int element)
11152 {
11153   CreateFieldExt(x, y, element, FALSE);
11154 }
11155
11156 static void CreateElementFromChange(int x, int y, int element)
11157 {
11158   element = GET_VALID_RUNTIME_ELEMENT(element);
11159
11160 #if USE_STOP_CHANGED_ELEMENTS
11161   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11162   {
11163     int old_element = Feld[x][y];
11164
11165     /* prevent changed element from moving in same engine frame
11166        unless both old and new element can either fall or move */
11167     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11168         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11169       Stop[x][y] = TRUE;
11170   }
11171 #endif
11172
11173   CreateFieldExt(x, y, element, TRUE);
11174 }
11175
11176 static boolean ChangeElement(int x, int y, int element, int page)
11177 {
11178   struct ElementInfo *ei = &element_info[element];
11179   struct ElementChangeInfo *change = &ei->change_page[page];
11180   int ce_value = CustomValue[x][y];
11181   int ce_score = ei->collect_score;
11182   int target_element;
11183   int old_element = Feld[x][y];
11184
11185   /* always use default change event to prevent running into a loop */
11186   if (ChangeEvent[x][y] == -1)
11187     ChangeEvent[x][y] = CE_DELAY;
11188
11189   if (ChangeEvent[x][y] == CE_DELAY)
11190   {
11191     /* reset actual trigger element, trigger player and action element */
11192     change->actual_trigger_element = EL_EMPTY;
11193     change->actual_trigger_player = EL_EMPTY;
11194     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11195     change->actual_trigger_side = CH_SIDE_NONE;
11196     change->actual_trigger_ce_value = 0;
11197     change->actual_trigger_ce_score = 0;
11198   }
11199
11200   /* do not change elements more than a specified maximum number of changes */
11201   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11202     return FALSE;
11203
11204   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11205
11206   if (change->explode)
11207   {
11208     Bang(x, y);
11209
11210     return TRUE;
11211   }
11212
11213   if (change->use_target_content)
11214   {
11215     boolean complete_replace = TRUE;
11216     boolean can_replace[3][3];
11217     int xx, yy;
11218
11219     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11220     {
11221       boolean is_empty;
11222       boolean is_walkable;
11223       boolean is_diggable;
11224       boolean is_collectible;
11225       boolean is_removable;
11226       boolean is_destructible;
11227       int ex = x + xx - 1;
11228       int ey = y + yy - 1;
11229       int content_element = change->target_content.e[xx][yy];
11230       int e;
11231
11232       can_replace[xx][yy] = TRUE;
11233
11234       if (ex == x && ey == y)   /* do not check changing element itself */
11235         continue;
11236
11237       if (content_element == EL_EMPTY_SPACE)
11238       {
11239         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11240
11241         continue;
11242       }
11243
11244       if (!IN_LEV_FIELD(ex, ey))
11245       {
11246         can_replace[xx][yy] = FALSE;
11247         complete_replace = FALSE;
11248
11249         continue;
11250       }
11251
11252       e = Feld[ex][ey];
11253
11254       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11255         e = MovingOrBlocked2Element(ex, ey);
11256
11257       is_empty = (IS_FREE(ex, ey) ||
11258                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11259
11260       is_walkable     = (is_empty || IS_WALKABLE(e));
11261       is_diggable     = (is_empty || IS_DIGGABLE(e));
11262       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11263       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11264       is_removable    = (is_diggable || is_collectible);
11265
11266       can_replace[xx][yy] =
11267         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11268           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11269           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11270           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11271           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11272           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11273          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11274
11275       if (!can_replace[xx][yy])
11276         complete_replace = FALSE;
11277     }
11278
11279     if (!change->only_if_complete || complete_replace)
11280     {
11281       boolean something_has_changed = FALSE;
11282
11283       if (change->only_if_complete && change->use_random_replace &&
11284           RND(100) < change->random_percentage)
11285         return FALSE;
11286
11287       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11288       {
11289         int ex = x + xx - 1;
11290         int ey = y + yy - 1;
11291         int content_element;
11292
11293         if (can_replace[xx][yy] && (!change->use_random_replace ||
11294                                     RND(100) < change->random_percentage))
11295         {
11296           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11297             RemoveMovingField(ex, ey);
11298
11299           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11300
11301           content_element = change->target_content.e[xx][yy];
11302           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11303                                               ce_value, ce_score);
11304
11305           CreateElementFromChange(ex, ey, target_element);
11306
11307           something_has_changed = TRUE;
11308
11309           /* for symmetry reasons, freeze newly created border elements */
11310           if (ex != x || ey != y)
11311             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11312         }
11313       }
11314
11315       if (something_has_changed)
11316       {
11317         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11318         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11319       }
11320     }
11321   }
11322   else
11323   {
11324     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11325                                         ce_value, ce_score);
11326
11327     if (element == EL_DIAGONAL_GROWING ||
11328         element == EL_DIAGONAL_SHRINKING)
11329     {
11330       target_element = Store[x][y];
11331
11332       Store[x][y] = EL_EMPTY;
11333     }
11334
11335     CreateElementFromChange(x, y, target_element);
11336
11337     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11338     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11339   }
11340
11341   /* this uses direct change before indirect change */
11342   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11343
11344   return TRUE;
11345 }
11346
11347 #if USE_NEW_DELAYED_ACTION
11348
11349 static void HandleElementChange(int x, int y, int page)
11350 {
11351   int element = MovingOrBlocked2Element(x, y);
11352   struct ElementInfo *ei = &element_info[element];
11353   struct ElementChangeInfo *change = &ei->change_page[page];
11354   boolean handle_action_before_change = FALSE;
11355
11356 #ifdef DEBUG
11357   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11358       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11359   {
11360     printf("\n\n");
11361     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11362            x, y, element, element_info[element].token_name);
11363     printf("HandleElementChange(): This should never happen!\n");
11364     printf("\n\n");
11365   }
11366 #endif
11367
11368   /* this can happen with classic bombs on walkable, changing elements */
11369   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11370   {
11371 #if 0
11372     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11373       ChangeDelay[x][y] = 0;
11374 #endif
11375
11376     return;
11377   }
11378
11379   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11380   {
11381     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11382
11383     if (change->can_change)
11384     {
11385 #if 1
11386       /* !!! not clear why graphic animation should be reset at all here !!! */
11387       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11388 #if USE_GFX_RESET_WHEN_NOT_MOVING
11389       /* when a custom element is about to change (for example by change delay),
11390          do not reset graphic animation when the custom element is moving */
11391       if (!IS_MOVING(x, y))
11392 #endif
11393       {
11394         ResetGfxAnimation(x, y);
11395         ResetRandomAnimationValue(x, y);
11396       }
11397 #endif
11398
11399       if (change->pre_change_function)
11400         change->pre_change_function(x, y);
11401     }
11402   }
11403
11404   ChangeDelay[x][y]--;
11405
11406   if (ChangeDelay[x][y] != 0)           /* continue element change */
11407   {
11408     if (change->can_change)
11409     {
11410       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11411
11412       if (IS_ANIMATED(graphic))
11413         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11414
11415       if (change->change_function)
11416         change->change_function(x, y);
11417     }
11418   }
11419   else                                  /* finish element change */
11420   {
11421     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11422     {
11423       page = ChangePage[x][y];
11424       ChangePage[x][y] = -1;
11425
11426       change = &ei->change_page[page];
11427     }
11428
11429     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11430     {
11431       ChangeDelay[x][y] = 1;            /* try change after next move step */
11432       ChangePage[x][y] = page;          /* remember page to use for change */
11433
11434       return;
11435     }
11436
11437 #if 1
11438     /* special case: set new level random seed before changing element */
11439     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11440       handle_action_before_change = TRUE;
11441
11442     if (change->has_action && handle_action_before_change)
11443       ExecuteCustomElementAction(x, y, element, page);
11444 #endif
11445
11446     if (change->can_change)
11447     {
11448       if (ChangeElement(x, y, element, page))
11449       {
11450         if (change->post_change_function)
11451           change->post_change_function(x, y);
11452       }
11453     }
11454
11455     if (change->has_action && !handle_action_before_change)
11456       ExecuteCustomElementAction(x, y, element, page);
11457   }
11458 }
11459
11460 #else
11461
11462 static void HandleElementChange(int x, int y, int page)
11463 {
11464   int element = MovingOrBlocked2Element(x, y);
11465   struct ElementInfo *ei = &element_info[element];
11466   struct ElementChangeInfo *change = &ei->change_page[page];
11467
11468 #ifdef DEBUG
11469   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11470   {
11471     printf("\n\n");
11472     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11473            x, y, element, element_info[element].token_name);
11474     printf("HandleElementChange(): This should never happen!\n");
11475     printf("\n\n");
11476   }
11477 #endif
11478
11479   /* this can happen with classic bombs on walkable, changing elements */
11480   if (!CAN_CHANGE(element))
11481   {
11482 #if 0
11483     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11484       ChangeDelay[x][y] = 0;
11485 #endif
11486
11487     return;
11488   }
11489
11490   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11491   {
11492     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11493
11494     ResetGfxAnimation(x, y);
11495     ResetRandomAnimationValue(x, y);
11496
11497     if (change->pre_change_function)
11498       change->pre_change_function(x, y);
11499   }
11500
11501   ChangeDelay[x][y]--;
11502
11503   if (ChangeDelay[x][y] != 0)           /* continue element change */
11504   {
11505     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11506
11507     if (IS_ANIMATED(graphic))
11508       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11509
11510     if (change->change_function)
11511       change->change_function(x, y);
11512   }
11513   else                                  /* finish element change */
11514   {
11515     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11516     {
11517       page = ChangePage[x][y];
11518       ChangePage[x][y] = -1;
11519
11520       change = &ei->change_page[page];
11521     }
11522
11523     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11524     {
11525       ChangeDelay[x][y] = 1;            /* try change after next move step */
11526       ChangePage[x][y] = page;          /* remember page to use for change */
11527
11528       return;
11529     }
11530
11531     if (ChangeElement(x, y, element, page))
11532     {
11533       if (change->post_change_function)
11534         change->post_change_function(x, y);
11535     }
11536   }
11537 }
11538
11539 #endif
11540
11541 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11542                                               int trigger_element,
11543                                               int trigger_event,
11544                                               int trigger_player,
11545                                               int trigger_side,
11546                                               int trigger_page)
11547 {
11548   boolean change_done_any = FALSE;
11549   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11550   int i;
11551
11552   if (!(trigger_events[trigger_element][trigger_event]))
11553     return FALSE;
11554
11555 #if 0
11556   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11557          trigger_event, recursion_loop_depth, recursion_loop_detected,
11558          recursion_loop_element, EL_NAME(recursion_loop_element));
11559 #endif
11560
11561   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11562
11563   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11564   {
11565     int element = EL_CUSTOM_START + i;
11566     boolean change_done = FALSE;
11567     int p;
11568
11569     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11570         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11571       continue;
11572
11573     for (p = 0; p < element_info[element].num_change_pages; p++)
11574     {
11575       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11576
11577       if (change->can_change_or_has_action &&
11578           change->has_event[trigger_event] &&
11579           change->trigger_side & trigger_side &&
11580           change->trigger_player & trigger_player &&
11581           change->trigger_page & trigger_page_bits &&
11582           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11583       {
11584         change->actual_trigger_element = trigger_element;
11585         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11586         change->actual_trigger_player_bits = trigger_player;
11587         change->actual_trigger_side = trigger_side;
11588         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11589         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11590
11591 #if 0
11592         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11593                element, EL_NAME(element), p);
11594 #endif
11595
11596         if ((change->can_change && !change_done) || change->has_action)
11597         {
11598           int x, y;
11599
11600           SCAN_PLAYFIELD(x, y)
11601           {
11602             if (Feld[x][y] == element)
11603             {
11604               if (change->can_change && !change_done)
11605               {
11606 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11607                 /* if element already changed in this frame, not only prevent
11608                    another element change (checked in ChangeElement()), but
11609                    also prevent additional element actions for this element */
11610
11611                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11612                     !level.use_action_after_change_bug)
11613                   continue;
11614 #endif
11615
11616 #if 0
11617                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11618                        element, EL_NAME(element), p);
11619 #endif
11620
11621                 ChangeDelay[x][y] = 1;
11622                 ChangeEvent[x][y] = trigger_event;
11623
11624                 HandleElementChange(x, y, p);
11625               }
11626 #if USE_NEW_DELAYED_ACTION
11627               else if (change->has_action)
11628               {
11629 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11630                 /* if element already changed in this frame, not only prevent
11631                    another element change (checked in ChangeElement()), but
11632                    also prevent additional element actions for this element */
11633
11634                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11635                     !level.use_action_after_change_bug)
11636                   continue;
11637 #endif
11638
11639
11640 #if 0
11641                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11642                        element, EL_NAME(element), p);
11643 #endif
11644
11645                 ExecuteCustomElementAction(x, y, element, p);
11646                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11647               }
11648 #else
11649               if (change->has_action)
11650               {
11651                 ExecuteCustomElementAction(x, y, element, p);
11652                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11653               }
11654 #endif
11655             }
11656           }
11657
11658           if (change->can_change)
11659           {
11660             change_done = TRUE;
11661             change_done_any = TRUE;
11662
11663 #if 0
11664             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11665                    element, EL_NAME(element), p);
11666 #endif
11667
11668           }
11669         }
11670       }
11671     }
11672   }
11673
11674   RECURSION_LOOP_DETECTION_END();
11675
11676   return change_done_any;
11677 }
11678
11679 static boolean CheckElementChangeExt(int x, int y,
11680                                      int element,
11681                                      int trigger_element,
11682                                      int trigger_event,
11683                                      int trigger_player,
11684                                      int trigger_side)
11685 {
11686   boolean change_done = FALSE;
11687   int p;
11688
11689   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11690       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11691     return FALSE;
11692
11693   if (Feld[x][y] == EL_BLOCKED)
11694   {
11695     Blocked2Moving(x, y, &x, &y);
11696     element = Feld[x][y];
11697   }
11698
11699 #if 0
11700   /* check if element has already changed */
11701   if (Feld[x][y] != element)
11702     return FALSE;
11703 #else
11704   /* check if element has already changed or is about to change after moving */
11705   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11706        Feld[x][y] != element) ||
11707
11708       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11709        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11710         ChangePage[x][y] != -1)))
11711     return FALSE;
11712 #endif
11713
11714 #if 0
11715   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11716          trigger_event, recursion_loop_depth, recursion_loop_detected,
11717          recursion_loop_element, EL_NAME(recursion_loop_element));
11718 #endif
11719
11720   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11721
11722 #if 0
11723   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11724 #endif
11725
11726   for (p = 0; p < element_info[element].num_change_pages; p++)
11727   {
11728     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11729
11730     /* check trigger element for all events where the element that is checked
11731        for changing interacts with a directly adjacent element -- this is
11732        different to element changes that affect other elements to change on the
11733        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11734     boolean check_trigger_element =
11735       (trigger_event == CE_TOUCHING_X ||
11736        trigger_event == CE_HITTING_X ||
11737        trigger_event == CE_HIT_BY_X ||
11738 #if 1
11739        /* this one was forgotten until 3.2.3 */
11740        trigger_event == CE_DIGGING_X);
11741 #endif
11742
11743     if (change->can_change_or_has_action &&
11744         change->has_event[trigger_event] &&
11745         change->trigger_side & trigger_side &&
11746         change->trigger_player & trigger_player &&
11747         (!check_trigger_element ||
11748          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11749     {
11750       change->actual_trigger_element = trigger_element;
11751       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11752       change->actual_trigger_player_bits = trigger_player;
11753       change->actual_trigger_side = trigger_side;
11754       change->actual_trigger_ce_value = CustomValue[x][y];
11755       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11756
11757       /* special case: trigger element not at (x,y) position for some events */
11758       if (check_trigger_element)
11759       {
11760         static struct
11761         {
11762           int dx, dy;
11763         } move_xy[] =
11764           {
11765             {  0,  0 },
11766             { -1,  0 },
11767             { +1,  0 },
11768             {  0,  0 },
11769             {  0, -1 },
11770             {  0,  0 }, { 0, 0 }, { 0, 0 },
11771             {  0, +1 }
11772           };
11773
11774         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11775         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11776
11777         change->actual_trigger_ce_value = CustomValue[xx][yy];
11778         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11779       }
11780
11781       if (change->can_change && !change_done)
11782       {
11783         ChangeDelay[x][y] = 1;
11784         ChangeEvent[x][y] = trigger_event;
11785
11786         HandleElementChange(x, y, p);
11787
11788         change_done = TRUE;
11789       }
11790 #if USE_NEW_DELAYED_ACTION
11791       else if (change->has_action)
11792       {
11793         ExecuteCustomElementAction(x, y, element, p);
11794         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11795       }
11796 #else
11797       if (change->has_action)
11798       {
11799         ExecuteCustomElementAction(x, y, element, p);
11800         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11801       }
11802 #endif
11803     }
11804   }
11805
11806   RECURSION_LOOP_DETECTION_END();
11807
11808   return change_done;
11809 }
11810
11811 static void PlayPlayerSound(struct PlayerInfo *player)
11812 {
11813   int jx = player->jx, jy = player->jy;
11814   int sound_element = player->artwork_element;
11815   int last_action = player->last_action_waiting;
11816   int action = player->action_waiting;
11817
11818   if (player->is_waiting)
11819   {
11820     if (action != last_action)
11821       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11822     else
11823       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11824   }
11825   else
11826   {
11827     if (action != last_action)
11828       StopSound(element_info[sound_element].sound[last_action]);
11829
11830     if (last_action == ACTION_SLEEPING)
11831       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11832   }
11833 }
11834
11835 static void PlayAllPlayersSound()
11836 {
11837   int i;
11838
11839   for (i = 0; i < MAX_PLAYERS; i++)
11840     if (stored_player[i].active)
11841       PlayPlayerSound(&stored_player[i]);
11842 }
11843
11844 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11845 {
11846   boolean last_waiting = player->is_waiting;
11847   int move_dir = player->MovDir;
11848
11849   player->dir_waiting = move_dir;
11850   player->last_action_waiting = player->action_waiting;
11851
11852   if (is_waiting)
11853   {
11854     if (!last_waiting)          /* not waiting -> waiting */
11855     {
11856       player->is_waiting = TRUE;
11857
11858       player->frame_counter_bored =
11859         FrameCounter +
11860         game.player_boring_delay_fixed +
11861         GetSimpleRandom(game.player_boring_delay_random);
11862       player->frame_counter_sleeping =
11863         FrameCounter +
11864         game.player_sleeping_delay_fixed +
11865         GetSimpleRandom(game.player_sleeping_delay_random);
11866
11867       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11868     }
11869
11870     if (game.player_sleeping_delay_fixed +
11871         game.player_sleeping_delay_random > 0 &&
11872         player->anim_delay_counter == 0 &&
11873         player->post_delay_counter == 0 &&
11874         FrameCounter >= player->frame_counter_sleeping)
11875       player->is_sleeping = TRUE;
11876     else if (game.player_boring_delay_fixed +
11877              game.player_boring_delay_random > 0 &&
11878              FrameCounter >= player->frame_counter_bored)
11879       player->is_bored = TRUE;
11880
11881     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11882                               player->is_bored ? ACTION_BORING :
11883                               ACTION_WAITING);
11884
11885     if (player->is_sleeping && player->use_murphy)
11886     {
11887       /* special case for sleeping Murphy when leaning against non-free tile */
11888
11889       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11890           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11891            !IS_MOVING(player->jx - 1, player->jy)))
11892         move_dir = MV_LEFT;
11893       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11894                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11895                 !IS_MOVING(player->jx + 1, player->jy)))
11896         move_dir = MV_RIGHT;
11897       else
11898         player->is_sleeping = FALSE;
11899
11900       player->dir_waiting = move_dir;
11901     }
11902
11903     if (player->is_sleeping)
11904     {
11905       if (player->num_special_action_sleeping > 0)
11906       {
11907         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11908         {
11909           int last_special_action = player->special_action_sleeping;
11910           int num_special_action = player->num_special_action_sleeping;
11911           int special_action =
11912             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11913              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11914              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11915              last_special_action + 1 : ACTION_SLEEPING);
11916           int special_graphic =
11917             el_act_dir2img(player->artwork_element, special_action, move_dir);
11918
11919           player->anim_delay_counter =
11920             graphic_info[special_graphic].anim_delay_fixed +
11921             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11922           player->post_delay_counter =
11923             graphic_info[special_graphic].post_delay_fixed +
11924             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11925
11926           player->special_action_sleeping = special_action;
11927         }
11928
11929         if (player->anim_delay_counter > 0)
11930         {
11931           player->action_waiting = player->special_action_sleeping;
11932           player->anim_delay_counter--;
11933         }
11934         else if (player->post_delay_counter > 0)
11935         {
11936           player->post_delay_counter--;
11937         }
11938       }
11939     }
11940     else if (player->is_bored)
11941     {
11942       if (player->num_special_action_bored > 0)
11943       {
11944         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11945         {
11946           int special_action =
11947             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11948           int special_graphic =
11949             el_act_dir2img(player->artwork_element, special_action, move_dir);
11950
11951           player->anim_delay_counter =
11952             graphic_info[special_graphic].anim_delay_fixed +
11953             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11954           player->post_delay_counter =
11955             graphic_info[special_graphic].post_delay_fixed +
11956             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11957
11958           player->special_action_bored = special_action;
11959         }
11960
11961         if (player->anim_delay_counter > 0)
11962         {
11963           player->action_waiting = player->special_action_bored;
11964           player->anim_delay_counter--;
11965         }
11966         else if (player->post_delay_counter > 0)
11967         {
11968           player->post_delay_counter--;
11969         }
11970       }
11971     }
11972   }
11973   else if (last_waiting)        /* waiting -> not waiting */
11974   {
11975     player->is_waiting = FALSE;
11976     player->is_bored = FALSE;
11977     player->is_sleeping = FALSE;
11978
11979     player->frame_counter_bored = -1;
11980     player->frame_counter_sleeping = -1;
11981
11982     player->anim_delay_counter = 0;
11983     player->post_delay_counter = 0;
11984
11985     player->dir_waiting = player->MovDir;
11986     player->action_waiting = ACTION_DEFAULT;
11987
11988     player->special_action_bored = ACTION_DEFAULT;
11989     player->special_action_sleeping = ACTION_DEFAULT;
11990   }
11991 }
11992
11993 static void CheckSingleStepMode(struct PlayerInfo *player)
11994 {
11995   if (tape.single_step && tape.recording && !tape.pausing)
11996   {
11997     /* as it is called "single step mode", just return to pause mode when the
11998        player stopped moving after one tile (or never starts moving at all) */
11999     if (!player->is_moving && !player->is_pushing)
12000     {
12001       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12002       SnapField(player, 0, 0);                  /* stop snapping */
12003     }
12004   }
12005 }
12006
12007 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12008 {
12009   int left      = player_action & JOY_LEFT;
12010   int right     = player_action & JOY_RIGHT;
12011   int up        = player_action & JOY_UP;
12012   int down      = player_action & JOY_DOWN;
12013   int button1   = player_action & JOY_BUTTON_1;
12014   int button2   = player_action & JOY_BUTTON_2;
12015   int dx        = (left ? -1 : right ? 1 : 0);
12016   int dy        = (up   ? -1 : down  ? 1 : 0);
12017
12018   if (!player->active || tape.pausing)
12019     return 0;
12020
12021   if (player_action)
12022   {
12023     if (button1)
12024       SnapField(player, dx, dy);
12025     else
12026     {
12027       if (button2)
12028         DropElement(player);
12029
12030       MovePlayer(player, dx, dy);
12031     }
12032
12033     CheckSingleStepMode(player);
12034
12035     SetPlayerWaiting(player, FALSE);
12036
12037     return player_action;
12038   }
12039   else
12040   {
12041     /* no actions for this player (no input at player's configured device) */
12042
12043     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12044     SnapField(player, 0, 0);
12045     CheckGravityMovementWhenNotMoving(player);
12046
12047     if (player->MovPos == 0)
12048       SetPlayerWaiting(player, TRUE);
12049
12050     if (player->MovPos == 0)    /* needed for tape.playing */
12051       player->is_moving = FALSE;
12052
12053     player->is_dropping = FALSE;
12054     player->is_dropping_pressed = FALSE;
12055     player->drop_pressed_delay = 0;
12056
12057     CheckSingleStepMode(player);
12058
12059     return 0;
12060   }
12061 }
12062
12063 static void CheckLevelTime()
12064 {
12065   int i;
12066
12067   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12068   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12069   {
12070     if (level.native_em_level->lev->home == 0)  /* all players at home */
12071     {
12072       PlayerWins(local_player);
12073
12074       AllPlayersGone = TRUE;
12075
12076       level.native_em_level->lev->home = -1;
12077     }
12078
12079     if (level.native_em_level->ply[0]->alive == 0 &&
12080         level.native_em_level->ply[1]->alive == 0 &&
12081         level.native_em_level->ply[2]->alive == 0 &&
12082         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12083       AllPlayersGone = TRUE;
12084   }
12085   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12086   {
12087     if (game_sp.LevelSolved &&
12088         !game_sp.GameOver)                              /* game won */
12089     {
12090       PlayerWins(local_player);
12091
12092       game_sp.GameOver = TRUE;
12093
12094       AllPlayersGone = TRUE;
12095     }
12096
12097     if (game_sp.GameOver)                               /* game lost */
12098       AllPlayersGone = TRUE;
12099   }
12100
12101   if (TimeFrames >= FRAMES_PER_SECOND)
12102   {
12103     TimeFrames = 0;
12104     TapeTime++;
12105
12106     for (i = 0; i < MAX_PLAYERS; i++)
12107     {
12108       struct PlayerInfo *player = &stored_player[i];
12109
12110       if (SHIELD_ON(player))
12111       {
12112         player->shield_normal_time_left--;
12113
12114         if (player->shield_deadly_time_left > 0)
12115           player->shield_deadly_time_left--;
12116       }
12117     }
12118
12119     if (!local_player->LevelSolved && !level.use_step_counter)
12120     {
12121       TimePlayed++;
12122
12123       if (TimeLeft > 0)
12124       {
12125         TimeLeft--;
12126
12127         if (TimeLeft <= 10 && setup.time_limit)
12128           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12129
12130 #if 1
12131         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12132            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12133
12134         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12135
12136         /* (already called by UpdateAndDisplayGameControlValues() below) */
12137         // DisplayGameControlValues();
12138 #else
12139         DrawGameValue_Time(TimeLeft);
12140 #endif
12141
12142         if (!TimeLeft && setup.time_limit)
12143         {
12144           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12145             level.native_em_level->lev->killed_out_of_time = TRUE;
12146           else
12147             for (i = 0; i < MAX_PLAYERS; i++)
12148               KillPlayer(&stored_player[i]);
12149         }
12150       }
12151 #if 1
12152       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12153       {
12154         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12155
12156         /* (already called by UpdateAndDisplayGameControlValues() below) */
12157         // DisplayGameControlValues();
12158       }
12159 #else
12160       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12161         DrawGameValue_Time(TimePlayed);
12162 #endif
12163
12164       level.native_em_level->lev->time =
12165         (game.no_time_limit ? TimePlayed : TimeLeft);
12166     }
12167
12168     if (tape.recording || tape.playing)
12169       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12170   }
12171
12172 #if 1
12173   UpdateAndDisplayGameControlValues();
12174 #else
12175   UpdateGameDoorValues();
12176   DrawGameDoorValues();
12177 #endif
12178 }
12179
12180 void AdvanceFrameAndPlayerCounters(int player_nr)
12181 {
12182   int i;
12183
12184   /* advance frame counters (global frame counter and time frame counter) */
12185   FrameCounter++;
12186   TimeFrames++;
12187
12188   /* advance player counters (counters for move delay, move animation etc.) */
12189   for (i = 0; i < MAX_PLAYERS; i++)
12190   {
12191     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12192     int move_delay_value = stored_player[i].move_delay_value;
12193     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12194
12195     if (!advance_player_counters)       /* not all players may be affected */
12196       continue;
12197
12198 #if USE_NEW_PLAYER_ANIM
12199     if (move_frames == 0)       /* less than one move per game frame */
12200     {
12201       int stepsize = TILEX / move_delay_value;
12202       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12203       int count = (stored_player[i].is_moving ?
12204                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12205
12206       if (count % delay == 0)
12207         move_frames = 1;
12208     }
12209 #endif
12210
12211     stored_player[i].Frame += move_frames;
12212
12213     if (stored_player[i].MovPos != 0)
12214       stored_player[i].StepFrame += move_frames;
12215
12216     if (stored_player[i].move_delay > 0)
12217       stored_player[i].move_delay--;
12218
12219     /* due to bugs in previous versions, counter must count up, not down */
12220     if (stored_player[i].push_delay != -1)
12221       stored_player[i].push_delay++;
12222
12223     if (stored_player[i].drop_delay > 0)
12224       stored_player[i].drop_delay--;
12225
12226     if (stored_player[i].is_dropping_pressed)
12227       stored_player[i].drop_pressed_delay++;
12228   }
12229 }
12230
12231 void StartGameActions(boolean init_network_game, boolean record_tape,
12232                       int random_seed)
12233 {
12234   unsigned int new_random_seed = InitRND(random_seed);
12235
12236   if (record_tape)
12237     TapeStartRecording(new_random_seed);
12238
12239 #if defined(NETWORK_AVALIABLE)
12240   if (init_network_game)
12241   {
12242     SendToServer_StartPlaying();
12243
12244     return;
12245   }
12246 #endif
12247
12248   InitGame();
12249 }
12250
12251 void GameActions()
12252 {
12253   static unsigned int game_frame_delay = 0;
12254   unsigned int game_frame_delay_value;
12255   byte *recorded_player_action;
12256   byte summarized_player_action = 0;
12257   byte tape_action[MAX_PLAYERS];
12258   int i;
12259
12260   /* detect endless loops, caused by custom element programming */
12261   if (recursion_loop_detected && recursion_loop_depth == 0)
12262   {
12263     char *message = getStringCat3("Internal Error! Element ",
12264                                   EL_NAME(recursion_loop_element),
12265                                   " caused endless loop! Quit the game?");
12266
12267     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12268           EL_NAME(recursion_loop_element));
12269
12270     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12271
12272     recursion_loop_detected = FALSE;    /* if game should be continued */
12273
12274     free(message);
12275
12276     return;
12277   }
12278
12279   if (game.restart_level)
12280     StartGameActions(options.network, setup.autorecord, level.random_seed);
12281
12282   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12283   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12284   {
12285     if (level.native_em_level->lev->home == 0)  /* all players at home */
12286     {
12287       PlayerWins(local_player);
12288
12289       AllPlayersGone = TRUE;
12290
12291       level.native_em_level->lev->home = -1;
12292     }
12293
12294     if (level.native_em_level->ply[0]->alive == 0 &&
12295         level.native_em_level->ply[1]->alive == 0 &&
12296         level.native_em_level->ply[2]->alive == 0 &&
12297         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12298       AllPlayersGone = TRUE;
12299   }
12300   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12301   {
12302     if (game_sp.LevelSolved &&
12303         !game_sp.GameOver)                              /* game won */
12304     {
12305       PlayerWins(local_player);
12306
12307       game_sp.GameOver = TRUE;
12308
12309       AllPlayersGone = TRUE;
12310     }
12311
12312     if (game_sp.GameOver)                               /* game lost */
12313       AllPlayersGone = TRUE;
12314   }
12315
12316   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12317     GameWon();
12318
12319   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12320     TapeStop();
12321
12322   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12323     return;
12324
12325   game_frame_delay_value =
12326     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12327
12328   if (tape.playing && tape.warp_forward && !tape.pausing)
12329     game_frame_delay_value = 0;
12330
12331   /* ---------- main game synchronization point ---------- */
12332
12333   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12334
12335   if (network_playing && !network_player_action_received)
12336   {
12337     /* try to get network player actions in time */
12338
12339 #if defined(NETWORK_AVALIABLE)
12340     /* last chance to get network player actions without main loop delay */
12341     HandleNetworking();
12342 #endif
12343
12344     /* game was quit by network peer */
12345     if (game_status != GAME_MODE_PLAYING)
12346       return;
12347
12348     if (!network_player_action_received)
12349       return;           /* failed to get network player actions in time */
12350
12351     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12352   }
12353
12354   if (tape.pausing)
12355     return;
12356
12357   /* at this point we know that we really continue executing the game */
12358
12359   network_player_action_received = FALSE;
12360
12361   /* when playing tape, read previously recorded player input from tape data */
12362   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12363
12364 #if 1
12365   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12366   if (tape.pausing)
12367     return;
12368 #endif
12369
12370   if (tape.set_centered_player)
12371   {
12372     game.centered_player_nr_next = tape.centered_player_nr_next;
12373     game.set_centered_player = TRUE;
12374   }
12375
12376   for (i = 0; i < MAX_PLAYERS; i++)
12377   {
12378     summarized_player_action |= stored_player[i].action;
12379
12380 #if 1
12381     if (!network_playing && (game.team_mode || tape.playing))
12382       stored_player[i].effective_action = stored_player[i].action;
12383 #else
12384     if (!network_playing)
12385       stored_player[i].effective_action = stored_player[i].action;
12386 #endif
12387   }
12388
12389 #if defined(NETWORK_AVALIABLE)
12390   if (network_playing)
12391     SendToServer_MovePlayer(summarized_player_action);
12392 #endif
12393
12394   if (!options.network && !game.team_mode)
12395     local_player->effective_action = summarized_player_action;
12396
12397   if (tape.recording &&
12398       setup.team_mode &&
12399       setup.input_on_focus &&
12400       game.centered_player_nr != -1)
12401   {
12402     for (i = 0; i < MAX_PLAYERS; i++)
12403       stored_player[i].effective_action =
12404         (i == game.centered_player_nr ? summarized_player_action : 0);
12405   }
12406
12407   if (recorded_player_action != NULL)
12408     for (i = 0; i < MAX_PLAYERS; i++)
12409       stored_player[i].effective_action = recorded_player_action[i];
12410
12411   for (i = 0; i < MAX_PLAYERS; i++)
12412   {
12413     tape_action[i] = stored_player[i].effective_action;
12414
12415 #if 1
12416     /* (this may happen in the RND game engine if a player was not present on
12417        the playfield on level start, but appeared later from a custom element */
12418     if (tape.recording &&
12419         setup.team_mode &&
12420         tape_action[i] &&
12421         !tape.player_participates[i])
12422       tape.player_participates[i] = TRUE;
12423 #else
12424     /* (this can only happen in the R'n'D game engine) */
12425     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12426       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12427 #endif
12428   }
12429
12430   /* only record actions from input devices, but not programmed actions */
12431   if (tape.recording)
12432     TapeRecordAction(tape_action);
12433
12434 #if USE_NEW_PLAYER_ASSIGNMENTS
12435 #if 1
12436   if (game.team_mode)
12437 #endif
12438   {
12439     byte mapped_action[MAX_PLAYERS];
12440
12441 #if DEBUG_PLAYER_ACTIONS
12442     printf(":::");
12443     for (i = 0; i < MAX_PLAYERS; i++)
12444       printf(" %d, ", stored_player[i].effective_action);
12445 #endif
12446
12447     for (i = 0; i < MAX_PLAYERS; i++)
12448       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12449
12450     for (i = 0; i < MAX_PLAYERS; i++)
12451       stored_player[i].effective_action = mapped_action[i];
12452
12453 #if DEBUG_PLAYER_ACTIONS
12454     printf(" =>");
12455     for (i = 0; i < MAX_PLAYERS; i++)
12456       printf(" %d, ", stored_player[i].effective_action);
12457     printf("\n");
12458 #endif
12459   }
12460 #if DEBUG_PLAYER_ACTIONS
12461   else
12462   {
12463     printf(":::");
12464     for (i = 0; i < MAX_PLAYERS; i++)
12465       printf(" %d, ", stored_player[i].effective_action);
12466     printf("\n");
12467   }
12468 #endif
12469 #endif
12470
12471 #if 0
12472   printf("::: summarized_player_action == %d\n",
12473          local_player->effective_action);
12474 #endif
12475
12476
12477
12478
12479 #if 0
12480 #if DEBUG_INIT_PLAYER
12481     if (options.debug)
12482     {
12483       printf("Player status (final):\n");
12484
12485       for (i = 0; i < MAX_PLAYERS; i++)
12486       {
12487         struct PlayerInfo *player = &stored_player[i];
12488
12489         printf("- player %d: present == %d, connected == %d, active == %d",
12490                i + 1,
12491                player->present,
12492                player->connected,
12493                player->active);
12494
12495         if (local_player == player)
12496           printf(" (local player)");
12497
12498         printf("\n");
12499       }
12500     }
12501 #endif
12502 #endif
12503
12504
12505
12506   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12507   {
12508     GameActions_EM_Main();
12509   }
12510   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12511   {
12512     GameActions_SP_Main();
12513   }
12514   else
12515   {
12516     GameActions_RND();
12517   }
12518 }
12519
12520 void GameActions_EM_Main()
12521 {
12522   byte effective_action[MAX_PLAYERS];
12523   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12524   int i;
12525
12526   for (i = 0; i < MAX_PLAYERS; i++)
12527     effective_action[i] = stored_player[i].effective_action;
12528
12529   GameActions_EM(effective_action, warp_mode);
12530
12531   CheckLevelTime();
12532
12533   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12534 }
12535
12536 void GameActions_SP_Main()
12537 {
12538   byte effective_action[MAX_PLAYERS];
12539   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12540   int i;
12541
12542   for (i = 0; i < MAX_PLAYERS; i++)
12543     effective_action[i] = stored_player[i].effective_action;
12544
12545   GameActions_SP(effective_action, warp_mode);
12546
12547   CheckLevelTime();
12548
12549   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12550 }
12551
12552 void GameActions_RND()
12553 {
12554   int magic_wall_x = 0, magic_wall_y = 0;
12555   int i, x, y, element, graphic;
12556
12557   InitPlayfieldScanModeVars();
12558
12559 #if USE_ONE_MORE_CHANGE_PER_FRAME
12560   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12561   {
12562     SCAN_PLAYFIELD(x, y)
12563     {
12564       ChangeCount[x][y] = 0;
12565       ChangeEvent[x][y] = -1;
12566     }
12567   }
12568 #endif
12569
12570   if (game.set_centered_player)
12571   {
12572     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12573
12574     /* switching to "all players" only possible if all players fit to screen */
12575     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12576     {
12577       game.centered_player_nr_next = game.centered_player_nr;
12578       game.set_centered_player = FALSE;
12579     }
12580
12581     /* do not switch focus to non-existing (or non-active) player */
12582     if (game.centered_player_nr_next >= 0 &&
12583         !stored_player[game.centered_player_nr_next].active)
12584     {
12585       game.centered_player_nr_next = game.centered_player_nr;
12586       game.set_centered_player = FALSE;
12587     }
12588   }
12589
12590   if (game.set_centered_player &&
12591       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12592   {
12593     int sx, sy;
12594
12595     if (game.centered_player_nr_next == -1)
12596     {
12597       setScreenCenteredToAllPlayers(&sx, &sy);
12598     }
12599     else
12600     {
12601       sx = stored_player[game.centered_player_nr_next].jx;
12602       sy = stored_player[game.centered_player_nr_next].jy;
12603     }
12604
12605     game.centered_player_nr = game.centered_player_nr_next;
12606     game.set_centered_player = FALSE;
12607
12608     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12609     DrawGameDoorValues();
12610   }
12611
12612   for (i = 0; i < MAX_PLAYERS; i++)
12613   {
12614     int actual_player_action = stored_player[i].effective_action;
12615
12616 #if 1
12617     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12618        - rnd_equinox_tetrachloride 048
12619        - rnd_equinox_tetrachloride_ii 096
12620        - rnd_emanuel_schmieg 002
12621        - doctor_sloan_ww 001, 020
12622     */
12623     if (stored_player[i].MovPos == 0)
12624       CheckGravityMovement(&stored_player[i]);
12625 #endif
12626
12627     /* overwrite programmed action with tape action */
12628     if (stored_player[i].programmed_action)
12629       actual_player_action = stored_player[i].programmed_action;
12630
12631     PlayerActions(&stored_player[i], actual_player_action);
12632
12633     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12634   }
12635
12636   ScrollScreen(NULL, SCROLL_GO_ON);
12637
12638   /* for backwards compatibility, the following code emulates a fixed bug that
12639      occured when pushing elements (causing elements that just made their last
12640      pushing step to already (if possible) make their first falling step in the
12641      same game frame, which is bad); this code is also needed to use the famous
12642      "spring push bug" which is used in older levels and might be wanted to be
12643      used also in newer levels, but in this case the buggy pushing code is only
12644      affecting the "spring" element and no other elements */
12645
12646   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12647   {
12648     for (i = 0; i < MAX_PLAYERS; i++)
12649     {
12650       struct PlayerInfo *player = &stored_player[i];
12651       int x = player->jx;
12652       int y = player->jy;
12653
12654       if (player->active && player->is_pushing && player->is_moving &&
12655           IS_MOVING(x, y) &&
12656           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12657            Feld[x][y] == EL_SPRING))
12658       {
12659         ContinueMoving(x, y);
12660
12661         /* continue moving after pushing (this is actually a bug) */
12662         if (!IS_MOVING(x, y))
12663           Stop[x][y] = FALSE;
12664       }
12665     }
12666   }
12667
12668 #if 0
12669   debug_print_timestamp(0, "start main loop profiling");
12670 #endif
12671
12672   SCAN_PLAYFIELD(x, y)
12673   {
12674     ChangeCount[x][y] = 0;
12675     ChangeEvent[x][y] = -1;
12676
12677     /* this must be handled before main playfield loop */
12678     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12679     {
12680       MovDelay[x][y]--;
12681       if (MovDelay[x][y] <= 0)
12682         RemoveField(x, y);
12683     }
12684
12685 #if USE_NEW_SNAP_DELAY
12686     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12687     {
12688       MovDelay[x][y]--;
12689       if (MovDelay[x][y] <= 0)
12690       {
12691         RemoveField(x, y);
12692         TEST_DrawLevelField(x, y);
12693
12694         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12695       }
12696     }
12697 #endif
12698
12699 #if DEBUG
12700     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12701     {
12702       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12703       printf("GameActions(): This should never happen!\n");
12704
12705       ChangePage[x][y] = -1;
12706     }
12707 #endif
12708
12709     Stop[x][y] = FALSE;
12710     if (WasJustMoving[x][y] > 0)
12711       WasJustMoving[x][y]--;
12712     if (WasJustFalling[x][y] > 0)
12713       WasJustFalling[x][y]--;
12714     if (CheckCollision[x][y] > 0)
12715       CheckCollision[x][y]--;
12716     if (CheckImpact[x][y] > 0)
12717       CheckImpact[x][y]--;
12718
12719     GfxFrame[x][y]++;
12720
12721     /* reset finished pushing action (not done in ContinueMoving() to allow
12722        continuous pushing animation for elements with zero push delay) */
12723     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12724     {
12725       ResetGfxAnimation(x, y);
12726       TEST_DrawLevelField(x, y);
12727     }
12728
12729 #if DEBUG
12730     if (IS_BLOCKED(x, y))
12731     {
12732       int oldx, oldy;
12733
12734       Blocked2Moving(x, y, &oldx, &oldy);
12735       if (!IS_MOVING(oldx, oldy))
12736       {
12737         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12738         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12739         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12740         printf("GameActions(): This should never happen!\n");
12741       }
12742     }
12743 #endif
12744   }
12745
12746 #if 0
12747   debug_print_timestamp(0, "- time for pre-main loop:");
12748 #endif
12749
12750 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12751   SCAN_PLAYFIELD(x, y)
12752   {
12753     element = Feld[x][y];
12754     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12755
12756 #if 1
12757     {
12758 #if 1
12759       int element2 = element;
12760       int graphic2 = graphic;
12761 #else
12762       int element2 = Feld[x][y];
12763       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12764 #endif
12765       int last_gfx_frame = GfxFrame[x][y];
12766
12767       if (graphic_info[graphic2].anim_global_sync)
12768         GfxFrame[x][y] = FrameCounter;
12769       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12770         GfxFrame[x][y] = CustomValue[x][y];
12771       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12772         GfxFrame[x][y] = element_info[element2].collect_score;
12773       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12774         GfxFrame[x][y] = ChangeDelay[x][y];
12775
12776       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12777         DrawLevelGraphicAnimation(x, y, graphic2);
12778     }
12779 #else
12780     ResetGfxFrame(x, y, TRUE);
12781 #endif
12782
12783 #if 1
12784     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12785         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12786       ResetRandomAnimationValue(x, y);
12787 #endif
12788
12789 #if 1
12790     SetRandomAnimationValue(x, y);
12791 #endif
12792
12793 #if 1
12794     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12795 #endif
12796   }
12797 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12798
12799 #if 0
12800   debug_print_timestamp(0, "- time for TEST loop:     -->");
12801 #endif
12802
12803   SCAN_PLAYFIELD(x, y)
12804   {
12805     element = Feld[x][y];
12806     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12807
12808     ResetGfxFrame(x, y, TRUE);
12809
12810     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12811         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12812       ResetRandomAnimationValue(x, y);
12813
12814     SetRandomAnimationValue(x, y);
12815
12816     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12817
12818     if (IS_INACTIVE(element))
12819     {
12820       if (IS_ANIMATED(graphic))
12821         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12822
12823       continue;
12824     }
12825
12826     /* this may take place after moving, so 'element' may have changed */
12827     if (IS_CHANGING(x, y) &&
12828         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12829     {
12830       int page = element_info[element].event_page_nr[CE_DELAY];
12831
12832 #if 1
12833       HandleElementChange(x, y, page);
12834 #else
12835       if (CAN_CHANGE(element))
12836         HandleElementChange(x, y, page);
12837
12838       if (HAS_ACTION(element))
12839         ExecuteCustomElementAction(x, y, element, page);
12840 #endif
12841
12842       element = Feld[x][y];
12843       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12844     }
12845
12846 #if 0   // ---------------------------------------------------------------------
12847
12848     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12849     {
12850       StartMoving(x, y);
12851
12852       element = Feld[x][y];
12853       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12854
12855       if (IS_ANIMATED(graphic) &&
12856           !IS_MOVING(x, y) &&
12857           !Stop[x][y])
12858         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12859
12860       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12861         TEST_DrawTwinkleOnField(x, y);
12862     }
12863     else if (IS_MOVING(x, y))
12864       ContinueMoving(x, y);
12865     else
12866     {
12867       switch (element)
12868       {
12869         case EL_ACID:
12870         case EL_EXIT_OPEN:
12871         case EL_EM_EXIT_OPEN:
12872         case EL_SP_EXIT_OPEN:
12873         case EL_STEEL_EXIT_OPEN:
12874         case EL_EM_STEEL_EXIT_OPEN:
12875         case EL_SP_TERMINAL:
12876         case EL_SP_TERMINAL_ACTIVE:
12877         case EL_EXTRA_TIME:
12878         case EL_SHIELD_NORMAL:
12879         case EL_SHIELD_DEADLY:
12880           if (IS_ANIMATED(graphic))
12881             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12882           break;
12883
12884         case EL_DYNAMITE_ACTIVE:
12885         case EL_EM_DYNAMITE_ACTIVE:
12886         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12887         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12888         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12889         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12890         case EL_SP_DISK_RED_ACTIVE:
12891           CheckDynamite(x, y);
12892           break;
12893
12894         case EL_AMOEBA_GROWING:
12895           AmoebeWaechst(x, y);
12896           break;
12897
12898         case EL_AMOEBA_SHRINKING:
12899           AmoebaDisappearing(x, y);
12900           break;
12901
12902 #if !USE_NEW_AMOEBA_CODE
12903         case EL_AMOEBA_WET:
12904         case EL_AMOEBA_DRY:
12905         case EL_AMOEBA_FULL:
12906         case EL_BD_AMOEBA:
12907         case EL_EMC_DRIPPER:
12908           AmoebeAbleger(x, y);
12909           break;
12910 #endif
12911
12912         case EL_GAME_OF_LIFE:
12913         case EL_BIOMAZE:
12914           Life(x, y);
12915           break;
12916
12917         case EL_EXIT_CLOSED:
12918           CheckExit(x, y);
12919           break;
12920
12921         case EL_EM_EXIT_CLOSED:
12922           CheckExitEM(x, y);
12923           break;
12924
12925         case EL_STEEL_EXIT_CLOSED:
12926           CheckExitSteel(x, y);
12927           break;
12928
12929         case EL_EM_STEEL_EXIT_CLOSED:
12930           CheckExitSteelEM(x, y);
12931           break;
12932
12933         case EL_SP_EXIT_CLOSED:
12934           CheckExitSP(x, y);
12935           break;
12936
12937         case EL_EXPANDABLE_WALL_GROWING:
12938         case EL_EXPANDABLE_STEELWALL_GROWING:
12939           MauerWaechst(x, y);
12940           break;
12941
12942         case EL_EXPANDABLE_WALL:
12943         case EL_EXPANDABLE_WALL_HORIZONTAL:
12944         case EL_EXPANDABLE_WALL_VERTICAL:
12945         case EL_EXPANDABLE_WALL_ANY:
12946         case EL_BD_EXPANDABLE_WALL:
12947           MauerAbleger(x, y);
12948           break;
12949
12950         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12951         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12952         case EL_EXPANDABLE_STEELWALL_ANY:
12953           MauerAblegerStahl(x, y);
12954           break;
12955
12956         case EL_FLAMES:
12957           CheckForDragon(x, y);
12958           break;
12959
12960         case EL_EXPLOSION:
12961           break;
12962
12963         case EL_ELEMENT_SNAPPING:
12964         case EL_DIAGONAL_SHRINKING:
12965         case EL_DIAGONAL_GROWING:
12966         {
12967           graphic =
12968             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12969
12970           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12971           break;
12972         }
12973
12974         default:
12975           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12976             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12977           break;
12978       }
12979     }
12980
12981 #else   // ---------------------------------------------------------------------
12982
12983     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12984     {
12985       StartMoving(x, y);
12986
12987       element = Feld[x][y];
12988       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12989
12990       if (IS_ANIMATED(graphic) &&
12991           !IS_MOVING(x, y) &&
12992           !Stop[x][y])
12993         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12994
12995       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12996         TEST_DrawTwinkleOnField(x, y);
12997     }
12998     else if ((element == EL_ACID ||
12999               element == EL_EXIT_OPEN ||
13000               element == EL_EM_EXIT_OPEN ||
13001               element == EL_SP_EXIT_OPEN ||
13002               element == EL_STEEL_EXIT_OPEN ||
13003               element == EL_EM_STEEL_EXIT_OPEN ||
13004               element == EL_SP_TERMINAL ||
13005               element == EL_SP_TERMINAL_ACTIVE ||
13006               element == EL_EXTRA_TIME ||
13007               element == EL_SHIELD_NORMAL ||
13008               element == EL_SHIELD_DEADLY) &&
13009              IS_ANIMATED(graphic))
13010       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13011     else if (IS_MOVING(x, y))
13012       ContinueMoving(x, y);
13013     else if (IS_ACTIVE_BOMB(element))
13014       CheckDynamite(x, y);
13015     else if (element == EL_AMOEBA_GROWING)
13016       AmoebeWaechst(x, y);
13017     else if (element == EL_AMOEBA_SHRINKING)
13018       AmoebaDisappearing(x, y);
13019
13020 #if !USE_NEW_AMOEBA_CODE
13021     else if (IS_AMOEBALIVE(element))
13022       AmoebeAbleger(x, y);
13023 #endif
13024
13025     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13026       Life(x, y);
13027     else if (element == EL_EXIT_CLOSED)
13028       CheckExit(x, y);
13029     else if (element == EL_EM_EXIT_CLOSED)
13030       CheckExitEM(x, y);
13031     else if (element == EL_STEEL_EXIT_CLOSED)
13032       CheckExitSteel(x, y);
13033     else if (element == EL_EM_STEEL_EXIT_CLOSED)
13034       CheckExitSteelEM(x, y);
13035     else if (element == EL_SP_EXIT_CLOSED)
13036       CheckExitSP(x, y);
13037     else if (element == EL_EXPANDABLE_WALL_GROWING ||
13038              element == EL_EXPANDABLE_STEELWALL_GROWING)
13039       MauerWaechst(x, y);
13040     else if (element == EL_EXPANDABLE_WALL ||
13041              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13042              element == EL_EXPANDABLE_WALL_VERTICAL ||
13043              element == EL_EXPANDABLE_WALL_ANY ||
13044              element == EL_BD_EXPANDABLE_WALL)
13045       MauerAbleger(x, y);
13046     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13047              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13048              element == EL_EXPANDABLE_STEELWALL_ANY)
13049       MauerAblegerStahl(x, y);
13050     else if (element == EL_FLAMES)
13051       CheckForDragon(x, y);
13052     else if (element == EL_EXPLOSION)
13053       ; /* drawing of correct explosion animation is handled separately */
13054     else if (element == EL_ELEMENT_SNAPPING ||
13055              element == EL_DIAGONAL_SHRINKING ||
13056              element == EL_DIAGONAL_GROWING)
13057     {
13058       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13059
13060       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13061     }
13062     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13063       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13064
13065 #endif  // ---------------------------------------------------------------------
13066
13067     if (IS_BELT_ACTIVE(element))
13068       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13069
13070     if (game.magic_wall_active)
13071     {
13072       int jx = local_player->jx, jy = local_player->jy;
13073
13074       /* play the element sound at the position nearest to the player */
13075       if ((element == EL_MAGIC_WALL_FULL ||
13076            element == EL_MAGIC_WALL_ACTIVE ||
13077            element == EL_MAGIC_WALL_EMPTYING ||
13078            element == EL_BD_MAGIC_WALL_FULL ||
13079            element == EL_BD_MAGIC_WALL_ACTIVE ||
13080            element == EL_BD_MAGIC_WALL_EMPTYING ||
13081            element == EL_DC_MAGIC_WALL_FULL ||
13082            element == EL_DC_MAGIC_WALL_ACTIVE ||
13083            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13084           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13085       {
13086         magic_wall_x = x;
13087         magic_wall_y = y;
13088       }
13089     }
13090   }
13091
13092 #if 0
13093   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13094 #endif
13095
13096 #if USE_NEW_AMOEBA_CODE
13097   /* new experimental amoeba growth stuff */
13098   if (!(FrameCounter % 8))
13099   {
13100     static unsigned int random = 1684108901;
13101
13102     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13103     {
13104       x = RND(lev_fieldx);
13105       y = RND(lev_fieldy);
13106       element = Feld[x][y];
13107
13108       if (!IS_PLAYER(x,y) &&
13109           (element == EL_EMPTY ||
13110            CAN_GROW_INTO(element) ||
13111            element == EL_QUICKSAND_EMPTY ||
13112            element == EL_QUICKSAND_FAST_EMPTY ||
13113            element == EL_ACID_SPLASH_LEFT ||
13114            element == EL_ACID_SPLASH_RIGHT))
13115       {
13116         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13117             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13118             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13119             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13120           Feld[x][y] = EL_AMOEBA_DROP;
13121       }
13122
13123       random = random * 129 + 1;
13124     }
13125   }
13126 #endif
13127
13128 #if 0
13129   if (game.explosions_delayed)
13130 #endif
13131   {
13132     game.explosions_delayed = FALSE;
13133
13134     SCAN_PLAYFIELD(x, y)
13135     {
13136       element = Feld[x][y];
13137
13138       if (ExplodeField[x][y])
13139         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13140       else if (element == EL_EXPLOSION)
13141         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13142
13143       ExplodeField[x][y] = EX_TYPE_NONE;
13144     }
13145
13146     game.explosions_delayed = TRUE;
13147   }
13148
13149   if (game.magic_wall_active)
13150   {
13151     if (!(game.magic_wall_time_left % 4))
13152     {
13153       int element = Feld[magic_wall_x][magic_wall_y];
13154
13155       if (element == EL_BD_MAGIC_WALL_FULL ||
13156           element == EL_BD_MAGIC_WALL_ACTIVE ||
13157           element == EL_BD_MAGIC_WALL_EMPTYING)
13158         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13159       else if (element == EL_DC_MAGIC_WALL_FULL ||
13160                element == EL_DC_MAGIC_WALL_ACTIVE ||
13161                element == EL_DC_MAGIC_WALL_EMPTYING)
13162         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13163       else
13164         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13165     }
13166
13167     if (game.magic_wall_time_left > 0)
13168     {
13169       game.magic_wall_time_left--;
13170
13171       if (!game.magic_wall_time_left)
13172       {
13173         SCAN_PLAYFIELD(x, y)
13174         {
13175           element = Feld[x][y];
13176
13177           if (element == EL_MAGIC_WALL_ACTIVE ||
13178               element == EL_MAGIC_WALL_FULL)
13179           {
13180             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13181             TEST_DrawLevelField(x, y);
13182           }
13183           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13184                    element == EL_BD_MAGIC_WALL_FULL)
13185           {
13186             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13187             TEST_DrawLevelField(x, y);
13188           }
13189           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13190                    element == EL_DC_MAGIC_WALL_FULL)
13191           {
13192             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13193             TEST_DrawLevelField(x, y);
13194           }
13195         }
13196
13197         game.magic_wall_active = FALSE;
13198       }
13199     }
13200   }
13201
13202   if (game.light_time_left > 0)
13203   {
13204     game.light_time_left--;
13205
13206     if (game.light_time_left == 0)
13207       RedrawAllLightSwitchesAndInvisibleElements();
13208   }
13209
13210   if (game.timegate_time_left > 0)
13211   {
13212     game.timegate_time_left--;
13213
13214     if (game.timegate_time_left == 0)
13215       CloseAllOpenTimegates();
13216   }
13217
13218   if (game.lenses_time_left > 0)
13219   {
13220     game.lenses_time_left--;
13221
13222     if (game.lenses_time_left == 0)
13223       RedrawAllInvisibleElementsForLenses();
13224   }
13225
13226   if (game.magnify_time_left > 0)
13227   {
13228     game.magnify_time_left--;
13229
13230     if (game.magnify_time_left == 0)
13231       RedrawAllInvisibleElementsForMagnifier();
13232   }
13233
13234   for (i = 0; i < MAX_PLAYERS; i++)
13235   {
13236     struct PlayerInfo *player = &stored_player[i];
13237
13238     if (SHIELD_ON(player))
13239     {
13240       if (player->shield_deadly_time_left)
13241         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13242       else if (player->shield_normal_time_left)
13243         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13244     }
13245   }
13246
13247 #if USE_DELAYED_GFX_REDRAW
13248   SCAN_PLAYFIELD(x, y)
13249   {
13250 #if 1
13251     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13252 #else
13253     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13254         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13255 #endif
13256     {
13257       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13258          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13259
13260       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13261         DrawLevelField(x, y);
13262
13263       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13264         DrawLevelFieldCrumbled(x, y);
13265
13266       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13267         DrawLevelFieldCrumbledNeighbours(x, y);
13268
13269       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13270         DrawTwinkleOnField(x, y);
13271     }
13272
13273     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13274   }
13275 #endif
13276
13277   CheckLevelTime();
13278
13279   DrawAllPlayers();
13280   PlayAllPlayersSound();
13281
13282   if (options.debug)                    /* calculate frames per second */
13283   {
13284     static unsigned int fps_counter = 0;
13285     static int fps_frames = 0;
13286     unsigned int fps_delay_ms = Counter() - fps_counter;
13287
13288     fps_frames++;
13289
13290     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13291     {
13292       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13293
13294       fps_frames = 0;
13295       fps_counter = Counter();
13296     }
13297
13298     redraw_mask |= REDRAW_FPS;
13299   }
13300
13301   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13302
13303   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13304   {
13305     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13306
13307     local_player->show_envelope = 0;
13308   }
13309
13310 #if 0
13311   debug_print_timestamp(0, "stop main loop profiling ");
13312   printf("----------------------------------------------------------\n");
13313 #endif
13314
13315   /* use random number generator in every frame to make it less predictable */
13316   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13317     RND(1);
13318 }
13319
13320 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13321 {
13322   int min_x = x, min_y = y, max_x = x, max_y = y;
13323   int i;
13324
13325   for (i = 0; i < MAX_PLAYERS; i++)
13326   {
13327     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13328
13329     if (!stored_player[i].active || &stored_player[i] == player)
13330       continue;
13331
13332     min_x = MIN(min_x, jx);
13333     min_y = MIN(min_y, jy);
13334     max_x = MAX(max_x, jx);
13335     max_y = MAX(max_y, jy);
13336   }
13337
13338   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13339 }
13340
13341 static boolean AllPlayersInVisibleScreen()
13342 {
13343   int i;
13344
13345   for (i = 0; i < MAX_PLAYERS; i++)
13346   {
13347     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13348
13349     if (!stored_player[i].active)
13350       continue;
13351
13352     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13353       return FALSE;
13354   }
13355
13356   return TRUE;
13357 }
13358
13359 void ScrollLevel(int dx, int dy)
13360 {
13361 #if 0
13362   /* (directly solved in BlitBitmap() now) */
13363   static Bitmap *bitmap_db_field2 = NULL;
13364   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13365   int x, y;
13366 #else
13367   int x, y;
13368 #endif
13369
13370 #if 0
13371   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13372   /* only horizontal XOR vertical scroll direction allowed */
13373   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13374     return;
13375 #endif
13376
13377 #if 0
13378   /* (directly solved in BlitBitmap() now) */
13379   if (bitmap_db_field2 == NULL)
13380     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13381
13382   /* needed when blitting directly to same bitmap -- should not be needed with
13383      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13384   BlitBitmap(drawto_field, bitmap_db_field2,
13385              FX + TILEX * (dx == -1) - softscroll_offset,
13386              FY + TILEY * (dy == -1) - softscroll_offset,
13387              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13388              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13389              FX + TILEX * (dx == 1) - softscroll_offset,
13390              FY + TILEY * (dy == 1) - softscroll_offset);
13391   BlitBitmap(bitmap_db_field2, drawto_field,
13392              FX + TILEX * (dx == 1) - softscroll_offset,
13393              FY + TILEY * (dy == 1) - softscroll_offset,
13394              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13395              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13396              FX + TILEX * (dx == 1) - softscroll_offset,
13397              FY + TILEY * (dy == 1) - softscroll_offset);
13398
13399 #else
13400
13401 #if 0
13402   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13403   int xsize = (BX2 - BX1 + 1);
13404   int ysize = (BY2 - BY1 + 1);
13405   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13406   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13407   int step  = (start < end ? +1 : -1);
13408
13409   for (i = start; i != end; i += step)
13410   {
13411     BlitBitmap(drawto_field, drawto_field,
13412                FX + TILEX * (dx != 0 ? i + step : 0),
13413                FY + TILEY * (dy != 0 ? i + step : 0),
13414                TILEX * (dx != 0 ? 1 : xsize),
13415                TILEY * (dy != 0 ? 1 : ysize),
13416                FX + TILEX * (dx != 0 ? i : 0),
13417                FY + TILEY * (dy != 0 ? i : 0));
13418   }
13419
13420 #else
13421
13422 #if NEW_TILESIZE
13423 #if NEW_SCROLL
13424   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13425 #else
13426   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13427 #endif
13428 #else
13429 #if NEW_SCROLL
13430   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13431 #else
13432   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13433 #endif
13434 #endif
13435
13436 #if NEW_TILESIZE
13437   BlitBitmap(drawto_field, drawto_field,
13438              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13439              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13440              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13441              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13442              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13443              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13444 #else
13445   BlitBitmap(drawto_field, drawto_field,
13446              FX + TILEX * (dx == -1) - softscroll_offset,
13447              FY + TILEY * (dy == -1) - softscroll_offset,
13448              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13449              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13450              FX + TILEX * (dx == 1) - softscroll_offset,
13451              FY + TILEY * (dy == 1) - softscroll_offset);
13452 #endif
13453
13454 #endif
13455 #endif
13456
13457   if (dx != 0)
13458   {
13459     x = (dx == 1 ? BX1 : BX2);
13460     for (y = BY1; y <= BY2; y++)
13461       DrawScreenField(x, y);
13462   }
13463
13464   if (dy != 0)
13465   {
13466     y = (dy == 1 ? BY1 : BY2);
13467     for (x = BX1; x <= BX2; x++)
13468       DrawScreenField(x, y);
13469   }
13470
13471   redraw_mask |= REDRAW_FIELD;
13472 }
13473
13474 static boolean canFallDown(struct PlayerInfo *player)
13475 {
13476   int jx = player->jx, jy = player->jy;
13477
13478   return (IN_LEV_FIELD(jx, jy + 1) &&
13479           (IS_FREE(jx, jy + 1) ||
13480            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13481           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13482           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13483 }
13484
13485 static boolean canPassField(int x, int y, int move_dir)
13486 {
13487   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13488   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13489   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13490   int nextx = x + dx;
13491   int nexty = y + dy;
13492   int element = Feld[x][y];
13493
13494   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13495           !CAN_MOVE(element) &&
13496           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13497           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13498           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13499 }
13500
13501 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13502 {
13503   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13504   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13505   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13506   int newx = x + dx;
13507   int newy = y + dy;
13508
13509   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13510           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13511           (IS_DIGGABLE(Feld[newx][newy]) ||
13512            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13513            canPassField(newx, newy, move_dir)));
13514 }
13515
13516 static void CheckGravityMovement(struct PlayerInfo *player)
13517 {
13518 #if USE_PLAYER_GRAVITY
13519   if (player->gravity && !player->programmed_action)
13520 #else
13521   if (game.gravity && !player->programmed_action)
13522 #endif
13523   {
13524     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13525     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13526     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13527     int jx = player->jx, jy = player->jy;
13528     boolean player_is_moving_to_valid_field =
13529       (!player_is_snapping &&
13530        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13531         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13532     boolean player_can_fall_down = canFallDown(player);
13533
13534     if (player_can_fall_down &&
13535         !player_is_moving_to_valid_field)
13536       player->programmed_action = MV_DOWN;
13537   }
13538 }
13539
13540 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13541 {
13542   return CheckGravityMovement(player);
13543
13544 #if USE_PLAYER_GRAVITY
13545   if (player->gravity && !player->programmed_action)
13546 #else
13547   if (game.gravity && !player->programmed_action)
13548 #endif
13549   {
13550     int jx = player->jx, jy = player->jy;
13551     boolean field_under_player_is_free =
13552       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13553     boolean player_is_standing_on_valid_field =
13554       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13555        (IS_WALKABLE(Feld[jx][jy]) &&
13556         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13557
13558     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13559       player->programmed_action = MV_DOWN;
13560   }
13561 }
13562
13563 /*
13564   MovePlayerOneStep()
13565   -----------------------------------------------------------------------------
13566   dx, dy:               direction (non-diagonal) to try to move the player to
13567   real_dx, real_dy:     direction as read from input device (can be diagonal)
13568 */
13569
13570 boolean MovePlayerOneStep(struct PlayerInfo *player,
13571                           int dx, int dy, int real_dx, int real_dy)
13572 {
13573   int jx = player->jx, jy = player->jy;
13574   int new_jx = jx + dx, new_jy = jy + dy;
13575 #if !USE_FIXED_DONT_RUN_INTO
13576   int element;
13577 #endif
13578   int can_move;
13579   boolean player_can_move = !player->cannot_move;
13580
13581   if (!player->active || (!dx && !dy))
13582     return MP_NO_ACTION;
13583
13584   player->MovDir = (dx < 0 ? MV_LEFT :
13585                     dx > 0 ? MV_RIGHT :
13586                     dy < 0 ? MV_UP :
13587                     dy > 0 ? MV_DOWN :  MV_NONE);
13588
13589   if (!IN_LEV_FIELD(new_jx, new_jy))
13590     return MP_NO_ACTION;
13591
13592   if (!player_can_move)
13593   {
13594     if (player->MovPos == 0)
13595     {
13596       player->is_moving = FALSE;
13597       player->is_digging = FALSE;
13598       player->is_collecting = FALSE;
13599       player->is_snapping = FALSE;
13600       player->is_pushing = FALSE;
13601     }
13602   }
13603
13604 #if 1
13605   if (!options.network && game.centered_player_nr == -1 &&
13606       !AllPlayersInSight(player, new_jx, new_jy))
13607     return MP_NO_ACTION;
13608 #else
13609   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13610     return MP_NO_ACTION;
13611 #endif
13612
13613 #if !USE_FIXED_DONT_RUN_INTO
13614   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13615
13616   /* (moved to DigField()) */
13617   if (player_can_move && DONT_RUN_INTO(element))
13618   {
13619     if (element == EL_ACID && dx == 0 && dy == 1)
13620     {
13621       SplashAcid(new_jx, new_jy);
13622       Feld[jx][jy] = EL_PLAYER_1;
13623       InitMovingField(jx, jy, MV_DOWN);
13624       Store[jx][jy] = EL_ACID;
13625       ContinueMoving(jx, jy);
13626       BuryPlayer(player);
13627     }
13628     else
13629       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13630
13631     return MP_MOVING;
13632   }
13633 #endif
13634
13635   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13636   if (can_move != MP_MOVING)
13637     return can_move;
13638
13639   /* check if DigField() has caused relocation of the player */
13640   if (player->jx != jx || player->jy != jy)
13641     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13642
13643   StorePlayer[jx][jy] = 0;
13644   player->last_jx = jx;
13645   player->last_jy = jy;
13646   player->jx = new_jx;
13647   player->jy = new_jy;
13648   StorePlayer[new_jx][new_jy] = player->element_nr;
13649
13650   if (player->move_delay_value_next != -1)
13651   {
13652     player->move_delay_value = player->move_delay_value_next;
13653     player->move_delay_value_next = -1;
13654   }
13655
13656   player->MovPos =
13657     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13658
13659   player->step_counter++;
13660
13661   PlayerVisit[jx][jy] = FrameCounter;
13662
13663 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13664   player->is_moving = TRUE;
13665 #endif
13666
13667 #if 1
13668   /* should better be called in MovePlayer(), but this breaks some tapes */
13669   ScrollPlayer(player, SCROLL_INIT);
13670 #endif
13671
13672   return MP_MOVING;
13673 }
13674
13675 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13676 {
13677   int jx = player->jx, jy = player->jy;
13678   int old_jx = jx, old_jy = jy;
13679   int moved = MP_NO_ACTION;
13680
13681   if (!player->active)
13682     return FALSE;
13683
13684   if (!dx && !dy)
13685   {
13686     if (player->MovPos == 0)
13687     {
13688       player->is_moving = FALSE;
13689       player->is_digging = FALSE;
13690       player->is_collecting = FALSE;
13691       player->is_snapping = FALSE;
13692       player->is_pushing = FALSE;
13693     }
13694
13695     return FALSE;
13696   }
13697
13698   if (player->move_delay > 0)
13699     return FALSE;
13700
13701   player->move_delay = -1;              /* set to "uninitialized" value */
13702
13703   /* store if player is automatically moved to next field */
13704   player->is_auto_moving = (player->programmed_action != MV_NONE);
13705
13706   /* remove the last programmed player action */
13707   player->programmed_action = 0;
13708
13709   if (player->MovPos)
13710   {
13711     /* should only happen if pre-1.2 tape recordings are played */
13712     /* this is only for backward compatibility */
13713
13714     int original_move_delay_value = player->move_delay_value;
13715
13716 #if DEBUG
13717     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13718            tape.counter);
13719 #endif
13720
13721     /* scroll remaining steps with finest movement resolution */
13722     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13723
13724     while (player->MovPos)
13725     {
13726       ScrollPlayer(player, SCROLL_GO_ON);
13727       ScrollScreen(NULL, SCROLL_GO_ON);
13728
13729       AdvanceFrameAndPlayerCounters(player->index_nr);
13730
13731       DrawAllPlayers();
13732       BackToFront();
13733     }
13734
13735     player->move_delay_value = original_move_delay_value;
13736   }
13737
13738   player->is_active = FALSE;
13739
13740   if (player->last_move_dir & MV_HORIZONTAL)
13741   {
13742     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13743       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13744   }
13745   else
13746   {
13747     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13748       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13749   }
13750
13751 #if USE_FIXED_BORDER_RUNNING_GFX
13752   if (!moved && !player->is_active)
13753   {
13754     player->is_moving = FALSE;
13755     player->is_digging = FALSE;
13756     player->is_collecting = FALSE;
13757     player->is_snapping = FALSE;
13758     player->is_pushing = FALSE;
13759   }
13760 #endif
13761
13762   jx = player->jx;
13763   jy = player->jy;
13764
13765 #if 1
13766   if (moved & MP_MOVING && !ScreenMovPos &&
13767       (player->index_nr == game.centered_player_nr ||
13768        game.centered_player_nr == -1))
13769 #else
13770   if (moved & MP_MOVING && !ScreenMovPos &&
13771       (player == local_player || !options.network))
13772 #endif
13773   {
13774     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13775     int offset = game.scroll_delay_value;
13776
13777     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13778     {
13779       /* actual player has left the screen -- scroll in that direction */
13780       if (jx != old_jx)         /* player has moved horizontally */
13781         scroll_x += (jx - old_jx);
13782       else                      /* player has moved vertically */
13783         scroll_y += (jy - old_jy);
13784     }
13785     else
13786     {
13787       if (jx != old_jx)         /* player has moved horizontally */
13788       {
13789         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13790             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13791           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13792
13793         /* don't scroll over playfield boundaries */
13794         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13795           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13796
13797         /* don't scroll more than one field at a time */
13798         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13799
13800         /* don't scroll against the player's moving direction */
13801         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13802             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13803           scroll_x = old_scroll_x;
13804       }
13805       else                      /* player has moved vertically */
13806       {
13807         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13808             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13809           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13810
13811         /* don't scroll over playfield boundaries */
13812         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13813           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13814
13815         /* don't scroll more than one field at a time */
13816         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13817
13818         /* don't scroll against the player's moving direction */
13819         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13820             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13821           scroll_y = old_scroll_y;
13822       }
13823     }
13824
13825     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13826     {
13827 #if 1
13828       if (!options.network && game.centered_player_nr == -1 &&
13829           !AllPlayersInVisibleScreen())
13830       {
13831         scroll_x = old_scroll_x;
13832         scroll_y = old_scroll_y;
13833       }
13834       else
13835 #else
13836       if (!options.network && !AllPlayersInVisibleScreen())
13837       {
13838         scroll_x = old_scroll_x;
13839         scroll_y = old_scroll_y;
13840       }
13841       else
13842 #endif
13843       {
13844         ScrollScreen(player, SCROLL_INIT);
13845         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13846       }
13847     }
13848   }
13849
13850   player->StepFrame = 0;
13851
13852   if (moved & MP_MOVING)
13853   {
13854     if (old_jx != jx && old_jy == jy)
13855       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13856     else if (old_jx == jx && old_jy != jy)
13857       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13858
13859     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13860
13861     player->last_move_dir = player->MovDir;
13862     player->is_moving = TRUE;
13863     player->is_snapping = FALSE;
13864     player->is_switching = FALSE;
13865     player->is_dropping = FALSE;
13866     player->is_dropping_pressed = FALSE;
13867     player->drop_pressed_delay = 0;
13868
13869 #if 0
13870     /* should better be called here than above, but this breaks some tapes */
13871     ScrollPlayer(player, SCROLL_INIT);
13872 #endif
13873   }
13874   else
13875   {
13876     CheckGravityMovementWhenNotMoving(player);
13877
13878     player->is_moving = FALSE;
13879
13880     /* at this point, the player is allowed to move, but cannot move right now
13881        (e.g. because of something blocking the way) -- ensure that the player
13882        is also allowed to move in the next frame (in old versions before 3.1.1,
13883        the player was forced to wait again for eight frames before next try) */
13884
13885     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13886       player->move_delay = 0;   /* allow direct movement in the next frame */
13887   }
13888
13889   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13890     player->move_delay = player->move_delay_value;
13891
13892   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13893   {
13894     TestIfPlayerTouchesBadThing(jx, jy);
13895     TestIfPlayerTouchesCustomElement(jx, jy);
13896   }
13897
13898   if (!player->active)
13899     RemovePlayer(player);
13900
13901   return moved;
13902 }
13903
13904 void ScrollPlayer(struct PlayerInfo *player, int mode)
13905 {
13906   int jx = player->jx, jy = player->jy;
13907   int last_jx = player->last_jx, last_jy = player->last_jy;
13908   int move_stepsize = TILEX / player->move_delay_value;
13909
13910 #if USE_NEW_PLAYER_SPEED
13911   if (!player->active)
13912     return;
13913
13914   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13915     return;
13916 #else
13917   if (!player->active || player->MovPos == 0)
13918     return;
13919 #endif
13920
13921   if (mode == SCROLL_INIT)
13922   {
13923     player->actual_frame_counter = FrameCounter;
13924     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13925
13926     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13927         Feld[last_jx][last_jy] == EL_EMPTY)
13928     {
13929       int last_field_block_delay = 0;   /* start with no blocking at all */
13930       int block_delay_adjustment = player->block_delay_adjustment;
13931
13932       /* if player blocks last field, add delay for exactly one move */
13933       if (player->block_last_field)
13934       {
13935         last_field_block_delay += player->move_delay_value;
13936
13937         /* when blocking enabled, prevent moving up despite gravity */
13938 #if USE_PLAYER_GRAVITY
13939         if (player->gravity && player->MovDir == MV_UP)
13940           block_delay_adjustment = -1;
13941 #else
13942         if (game.gravity && player->MovDir == MV_UP)
13943           block_delay_adjustment = -1;
13944 #endif
13945       }
13946
13947       /* add block delay adjustment (also possible when not blocking) */
13948       last_field_block_delay += block_delay_adjustment;
13949
13950       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13951       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13952     }
13953
13954 #if USE_NEW_PLAYER_SPEED
13955     if (player->MovPos != 0)    /* player has not yet reached destination */
13956       return;
13957 #else
13958     return;
13959 #endif
13960   }
13961   else if (!FrameReached(&player->actual_frame_counter, 1))
13962     return;
13963
13964 #if USE_NEW_PLAYER_SPEED
13965   if (player->MovPos != 0)
13966   {
13967     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13968     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13969
13970     /* before DrawPlayer() to draw correct player graphic for this case */
13971     if (player->MovPos == 0)
13972       CheckGravityMovement(player);
13973   }
13974 #else
13975   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13976   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13977
13978   /* before DrawPlayer() to draw correct player graphic for this case */
13979   if (player->MovPos == 0)
13980     CheckGravityMovement(player);
13981 #endif
13982
13983   if (player->MovPos == 0)      /* player reached destination field */
13984   {
13985     if (player->move_delay_reset_counter > 0)
13986     {
13987       player->move_delay_reset_counter--;
13988
13989       if (player->move_delay_reset_counter == 0)
13990       {
13991         /* continue with normal speed after quickly moving through gate */
13992         HALVE_PLAYER_SPEED(player);
13993
13994         /* be able to make the next move without delay */
13995         player->move_delay = 0;
13996       }
13997     }
13998
13999     player->last_jx = jx;
14000     player->last_jy = jy;
14001
14002     if (Feld[jx][jy] == EL_EXIT_OPEN ||
14003         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14004 #if 1
14005         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14006 #endif
14007         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14008         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14009 #if 1
14010         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14011 #endif
14012         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14013         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
14014     {
14015       DrawPlayer(player);       /* needed here only to cleanup last field */
14016       RemovePlayer(player);
14017
14018       if (local_player->friends_still_needed == 0 ||
14019           IS_SP_ELEMENT(Feld[jx][jy]))
14020         PlayerWins(player);
14021     }
14022
14023     /* this breaks one level: "machine", level 000 */
14024     {
14025       int move_direction = player->MovDir;
14026       int enter_side = MV_DIR_OPPOSITE(move_direction);
14027       int leave_side = move_direction;
14028       int old_jx = last_jx;
14029       int old_jy = last_jy;
14030       int old_element = Feld[old_jx][old_jy];
14031       int new_element = Feld[jx][jy];
14032
14033       if (IS_CUSTOM_ELEMENT(old_element))
14034         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14035                                    CE_LEFT_BY_PLAYER,
14036                                    player->index_bit, leave_side);
14037
14038       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14039                                           CE_PLAYER_LEAVES_X,
14040                                           player->index_bit, leave_side);
14041
14042       if (IS_CUSTOM_ELEMENT(new_element))
14043         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14044                                    player->index_bit, enter_side);
14045
14046       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14047                                           CE_PLAYER_ENTERS_X,
14048                                           player->index_bit, enter_side);
14049
14050 #if USE_FIX_CE_ACTION_WITH_PLAYER
14051       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14052                                         CE_MOVE_OF_X, move_direction);
14053 #else
14054       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14055                                         CE_MOVE_OF_X, move_direction);
14056 #endif
14057     }
14058
14059     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14060     {
14061       TestIfPlayerTouchesBadThing(jx, jy);
14062       TestIfPlayerTouchesCustomElement(jx, jy);
14063
14064       /* needed because pushed element has not yet reached its destination,
14065          so it would trigger a change event at its previous field location */
14066       if (!player->is_pushing)
14067         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14068
14069       if (!player->active)
14070         RemovePlayer(player);
14071     }
14072
14073     if (!local_player->LevelSolved && level.use_step_counter)
14074     {
14075       int i;
14076
14077       TimePlayed++;
14078
14079       if (TimeLeft > 0)
14080       {
14081         TimeLeft--;
14082
14083         if (TimeLeft <= 10 && setup.time_limit)
14084           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14085
14086 #if 1
14087         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14088
14089         DisplayGameControlValues();
14090 #else
14091         DrawGameValue_Time(TimeLeft);
14092 #endif
14093
14094         if (!TimeLeft && setup.time_limit)
14095           for (i = 0; i < MAX_PLAYERS; i++)
14096             KillPlayer(&stored_player[i]);
14097       }
14098 #if 1
14099       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14100       {
14101         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14102
14103         DisplayGameControlValues();
14104       }
14105 #else
14106       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14107         DrawGameValue_Time(TimePlayed);
14108 #endif
14109     }
14110
14111     if (tape.single_step && tape.recording && !tape.pausing &&
14112         !player->programmed_action)
14113       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14114   }
14115 }
14116
14117 void ScrollScreen(struct PlayerInfo *player, int mode)
14118 {
14119   static unsigned int screen_frame_counter = 0;
14120
14121   if (mode == SCROLL_INIT)
14122   {
14123     /* set scrolling step size according to actual player's moving speed */
14124     ScrollStepSize = TILEX / player->move_delay_value;
14125
14126     screen_frame_counter = FrameCounter;
14127     ScreenMovDir = player->MovDir;
14128     ScreenMovPos = player->MovPos;
14129     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14130     return;
14131   }
14132   else if (!FrameReached(&screen_frame_counter, 1))
14133     return;
14134
14135   if (ScreenMovPos)
14136   {
14137     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14138     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14139     redraw_mask |= REDRAW_FIELD;
14140   }
14141   else
14142     ScreenMovDir = MV_NONE;
14143 }
14144
14145 void TestIfPlayerTouchesCustomElement(int x, int y)
14146 {
14147   static int xy[4][2] =
14148   {
14149     { 0, -1 },
14150     { -1, 0 },
14151     { +1, 0 },
14152     { 0, +1 }
14153   };
14154   static int trigger_sides[4][2] =
14155   {
14156     /* center side       border side */
14157     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14158     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14159     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14160     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14161   };
14162   static int touch_dir[4] =
14163   {
14164     MV_LEFT | MV_RIGHT,
14165     MV_UP   | MV_DOWN,
14166     MV_UP   | MV_DOWN,
14167     MV_LEFT | MV_RIGHT
14168   };
14169   int center_element = Feld[x][y];      /* should always be non-moving! */
14170   int i;
14171
14172   for (i = 0; i < NUM_DIRECTIONS; i++)
14173   {
14174     int xx = x + xy[i][0];
14175     int yy = y + xy[i][1];
14176     int center_side = trigger_sides[i][0];
14177     int border_side = trigger_sides[i][1];
14178     int border_element;
14179
14180     if (!IN_LEV_FIELD(xx, yy))
14181       continue;
14182
14183     if (IS_PLAYER(x, y))                /* player found at center element */
14184     {
14185       struct PlayerInfo *player = PLAYERINFO(x, y);
14186
14187       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14188         border_element = Feld[xx][yy];          /* may be moving! */
14189       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14190         border_element = Feld[xx][yy];
14191       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14192         border_element = MovingOrBlocked2Element(xx, yy);
14193       else
14194         continue;               /* center and border element do not touch */
14195
14196       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14197                                  player->index_bit, border_side);
14198       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14199                                           CE_PLAYER_TOUCHES_X,
14200                                           player->index_bit, border_side);
14201
14202 #if USE_FIX_CE_ACTION_WITH_PLAYER
14203       {
14204         /* use player element that is initially defined in the level playfield,
14205            not the player element that corresponds to the runtime player number
14206            (example: a level that contains EL_PLAYER_3 as the only player would
14207            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14208         int player_element = PLAYERINFO(x, y)->initial_element;
14209
14210         CheckElementChangeBySide(xx, yy, border_element, player_element,
14211                                  CE_TOUCHING_X, border_side);
14212       }
14213 #endif
14214     }
14215     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14216     {
14217       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14218
14219       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14220       {
14221         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14222           continue;             /* center and border element do not touch */
14223       }
14224
14225       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14226                                  player->index_bit, center_side);
14227       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14228                                           CE_PLAYER_TOUCHES_X,
14229                                           player->index_bit, center_side);
14230
14231 #if USE_FIX_CE_ACTION_WITH_PLAYER
14232       {
14233         /* use player element that is initially defined in the level playfield,
14234            not the player element that corresponds to the runtime player number
14235            (example: a level that contains EL_PLAYER_3 as the only player would
14236            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14237         int player_element = PLAYERINFO(xx, yy)->initial_element;
14238
14239         CheckElementChangeBySide(x, y, center_element, player_element,
14240                                  CE_TOUCHING_X, center_side);
14241       }
14242 #endif
14243
14244       break;
14245     }
14246   }
14247 }
14248
14249 #if USE_ELEMENT_TOUCHING_BUGFIX
14250
14251 void TestIfElementTouchesCustomElement(int x, int y)
14252 {
14253   static int xy[4][2] =
14254   {
14255     { 0, -1 },
14256     { -1, 0 },
14257     { +1, 0 },
14258     { 0, +1 }
14259   };
14260   static int trigger_sides[4][2] =
14261   {
14262     /* center side      border side */
14263     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14264     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14265     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14266     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14267   };
14268   static int touch_dir[4] =
14269   {
14270     MV_LEFT | MV_RIGHT,
14271     MV_UP   | MV_DOWN,
14272     MV_UP   | MV_DOWN,
14273     MV_LEFT | MV_RIGHT
14274   };
14275   boolean change_center_element = FALSE;
14276   int center_element = Feld[x][y];      /* should always be non-moving! */
14277   int border_element_old[NUM_DIRECTIONS];
14278   int i;
14279
14280   for (i = 0; i < NUM_DIRECTIONS; i++)
14281   {
14282     int xx = x + xy[i][0];
14283     int yy = y + xy[i][1];
14284     int border_element;
14285
14286     border_element_old[i] = -1;
14287
14288     if (!IN_LEV_FIELD(xx, yy))
14289       continue;
14290
14291     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14292       border_element = Feld[xx][yy];    /* may be moving! */
14293     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14294       border_element = Feld[xx][yy];
14295     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14296       border_element = MovingOrBlocked2Element(xx, yy);
14297     else
14298       continue;                 /* center and border element do not touch */
14299
14300     border_element_old[i] = border_element;
14301   }
14302
14303   for (i = 0; i < NUM_DIRECTIONS; i++)
14304   {
14305     int xx = x + xy[i][0];
14306     int yy = y + xy[i][1];
14307     int center_side = trigger_sides[i][0];
14308     int border_element = border_element_old[i];
14309
14310     if (border_element == -1)
14311       continue;
14312
14313     /* check for change of border element */
14314     CheckElementChangeBySide(xx, yy, border_element, center_element,
14315                              CE_TOUCHING_X, center_side);
14316
14317     /* (center element cannot be player, so we dont have to check this here) */
14318   }
14319
14320   for (i = 0; i < NUM_DIRECTIONS; i++)
14321   {
14322     int xx = x + xy[i][0];
14323     int yy = y + xy[i][1];
14324     int border_side = trigger_sides[i][1];
14325     int border_element = border_element_old[i];
14326
14327     if (border_element == -1)
14328       continue;
14329
14330     /* check for change of center element (but change it only once) */
14331     if (!change_center_element)
14332       change_center_element =
14333         CheckElementChangeBySide(x, y, center_element, border_element,
14334                                  CE_TOUCHING_X, border_side);
14335
14336 #if USE_FIX_CE_ACTION_WITH_PLAYER
14337     if (IS_PLAYER(xx, yy))
14338     {
14339       /* use player element that is initially defined in the level playfield,
14340          not the player element that corresponds to the runtime player number
14341          (example: a level that contains EL_PLAYER_3 as the only player would
14342          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14343       int player_element = PLAYERINFO(xx, yy)->initial_element;
14344
14345       CheckElementChangeBySide(x, y, center_element, player_element,
14346                                CE_TOUCHING_X, border_side);
14347     }
14348 #endif
14349   }
14350 }
14351
14352 #else
14353
14354 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14355 {
14356   static int xy[4][2] =
14357   {
14358     { 0, -1 },
14359     { -1, 0 },
14360     { +1, 0 },
14361     { 0, +1 }
14362   };
14363   static int trigger_sides[4][2] =
14364   {
14365     /* center side      border side */
14366     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14367     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14368     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14369     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14370   };
14371   static int touch_dir[4] =
14372   {
14373     MV_LEFT | MV_RIGHT,
14374     MV_UP   | MV_DOWN,
14375     MV_UP   | MV_DOWN,
14376     MV_LEFT | MV_RIGHT
14377   };
14378   boolean change_center_element = FALSE;
14379   int center_element = Feld[x][y];      /* should always be non-moving! */
14380   int i;
14381
14382   for (i = 0; i < NUM_DIRECTIONS; i++)
14383   {
14384     int xx = x + xy[i][0];
14385     int yy = y + xy[i][1];
14386     int center_side = trigger_sides[i][0];
14387     int border_side = trigger_sides[i][1];
14388     int border_element;
14389
14390     if (!IN_LEV_FIELD(xx, yy))
14391       continue;
14392
14393     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14394       border_element = Feld[xx][yy];    /* may be moving! */
14395     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14396       border_element = Feld[xx][yy];
14397     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14398       border_element = MovingOrBlocked2Element(xx, yy);
14399     else
14400       continue;                 /* center and border element do not touch */
14401
14402     /* check for change of center element (but change it only once) */
14403     if (!change_center_element)
14404       change_center_element =
14405         CheckElementChangeBySide(x, y, center_element, border_element,
14406                                  CE_TOUCHING_X, border_side);
14407
14408     /* check for change of border element */
14409     CheckElementChangeBySide(xx, yy, border_element, center_element,
14410                              CE_TOUCHING_X, center_side);
14411   }
14412 }
14413
14414 #endif
14415
14416 void TestIfElementHitsCustomElement(int x, int y, int direction)
14417 {
14418   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14419   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14420   int hitx = x + dx, hity = y + dy;
14421   int hitting_element = Feld[x][y];
14422   int touched_element;
14423
14424   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14425     return;
14426
14427   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14428                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14429
14430   if (IN_LEV_FIELD(hitx, hity))
14431   {
14432     int opposite_direction = MV_DIR_OPPOSITE(direction);
14433     int hitting_side = direction;
14434     int touched_side = opposite_direction;
14435     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14436                           MovDir[hitx][hity] != direction ||
14437                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14438
14439     object_hit = TRUE;
14440
14441     if (object_hit)
14442     {
14443       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14444                                CE_HITTING_X, touched_side);
14445
14446       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14447                                CE_HIT_BY_X, hitting_side);
14448
14449       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14450                                CE_HIT_BY_SOMETHING, opposite_direction);
14451
14452 #if USE_FIX_CE_ACTION_WITH_PLAYER
14453       if (IS_PLAYER(hitx, hity))
14454       {
14455         /* use player element that is initially defined in the level playfield,
14456            not the player element that corresponds to the runtime player number
14457            (example: a level that contains EL_PLAYER_3 as the only player would
14458            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14459         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14460
14461         CheckElementChangeBySide(x, y, hitting_element, player_element,
14462                                  CE_HITTING_X, touched_side);
14463       }
14464 #endif
14465     }
14466   }
14467
14468   /* "hitting something" is also true when hitting the playfield border */
14469   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14470                            CE_HITTING_SOMETHING, direction);
14471 }
14472
14473 #if 0
14474 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14475 {
14476   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14477   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14478   int hitx = x + dx, hity = y + dy;
14479   int hitting_element = Feld[x][y];
14480   int touched_element;
14481 #if 0
14482   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14483                         !IS_FREE(hitx, hity) &&
14484                         (!IS_MOVING(hitx, hity) ||
14485                          MovDir[hitx][hity] != direction ||
14486                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14487 #endif
14488
14489   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14490     return;
14491
14492 #if 0
14493   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14494     return;
14495 #endif
14496
14497   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14498                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14499
14500   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14501                            EP_CAN_SMASH_EVERYTHING, direction);
14502
14503   if (IN_LEV_FIELD(hitx, hity))
14504   {
14505     int opposite_direction = MV_DIR_OPPOSITE(direction);
14506     int hitting_side = direction;
14507     int touched_side = opposite_direction;
14508 #if 0
14509     int touched_element = MovingOrBlocked2Element(hitx, hity);
14510 #endif
14511 #if 1
14512     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14513                           MovDir[hitx][hity] != direction ||
14514                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14515
14516     object_hit = TRUE;
14517 #endif
14518
14519     if (object_hit)
14520     {
14521       int i;
14522
14523       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14524                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14525
14526       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14527                                CE_OTHER_IS_SMASHING, touched_side);
14528
14529       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14530                                CE_OTHER_GETS_SMASHED, hitting_side);
14531     }
14532   }
14533 }
14534 #endif
14535
14536 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14537 {
14538   int i, kill_x = -1, kill_y = -1;
14539
14540   int bad_element = -1;
14541   static int test_xy[4][2] =
14542   {
14543     { 0, -1 },
14544     { -1, 0 },
14545     { +1, 0 },
14546     { 0, +1 }
14547   };
14548   static int test_dir[4] =
14549   {
14550     MV_UP,
14551     MV_LEFT,
14552     MV_RIGHT,
14553     MV_DOWN
14554   };
14555
14556   for (i = 0; i < NUM_DIRECTIONS; i++)
14557   {
14558     int test_x, test_y, test_move_dir, test_element;
14559
14560     test_x = good_x + test_xy[i][0];
14561     test_y = good_y + test_xy[i][1];
14562
14563     if (!IN_LEV_FIELD(test_x, test_y))
14564       continue;
14565
14566     test_move_dir =
14567       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14568
14569     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14570
14571     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14572        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14573     */
14574     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14575         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14576     {
14577       kill_x = test_x;
14578       kill_y = test_y;
14579       bad_element = test_element;
14580
14581       break;
14582     }
14583   }
14584
14585   if (kill_x != -1 || kill_y != -1)
14586   {
14587     if (IS_PLAYER(good_x, good_y))
14588     {
14589       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14590
14591       if (player->shield_deadly_time_left > 0 &&
14592           !IS_INDESTRUCTIBLE(bad_element))
14593         Bang(kill_x, kill_y);
14594       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14595         KillPlayer(player);
14596     }
14597     else
14598       Bang(good_x, good_y);
14599   }
14600 }
14601
14602 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14603 {
14604   int i, kill_x = -1, kill_y = -1;
14605   int bad_element = Feld[bad_x][bad_y];
14606   static int test_xy[4][2] =
14607   {
14608     { 0, -1 },
14609     { -1, 0 },
14610     { +1, 0 },
14611     { 0, +1 }
14612   };
14613   static int touch_dir[4] =
14614   {
14615     MV_LEFT | MV_RIGHT,
14616     MV_UP   | MV_DOWN,
14617     MV_UP   | MV_DOWN,
14618     MV_LEFT | MV_RIGHT
14619   };
14620   static int test_dir[4] =
14621   {
14622     MV_UP,
14623     MV_LEFT,
14624     MV_RIGHT,
14625     MV_DOWN
14626   };
14627
14628   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14629     return;
14630
14631   for (i = 0; i < NUM_DIRECTIONS; i++)
14632   {
14633     int test_x, test_y, test_move_dir, test_element;
14634
14635     test_x = bad_x + test_xy[i][0];
14636     test_y = bad_y + test_xy[i][1];
14637
14638     if (!IN_LEV_FIELD(test_x, test_y))
14639       continue;
14640
14641     test_move_dir =
14642       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14643
14644     test_element = Feld[test_x][test_y];
14645
14646     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14647        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14648     */
14649     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14650         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14651     {
14652       /* good thing is player or penguin that does not move away */
14653       if (IS_PLAYER(test_x, test_y))
14654       {
14655         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14656
14657         if (bad_element == EL_ROBOT && player->is_moving)
14658           continue;     /* robot does not kill player if he is moving */
14659
14660         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14661         {
14662           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14663             continue;           /* center and border element do not touch */
14664         }
14665
14666         kill_x = test_x;
14667         kill_y = test_y;
14668
14669         break;
14670       }
14671       else if (test_element == EL_PENGUIN)
14672       {
14673         kill_x = test_x;
14674         kill_y = test_y;
14675
14676         break;
14677       }
14678     }
14679   }
14680
14681   if (kill_x != -1 || kill_y != -1)
14682   {
14683     if (IS_PLAYER(kill_x, kill_y))
14684     {
14685       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14686
14687       if (player->shield_deadly_time_left > 0 &&
14688           !IS_INDESTRUCTIBLE(bad_element))
14689         Bang(bad_x, bad_y);
14690       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14691         KillPlayer(player);
14692     }
14693     else
14694       Bang(kill_x, kill_y);
14695   }
14696 }
14697
14698 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14699 {
14700   int bad_element = Feld[bad_x][bad_y];
14701   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14702   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14703   int test_x = bad_x + dx, test_y = bad_y + dy;
14704   int test_move_dir, test_element;
14705   int kill_x = -1, kill_y = -1;
14706
14707   if (!IN_LEV_FIELD(test_x, test_y))
14708     return;
14709
14710   test_move_dir =
14711     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14712
14713   test_element = Feld[test_x][test_y];
14714
14715   if (test_move_dir != bad_move_dir)
14716   {
14717     /* good thing can be player or penguin that does not move away */
14718     if (IS_PLAYER(test_x, test_y))
14719     {
14720       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14721
14722       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14723          player as being hit when he is moving towards the bad thing, because
14724          the "get hit by" condition would be lost after the player stops) */
14725       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14726         return;         /* player moves away from bad thing */
14727
14728       kill_x = test_x;
14729       kill_y = test_y;
14730     }
14731     else if (test_element == EL_PENGUIN)
14732     {
14733       kill_x = test_x;
14734       kill_y = test_y;
14735     }
14736   }
14737
14738   if (kill_x != -1 || kill_y != -1)
14739   {
14740     if (IS_PLAYER(kill_x, kill_y))
14741     {
14742       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14743
14744       if (player->shield_deadly_time_left > 0 &&
14745           !IS_INDESTRUCTIBLE(bad_element))
14746         Bang(bad_x, bad_y);
14747       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14748         KillPlayer(player);
14749     }
14750     else
14751       Bang(kill_x, kill_y);
14752   }
14753 }
14754
14755 void TestIfPlayerTouchesBadThing(int x, int y)
14756 {
14757   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14758 }
14759
14760 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14761 {
14762   TestIfGoodThingHitsBadThing(x, y, move_dir);
14763 }
14764
14765 void TestIfBadThingTouchesPlayer(int x, int y)
14766 {
14767   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14768 }
14769
14770 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14771 {
14772   TestIfBadThingHitsGoodThing(x, y, move_dir);
14773 }
14774
14775 void TestIfFriendTouchesBadThing(int x, int y)
14776 {
14777   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14778 }
14779
14780 void TestIfBadThingTouchesFriend(int x, int y)
14781 {
14782   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14783 }
14784
14785 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14786 {
14787   int i, kill_x = bad_x, kill_y = bad_y;
14788   static int xy[4][2] =
14789   {
14790     { 0, -1 },
14791     { -1, 0 },
14792     { +1, 0 },
14793     { 0, +1 }
14794   };
14795
14796   for (i = 0; i < NUM_DIRECTIONS; i++)
14797   {
14798     int x, y, element;
14799
14800     x = bad_x + xy[i][0];
14801     y = bad_y + xy[i][1];
14802     if (!IN_LEV_FIELD(x, y))
14803       continue;
14804
14805     element = Feld[x][y];
14806     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14807         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14808     {
14809       kill_x = x;
14810       kill_y = y;
14811       break;
14812     }
14813   }
14814
14815   if (kill_x != bad_x || kill_y != bad_y)
14816     Bang(bad_x, bad_y);
14817 }
14818
14819 void KillPlayer(struct PlayerInfo *player)
14820 {
14821   int jx = player->jx, jy = player->jy;
14822
14823   if (!player->active)
14824     return;
14825
14826 #if 0
14827   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14828          player->killed, player->active, player->reanimated);
14829 #endif
14830
14831   /* the following code was introduced to prevent an infinite loop when calling
14832      -> Bang()
14833      -> CheckTriggeredElementChangeExt()
14834      -> ExecuteCustomElementAction()
14835      -> KillPlayer()
14836      -> (infinitely repeating the above sequence of function calls)
14837      which occurs when killing the player while having a CE with the setting
14838      "kill player X when explosion of <player X>"; the solution using a new
14839      field "player->killed" was chosen for backwards compatibility, although
14840      clever use of the fields "player->active" etc. would probably also work */
14841 #if 1
14842   if (player->killed)
14843     return;
14844 #endif
14845
14846   player->killed = TRUE;
14847
14848   /* remove accessible field at the player's position */
14849   Feld[jx][jy] = EL_EMPTY;
14850
14851   /* deactivate shield (else Bang()/Explode() would not work right) */
14852   player->shield_normal_time_left = 0;
14853   player->shield_deadly_time_left = 0;
14854
14855 #if 0
14856   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14857          player->killed, player->active, player->reanimated);
14858 #endif
14859
14860   Bang(jx, jy);
14861
14862 #if 0
14863   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14864          player->killed, player->active, player->reanimated);
14865 #endif
14866
14867 #if USE_PLAYER_REANIMATION
14868 #if 1
14869   if (player->reanimated)       /* killed player may have been reanimated */
14870     player->killed = player->reanimated = FALSE;
14871   else
14872     BuryPlayer(player);
14873 #else
14874   if (player->killed)           /* player may have been reanimated */
14875     BuryPlayer(player);
14876 #endif
14877 #else
14878   BuryPlayer(player);
14879 #endif
14880 }
14881
14882 static void KillPlayerUnlessEnemyProtected(int x, int y)
14883 {
14884   if (!PLAYER_ENEMY_PROTECTED(x, y))
14885     KillPlayer(PLAYERINFO(x, y));
14886 }
14887
14888 static void KillPlayerUnlessExplosionProtected(int x, int y)
14889 {
14890   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14891     KillPlayer(PLAYERINFO(x, y));
14892 }
14893
14894 void BuryPlayer(struct PlayerInfo *player)
14895 {
14896   int jx = player->jx, jy = player->jy;
14897
14898   if (!player->active)
14899     return;
14900
14901   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14902   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14903
14904   player->GameOver = TRUE;
14905   RemovePlayer(player);
14906 }
14907
14908 void RemovePlayer(struct PlayerInfo *player)
14909 {
14910   int jx = player->jx, jy = player->jy;
14911   int i, found = FALSE;
14912
14913   player->present = FALSE;
14914   player->active = FALSE;
14915
14916   if (!ExplodeField[jx][jy])
14917     StorePlayer[jx][jy] = 0;
14918
14919   if (player->is_moving)
14920     TEST_DrawLevelField(player->last_jx, player->last_jy);
14921
14922   for (i = 0; i < MAX_PLAYERS; i++)
14923     if (stored_player[i].active)
14924       found = TRUE;
14925
14926   if (!found)
14927     AllPlayersGone = TRUE;
14928
14929   ExitX = ZX = jx;
14930   ExitY = ZY = jy;
14931 }
14932
14933 #if USE_NEW_SNAP_DELAY
14934 static void setFieldForSnapping(int x, int y, int element, int direction)
14935 {
14936   struct ElementInfo *ei = &element_info[element];
14937   int direction_bit = MV_DIR_TO_BIT(direction);
14938   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14939   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14940                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14941
14942   Feld[x][y] = EL_ELEMENT_SNAPPING;
14943   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14944
14945   ResetGfxAnimation(x, y);
14946
14947   GfxElement[x][y] = element;
14948   GfxAction[x][y] = action;
14949   GfxDir[x][y] = direction;
14950   GfxFrame[x][y] = -1;
14951 }
14952 #endif
14953
14954 /*
14955   =============================================================================
14956   checkDiagonalPushing()
14957   -----------------------------------------------------------------------------
14958   check if diagonal input device direction results in pushing of object
14959   (by checking if the alternative direction is walkable, diggable, ...)
14960   =============================================================================
14961 */
14962
14963 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14964                                     int x, int y, int real_dx, int real_dy)
14965 {
14966   int jx, jy, dx, dy, xx, yy;
14967
14968   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14969     return TRUE;
14970
14971   /* diagonal direction: check alternative direction */
14972   jx = player->jx;
14973   jy = player->jy;
14974   dx = x - jx;
14975   dy = y - jy;
14976   xx = jx + (dx == 0 ? real_dx : 0);
14977   yy = jy + (dy == 0 ? real_dy : 0);
14978
14979   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14980 }
14981
14982 /*
14983   =============================================================================
14984   DigField()
14985   -----------------------------------------------------------------------------
14986   x, y:                 field next to player (non-diagonal) to try to dig to
14987   real_dx, real_dy:     direction as read from input device (can be diagonal)
14988   =============================================================================
14989 */
14990
14991 static int DigField(struct PlayerInfo *player,
14992                     int oldx, int oldy, int x, int y,
14993                     int real_dx, int real_dy, int mode)
14994 {
14995   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14996   boolean player_was_pushing = player->is_pushing;
14997   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14998   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14999   int jx = oldx, jy = oldy;
15000   int dx = x - jx, dy = y - jy;
15001   int nextx = x + dx, nexty = y + dy;
15002   int move_direction = (dx == -1 ? MV_LEFT  :
15003                         dx == +1 ? MV_RIGHT :
15004                         dy == -1 ? MV_UP    :
15005                         dy == +1 ? MV_DOWN  : MV_NONE);
15006   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15007   int dig_side = MV_DIR_OPPOSITE(move_direction);
15008   int old_element = Feld[jx][jy];
15009 #if USE_FIXED_DONT_RUN_INTO
15010   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15011 #else
15012   int element;
15013 #endif
15014   int collect_count;
15015
15016   if (is_player)                /* function can also be called by EL_PENGUIN */
15017   {
15018     if (player->MovPos == 0)
15019     {
15020       player->is_digging = FALSE;
15021       player->is_collecting = FALSE;
15022     }
15023
15024     if (player->MovPos == 0)    /* last pushing move finished */
15025       player->is_pushing = FALSE;
15026
15027     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
15028     {
15029       player->is_switching = FALSE;
15030       player->push_delay = -1;
15031
15032       return MP_NO_ACTION;
15033     }
15034   }
15035
15036 #if !USE_FIXED_DONT_RUN_INTO
15037   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15038     return MP_NO_ACTION;
15039 #endif
15040
15041   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15042     old_element = Back[jx][jy];
15043
15044   /* in case of element dropped at player position, check background */
15045   else if (Back[jx][jy] != EL_EMPTY &&
15046            game.engine_version >= VERSION_IDENT(2,2,0,0))
15047     old_element = Back[jx][jy];
15048
15049   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15050     return MP_NO_ACTION;        /* field has no opening in this direction */
15051
15052   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15053     return MP_NO_ACTION;        /* field has no opening in this direction */
15054
15055 #if USE_FIXED_DONT_RUN_INTO
15056   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15057   {
15058     SplashAcid(x, y);
15059
15060     Feld[jx][jy] = player->artwork_element;
15061     InitMovingField(jx, jy, MV_DOWN);
15062     Store[jx][jy] = EL_ACID;
15063     ContinueMoving(jx, jy);
15064     BuryPlayer(player);
15065
15066     return MP_DONT_RUN_INTO;
15067   }
15068 #endif
15069
15070 #if USE_FIXED_DONT_RUN_INTO
15071   if (player_can_move && DONT_RUN_INTO(element))
15072   {
15073     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15074
15075     return MP_DONT_RUN_INTO;
15076   }
15077 #endif
15078
15079 #if USE_FIXED_DONT_RUN_INTO
15080   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15081     return MP_NO_ACTION;
15082 #endif
15083
15084 #if !USE_FIXED_DONT_RUN_INTO
15085   element = Feld[x][y];
15086 #endif
15087
15088   collect_count = element_info[element].collect_count_initial;
15089
15090   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15091     return MP_NO_ACTION;
15092
15093   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15094     player_can_move = player_can_move_or_snap;
15095
15096   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15097       game.engine_version >= VERSION_IDENT(2,2,0,0))
15098   {
15099     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15100                                player->index_bit, dig_side);
15101     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15102                                         player->index_bit, dig_side);
15103
15104     if (element == EL_DC_LANDMINE)
15105       Bang(x, y);
15106
15107     if (Feld[x][y] != element)          /* field changed by snapping */
15108       return MP_ACTION;
15109
15110     return MP_NO_ACTION;
15111   }
15112
15113 #if USE_PLAYER_GRAVITY
15114   if (player->gravity && is_player && !player->is_auto_moving &&
15115       canFallDown(player) && move_direction != MV_DOWN &&
15116       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15117     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15118 #else
15119   if (game.gravity && is_player && !player->is_auto_moving &&
15120       canFallDown(player) && move_direction != MV_DOWN &&
15121       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15122     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15123 #endif
15124
15125   if (player_can_move &&
15126       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15127   {
15128     int sound_element = SND_ELEMENT(element);
15129     int sound_action = ACTION_WALKING;
15130
15131     if (IS_RND_GATE(element))
15132     {
15133       if (!player->key[RND_GATE_NR(element)])
15134         return MP_NO_ACTION;
15135     }
15136     else if (IS_RND_GATE_GRAY(element))
15137     {
15138       if (!player->key[RND_GATE_GRAY_NR(element)])
15139         return MP_NO_ACTION;
15140     }
15141     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15142     {
15143       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15144         return MP_NO_ACTION;
15145     }
15146     else if (element == EL_EXIT_OPEN ||
15147              element == EL_EM_EXIT_OPEN ||
15148 #if 1
15149              element == EL_EM_EXIT_OPENING ||
15150 #endif
15151              element == EL_STEEL_EXIT_OPEN ||
15152              element == EL_EM_STEEL_EXIT_OPEN ||
15153 #if 1
15154              element == EL_EM_STEEL_EXIT_OPENING ||
15155 #endif
15156              element == EL_SP_EXIT_OPEN ||
15157              element == EL_SP_EXIT_OPENING)
15158     {
15159       sound_action = ACTION_PASSING;    /* player is passing exit */
15160     }
15161     else if (element == EL_EMPTY)
15162     {
15163       sound_action = ACTION_MOVING;             /* nothing to walk on */
15164     }
15165
15166     /* play sound from background or player, whatever is available */
15167     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15168       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15169     else
15170       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15171   }
15172   else if (player_can_move &&
15173            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15174   {
15175     if (!ACCESS_FROM(element, opposite_direction))
15176       return MP_NO_ACTION;      /* field not accessible from this direction */
15177
15178     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15179       return MP_NO_ACTION;
15180
15181     if (IS_EM_GATE(element))
15182     {
15183       if (!player->key[EM_GATE_NR(element)])
15184         return MP_NO_ACTION;
15185     }
15186     else if (IS_EM_GATE_GRAY(element))
15187     {
15188       if (!player->key[EM_GATE_GRAY_NR(element)])
15189         return MP_NO_ACTION;
15190     }
15191     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15192     {
15193       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15194         return MP_NO_ACTION;
15195     }
15196     else if (IS_EMC_GATE(element))
15197     {
15198       if (!player->key[EMC_GATE_NR(element)])
15199         return MP_NO_ACTION;
15200     }
15201     else if (IS_EMC_GATE_GRAY(element))
15202     {
15203       if (!player->key[EMC_GATE_GRAY_NR(element)])
15204         return MP_NO_ACTION;
15205     }
15206     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15207     {
15208       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15209         return MP_NO_ACTION;
15210     }
15211     else if (element == EL_DC_GATE_WHITE ||
15212              element == EL_DC_GATE_WHITE_GRAY ||
15213              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15214     {
15215       if (player->num_white_keys == 0)
15216         return MP_NO_ACTION;
15217
15218       player->num_white_keys--;
15219     }
15220     else if (IS_SP_PORT(element))
15221     {
15222       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15223           element == EL_SP_GRAVITY_PORT_RIGHT ||
15224           element == EL_SP_GRAVITY_PORT_UP ||
15225           element == EL_SP_GRAVITY_PORT_DOWN)
15226 #if USE_PLAYER_GRAVITY
15227         player->gravity = !player->gravity;
15228 #else
15229         game.gravity = !game.gravity;
15230 #endif
15231       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15232                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15233                element == EL_SP_GRAVITY_ON_PORT_UP ||
15234                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15235 #if USE_PLAYER_GRAVITY
15236         player->gravity = TRUE;
15237 #else
15238         game.gravity = TRUE;
15239 #endif
15240       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15241                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15242                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15243                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15244 #if USE_PLAYER_GRAVITY
15245         player->gravity = FALSE;
15246 #else
15247         game.gravity = FALSE;
15248 #endif
15249     }
15250
15251     /* automatically move to the next field with double speed */
15252     player->programmed_action = move_direction;
15253
15254     if (player->move_delay_reset_counter == 0)
15255     {
15256       player->move_delay_reset_counter = 2;     /* two double speed steps */
15257
15258       DOUBLE_PLAYER_SPEED(player);
15259     }
15260
15261     PlayLevelSoundAction(x, y, ACTION_PASSING);
15262   }
15263   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15264   {
15265     RemoveField(x, y);
15266
15267     if (mode != DF_SNAP)
15268     {
15269       GfxElement[x][y] = GFX_ELEMENT(element);
15270       player->is_digging = TRUE;
15271     }
15272
15273     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15274
15275     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15276                                         player->index_bit, dig_side);
15277
15278     if (mode == DF_SNAP)
15279     {
15280 #if USE_NEW_SNAP_DELAY
15281       if (level.block_snap_field)
15282         setFieldForSnapping(x, y, element, move_direction);
15283       else
15284         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15285 #else
15286       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15287 #endif
15288
15289       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15290                                           player->index_bit, dig_side);
15291     }
15292   }
15293   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15294   {
15295     RemoveField(x, y);
15296
15297     if (is_player && mode != DF_SNAP)
15298     {
15299       GfxElement[x][y] = element;
15300       player->is_collecting = TRUE;
15301     }
15302
15303     if (element == EL_SPEED_PILL)
15304     {
15305       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15306     }
15307     else if (element == EL_EXTRA_TIME && level.time > 0)
15308     {
15309       TimeLeft += level.extra_time;
15310
15311 #if 1
15312       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15313
15314       DisplayGameControlValues();
15315 #else
15316       DrawGameValue_Time(TimeLeft);
15317 #endif
15318     }
15319     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15320     {
15321       player->shield_normal_time_left += level.shield_normal_time;
15322       if (element == EL_SHIELD_DEADLY)
15323         player->shield_deadly_time_left += level.shield_deadly_time;
15324     }
15325     else if (element == EL_DYNAMITE ||
15326              element == EL_EM_DYNAMITE ||
15327              element == EL_SP_DISK_RED)
15328     {
15329       if (player->inventory_size < MAX_INVENTORY_SIZE)
15330         player->inventory_element[player->inventory_size++] = element;
15331
15332       DrawGameDoorValues();
15333     }
15334     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15335     {
15336       player->dynabomb_count++;
15337       player->dynabombs_left++;
15338     }
15339     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15340     {
15341       player->dynabomb_size++;
15342     }
15343     else if (element == EL_DYNABOMB_INCREASE_POWER)
15344     {
15345       player->dynabomb_xl = TRUE;
15346     }
15347     else if (IS_KEY(element))
15348     {
15349       player->key[KEY_NR(element)] = TRUE;
15350
15351       DrawGameDoorValues();
15352     }
15353     else if (element == EL_DC_KEY_WHITE)
15354     {
15355       player->num_white_keys++;
15356
15357       /* display white keys? */
15358       /* DrawGameDoorValues(); */
15359     }
15360     else if (IS_ENVELOPE(element))
15361     {
15362       player->show_envelope = element;
15363     }
15364     else if (element == EL_EMC_LENSES)
15365     {
15366       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15367
15368       RedrawAllInvisibleElementsForLenses();
15369     }
15370     else if (element == EL_EMC_MAGNIFIER)
15371     {
15372       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15373
15374       RedrawAllInvisibleElementsForMagnifier();
15375     }
15376     else if (IS_DROPPABLE(element) ||
15377              IS_THROWABLE(element))     /* can be collected and dropped */
15378     {
15379       int i;
15380
15381       if (collect_count == 0)
15382         player->inventory_infinite_element = element;
15383       else
15384         for (i = 0; i < collect_count; i++)
15385           if (player->inventory_size < MAX_INVENTORY_SIZE)
15386             player->inventory_element[player->inventory_size++] = element;
15387
15388       DrawGameDoorValues();
15389     }
15390     else if (collect_count > 0)
15391     {
15392       local_player->gems_still_needed -= collect_count;
15393       if (local_player->gems_still_needed < 0)
15394         local_player->gems_still_needed = 0;
15395
15396 #if 1
15397       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15398
15399       DisplayGameControlValues();
15400 #else
15401       DrawGameValue_Emeralds(local_player->gems_still_needed);
15402 #endif
15403     }
15404
15405     RaiseScoreElement(element);
15406     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15407
15408     if (is_player)
15409       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15410                                           player->index_bit, dig_side);
15411
15412     if (mode == DF_SNAP)
15413     {
15414 #if USE_NEW_SNAP_DELAY
15415       if (level.block_snap_field)
15416         setFieldForSnapping(x, y, element, move_direction);
15417       else
15418         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15419 #else
15420       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15421 #endif
15422
15423       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15424                                           player->index_bit, dig_side);
15425     }
15426   }
15427   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15428   {
15429     if (mode == DF_SNAP && element != EL_BD_ROCK)
15430       return MP_NO_ACTION;
15431
15432     if (CAN_FALL(element) && dy)
15433       return MP_NO_ACTION;
15434
15435     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15436         !(element == EL_SPRING && level.use_spring_bug))
15437       return MP_NO_ACTION;
15438
15439     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15440         ((move_direction & MV_VERTICAL &&
15441           ((element_info[element].move_pattern & MV_LEFT &&
15442             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15443            (element_info[element].move_pattern & MV_RIGHT &&
15444             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15445          (move_direction & MV_HORIZONTAL &&
15446           ((element_info[element].move_pattern & MV_UP &&
15447             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15448            (element_info[element].move_pattern & MV_DOWN &&
15449             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15450       return MP_NO_ACTION;
15451
15452     /* do not push elements already moving away faster than player */
15453     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15454         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15455       return MP_NO_ACTION;
15456
15457     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15458     {
15459       if (player->push_delay_value == -1 || !player_was_pushing)
15460         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15461     }
15462     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15463     {
15464       if (player->push_delay_value == -1)
15465         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15466     }
15467     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15468     {
15469       if (!player->is_pushing)
15470         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15471     }
15472
15473     player->is_pushing = TRUE;
15474     player->is_active = TRUE;
15475
15476     if (!(IN_LEV_FIELD(nextx, nexty) &&
15477           (IS_FREE(nextx, nexty) ||
15478            (IS_SB_ELEMENT(element) &&
15479             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15480            (IS_CUSTOM_ELEMENT(element) &&
15481             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15482       return MP_NO_ACTION;
15483
15484     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15485       return MP_NO_ACTION;
15486
15487     if (player->push_delay == -1)       /* new pushing; restart delay */
15488       player->push_delay = 0;
15489
15490     if (player->push_delay < player->push_delay_value &&
15491         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15492         element != EL_SPRING && element != EL_BALLOON)
15493     {
15494       /* make sure that there is no move delay before next try to push */
15495       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15496         player->move_delay = 0;
15497
15498       return MP_NO_ACTION;
15499     }
15500
15501     if (IS_CUSTOM_ELEMENT(element) &&
15502         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15503     {
15504       if (!DigFieldByCE(nextx, nexty, element))
15505         return MP_NO_ACTION;
15506     }
15507
15508     if (IS_SB_ELEMENT(element))
15509     {
15510       if (element == EL_SOKOBAN_FIELD_FULL)
15511       {
15512         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15513         local_player->sokobanfields_still_needed++;
15514       }
15515
15516       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15517       {
15518         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15519         local_player->sokobanfields_still_needed--;
15520       }
15521
15522       Feld[x][y] = EL_SOKOBAN_OBJECT;
15523
15524       if (Back[x][y] == Back[nextx][nexty])
15525         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15526       else if (Back[x][y] != 0)
15527         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15528                                     ACTION_EMPTYING);
15529       else
15530         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15531                                     ACTION_FILLING);
15532
15533 #if 1
15534       if (local_player->sokobanfields_still_needed == 0 &&
15535           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15536 #else
15537       if (local_player->sokobanfields_still_needed == 0 &&
15538           game.emulation == EMU_SOKOBAN)
15539 #endif
15540       {
15541         PlayerWins(player);
15542
15543         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15544       }
15545     }
15546     else
15547       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15548
15549     InitMovingField(x, y, move_direction);
15550     GfxAction[x][y] = ACTION_PUSHING;
15551
15552     if (mode == DF_SNAP)
15553       ContinueMoving(x, y);
15554     else
15555       MovPos[x][y] = (dx != 0 ? dx : dy);
15556
15557     Pushed[x][y] = TRUE;
15558     Pushed[nextx][nexty] = TRUE;
15559
15560     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15561       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15562     else
15563       player->push_delay_value = -1;    /* get new value later */
15564
15565     /* check for element change _after_ element has been pushed */
15566     if (game.use_change_when_pushing_bug)
15567     {
15568       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15569                                  player->index_bit, dig_side);
15570       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15571                                           player->index_bit, dig_side);
15572     }
15573   }
15574   else if (IS_SWITCHABLE(element))
15575   {
15576     if (PLAYER_SWITCHING(player, x, y))
15577     {
15578       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15579                                           player->index_bit, dig_side);
15580
15581       return MP_ACTION;
15582     }
15583
15584     player->is_switching = TRUE;
15585     player->switch_x = x;
15586     player->switch_y = y;
15587
15588     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15589
15590     if (element == EL_ROBOT_WHEEL)
15591     {
15592       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15593       ZX = x;
15594       ZY = y;
15595
15596       game.robot_wheel_active = TRUE;
15597
15598       TEST_DrawLevelField(x, y);
15599     }
15600     else if (element == EL_SP_TERMINAL)
15601     {
15602       int xx, yy;
15603
15604       SCAN_PLAYFIELD(xx, yy)
15605       {
15606         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15607           Bang(xx, yy);
15608         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15609           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15610       }
15611     }
15612     else if (IS_BELT_SWITCH(element))
15613     {
15614       ToggleBeltSwitch(x, y);
15615     }
15616     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15617              element == EL_SWITCHGATE_SWITCH_DOWN ||
15618              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15619              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15620     {
15621       ToggleSwitchgateSwitch(x, y);
15622     }
15623     else if (element == EL_LIGHT_SWITCH ||
15624              element == EL_LIGHT_SWITCH_ACTIVE)
15625     {
15626       ToggleLightSwitch(x, y);
15627     }
15628     else if (element == EL_TIMEGATE_SWITCH ||
15629              element == EL_DC_TIMEGATE_SWITCH)
15630     {
15631       ActivateTimegateSwitch(x, y);
15632     }
15633     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15634              element == EL_BALLOON_SWITCH_RIGHT ||
15635              element == EL_BALLOON_SWITCH_UP    ||
15636              element == EL_BALLOON_SWITCH_DOWN  ||
15637              element == EL_BALLOON_SWITCH_NONE  ||
15638              element == EL_BALLOON_SWITCH_ANY)
15639     {
15640       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15641                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15642                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15643                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15644                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15645                              move_direction);
15646     }
15647     else if (element == EL_LAMP)
15648     {
15649       Feld[x][y] = EL_LAMP_ACTIVE;
15650       local_player->lights_still_needed--;
15651
15652       ResetGfxAnimation(x, y);
15653       TEST_DrawLevelField(x, y);
15654     }
15655     else if (element == EL_TIME_ORB_FULL)
15656     {
15657       Feld[x][y] = EL_TIME_ORB_EMPTY;
15658
15659       if (level.time > 0 || level.use_time_orb_bug)
15660       {
15661         TimeLeft += level.time_orb_time;
15662         game.no_time_limit = FALSE;
15663
15664 #if 1
15665         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15666
15667         DisplayGameControlValues();
15668 #else
15669         DrawGameValue_Time(TimeLeft);
15670 #endif
15671       }
15672
15673       ResetGfxAnimation(x, y);
15674       TEST_DrawLevelField(x, y);
15675     }
15676     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15677              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15678     {
15679       int xx, yy;
15680
15681       game.ball_state = !game.ball_state;
15682
15683       SCAN_PLAYFIELD(xx, yy)
15684       {
15685         int e = Feld[xx][yy];
15686
15687         if (game.ball_state)
15688         {
15689           if (e == EL_EMC_MAGIC_BALL)
15690             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15691           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15692             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15693         }
15694         else
15695         {
15696           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15697             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15698           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15699             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15700         }
15701       }
15702     }
15703
15704     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15705                                         player->index_bit, dig_side);
15706
15707     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15708                                         player->index_bit, dig_side);
15709
15710     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15711                                         player->index_bit, dig_side);
15712
15713     return MP_ACTION;
15714   }
15715   else
15716   {
15717     if (!PLAYER_SWITCHING(player, x, y))
15718     {
15719       player->is_switching = TRUE;
15720       player->switch_x = x;
15721       player->switch_y = y;
15722
15723       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15724                                  player->index_bit, dig_side);
15725       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15726                                           player->index_bit, dig_side);
15727
15728       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15729                                  player->index_bit, dig_side);
15730       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15731                                           player->index_bit, dig_side);
15732     }
15733
15734     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15735                                player->index_bit, dig_side);
15736     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15737                                         player->index_bit, dig_side);
15738
15739     return MP_NO_ACTION;
15740   }
15741
15742   player->push_delay = -1;
15743
15744   if (is_player)                /* function can also be called by EL_PENGUIN */
15745   {
15746     if (Feld[x][y] != element)          /* really digged/collected something */
15747     {
15748       player->is_collecting = !player->is_digging;
15749       player->is_active = TRUE;
15750     }
15751   }
15752
15753   return MP_MOVING;
15754 }
15755
15756 static boolean DigFieldByCE(int x, int y, int digging_element)
15757 {
15758   int element = Feld[x][y];
15759
15760   if (!IS_FREE(x, y))
15761   {
15762     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15763                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15764                   ACTION_BREAKING);
15765
15766     /* no element can dig solid indestructible elements */
15767     if (IS_INDESTRUCTIBLE(element) &&
15768         !IS_DIGGABLE(element) &&
15769         !IS_COLLECTIBLE(element))
15770       return FALSE;
15771
15772     if (AmoebaNr[x][y] &&
15773         (element == EL_AMOEBA_FULL ||
15774          element == EL_BD_AMOEBA ||
15775          element == EL_AMOEBA_GROWING))
15776     {
15777       AmoebaCnt[AmoebaNr[x][y]]--;
15778       AmoebaCnt2[AmoebaNr[x][y]]--;
15779     }
15780
15781     if (IS_MOVING(x, y))
15782       RemoveMovingField(x, y);
15783     else
15784     {
15785       RemoveField(x, y);
15786       TEST_DrawLevelField(x, y);
15787     }
15788
15789     /* if digged element was about to explode, prevent the explosion */
15790     ExplodeField[x][y] = EX_TYPE_NONE;
15791
15792     PlayLevelSoundAction(x, y, action);
15793   }
15794
15795   Store[x][y] = EL_EMPTY;
15796
15797 #if 1
15798   /* this makes it possible to leave the removed element again */
15799   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15800     Store[x][y] = element;
15801 #else
15802   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15803   {
15804     int move_leave_element = element_info[digging_element].move_leave_element;
15805
15806     /* this makes it possible to leave the removed element again */
15807     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15808                    element : move_leave_element);
15809   }
15810 #endif
15811
15812   return TRUE;
15813 }
15814
15815 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15816 {
15817   int jx = player->jx, jy = player->jy;
15818   int x = jx + dx, y = jy + dy;
15819   int snap_direction = (dx == -1 ? MV_LEFT  :
15820                         dx == +1 ? MV_RIGHT :
15821                         dy == -1 ? MV_UP    :
15822                         dy == +1 ? MV_DOWN  : MV_NONE);
15823   boolean can_continue_snapping = (level.continuous_snapping &&
15824                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15825
15826   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15827     return FALSE;
15828
15829   if (!player->active || !IN_LEV_FIELD(x, y))
15830     return FALSE;
15831
15832   if (dx && dy)
15833     return FALSE;
15834
15835   if (!dx && !dy)
15836   {
15837     if (player->MovPos == 0)
15838       player->is_pushing = FALSE;
15839
15840     player->is_snapping = FALSE;
15841
15842     if (player->MovPos == 0)
15843     {
15844       player->is_moving = FALSE;
15845       player->is_digging = FALSE;
15846       player->is_collecting = FALSE;
15847     }
15848
15849     return FALSE;
15850   }
15851
15852 #if USE_NEW_CONTINUOUS_SNAPPING
15853   /* prevent snapping with already pressed snap key when not allowed */
15854   if (player->is_snapping && !can_continue_snapping)
15855     return FALSE;
15856 #else
15857   if (player->is_snapping)
15858     return FALSE;
15859 #endif
15860
15861   player->MovDir = snap_direction;
15862
15863   if (player->MovPos == 0)
15864   {
15865     player->is_moving = FALSE;
15866     player->is_digging = FALSE;
15867     player->is_collecting = FALSE;
15868   }
15869
15870   player->is_dropping = FALSE;
15871   player->is_dropping_pressed = FALSE;
15872   player->drop_pressed_delay = 0;
15873
15874   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15875     return FALSE;
15876
15877   player->is_snapping = TRUE;
15878   player->is_active = TRUE;
15879
15880   if (player->MovPos == 0)
15881   {
15882     player->is_moving = FALSE;
15883     player->is_digging = FALSE;
15884     player->is_collecting = FALSE;
15885   }
15886
15887   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15888     TEST_DrawLevelField(player->last_jx, player->last_jy);
15889
15890   TEST_DrawLevelField(x, y);
15891
15892   return TRUE;
15893 }
15894
15895 static boolean DropElement(struct PlayerInfo *player)
15896 {
15897   int old_element, new_element;
15898   int dropx = player->jx, dropy = player->jy;
15899   int drop_direction = player->MovDir;
15900   int drop_side = drop_direction;
15901 #if 1
15902   int drop_element = get_next_dropped_element(player);
15903 #else
15904   int drop_element = (player->inventory_size > 0 ?
15905                       player->inventory_element[player->inventory_size - 1] :
15906                       player->inventory_infinite_element != EL_UNDEFINED ?
15907                       player->inventory_infinite_element :
15908                       player->dynabombs_left > 0 ?
15909                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15910                       EL_UNDEFINED);
15911 #endif
15912
15913   player->is_dropping_pressed = TRUE;
15914
15915   /* do not drop an element on top of another element; when holding drop key
15916      pressed without moving, dropped element must move away before the next
15917      element can be dropped (this is especially important if the next element
15918      is dynamite, which can be placed on background for historical reasons) */
15919   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15920     return MP_ACTION;
15921
15922   if (IS_THROWABLE(drop_element))
15923   {
15924     dropx += GET_DX_FROM_DIR(drop_direction);
15925     dropy += GET_DY_FROM_DIR(drop_direction);
15926
15927     if (!IN_LEV_FIELD(dropx, dropy))
15928       return FALSE;
15929   }
15930
15931   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15932   new_element = drop_element;           /* default: no change when dropping */
15933
15934   /* check if player is active, not moving and ready to drop */
15935   if (!player->active || player->MovPos || player->drop_delay > 0)
15936     return FALSE;
15937
15938   /* check if player has anything that can be dropped */
15939   if (new_element == EL_UNDEFINED)
15940     return FALSE;
15941
15942   /* check if drop key was pressed long enough for EM style dynamite */
15943   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15944     return FALSE;
15945
15946   /* check if anything can be dropped at the current position */
15947   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15948     return FALSE;
15949
15950   /* collected custom elements can only be dropped on empty fields */
15951   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15952     return FALSE;
15953
15954   if (old_element != EL_EMPTY)
15955     Back[dropx][dropy] = old_element;   /* store old element on this field */
15956
15957   ResetGfxAnimation(dropx, dropy);
15958   ResetRandomAnimationValue(dropx, dropy);
15959
15960   if (player->inventory_size > 0 ||
15961       player->inventory_infinite_element != EL_UNDEFINED)
15962   {
15963     if (player->inventory_size > 0)
15964     {
15965       player->inventory_size--;
15966
15967       DrawGameDoorValues();
15968
15969       if (new_element == EL_DYNAMITE)
15970         new_element = EL_DYNAMITE_ACTIVE;
15971       else if (new_element == EL_EM_DYNAMITE)
15972         new_element = EL_EM_DYNAMITE_ACTIVE;
15973       else if (new_element == EL_SP_DISK_RED)
15974         new_element = EL_SP_DISK_RED_ACTIVE;
15975     }
15976
15977     Feld[dropx][dropy] = new_element;
15978
15979     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15980       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15981                           el2img(Feld[dropx][dropy]), 0);
15982
15983     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15984
15985     /* needed if previous element just changed to "empty" in the last frame */
15986     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15987
15988     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15989                                player->index_bit, drop_side);
15990     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15991                                         CE_PLAYER_DROPS_X,
15992                                         player->index_bit, drop_side);
15993
15994     TestIfElementTouchesCustomElement(dropx, dropy);
15995   }
15996   else          /* player is dropping a dyna bomb */
15997   {
15998     player->dynabombs_left--;
15999
16000     Feld[dropx][dropy] = new_element;
16001
16002     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16003       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16004                           el2img(Feld[dropx][dropy]), 0);
16005
16006     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16007   }
16008
16009   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16010     InitField_WithBug1(dropx, dropy, FALSE);
16011
16012   new_element = Feld[dropx][dropy];     /* element might have changed */
16013
16014   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16015       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16016   {
16017 #if 0
16018     int move_direction;
16019     int nextx, nexty;
16020 #endif
16021
16022     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16023       MovDir[dropx][dropy] = drop_direction;
16024
16025 #if 0
16026     move_direction = MovDir[dropx][dropy];
16027     nextx = dropx + GET_DX_FROM_DIR(move_direction);
16028     nexty = dropy + GET_DY_FROM_DIR(move_direction);
16029 #endif
16030
16031     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16032
16033 #if USE_FIX_IMPACT_COLLISION
16034     /* do not cause impact style collision by dropping elements that can fall */
16035     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16036 #else
16037     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16038 #endif
16039   }
16040
16041   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16042   player->is_dropping = TRUE;
16043
16044   player->drop_pressed_delay = 0;
16045   player->is_dropping_pressed = FALSE;
16046
16047   player->drop_x = dropx;
16048   player->drop_y = dropy;
16049
16050   return TRUE;
16051 }
16052
16053 /* ------------------------------------------------------------------------- */
16054 /* game sound playing functions                                              */
16055 /* ------------------------------------------------------------------------- */
16056
16057 static int *loop_sound_frame = NULL;
16058 static int *loop_sound_volume = NULL;
16059
16060 void InitPlayLevelSound()
16061 {
16062   int num_sounds = getSoundListSize();
16063
16064   checked_free(loop_sound_frame);
16065   checked_free(loop_sound_volume);
16066
16067   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16068   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16069 }
16070
16071 static void PlayLevelSound(int x, int y, int nr)
16072 {
16073   int sx = SCREENX(x), sy = SCREENY(y);
16074   int volume, stereo_position;
16075   int max_distance = 8;
16076   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16077
16078   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16079       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16080     return;
16081
16082   if (!IN_LEV_FIELD(x, y) ||
16083       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16084       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16085     return;
16086
16087   volume = SOUND_MAX_VOLUME;
16088
16089   if (!IN_SCR_FIELD(sx, sy))
16090   {
16091     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16092     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16093
16094     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16095   }
16096
16097   stereo_position = (SOUND_MAX_LEFT +
16098                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16099                      (SCR_FIELDX + 2 * max_distance));
16100
16101   if (IS_LOOP_SOUND(nr))
16102   {
16103     /* This assures that quieter loop sounds do not overwrite louder ones,
16104        while restarting sound volume comparison with each new game frame. */
16105
16106     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16107       return;
16108
16109     loop_sound_volume[nr] = volume;
16110     loop_sound_frame[nr] = FrameCounter;
16111   }
16112
16113   PlaySoundExt(nr, volume, stereo_position, type);
16114 }
16115
16116 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16117 {
16118   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16119                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16120                  y < LEVELY(BY1) ? LEVELY(BY1) :
16121                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16122                  sound_action);
16123 }
16124
16125 static void PlayLevelSoundAction(int x, int y, int action)
16126 {
16127   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16128 }
16129
16130 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16131 {
16132   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16133
16134   if (sound_effect != SND_UNDEFINED)
16135     PlayLevelSound(x, y, sound_effect);
16136 }
16137
16138 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16139                                               int action)
16140 {
16141   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16142
16143   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16144     PlayLevelSound(x, y, sound_effect);
16145 }
16146
16147 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16148 {
16149   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16150
16151   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16152     PlayLevelSound(x, y, sound_effect);
16153 }
16154
16155 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16156 {
16157   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16158
16159   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16160     StopSound(sound_effect);
16161 }
16162
16163 static void PlayLevelMusic()
16164 {
16165   if (levelset.music[level_nr] != MUS_UNDEFINED)
16166     PlayMusic(levelset.music[level_nr]);        /* from config file */
16167   else
16168     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16169 }
16170
16171 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16172 {
16173   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16174   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16175   int x = xx - 1 - offset;
16176   int y = yy - 1 - offset;
16177
16178   switch (sample)
16179   {
16180     case SAMPLE_blank:
16181       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16182       break;
16183
16184     case SAMPLE_roll:
16185       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16186       break;
16187
16188     case SAMPLE_stone:
16189       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16190       break;
16191
16192     case SAMPLE_nut:
16193       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16194       break;
16195
16196     case SAMPLE_crack:
16197       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16198       break;
16199
16200     case SAMPLE_bug:
16201       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16202       break;
16203
16204     case SAMPLE_tank:
16205       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16206       break;
16207
16208     case SAMPLE_android_clone:
16209       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16210       break;
16211
16212     case SAMPLE_android_move:
16213       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16214       break;
16215
16216     case SAMPLE_spring:
16217       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16218       break;
16219
16220     case SAMPLE_slurp:
16221       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16222       break;
16223
16224     case SAMPLE_eater:
16225       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16226       break;
16227
16228     case SAMPLE_eater_eat:
16229       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16230       break;
16231
16232     case SAMPLE_alien:
16233       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16234       break;
16235
16236     case SAMPLE_collect:
16237       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16238       break;
16239
16240     case SAMPLE_diamond:
16241       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16242       break;
16243
16244     case SAMPLE_squash:
16245       /* !!! CHECK THIS !!! */
16246 #if 1
16247       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16248 #else
16249       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16250 #endif
16251       break;
16252
16253     case SAMPLE_wonderfall:
16254       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16255       break;
16256
16257     case SAMPLE_drip:
16258       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16259       break;
16260
16261     case SAMPLE_push:
16262       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16263       break;
16264
16265     case SAMPLE_dirt:
16266       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16267       break;
16268
16269     case SAMPLE_acid:
16270       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16271       break;
16272
16273     case SAMPLE_ball:
16274       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16275       break;
16276
16277     case SAMPLE_grow:
16278       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16279       break;
16280
16281     case SAMPLE_wonder:
16282       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16283       break;
16284
16285     case SAMPLE_door:
16286       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16287       break;
16288
16289     case SAMPLE_exit_open:
16290       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16291       break;
16292
16293     case SAMPLE_exit_leave:
16294       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16295       break;
16296
16297     case SAMPLE_dynamite:
16298       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16299       break;
16300
16301     case SAMPLE_tick:
16302       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16303       break;
16304
16305     case SAMPLE_press:
16306       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16307       break;
16308
16309     case SAMPLE_wheel:
16310       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16311       break;
16312
16313     case SAMPLE_boom:
16314       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16315       break;
16316
16317     case SAMPLE_die:
16318       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16319       break;
16320
16321     case SAMPLE_time:
16322       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16323       break;
16324
16325     default:
16326       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16327       break;
16328   }
16329 }
16330
16331 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16332 {
16333   int element = map_element_SP_to_RND(element_sp);
16334   int action = map_action_SP_to_RND(action_sp);
16335   int offset = (setup.sp_show_border_elements ? 0 : 1);
16336   int x = xx - offset;
16337   int y = yy - offset;
16338
16339 #if 0
16340   printf("::: %d -> %d\n", element_sp, action_sp);
16341 #endif
16342
16343   PlayLevelSoundElementAction(x, y, element, action);
16344 }
16345
16346 void RaiseScore(int value)
16347 {
16348   local_player->score += value;
16349
16350 #if 1
16351   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16352
16353   DisplayGameControlValues();
16354 #else
16355   DrawGameValue_Score(local_player->score);
16356 #endif
16357 }
16358
16359 void RaiseScoreElement(int element)
16360 {
16361   switch (element)
16362   {
16363     case EL_EMERALD:
16364     case EL_BD_DIAMOND:
16365     case EL_EMERALD_YELLOW:
16366     case EL_EMERALD_RED:
16367     case EL_EMERALD_PURPLE:
16368     case EL_SP_INFOTRON:
16369       RaiseScore(level.score[SC_EMERALD]);
16370       break;
16371     case EL_DIAMOND:
16372       RaiseScore(level.score[SC_DIAMOND]);
16373       break;
16374     case EL_CRYSTAL:
16375       RaiseScore(level.score[SC_CRYSTAL]);
16376       break;
16377     case EL_PEARL:
16378       RaiseScore(level.score[SC_PEARL]);
16379       break;
16380     case EL_BUG:
16381     case EL_BD_BUTTERFLY:
16382     case EL_SP_ELECTRON:
16383       RaiseScore(level.score[SC_BUG]);
16384       break;
16385     case EL_SPACESHIP:
16386     case EL_BD_FIREFLY:
16387     case EL_SP_SNIKSNAK:
16388       RaiseScore(level.score[SC_SPACESHIP]);
16389       break;
16390     case EL_YAMYAM:
16391     case EL_DARK_YAMYAM:
16392       RaiseScore(level.score[SC_YAMYAM]);
16393       break;
16394     case EL_ROBOT:
16395       RaiseScore(level.score[SC_ROBOT]);
16396       break;
16397     case EL_PACMAN:
16398       RaiseScore(level.score[SC_PACMAN]);
16399       break;
16400     case EL_NUT:
16401       RaiseScore(level.score[SC_NUT]);
16402       break;
16403     case EL_DYNAMITE:
16404     case EL_EM_DYNAMITE:
16405     case EL_SP_DISK_RED:
16406     case EL_DYNABOMB_INCREASE_NUMBER:
16407     case EL_DYNABOMB_INCREASE_SIZE:
16408     case EL_DYNABOMB_INCREASE_POWER:
16409       RaiseScore(level.score[SC_DYNAMITE]);
16410       break;
16411     case EL_SHIELD_NORMAL:
16412     case EL_SHIELD_DEADLY:
16413       RaiseScore(level.score[SC_SHIELD]);
16414       break;
16415     case EL_EXTRA_TIME:
16416       RaiseScore(level.extra_time_score);
16417       break;
16418     case EL_KEY_1:
16419     case EL_KEY_2:
16420     case EL_KEY_3:
16421     case EL_KEY_4:
16422     case EL_EM_KEY_1:
16423     case EL_EM_KEY_2:
16424     case EL_EM_KEY_3:
16425     case EL_EM_KEY_4:
16426     case EL_EMC_KEY_5:
16427     case EL_EMC_KEY_6:
16428     case EL_EMC_KEY_7:
16429     case EL_EMC_KEY_8:
16430     case EL_DC_KEY_WHITE:
16431       RaiseScore(level.score[SC_KEY]);
16432       break;
16433     default:
16434       RaiseScore(element_info[element].collect_score);
16435       break;
16436   }
16437 }
16438
16439 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16440 {
16441   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16442   {
16443 #if defined(NETWORK_AVALIABLE)
16444     if (options.network)
16445       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16446     else
16447 #endif
16448     {
16449       if (quick_quit)
16450       {
16451 #if 1
16452
16453 #if 1
16454         FadeSkipNextFadeIn();
16455 #else
16456         fading = fading_none;
16457 #endif
16458
16459 #else
16460         OpenDoor(DOOR_CLOSE_1);
16461 #endif
16462
16463         game_status = GAME_MODE_MAIN;
16464
16465 #if 1
16466         DrawAndFadeInMainMenu(REDRAW_FIELD);
16467 #else
16468         DrawMainMenu();
16469 #endif
16470       }
16471       else
16472       {
16473 #if 0
16474         FadeOut(REDRAW_FIELD);
16475 #endif
16476
16477         game_status = GAME_MODE_MAIN;
16478
16479         DrawAndFadeInMainMenu(REDRAW_FIELD);
16480       }
16481     }
16482   }
16483   else          /* continue playing the game */
16484   {
16485     if (tape.playing && tape.deactivate_display)
16486       TapeDeactivateDisplayOff(TRUE);
16487
16488     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16489
16490     if (tape.playing && tape.deactivate_display)
16491       TapeDeactivateDisplayOn();
16492   }
16493 }
16494
16495 void RequestQuitGame(boolean ask_if_really_quit)
16496 {
16497   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16498   boolean skip_request = AllPlayersGone || quick_quit;
16499
16500   RequestQuitGameExt(skip_request, quick_quit,
16501                      "Do you really want to quit the game?");
16502 }
16503
16504
16505 /* ------------------------------------------------------------------------- */
16506 /* random generator functions                                                */
16507 /* ------------------------------------------------------------------------- */
16508
16509 unsigned int InitEngineRandom_RND(int seed)
16510 {
16511   game.num_random_calls = 0;
16512
16513 #if 0
16514   unsigned int rnd_seed = InitEngineRandom(seed);
16515
16516   printf("::: START RND: %d\n", rnd_seed);
16517
16518   return rnd_seed;
16519 #else
16520
16521   return InitEngineRandom(seed);
16522
16523 #endif
16524
16525 }
16526
16527 unsigned int RND(int max)
16528 {
16529   if (max > 0)
16530   {
16531     game.num_random_calls++;
16532
16533     return GetEngineRandom(max);
16534   }
16535
16536   return 0;
16537 }
16538
16539
16540 /* ------------------------------------------------------------------------- */
16541 /* game engine snapshot handling functions                                   */
16542 /* ------------------------------------------------------------------------- */
16543
16544 struct EngineSnapshotInfo
16545 {
16546   /* runtime values for custom element collect score */
16547   int collect_score[NUM_CUSTOM_ELEMENTS];
16548
16549   /* runtime values for group element choice position */
16550   int choice_pos[NUM_GROUP_ELEMENTS];
16551
16552   /* runtime values for belt position animations */
16553   int belt_graphic[4][NUM_BELT_PARTS];
16554   int belt_anim_mode[4][NUM_BELT_PARTS];
16555 };
16556
16557 static struct EngineSnapshotInfo engine_snapshot_rnd;
16558 static char *snapshot_level_identifier = NULL;
16559 static int snapshot_level_nr = -1;
16560
16561 static void SaveEngineSnapshotValues_RND()
16562 {
16563   static int belt_base_active_element[4] =
16564   {
16565     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16566     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16567     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16568     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16569   };
16570   int i, j;
16571
16572   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16573   {
16574     int element = EL_CUSTOM_START + i;
16575
16576     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16577   }
16578
16579   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16580   {
16581     int element = EL_GROUP_START + i;
16582
16583     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16584   }
16585
16586   for (i = 0; i < 4; i++)
16587   {
16588     for (j = 0; j < NUM_BELT_PARTS; j++)
16589     {
16590       int element = belt_base_active_element[i] + j;
16591       int graphic = el2img(element);
16592       int anim_mode = graphic_info[graphic].anim_mode;
16593
16594       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16595       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16596     }
16597   }
16598 }
16599
16600 static void LoadEngineSnapshotValues_RND()
16601 {
16602   unsigned int num_random_calls = game.num_random_calls;
16603   int i, j;
16604
16605   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16606   {
16607     int element = EL_CUSTOM_START + i;
16608
16609     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16610   }
16611
16612   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16613   {
16614     int element = EL_GROUP_START + i;
16615
16616     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16617   }
16618
16619   for (i = 0; i < 4; i++)
16620   {
16621     for (j = 0; j < NUM_BELT_PARTS; j++)
16622     {
16623       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16624       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16625
16626       graphic_info[graphic].anim_mode = anim_mode;
16627     }
16628   }
16629
16630   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16631   {
16632     InitRND(tape.random_seed);
16633     for (i = 0; i < num_random_calls; i++)
16634       RND(1);
16635   }
16636
16637   if (game.num_random_calls != num_random_calls)
16638   {
16639     Error(ERR_INFO, "number of random calls out of sync");
16640     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16641     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16642     Error(ERR_EXIT, "this should not happen -- please debug");
16643   }
16644 }
16645
16646 void SaveEngineSnapshot()
16647 {
16648   /* do not save snapshots from editor */
16649   if (level_editor_test_game)
16650     return;
16651
16652   /* free previous snapshot buffers, if needed */
16653   FreeEngineSnapshotBuffers();
16654
16655   /* copy some special values to a structure better suited for the snapshot */
16656
16657   SaveEngineSnapshotValues_RND();
16658   SaveEngineSnapshotValues_EM();
16659   SaveEngineSnapshotValues_SP();
16660
16661   /* save values stored in special snapshot structure */
16662
16663   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16664   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16665   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16666
16667   /* save further RND engine values */
16668
16669   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16670   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16671   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16672
16673   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16674   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16675   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16676   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16677
16678   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16679   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16680   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16681   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16682   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16683
16684   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16685   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16686   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16687
16688   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16689
16690   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16691
16692   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16693   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16694
16695   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16696   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16697   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16698   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16699   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16700   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16701   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16702   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16703   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16704   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16705   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16706   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16707   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16708   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16709   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16710   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16711   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16712   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16713
16714   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16715   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16716
16717   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16718   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16719   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16720
16721   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16722   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16723
16724   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16725   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16726   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16727   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16728   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16729
16730   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16731   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16732
16733   /* save level identification information */
16734
16735   setString(&snapshot_level_identifier, leveldir_current->identifier);
16736   snapshot_level_nr = level_nr;
16737
16738 #if 0
16739   ListNode *node = engine_snapshot_list_rnd;
16740   int num_bytes = 0;
16741
16742   while (node != NULL)
16743   {
16744     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16745
16746     node = node->next;
16747   }
16748
16749   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16750 #endif
16751 }
16752
16753 void LoadEngineSnapshot()
16754 {
16755   /* restore generically stored snapshot buffers */
16756
16757   LoadEngineSnapshotBuffers();
16758
16759   /* restore special values from snapshot structure */
16760
16761   LoadEngineSnapshotValues_RND();
16762   LoadEngineSnapshotValues_EM();
16763   LoadEngineSnapshotValues_SP();
16764 }
16765
16766 boolean CheckEngineSnapshot()
16767 {
16768   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16769           snapshot_level_nr == level_nr);
16770 }
16771
16772
16773 /* ---------- new game button stuff ---------------------------------------- */
16774
16775 static struct
16776 {
16777   int graphic;
16778   struct Rect *pos;
16779   int gadget_id;
16780   char *infotext;
16781 } gamebutton_info[NUM_GAME_BUTTONS] =
16782 {
16783   {
16784     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16785     GAME_CTRL_ID_STOP,                  "stop game"
16786   },
16787   {
16788     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16789     GAME_CTRL_ID_PAUSE,                 "pause game"
16790   },
16791   {
16792     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16793     GAME_CTRL_ID_PLAY,                  "play game"
16794   },
16795   {
16796     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16797     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16798   },
16799   {
16800     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16801     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16802   },
16803   {
16804     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16805     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16806   }
16807 };
16808
16809 void CreateGameButtons()
16810 {
16811   int i;
16812
16813   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16814   {
16815     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16816     struct Rect *pos = gamebutton_info[i].pos;
16817     struct GadgetInfo *gi;
16818     int button_type;
16819     boolean checked;
16820     unsigned int event_mask;
16821     int gd_x   = gfx->src_x;
16822     int gd_y   = gfx->src_y;
16823     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16824     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16825     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16826     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16827     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16828     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16829     int id = i;
16830
16831     if (id == GAME_CTRL_ID_STOP ||
16832         id == GAME_CTRL_ID_PAUSE ||
16833         id == GAME_CTRL_ID_PLAY)
16834     {
16835       button_type = GD_TYPE_NORMAL_BUTTON;
16836       checked = FALSE;
16837       event_mask = GD_EVENT_RELEASED;
16838     }
16839     else
16840     {
16841       button_type = GD_TYPE_CHECK_BUTTON;
16842       checked =
16843         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16844          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16845          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16846       event_mask = GD_EVENT_PRESSED;
16847     }
16848
16849     gi = CreateGadget(GDI_CUSTOM_ID, id,
16850                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16851                       GDI_X, DX + pos->x,
16852                       GDI_Y, DY + pos->y,
16853                       GDI_WIDTH, gfx->width,
16854                       GDI_HEIGHT, gfx->height,
16855                       GDI_TYPE, button_type,
16856                       GDI_STATE, GD_BUTTON_UNPRESSED,
16857                       GDI_CHECKED, checked,
16858                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16859                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16860                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16861                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16862                       GDI_DIRECT_DRAW, FALSE,
16863                       GDI_EVENT_MASK, event_mask,
16864                       GDI_CALLBACK_ACTION, HandleGameButtons,
16865                       GDI_END);
16866
16867     if (gi == NULL)
16868       Error(ERR_EXIT, "cannot create gadget");
16869
16870     game_gadget[id] = gi;
16871   }
16872 }
16873
16874 void FreeGameButtons()
16875 {
16876   int i;
16877
16878   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16879     FreeGadget(game_gadget[i]);
16880 }
16881
16882 static void MapGameButtons()
16883 {
16884   int i;
16885
16886   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16887     MapGadget(game_gadget[i]);
16888 }
16889
16890 void UnmapGameButtons()
16891 {
16892   int i;
16893
16894   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16895     UnmapGadget(game_gadget[i]);
16896 }
16897
16898 void RedrawGameButtons()
16899 {
16900   int i;
16901
16902   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16903     RedrawGadget(game_gadget[i]);
16904 }
16905
16906 static void HandleGameButtonsExt(int id)
16907 {
16908   if (game_status != GAME_MODE_PLAYING)
16909     return;
16910
16911   switch (id)
16912   {
16913     case GAME_CTRL_ID_STOP:
16914       if (tape.playing)
16915         TapeStop();
16916       else
16917         RequestQuitGame(TRUE);
16918       break;
16919
16920     case GAME_CTRL_ID_PAUSE:
16921       if (options.network)
16922       {
16923 #if defined(NETWORK_AVALIABLE)
16924         if (tape.pausing)
16925           SendToServer_ContinuePlaying();
16926         else
16927           SendToServer_PausePlaying();
16928 #endif
16929       }
16930       else
16931         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16932       break;
16933
16934     case GAME_CTRL_ID_PLAY:
16935       if (tape.pausing)
16936       {
16937 #if defined(NETWORK_AVALIABLE)
16938         if (options.network)
16939           SendToServer_ContinuePlaying();
16940         else
16941 #endif
16942         {
16943           tape.pausing = FALSE;
16944           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16945         }
16946       }
16947       break;
16948
16949     case SOUND_CTRL_ID_MUSIC:
16950       if (setup.sound_music)
16951       { 
16952         setup.sound_music = FALSE;
16953
16954         FadeMusic();
16955       }
16956       else if (audio.music_available)
16957       { 
16958         setup.sound = setup.sound_music = TRUE;
16959
16960         SetAudioMode(setup.sound);
16961
16962         PlayLevelMusic();
16963       }
16964       break;
16965
16966     case SOUND_CTRL_ID_LOOPS:
16967       if (setup.sound_loops)
16968         setup.sound_loops = FALSE;
16969       else if (audio.loops_available)
16970       {
16971         setup.sound = setup.sound_loops = TRUE;
16972
16973         SetAudioMode(setup.sound);
16974       }
16975       break;
16976
16977     case SOUND_CTRL_ID_SIMPLE:
16978       if (setup.sound_simple)
16979         setup.sound_simple = FALSE;
16980       else if (audio.sound_available)
16981       {
16982         setup.sound = setup.sound_simple = TRUE;
16983
16984         SetAudioMode(setup.sound);
16985       }
16986       break;
16987
16988     default:
16989       break;
16990   }
16991 }
16992
16993 static void HandleGameButtons(struct GadgetInfo *gi)
16994 {
16995   HandleGameButtonsExt(gi->custom_id);
16996 }
16997
16998 void HandleSoundButtonKeys(Key key)
16999 {
17000 #if 1
17001   if (key == setup.shortcut.sound_simple)
17002     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17003   else if (key == setup.shortcut.sound_loops)
17004     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17005   else if (key == setup.shortcut.sound_music)
17006     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17007 #else
17008   if (key == setup.shortcut.sound_simple)
17009     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17010   else if (key == setup.shortcut.sound_loops)
17011     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17012   else if (key == setup.shortcut.sound_music)
17013     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17014 #endif
17015 }