rnd-20130916-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
63
64 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
65
66 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
67
68 #define USE_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
69
70 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
71
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y)                               \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
78         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y)                           \
80         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
81 #else
82 #define TEST_DrawLevelField(x, y)                               \
83              DrawLevelField(x, y)
84 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
85              DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
87              DrawLevelFieldCrumbledNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y)                           \
89              DrawTwinkleOnField(x, y)
90 #endif
91
92
93 /* for DigField() */
94 #define DF_NO_PUSH              0
95 #define DF_DIG                  1
96 #define DF_SNAP                 2
97
98 /* for MovePlayer() */
99 #define MP_NO_ACTION            0
100 #define MP_MOVING               1
101 #define MP_ACTION               2
102 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
103
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT             0
106 #define SCROLL_GO_ON            1
107
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START          0
110 #define EX_TYPE_NONE            0
111 #define EX_TYPE_NORMAL          (1 << 0)
112 #define EX_TYPE_CENTER          (1 << 1)
113 #define EX_TYPE_BORDER          (1 << 2)
114 #define EX_TYPE_CROSS           (1 << 3)
115 #define EX_TYPE_DYNA            (1 << 4)
116 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
117
118 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
122
123 /* game panel display and control definitions */
124 #define GAME_PANEL_LEVEL_NUMBER                 0
125 #define GAME_PANEL_GEMS                         1
126 #define GAME_PANEL_INVENTORY_COUNT              2
127 #define GAME_PANEL_INVENTORY_FIRST_1            3
128 #define GAME_PANEL_INVENTORY_FIRST_2            4
129 #define GAME_PANEL_INVENTORY_FIRST_3            5
130 #define GAME_PANEL_INVENTORY_FIRST_4            6
131 #define GAME_PANEL_INVENTORY_FIRST_5            7
132 #define GAME_PANEL_INVENTORY_FIRST_6            8
133 #define GAME_PANEL_INVENTORY_FIRST_7            9
134 #define GAME_PANEL_INVENTORY_FIRST_8            10
135 #define GAME_PANEL_INVENTORY_LAST_1             11
136 #define GAME_PANEL_INVENTORY_LAST_2             12
137 #define GAME_PANEL_INVENTORY_LAST_3             13
138 #define GAME_PANEL_INVENTORY_LAST_4             14
139 #define GAME_PANEL_INVENTORY_LAST_5             15
140 #define GAME_PANEL_INVENTORY_LAST_6             16
141 #define GAME_PANEL_INVENTORY_LAST_7             17
142 #define GAME_PANEL_INVENTORY_LAST_8             18
143 #define GAME_PANEL_KEY_1                        19
144 #define GAME_PANEL_KEY_2                        20
145 #define GAME_PANEL_KEY_3                        21
146 #define GAME_PANEL_KEY_4                        22
147 #define GAME_PANEL_KEY_5                        23
148 #define GAME_PANEL_KEY_6                        24
149 #define GAME_PANEL_KEY_7                        25
150 #define GAME_PANEL_KEY_8                        26
151 #define GAME_PANEL_KEY_WHITE                    27
152 #define GAME_PANEL_KEY_WHITE_COUNT              28
153 #define GAME_PANEL_SCORE                        29
154 #define GAME_PANEL_HIGHSCORE                    30
155 #define GAME_PANEL_TIME                         31
156 #define GAME_PANEL_TIME_HH                      32
157 #define GAME_PANEL_TIME_MM                      33
158 #define GAME_PANEL_TIME_SS                      34
159 #define GAME_PANEL_FRAME                        35
160 #define GAME_PANEL_SHIELD_NORMAL                36
161 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
162 #define GAME_PANEL_SHIELD_DEADLY                38
163 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
164 #define GAME_PANEL_EXIT                         40
165 #define GAME_PANEL_EMC_MAGIC_BALL               41
166 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
167 #define GAME_PANEL_LIGHT_SWITCH                 43
168 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
169 #define GAME_PANEL_TIMEGATE_SWITCH              45
170 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
171 #define GAME_PANEL_SWITCHGATE_SWITCH            47
172 #define GAME_PANEL_EMC_LENSES                   48
173 #define GAME_PANEL_EMC_LENSES_TIME              49
174 #define GAME_PANEL_EMC_MAGNIFIER                50
175 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
176 #define GAME_PANEL_BALLOON_SWITCH               52
177 #define GAME_PANEL_DYNABOMB_NUMBER              53
178 #define GAME_PANEL_DYNABOMB_SIZE                54
179 #define GAME_PANEL_DYNABOMB_POWER               55
180 #define GAME_PANEL_PENGUINS                     56
181 #define GAME_PANEL_SOKOBAN_OBJECTS              57
182 #define GAME_PANEL_SOKOBAN_FIELDS               58
183 #define GAME_PANEL_ROBOT_WHEEL                  59
184 #define GAME_PANEL_CONVEYOR_BELT_1              60
185 #define GAME_PANEL_CONVEYOR_BELT_2              61
186 #define GAME_PANEL_CONVEYOR_BELT_3              62
187 #define GAME_PANEL_CONVEYOR_BELT_4              63
188 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
189 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
190 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
191 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
192 #define GAME_PANEL_MAGIC_WALL                   68
193 #define GAME_PANEL_MAGIC_WALL_TIME              69
194 #define GAME_PANEL_GRAVITY_STATE                70
195 #define GAME_PANEL_GRAPHIC_1                    71
196 #define GAME_PANEL_GRAPHIC_2                    72
197 #define GAME_PANEL_GRAPHIC_3                    73
198 #define GAME_PANEL_GRAPHIC_4                    74
199 #define GAME_PANEL_GRAPHIC_5                    75
200 #define GAME_PANEL_GRAPHIC_6                    76
201 #define GAME_PANEL_GRAPHIC_7                    77
202 #define GAME_PANEL_GRAPHIC_8                    78
203 #define GAME_PANEL_ELEMENT_1                    79
204 #define GAME_PANEL_ELEMENT_2                    80
205 #define GAME_PANEL_ELEMENT_3                    81
206 #define GAME_PANEL_ELEMENT_4                    82
207 #define GAME_PANEL_ELEMENT_5                    83
208 #define GAME_PANEL_ELEMENT_6                    84
209 #define GAME_PANEL_ELEMENT_7                    85
210 #define GAME_PANEL_ELEMENT_8                    86
211 #define GAME_PANEL_ELEMENT_COUNT_1              87
212 #define GAME_PANEL_ELEMENT_COUNT_2              88
213 #define GAME_PANEL_ELEMENT_COUNT_3              89
214 #define GAME_PANEL_ELEMENT_COUNT_4              90
215 #define GAME_PANEL_ELEMENT_COUNT_5              91
216 #define GAME_PANEL_ELEMENT_COUNT_6              92
217 #define GAME_PANEL_ELEMENT_COUNT_7              93
218 #define GAME_PANEL_ELEMENT_COUNT_8              94
219 #define GAME_PANEL_CE_SCORE_1                   95
220 #define GAME_PANEL_CE_SCORE_2                   96
221 #define GAME_PANEL_CE_SCORE_3                   97
222 #define GAME_PANEL_CE_SCORE_4                   98
223 #define GAME_PANEL_CE_SCORE_5                   99
224 #define GAME_PANEL_CE_SCORE_6                   100
225 #define GAME_PANEL_CE_SCORE_7                   101
226 #define GAME_PANEL_CE_SCORE_8                   102
227 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
228 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
229 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
230 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
231 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
232 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
233 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
234 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
235 #define GAME_PANEL_PLAYER_NAME                  111
236 #define GAME_PANEL_LEVEL_NAME                   112
237 #define GAME_PANEL_LEVEL_AUTHOR                 113
238
239 #define NUM_GAME_PANEL_CONTROLS                 114
240
241 struct GamePanelOrderInfo
242 {
243   int nr;
244   int sort_priority;
245 };
246
247 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
248
249 struct GamePanelControlInfo
250 {
251   int nr;
252
253   struct TextPosInfo *pos;
254   int type;
255
256   int value, last_value;
257   int frame, last_frame;
258   int gfx_frame;
259   int gfx_random;
260 };
261
262 static struct GamePanelControlInfo game_panel_controls[] =
263 {
264   {
265     GAME_PANEL_LEVEL_NUMBER,
266     &game.panel.level_number,
267     TYPE_INTEGER,
268   },
269   {
270     GAME_PANEL_GEMS,
271     &game.panel.gems,
272     TYPE_INTEGER,
273   },
274   {
275     GAME_PANEL_INVENTORY_COUNT,
276     &game.panel.inventory_count,
277     TYPE_INTEGER,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_1,
281     &game.panel.inventory_first[0],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_FIRST_2,
286     &game.panel.inventory_first[1],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_FIRST_3,
291     &game.panel.inventory_first[2],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_FIRST_4,
296     &game.panel.inventory_first[3],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_FIRST_5,
301     &game.panel.inventory_first[4],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_FIRST_6,
306     &game.panel.inventory_first[5],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_FIRST_7,
311     &game.panel.inventory_first[6],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_FIRST_8,
316     &game.panel.inventory_first[7],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_1,
321     &game.panel.inventory_last[0],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_INVENTORY_LAST_2,
326     &game.panel.inventory_last[1],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_INVENTORY_LAST_3,
331     &game.panel.inventory_last[2],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_INVENTORY_LAST_4,
336     &game.panel.inventory_last[3],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_INVENTORY_LAST_5,
341     &game.panel.inventory_last[4],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_INVENTORY_LAST_6,
346     &game.panel.inventory_last[5],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_INVENTORY_LAST_7,
351     &game.panel.inventory_last[6],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_INVENTORY_LAST_8,
356     &game.panel.inventory_last[7],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_1,
361     &game.panel.key[0],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_2,
366     &game.panel.key[1],
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_3,
371     &game.panel.key[2],
372     TYPE_ELEMENT,
373   },
374   {
375     GAME_PANEL_KEY_4,
376     &game.panel.key[3],
377     TYPE_ELEMENT,
378   },
379   {
380     GAME_PANEL_KEY_5,
381     &game.panel.key[4],
382     TYPE_ELEMENT,
383   },
384   {
385     GAME_PANEL_KEY_6,
386     &game.panel.key[5],
387     TYPE_ELEMENT,
388   },
389   {
390     GAME_PANEL_KEY_7,
391     &game.panel.key[6],
392     TYPE_ELEMENT,
393   },
394   {
395     GAME_PANEL_KEY_8,
396     &game.panel.key[7],
397     TYPE_ELEMENT,
398   },
399   {
400     GAME_PANEL_KEY_WHITE,
401     &game.panel.key_white,
402     TYPE_ELEMENT,
403   },
404   {
405     GAME_PANEL_KEY_WHITE_COUNT,
406     &game.panel.key_white_count,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SCORE,
411     &game.panel.score,
412     TYPE_INTEGER,
413   },
414   {
415     GAME_PANEL_HIGHSCORE,
416     &game.panel.highscore,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_TIME,
421     &game.panel.time,
422     TYPE_INTEGER,
423   },
424   {
425     GAME_PANEL_TIME_HH,
426     &game.panel.time_hh,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_TIME_MM,
431     &game.panel.time_mm,
432     TYPE_INTEGER,
433   },
434   {
435     GAME_PANEL_TIME_SS,
436     &game.panel.time_ss,
437     TYPE_INTEGER,
438   },
439   {
440     GAME_PANEL_FRAME,
441     &game.panel.frame,
442     TYPE_INTEGER,
443   },
444   {
445     GAME_PANEL_SHIELD_NORMAL,
446     &game.panel.shield_normal,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_SHIELD_NORMAL_TIME,
451     &game.panel.shield_normal_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_SHIELD_DEADLY,
456     &game.panel.shield_deadly,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_SHIELD_DEADLY_TIME,
461     &game.panel.shield_deadly_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_EXIT,
466     &game.panel.exit,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_MAGIC_BALL,
471     &game.panel.emc_magic_ball,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
476     &game.panel.emc_magic_ball_switch,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_LIGHT_SWITCH,
481     &game.panel.light_switch,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_LIGHT_SWITCH_TIME,
486     &game.panel.light_switch_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_TIMEGATE_SWITCH,
491     &game.panel.timegate_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_TIMEGATE_SWITCH_TIME,
496     &game.panel.timegate_switch_time,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_SWITCHGATE_SWITCH,
501     &game.panel.switchgate_switch,
502     TYPE_ELEMENT,
503   },
504   {
505     GAME_PANEL_EMC_LENSES,
506     &game.panel.emc_lenses,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_EMC_LENSES_TIME,
511     &game.panel.emc_lenses_time,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_EMC_MAGNIFIER,
516     &game.panel.emc_magnifier,
517     TYPE_ELEMENT,
518   },
519   {
520     GAME_PANEL_EMC_MAGNIFIER_TIME,
521     &game.panel.emc_magnifier_time,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_BALLOON_SWITCH,
526     &game.panel.balloon_switch,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_DYNABOMB_NUMBER,
531     &game.panel.dynabomb_number,
532     TYPE_INTEGER,
533   },
534   {
535     GAME_PANEL_DYNABOMB_SIZE,
536     &game.panel.dynabomb_size,
537     TYPE_INTEGER,
538   },
539   {
540     GAME_PANEL_DYNABOMB_POWER,
541     &game.panel.dynabomb_power,
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_PENGUINS,
546     &game.panel.penguins,
547     TYPE_INTEGER,
548   },
549   {
550     GAME_PANEL_SOKOBAN_OBJECTS,
551     &game.panel.sokoban_objects,
552     TYPE_INTEGER,
553   },
554   {
555     GAME_PANEL_SOKOBAN_FIELDS,
556     &game.panel.sokoban_fields,
557     TYPE_INTEGER,
558   },
559   {
560     GAME_PANEL_ROBOT_WHEEL,
561     &game.panel.robot_wheel,
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_1,
566     &game.panel.conveyor_belt[0],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_CONVEYOR_BELT_2,
571     &game.panel.conveyor_belt[1],
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_CONVEYOR_BELT_3,
576     &game.panel.conveyor_belt[2],
577     TYPE_ELEMENT,
578   },
579   {
580     GAME_PANEL_CONVEYOR_BELT_4,
581     &game.panel.conveyor_belt[3],
582     TYPE_ELEMENT,
583   },
584   {
585     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
586     &game.panel.conveyor_belt_switch[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
591     &game.panel.conveyor_belt_switch[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
596     &game.panel.conveyor_belt_switch[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
601     &game.panel.conveyor_belt_switch[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_MAGIC_WALL,
606     &game.panel.magic_wall,
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_MAGIC_WALL_TIME,
611     &game.panel.magic_wall_time,
612     TYPE_INTEGER,
613   },
614   {
615     GAME_PANEL_GRAVITY_STATE,
616     &game.panel.gravity_state,
617     TYPE_STRING,
618   },
619   {
620     GAME_PANEL_GRAPHIC_1,
621     &game.panel.graphic[0],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_GRAPHIC_2,
626     &game.panel.graphic[1],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_GRAPHIC_3,
631     &game.panel.graphic[2],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_GRAPHIC_4,
636     &game.panel.graphic[3],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_GRAPHIC_5,
641     &game.panel.graphic[4],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_GRAPHIC_6,
646     &game.panel.graphic[5],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_GRAPHIC_7,
651     &game.panel.graphic[6],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_8,
656     &game.panel.graphic[7],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_1,
661     &game.panel.element[0],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_2,
666     &game.panel.element[1],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_ELEMENT_3,
671     &game.panel.element[2],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_ELEMENT_4,
676     &game.panel.element[3],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_ELEMENT_5,
681     &game.panel.element[4],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_ELEMENT_6,
686     &game.panel.element[5],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_7,
691     &game.panel.element[6],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_8,
696     &game.panel.element[7],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_1,
701     &game.panel.element_count[0],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_ELEMENT_COUNT_2,
706     &game.panel.element_count[1],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_ELEMENT_COUNT_3,
711     &game.panel.element_count[2],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_ELEMENT_COUNT_4,
716     &game.panel.element_count[3],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_ELEMENT_COUNT_5,
721     &game.panel.element_count[4],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_ELEMENT_COUNT_6,
726     &game.panel.element_count[5],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_7,
731     &game.panel.element_count[6],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_8,
736     &game.panel.element_count[7],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_1,
741     &game.panel.ce_score[0],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_2,
746     &game.panel.ce_score[1],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_CE_SCORE_3,
751     &game.panel.ce_score[2],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_CE_SCORE_4,
756     &game.panel.ce_score[3],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_CE_SCORE_5,
761     &game.panel.ce_score[4],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_CE_SCORE_6,
766     &game.panel.ce_score[5],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_7,
771     &game.panel.ce_score[6],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_8,
776     &game.panel.ce_score[7],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_1_ELEMENT,
781     &game.panel.ce_score_element[0],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_CE_SCORE_2_ELEMENT,
786     &game.panel.ce_score_element[1],
787     TYPE_ELEMENT,
788   },
789   {
790     GAME_PANEL_CE_SCORE_3_ELEMENT,
791     &game.panel.ce_score_element[2],
792     TYPE_ELEMENT,
793   },
794   {
795     GAME_PANEL_CE_SCORE_4_ELEMENT,
796     &game.panel.ce_score_element[3],
797     TYPE_ELEMENT,
798   },
799   {
800     GAME_PANEL_CE_SCORE_5_ELEMENT,
801     &game.panel.ce_score_element[4],
802     TYPE_ELEMENT,
803   },
804   {
805     GAME_PANEL_CE_SCORE_6_ELEMENT,
806     &game.panel.ce_score_element[5],
807     TYPE_ELEMENT,
808   },
809   {
810     GAME_PANEL_CE_SCORE_7_ELEMENT,
811     &game.panel.ce_score_element[6],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_8_ELEMENT,
816     &game.panel.ce_score_element[7],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_PLAYER_NAME,
821     &game.panel.player_name,
822     TYPE_STRING,
823   },
824   {
825     GAME_PANEL_LEVEL_NAME,
826     &game.panel.level_name,
827     TYPE_STRING,
828   },
829   {
830     GAME_PANEL_LEVEL_AUTHOR,
831     &game.panel.level_author,
832     TYPE_STRING,
833   },
834
835   {
836     -1,
837     NULL,
838     -1,
839   }
840 };
841
842 /* values for delayed check of falling and moving elements and for collision */
843 #define CHECK_DELAY_MOVING      3
844 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
845 #define CHECK_DELAY_COLLISION   2
846 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
847
848 /* values for initial player move delay (initial delay counter value) */
849 #define INITIAL_MOVE_DELAY_OFF  -1
850 #define INITIAL_MOVE_DELAY_ON   0
851
852 /* values for player movement speed (which is in fact a delay value) */
853 #define MOVE_DELAY_MIN_SPEED    32
854 #define MOVE_DELAY_NORMAL_SPEED 8
855 #define MOVE_DELAY_HIGH_SPEED   4
856 #define MOVE_DELAY_MAX_SPEED    1
857
858 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
859 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
860
861 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
862 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP               0
1007 #define GAME_CTRL_ID_PAUSE              1
1008 #define GAME_CTRL_ID_PLAY               2
1009 #define SOUND_CTRL_ID_MUSIC             3
1010 #define SOUND_CTRL_ID_LOOPS             4
1011 #define SOUND_CTRL_ID_SIMPLE            5
1012
1013 #define NUM_GAME_BUTTONS                6
1014
1015
1016 /* forward declaration for internal use */
1017
1018 static void CreateField(int, int, int);
1019
1020 static void ResetGfxAnimation(int, int);
1021
1022 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1023 static void AdvanceFrameAndPlayerCounters(int);
1024
1025 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1026 static boolean MovePlayer(struct PlayerInfo *, int, int);
1027 static void ScrollPlayer(struct PlayerInfo *, int);
1028 static void ScrollScreen(struct PlayerInfo *, int);
1029
1030 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1031 static boolean DigFieldByCE(int, int, int);
1032 static boolean SnapField(struct PlayerInfo *, int, int);
1033 static boolean DropElement(struct PlayerInfo *);
1034
1035 static void InitBeltMovement(void);
1036 static void CloseAllOpenTimegates(void);
1037 static void CheckGravityMovement(struct PlayerInfo *);
1038 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1039 static void KillPlayerUnlessEnemyProtected(int, int);
1040 static void KillPlayerUnlessExplosionProtected(int, int);
1041
1042 static void TestIfPlayerTouchesCustomElement(int, int);
1043 static void TestIfElementTouchesCustomElement(int, int);
1044 static void TestIfElementHitsCustomElement(int, int, int);
1045 #if 0
1046 static void TestIfElementSmashesCustomElement(int, int, int);
1047 #endif
1048
1049 static void HandleElementChange(int, int, int);
1050 static void ExecuteCustomElementAction(int, int, int, int);
1051 static boolean ChangeElement(int, int, int, int);
1052
1053 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1054 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1055         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1056 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1057         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1058 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1059         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1060 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1061         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1062
1063 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1064 #define CheckElementChange(x, y, e, te, ev)                             \
1065         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1066 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1067         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1068 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1069         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1070
1071 static void PlayLevelSound(int, int, int);
1072 static void PlayLevelSoundNearest(int, int, int);
1073 static void PlayLevelSoundAction(int, int, int);
1074 static void PlayLevelSoundElementAction(int, int, int, int);
1075 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1076 static void PlayLevelSoundActionIfLoop(int, int, int);
1077 static void StopLevelSoundActionIfLoop(int, int, int);
1078 static void PlayLevelMusic();
1079
1080 static void MapGameButtons();
1081 static void HandleGameButtons(struct GadgetInfo *);
1082
1083 int AmoebeNachbarNr(int, int);
1084 void AmoebeUmwandeln(int, int);
1085 void ContinueMoving(int, int);
1086 void Bang(int, int);
1087 void InitMovDir(int, int);
1088 void InitAmoebaNr(int, int);
1089 int NewHiScore(void);
1090
1091 void TestIfGoodThingHitsBadThing(int, int, int);
1092 void TestIfBadThingHitsGoodThing(int, int, int);
1093 void TestIfPlayerTouchesBadThing(int, int);
1094 void TestIfPlayerRunsIntoBadThing(int, int, int);
1095 void TestIfBadThingTouchesPlayer(int, int);
1096 void TestIfBadThingRunsIntoPlayer(int, int, int);
1097 void TestIfFriendTouchesBadThing(int, int);
1098 void TestIfBadThingTouchesFriend(int, int);
1099 void TestIfBadThingTouchesOtherBadThing(int, int);
1100 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1101
1102 void KillPlayer(struct PlayerInfo *);
1103 void BuryPlayer(struct PlayerInfo *);
1104 void RemovePlayer(struct PlayerInfo *);
1105
1106 static int getInvisibleActiveFromInvisibleElement(int);
1107 static int getInvisibleFromInvisibleActiveElement(int);
1108
1109 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1110
1111 /* for detection of endless loops, caused by custom element programming */
1112 /* (using maximal playfield width x 10 is just a rough approximation) */
1113 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1114
1115 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1116 {                                                                       \
1117   if (recursion_loop_detected)                                          \
1118     return (rc);                                                        \
1119                                                                         \
1120   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1121   {                                                                     \
1122     recursion_loop_detected = TRUE;                                     \
1123     recursion_loop_element = (e);                                       \
1124   }                                                                     \
1125                                                                         \
1126   recursion_loop_depth++;                                               \
1127 }
1128
1129 #define RECURSION_LOOP_DETECTION_END()                                  \
1130 {                                                                       \
1131   recursion_loop_depth--;                                               \
1132 }
1133
1134 static int recursion_loop_depth;
1135 static boolean recursion_loop_detected;
1136 static boolean recursion_loop_element;
1137
1138 static int map_player_action[MAX_PLAYERS];
1139
1140
1141 /* ------------------------------------------------------------------------- */
1142 /* definition of elements that automatically change to other elements after  */
1143 /* a specified time, eventually calling a function when changing             */
1144 /* ------------------------------------------------------------------------- */
1145
1146 /* forward declaration for changer functions */
1147 static void InitBuggyBase(int, int);
1148 static void WarnBuggyBase(int, int);
1149
1150 static void InitTrap(int, int);
1151 static void ActivateTrap(int, int);
1152 static void ChangeActiveTrap(int, int);
1153
1154 static void InitRobotWheel(int, int);
1155 static void RunRobotWheel(int, int);
1156 static void StopRobotWheel(int, int);
1157
1158 static void InitTimegateWheel(int, int);
1159 static void RunTimegateWheel(int, int);
1160
1161 static void InitMagicBallDelay(int, int);
1162 static void ActivateMagicBall(int, int);
1163
1164 struct ChangingElementInfo
1165 {
1166   int element;
1167   int target_element;
1168   int change_delay;
1169   void (*pre_change_function)(int x, int y);
1170   void (*change_function)(int x, int y);
1171   void (*post_change_function)(int x, int y);
1172 };
1173
1174 static struct ChangingElementInfo change_delay_list[] =
1175 {
1176   {
1177     EL_NUT_BREAKING,
1178     EL_EMERALD,
1179     6,
1180     NULL,
1181     NULL,
1182     NULL
1183   },
1184   {
1185     EL_PEARL_BREAKING,
1186     EL_EMPTY,
1187     8,
1188     NULL,
1189     NULL,
1190     NULL
1191   },
1192   {
1193     EL_EXIT_OPENING,
1194     EL_EXIT_OPEN,
1195     29,
1196     NULL,
1197     NULL,
1198     NULL
1199   },
1200   {
1201     EL_EXIT_CLOSING,
1202     EL_EXIT_CLOSED,
1203     29,
1204     NULL,
1205     NULL,
1206     NULL
1207   },
1208   {
1209     EL_STEEL_EXIT_OPENING,
1210     EL_STEEL_EXIT_OPEN,
1211     29,
1212     NULL,
1213     NULL,
1214     NULL
1215   },
1216   {
1217     EL_STEEL_EXIT_CLOSING,
1218     EL_STEEL_EXIT_CLOSED,
1219     29,
1220     NULL,
1221     NULL,
1222     NULL
1223   },
1224   {
1225     EL_EM_EXIT_OPENING,
1226     EL_EM_EXIT_OPEN,
1227     29,
1228     NULL,
1229     NULL,
1230     NULL
1231   },
1232   {
1233     EL_EM_EXIT_CLOSING,
1234 #if 1
1235     EL_EMPTY,
1236 #else
1237     EL_EM_EXIT_CLOSED,
1238 #endif
1239     29,
1240     NULL,
1241     NULL,
1242     NULL
1243   },
1244   {
1245     EL_EM_STEEL_EXIT_OPENING,
1246     EL_EM_STEEL_EXIT_OPEN,
1247     29,
1248     NULL,
1249     NULL,
1250     NULL
1251   },
1252   {
1253     EL_EM_STEEL_EXIT_CLOSING,
1254 #if 1
1255     EL_STEELWALL,
1256 #else
1257     EL_EM_STEEL_EXIT_CLOSED,
1258 #endif
1259     29,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_SP_EXIT_OPENING,
1266     EL_SP_EXIT_OPEN,
1267     29,
1268     NULL,
1269     NULL,
1270     NULL
1271   },
1272   {
1273     EL_SP_EXIT_CLOSING,
1274     EL_SP_EXIT_CLOSED,
1275     29,
1276     NULL,
1277     NULL,
1278     NULL
1279   },
1280   {
1281     EL_SWITCHGATE_OPENING,
1282     EL_SWITCHGATE_OPEN,
1283     29,
1284     NULL,
1285     NULL,
1286     NULL
1287   },
1288   {
1289     EL_SWITCHGATE_CLOSING,
1290     EL_SWITCHGATE_CLOSED,
1291     29,
1292     NULL,
1293     NULL,
1294     NULL
1295   },
1296   {
1297     EL_TIMEGATE_OPENING,
1298     EL_TIMEGATE_OPEN,
1299     29,
1300     NULL,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_TIMEGATE_CLOSING,
1306     EL_TIMEGATE_CLOSED,
1307     29,
1308     NULL,
1309     NULL,
1310     NULL
1311   },
1312
1313   {
1314     EL_ACID_SPLASH_LEFT,
1315     EL_EMPTY,
1316     8,
1317     NULL,
1318     NULL,
1319     NULL
1320   },
1321   {
1322     EL_ACID_SPLASH_RIGHT,
1323     EL_EMPTY,
1324     8,
1325     NULL,
1326     NULL,
1327     NULL
1328   },
1329   {
1330     EL_SP_BUGGY_BASE,
1331     EL_SP_BUGGY_BASE_ACTIVATING,
1332     0,
1333     InitBuggyBase,
1334     NULL,
1335     NULL
1336   },
1337   {
1338     EL_SP_BUGGY_BASE_ACTIVATING,
1339     EL_SP_BUGGY_BASE_ACTIVE,
1340     0,
1341     InitBuggyBase,
1342     NULL,
1343     NULL
1344   },
1345   {
1346     EL_SP_BUGGY_BASE_ACTIVE,
1347     EL_SP_BUGGY_BASE,
1348     0,
1349     InitBuggyBase,
1350     WarnBuggyBase,
1351     NULL
1352   },
1353   {
1354     EL_TRAP,
1355     EL_TRAP_ACTIVE,
1356     0,
1357     InitTrap,
1358     NULL,
1359     ActivateTrap
1360   },
1361   {
1362     EL_TRAP_ACTIVE,
1363     EL_TRAP,
1364     31,
1365     NULL,
1366     ChangeActiveTrap,
1367     NULL
1368   },
1369   {
1370     EL_ROBOT_WHEEL_ACTIVE,
1371     EL_ROBOT_WHEEL,
1372     0,
1373     InitRobotWheel,
1374     RunRobotWheel,
1375     StopRobotWheel
1376   },
1377   {
1378     EL_TIMEGATE_SWITCH_ACTIVE,
1379     EL_TIMEGATE_SWITCH,
1380     0,
1381     InitTimegateWheel,
1382     RunTimegateWheel,
1383     NULL
1384   },
1385   {
1386     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1387     EL_DC_TIMEGATE_SWITCH,
1388     0,
1389     InitTimegateWheel,
1390     RunTimegateWheel,
1391     NULL
1392   },
1393   {
1394     EL_EMC_MAGIC_BALL_ACTIVE,
1395     EL_EMC_MAGIC_BALL_ACTIVE,
1396     0,
1397     InitMagicBallDelay,
1398     NULL,
1399     ActivateMagicBall
1400   },
1401   {
1402     EL_EMC_SPRING_BUMPER_ACTIVE,
1403     EL_EMC_SPRING_BUMPER,
1404     8,
1405     NULL,
1406     NULL,
1407     NULL
1408   },
1409   {
1410     EL_DIAGONAL_SHRINKING,
1411     EL_UNDEFINED,
1412     0,
1413     NULL,
1414     NULL,
1415     NULL
1416   },
1417   {
1418     EL_DIAGONAL_GROWING,
1419     EL_UNDEFINED,
1420     0,
1421     NULL,
1422     NULL,
1423     NULL,
1424   },
1425
1426   {
1427     EL_UNDEFINED,
1428     EL_UNDEFINED,
1429     -1,
1430     NULL,
1431     NULL,
1432     NULL
1433   }
1434 };
1435
1436 struct
1437 {
1438   int element;
1439   int push_delay_fixed, push_delay_random;
1440 }
1441 push_delay_list[] =
1442 {
1443   { EL_SPRING,                  0, 0 },
1444   { EL_BALLOON,                 0, 0 },
1445
1446   { EL_SOKOBAN_OBJECT,          2, 0 },
1447   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1448   { EL_SATELLITE,               2, 0 },
1449   { EL_SP_DISK_YELLOW,          2, 0 },
1450
1451   { EL_UNDEFINED,               0, 0 },
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int move_stepsize;
1458 }
1459 move_stepsize_list[] =
1460 {
1461   { EL_AMOEBA_DROP,             2 },
1462   { EL_AMOEBA_DROPPING,         2 },
1463   { EL_QUICKSAND_FILLING,       1 },
1464   { EL_QUICKSAND_EMPTYING,      1 },
1465   { EL_QUICKSAND_FAST_FILLING,  2 },
1466   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1467   { EL_MAGIC_WALL_FILLING,      2 },
1468   { EL_MAGIC_WALL_EMPTYING,     2 },
1469   { EL_BD_MAGIC_WALL_FILLING,   2 },
1470   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1471   { EL_DC_MAGIC_WALL_FILLING,   2 },
1472   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1473
1474   { EL_UNDEFINED,               0 },
1475 };
1476
1477 struct
1478 {
1479   int element;
1480   int count;
1481 }
1482 collect_count_list[] =
1483 {
1484   { EL_EMERALD,                 1 },
1485   { EL_BD_DIAMOND,              1 },
1486   { EL_EMERALD_YELLOW,          1 },
1487   { EL_EMERALD_RED,             1 },
1488   { EL_EMERALD_PURPLE,          1 },
1489   { EL_DIAMOND,                 3 },
1490   { EL_SP_INFOTRON,             1 },
1491   { EL_PEARL,                   5 },
1492   { EL_CRYSTAL,                 8 },
1493
1494   { EL_UNDEFINED,               0 },
1495 };
1496
1497 struct
1498 {
1499   int element;
1500   int direction;
1501 }
1502 access_direction_list[] =
1503 {
1504   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1505   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1506   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1507   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1510   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1511   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1512   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1513   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1514   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1515
1516   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1517   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1518   { EL_SP_PORT_UP,                                                   MV_DOWN },
1519   { EL_SP_PORT_DOWN,                                         MV_UP           },
1520   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1521   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1522   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1524   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1525   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1527   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1528   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1529   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1530   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1531   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1532   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1533   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1534   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1535
1536   { EL_UNDEFINED,                       MV_NONE                              }
1537 };
1538
1539 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1540
1541 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1542 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1543 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1544                                  IS_JUST_CHANGING(x, y))
1545
1546 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1547
1548 /* static variables for playfield scan mode (scanning forward or backward) */
1549 static int playfield_scan_start_x = 0;
1550 static int playfield_scan_start_y = 0;
1551 static int playfield_scan_delta_x = 1;
1552 static int playfield_scan_delta_y = 1;
1553
1554 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1555                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1556                                      (y) += playfield_scan_delta_y)     \
1557                                 for ((x) = playfield_scan_start_x;      \
1558                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1559                                      (x) += playfield_scan_delta_x)
1560
1561 #ifdef DEBUG
1562 void DEBUG_SetMaximumDynamite()
1563 {
1564   int i;
1565
1566   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1567     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1568       local_player->inventory_element[local_player->inventory_size++] =
1569         EL_DYNAMITE;
1570 }
1571 #endif
1572
1573 static void InitPlayfieldScanModeVars()
1574 {
1575   if (game.use_reverse_scan_direction)
1576   {
1577     playfield_scan_start_x = lev_fieldx - 1;
1578     playfield_scan_start_y = lev_fieldy - 1;
1579
1580     playfield_scan_delta_x = -1;
1581     playfield_scan_delta_y = -1;
1582   }
1583   else
1584   {
1585     playfield_scan_start_x = 0;
1586     playfield_scan_start_y = 0;
1587
1588     playfield_scan_delta_x = 1;
1589     playfield_scan_delta_y = 1;
1590   }
1591 }
1592
1593 static void InitPlayfieldScanMode(int mode)
1594 {
1595   game.use_reverse_scan_direction =
1596     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1597
1598   InitPlayfieldScanModeVars();
1599 }
1600
1601 static int get_move_delay_from_stepsize(int move_stepsize)
1602 {
1603   move_stepsize =
1604     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1605
1606   /* make sure that stepsize value is always a power of 2 */
1607   move_stepsize = (1 << log_2(move_stepsize));
1608
1609   return TILEX / move_stepsize;
1610 }
1611
1612 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1613                                boolean init_game)
1614 {
1615   int player_nr = player->index_nr;
1616   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1617   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1618
1619   /* do no immediately change move delay -- the player might just be moving */
1620   player->move_delay_value_next = move_delay;
1621
1622   /* information if player can move must be set separately */
1623   player->cannot_move = cannot_move;
1624
1625   if (init_game)
1626   {
1627     player->move_delay       = game.initial_move_delay[player_nr];
1628     player->move_delay_value = game.initial_move_delay_value[player_nr];
1629
1630     player->move_delay_value_next = -1;
1631
1632     player->move_delay_reset_counter = 0;
1633   }
1634 }
1635
1636 void GetPlayerConfig()
1637 {
1638   GameFrameDelay = setup.game_frame_delay;
1639
1640   if (!audio.sound_available)
1641     setup.sound_simple = FALSE;
1642
1643   if (!audio.loops_available)
1644     setup.sound_loops = FALSE;
1645
1646   if (!audio.music_available)
1647     setup.sound_music = FALSE;
1648
1649   if (!video.fullscreen_available)
1650     setup.fullscreen = FALSE;
1651
1652   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1653
1654   SetAudioMode(setup.sound);
1655   InitJoysticks();
1656 }
1657
1658 int GetElementFromGroupElement(int element)
1659 {
1660   if (IS_GROUP_ELEMENT(element))
1661   {
1662     struct ElementGroupInfo *group = element_info[element].group;
1663     int last_anim_random_frame = gfx.anim_random_frame;
1664     int element_pos;
1665
1666     if (group->choice_mode == ANIM_RANDOM)
1667       gfx.anim_random_frame = RND(group->num_elements_resolved);
1668
1669     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1670                                     group->choice_mode, 0,
1671                                     group->choice_pos);
1672
1673     if (group->choice_mode == ANIM_RANDOM)
1674       gfx.anim_random_frame = last_anim_random_frame;
1675
1676     group->choice_pos++;
1677
1678     element = group->element_resolved[element_pos];
1679   }
1680
1681   return element;
1682 }
1683
1684 static void InitPlayerField(int x, int y, int element, boolean init_game)
1685 {
1686   if (element == EL_SP_MURPHY)
1687   {
1688     if (init_game)
1689     {
1690       if (stored_player[0].present)
1691       {
1692         Feld[x][y] = EL_SP_MURPHY_CLONE;
1693
1694         return;
1695       }
1696       else
1697       {
1698         stored_player[0].initial_element = element;
1699         stored_player[0].use_murphy = TRUE;
1700
1701         if (!level.use_artwork_element[0])
1702           stored_player[0].artwork_element = EL_SP_MURPHY;
1703       }
1704
1705       Feld[x][y] = EL_PLAYER_1;
1706     }
1707   }
1708
1709   if (init_game)
1710   {
1711     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1712     int jx = player->jx, jy = player->jy;
1713
1714     player->present = TRUE;
1715
1716     player->block_last_field = (element == EL_SP_MURPHY ?
1717                                 level.sp_block_last_field :
1718                                 level.block_last_field);
1719
1720     /* ---------- initialize player's last field block delay --------------- */
1721
1722     /* always start with reliable default value (no adjustment needed) */
1723     player->block_delay_adjustment = 0;
1724
1725     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1726     if (player->block_last_field && element == EL_SP_MURPHY)
1727       player->block_delay_adjustment = 1;
1728
1729     /* special case 2: in game engines before 3.1.1, blocking was different */
1730     if (game.use_block_last_field_bug)
1731       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1732
1733     if (!options.network || player->connected)
1734     {
1735       player->active = TRUE;
1736
1737       /* remove potentially duplicate players */
1738       if (StorePlayer[jx][jy] == Feld[x][y])
1739         StorePlayer[jx][jy] = 0;
1740
1741       StorePlayer[x][y] = Feld[x][y];
1742
1743       if (options.debug)
1744       {
1745         printf("Player %d activated.\n", player->element_nr);
1746         printf("[Local player is %d and currently %s.]\n",
1747                local_player->element_nr,
1748                local_player->active ? "active" : "not active");
1749       }
1750     }
1751
1752     Feld[x][y] = EL_EMPTY;
1753
1754     player->jx = player->last_jx = x;
1755     player->jy = player->last_jy = y;
1756   }
1757
1758 #if USE_PLAYER_REANIMATION
1759   if (!init_game)
1760   {
1761     int player_nr = GET_PLAYER_NR(element);
1762     struct PlayerInfo *player = &stored_player[player_nr];
1763
1764     if (player->active && player->killed)
1765       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1766   }
1767 #endif
1768 }
1769
1770 static void InitField(int x, int y, boolean init_game)
1771 {
1772   int element = Feld[x][y];
1773
1774   switch (element)
1775   {
1776     case EL_SP_MURPHY:
1777     case EL_PLAYER_1:
1778     case EL_PLAYER_2:
1779     case EL_PLAYER_3:
1780     case EL_PLAYER_4:
1781       InitPlayerField(x, y, element, init_game);
1782       break;
1783
1784     case EL_SOKOBAN_FIELD_PLAYER:
1785       element = Feld[x][y] = EL_PLAYER_1;
1786       InitField(x, y, init_game);
1787
1788       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1789       InitField(x, y, init_game);
1790       break;
1791
1792     case EL_SOKOBAN_FIELD_EMPTY:
1793       local_player->sokobanfields_still_needed++;
1794       break;
1795
1796     case EL_STONEBLOCK:
1797       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1798         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1799       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1800         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1801       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1802         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1803       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1804         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1805       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1806         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1807       break;
1808
1809     case EL_BUG:
1810     case EL_BUG_RIGHT:
1811     case EL_BUG_UP:
1812     case EL_BUG_LEFT:
1813     case EL_BUG_DOWN:
1814     case EL_SPACESHIP:
1815     case EL_SPACESHIP_RIGHT:
1816     case EL_SPACESHIP_UP:
1817     case EL_SPACESHIP_LEFT:
1818     case EL_SPACESHIP_DOWN:
1819     case EL_BD_BUTTERFLY:
1820     case EL_BD_BUTTERFLY_RIGHT:
1821     case EL_BD_BUTTERFLY_UP:
1822     case EL_BD_BUTTERFLY_LEFT:
1823     case EL_BD_BUTTERFLY_DOWN:
1824     case EL_BD_FIREFLY:
1825     case EL_BD_FIREFLY_RIGHT:
1826     case EL_BD_FIREFLY_UP:
1827     case EL_BD_FIREFLY_LEFT:
1828     case EL_BD_FIREFLY_DOWN:
1829     case EL_PACMAN_RIGHT:
1830     case EL_PACMAN_UP:
1831     case EL_PACMAN_LEFT:
1832     case EL_PACMAN_DOWN:
1833     case EL_YAMYAM:
1834     case EL_YAMYAM_LEFT:
1835     case EL_YAMYAM_RIGHT:
1836     case EL_YAMYAM_UP:
1837     case EL_YAMYAM_DOWN:
1838     case EL_DARK_YAMYAM:
1839     case EL_ROBOT:
1840     case EL_PACMAN:
1841     case EL_SP_SNIKSNAK:
1842     case EL_SP_ELECTRON:
1843     case EL_MOLE:
1844     case EL_MOLE_LEFT:
1845     case EL_MOLE_RIGHT:
1846     case EL_MOLE_UP:
1847     case EL_MOLE_DOWN:
1848       InitMovDir(x, y);
1849       break;
1850
1851     case EL_AMOEBA_FULL:
1852     case EL_BD_AMOEBA:
1853       InitAmoebaNr(x, y);
1854       break;
1855
1856     case EL_AMOEBA_DROP:
1857       if (y == lev_fieldy - 1)
1858       {
1859         Feld[x][y] = EL_AMOEBA_GROWING;
1860         Store[x][y] = EL_AMOEBA_WET;
1861       }
1862       break;
1863
1864     case EL_DYNAMITE_ACTIVE:
1865     case EL_SP_DISK_RED_ACTIVE:
1866     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1867     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1870       MovDelay[x][y] = 96;
1871       break;
1872
1873     case EL_EM_DYNAMITE_ACTIVE:
1874       MovDelay[x][y] = 32;
1875       break;
1876
1877     case EL_LAMP:
1878       local_player->lights_still_needed++;
1879       break;
1880
1881     case EL_PENGUIN:
1882       local_player->friends_still_needed++;
1883       break;
1884
1885     case EL_PIG:
1886     case EL_DRAGON:
1887       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1888       break;
1889
1890     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1891     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1892     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1893     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1894     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1895     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1896     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1897     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1898     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1899     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1900     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1901     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1902       if (init_game)
1903       {
1904         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1905         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1906         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1907
1908         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1909         {
1910           game.belt_dir[belt_nr] = belt_dir;
1911           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1912         }
1913         else    /* more than one switch -- set it like the first switch */
1914         {
1915           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1916         }
1917       }
1918       break;
1919
1920 #if !USE_BOTH_SWITCHGATE_SWITCHES
1921     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1922       if (init_game)
1923         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1924       break;
1925
1926     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1927       if (init_game)
1928         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1929       break;
1930 #endif
1931
1932     case EL_LIGHT_SWITCH_ACTIVE:
1933       if (init_game)
1934         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1935       break;
1936
1937     case EL_INVISIBLE_STEELWALL:
1938     case EL_INVISIBLE_WALL:
1939     case EL_INVISIBLE_SAND:
1940       if (game.light_time_left > 0 ||
1941           game.lenses_time_left > 0)
1942         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1943       break;
1944
1945     case EL_EMC_MAGIC_BALL:
1946       if (game.ball_state)
1947         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1948       break;
1949
1950     case EL_EMC_MAGIC_BALL_SWITCH:
1951       if (game.ball_state)
1952         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1953       break;
1954
1955     case EL_TRIGGER_PLAYER:
1956     case EL_TRIGGER_ELEMENT:
1957     case EL_TRIGGER_CE_VALUE:
1958     case EL_TRIGGER_CE_SCORE:
1959     case EL_SELF:
1960     case EL_ANY_ELEMENT:
1961     case EL_CURRENT_CE_VALUE:
1962     case EL_CURRENT_CE_SCORE:
1963     case EL_PREV_CE_1:
1964     case EL_PREV_CE_2:
1965     case EL_PREV_CE_3:
1966     case EL_PREV_CE_4:
1967     case EL_PREV_CE_5:
1968     case EL_PREV_CE_6:
1969     case EL_PREV_CE_7:
1970     case EL_PREV_CE_8:
1971     case EL_NEXT_CE_1:
1972     case EL_NEXT_CE_2:
1973     case EL_NEXT_CE_3:
1974     case EL_NEXT_CE_4:
1975     case EL_NEXT_CE_5:
1976     case EL_NEXT_CE_6:
1977     case EL_NEXT_CE_7:
1978     case EL_NEXT_CE_8:
1979       /* reference elements should not be used on the playfield */
1980       Feld[x][y] = EL_EMPTY;
1981       break;
1982
1983     default:
1984       if (IS_CUSTOM_ELEMENT(element))
1985       {
1986         if (CAN_MOVE(element))
1987           InitMovDir(x, y);
1988
1989 #if USE_NEW_CUSTOM_VALUE
1990         if (!element_info[element].use_last_ce_value || init_game)
1991           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1992 #endif
1993       }
1994       else if (IS_GROUP_ELEMENT(element))
1995       {
1996         Feld[x][y] = GetElementFromGroupElement(element);
1997
1998         InitField(x, y, init_game);
1999       }
2000
2001       break;
2002   }
2003
2004   if (!init_game)
2005     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2006 }
2007
2008 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2009 {
2010   InitField(x, y, init_game);
2011
2012   /* not needed to call InitMovDir() -- already done by InitField()! */
2013   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014       CAN_MOVE(Feld[x][y]))
2015     InitMovDir(x, y);
2016 }
2017
2018 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2019 {
2020   int old_element = Feld[x][y];
2021
2022   InitField(x, y, init_game);
2023
2024   /* not needed to call InitMovDir() -- already done by InitField()! */
2025   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2026       CAN_MOVE(old_element) &&
2027       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2028     InitMovDir(x, y);
2029
2030   /* this case is in fact a combination of not less than three bugs:
2031      first, it calls InitMovDir() for elements that can move, although this is
2032      already done by InitField(); then, it checks the element that was at this
2033      field _before_ the call to InitField() (which can change it); lastly, it
2034      was not called for "mole with direction" elements, which were treated as
2035      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2036   */
2037 }
2038
2039 static int get_key_element_from_nr(int key_nr)
2040 {
2041   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2042                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2043                           EL_EM_KEY_1 : EL_KEY_1);
2044
2045   return key_base_element + key_nr;
2046 }
2047
2048 static int get_next_dropped_element(struct PlayerInfo *player)
2049 {
2050   return (player->inventory_size > 0 ?
2051           player->inventory_element[player->inventory_size - 1] :
2052           player->inventory_infinite_element != EL_UNDEFINED ?
2053           player->inventory_infinite_element :
2054           player->dynabombs_left > 0 ?
2055           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2056           EL_UNDEFINED);
2057 }
2058
2059 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2060 {
2061   /* pos >= 0: get element from bottom of the stack;
2062      pos <  0: get element from top of the stack */
2063
2064   if (pos < 0)
2065   {
2066     int min_inventory_size = -pos;
2067     int inventory_pos = player->inventory_size - min_inventory_size;
2068     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2069
2070     return (player->inventory_size >= min_inventory_size ?
2071             player->inventory_element[inventory_pos] :
2072             player->inventory_infinite_element != EL_UNDEFINED ?
2073             player->inventory_infinite_element :
2074             player->dynabombs_left >= min_dynabombs_left ?
2075             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076             EL_UNDEFINED);
2077   }
2078   else
2079   {
2080     int min_dynabombs_left = pos + 1;
2081     int min_inventory_size = pos + 1 - player->dynabombs_left;
2082     int inventory_pos = pos - player->dynabombs_left;
2083
2084     return (player->inventory_infinite_element != EL_UNDEFINED ?
2085             player->inventory_infinite_element :
2086             player->dynabombs_left >= min_dynabombs_left ?
2087             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2088             player->inventory_size >= min_inventory_size ?
2089             player->inventory_element[inventory_pos] :
2090             EL_UNDEFINED);
2091   }
2092 }
2093
2094 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2095 {
2096   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2097   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2098   int compare_result;
2099
2100   if (gpo1->sort_priority != gpo2->sort_priority)
2101     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2102   else
2103     compare_result = gpo1->nr - gpo2->nr;
2104
2105   return compare_result;
2106 }
2107
2108 void InitGameControlValues()
2109 {
2110   int i;
2111
2112   for (i = 0; game_panel_controls[i].nr != -1; i++)
2113   {
2114     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2115     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2116     struct TextPosInfo *pos = gpc->pos;
2117     int nr = gpc->nr;
2118     int type = gpc->type;
2119
2120     if (nr != i)
2121     {
2122       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2123       Error(ERR_EXIT, "this should not happen -- please debug");
2124     }
2125
2126     /* force update of game controls after initialization */
2127     gpc->value = gpc->last_value = -1;
2128     gpc->frame = gpc->last_frame = -1;
2129     gpc->gfx_frame = -1;
2130
2131     /* determine panel value width for later calculation of alignment */
2132     if (type == TYPE_INTEGER || type == TYPE_STRING)
2133     {
2134       pos->width = pos->size * getFontWidth(pos->font);
2135       pos->height = getFontHeight(pos->font);
2136     }
2137     else if (type == TYPE_ELEMENT)
2138     {
2139       pos->width = pos->size;
2140       pos->height = pos->size;
2141     }
2142
2143     /* fill structure for game panel draw order */
2144     gpo->nr = gpc->nr;
2145     gpo->sort_priority = pos->sort_priority;
2146   }
2147
2148   /* sort game panel controls according to sort_priority and control number */
2149   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2150         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2151 }
2152
2153 void UpdatePlayfieldElementCount()
2154 {
2155   boolean use_element_count = FALSE;
2156   int i, j, x, y;
2157
2158   /* first check if it is needed at all to calculate playfield element count */
2159   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2160     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2161       use_element_count = TRUE;
2162
2163   if (!use_element_count)
2164     return;
2165
2166   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2167     element_info[i].element_count = 0;
2168
2169   SCAN_PLAYFIELD(x, y)
2170   {
2171     element_info[Feld[x][y]].element_count++;
2172   }
2173
2174   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2175     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2176       if (IS_IN_GROUP(j, i))
2177         element_info[EL_GROUP_START + i].element_count +=
2178           element_info[j].element_count;
2179 }
2180
2181 void UpdateGameControlValues()
2182 {
2183   int i, k;
2184   int time = (local_player->LevelSolved ?
2185               local_player->LevelSolved_CountingTime :
2186               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2187               level.native_em_level->lev->time :
2188               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2189               level.native_sp_level->game_sp->time_played :
2190               game.no_time_limit ? TimePlayed : TimeLeft);
2191   int score = (local_player->LevelSolved ?
2192                local_player->LevelSolved_CountingScore :
2193                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194                level.native_em_level->lev->score :
2195                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196                level.native_sp_level->game_sp->score :
2197                local_player->score);
2198   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2199               level.native_em_level->lev->required :
2200               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2201               level.native_sp_level->game_sp->infotrons_still_needed :
2202               local_player->gems_still_needed);
2203   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2204                      level.native_em_level->lev->required > 0 :
2205                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2206                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2207                      local_player->gems_still_needed > 0 ||
2208                      local_player->sokobanfields_still_needed > 0 ||
2209                      local_player->lights_still_needed > 0);
2210
2211   UpdatePlayfieldElementCount();
2212
2213   /* update game panel control values */
2214
2215   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2216   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2217
2218   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2219   for (i = 0; i < MAX_NUM_KEYS; i++)
2220     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2221   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2222   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2223
2224   if (game.centered_player_nr == -1)
2225   {
2226     for (i = 0; i < MAX_PLAYERS; i++)
2227     {
2228       /* only one player in Supaplex game engine */
2229       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2230         break;
2231
2232       for (k = 0; k < MAX_NUM_KEYS; k++)
2233       {
2234         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2235         {
2236           if (level.native_em_level->ply[i]->keys & (1 << k))
2237             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2238               get_key_element_from_nr(k);
2239         }
2240         else if (stored_player[i].key[k])
2241           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2242             get_key_element_from_nr(k);
2243       }
2244
2245       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2247           level.native_em_level->ply[i]->dynamite;
2248       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2249         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2250           level.native_sp_level->game_sp->red_disk_count;
2251       else
2252         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2253           stored_player[i].inventory_size;
2254
2255       if (stored_player[i].num_white_keys > 0)
2256         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2257           EL_DC_KEY_WHITE;
2258
2259       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2260         stored_player[i].num_white_keys;
2261     }
2262   }
2263   else
2264   {
2265     int player_nr = game.centered_player_nr;
2266
2267     for (k = 0; k < MAX_NUM_KEYS; k++)
2268     {
2269       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270       {
2271         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2272           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2273             get_key_element_from_nr(k);
2274       }
2275       else if (stored_player[player_nr].key[k])
2276         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277           get_key_element_from_nr(k);
2278     }
2279
2280     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2282         level.native_em_level->ply[player_nr]->dynamite;
2283     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2284       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285         level.native_sp_level->game_sp->red_disk_count;
2286     else
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288         stored_player[player_nr].inventory_size;
2289
2290     if (stored_player[player_nr].num_white_keys > 0)
2291       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2292
2293     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2294       stored_player[player_nr].num_white_keys;
2295   }
2296
2297   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2298   {
2299     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2300       get_inventory_element_from_pos(local_player, i);
2301     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2302       get_inventory_element_from_pos(local_player, -i - 1);
2303   }
2304
2305   game_panel_controls[GAME_PANEL_SCORE].value = score;
2306   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2307
2308   game_panel_controls[GAME_PANEL_TIME].value = time;
2309
2310   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2311   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2312   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2313
2314   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2315
2316   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2317     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2318      EL_EMPTY);
2319   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2320     local_player->shield_normal_time_left;
2321   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2322     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2323      EL_EMPTY);
2324   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2325     local_player->shield_deadly_time_left;
2326
2327   game_panel_controls[GAME_PANEL_EXIT].value =
2328     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2329
2330   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2331     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2332   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2333     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2334      EL_EMC_MAGIC_BALL_SWITCH);
2335
2336   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2337     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2338   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2339     game.light_time_left;
2340
2341   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2342     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2343   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2344     game.timegate_time_left;
2345
2346   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2347     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2348
2349   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2350     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2351   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2352     game.lenses_time_left;
2353
2354   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2355     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2356   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2357     game.magnify_time_left;
2358
2359   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2360     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2361      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2362      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2363      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2364      EL_BALLOON_SWITCH_NONE);
2365
2366   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2367     local_player->dynabomb_count;
2368   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2369     local_player->dynabomb_size;
2370   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2371     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2372
2373   game_panel_controls[GAME_PANEL_PENGUINS].value =
2374     local_player->friends_still_needed;
2375
2376   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2377     local_player->sokobanfields_still_needed;
2378   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2379     local_player->sokobanfields_still_needed;
2380
2381   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2382     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2383
2384   for (i = 0; i < NUM_BELTS; i++)
2385   {
2386     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2387       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2388        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2389     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2390       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2391   }
2392
2393   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2394     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2395   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2396     game.magic_wall_time_left;
2397
2398 #if USE_PLAYER_GRAVITY
2399   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2400     local_player->gravity;
2401 #else
2402   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2403 #endif
2404
2405   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2406     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2407
2408   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2409     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2410       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2411        game.panel.element[i].id : EL_UNDEFINED);
2412
2413   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2414     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2415       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2416        element_info[game.panel.element_count[i].id].element_count : 0);
2417
2418   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2419     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2420       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2421        element_info[game.panel.ce_score[i].id].collect_score : 0);
2422
2423   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2424     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2425       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2426        element_info[game.panel.ce_score_element[i].id].collect_score :
2427        EL_UNDEFINED);
2428
2429   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2430   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2431   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2432
2433   /* update game panel control frames */
2434
2435   for (i = 0; game_panel_controls[i].nr != -1; i++)
2436   {
2437     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2438
2439     if (gpc->type == TYPE_ELEMENT)
2440     {
2441       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2442       {
2443         int last_anim_random_frame = gfx.anim_random_frame;
2444         int element = gpc->value;
2445         int graphic = el2panelimg(element);
2446
2447         if (gpc->value != gpc->last_value)
2448         {
2449           gpc->gfx_frame = 0;
2450           gpc->gfx_random = INIT_GFX_RANDOM();
2451         }
2452         else
2453         {
2454           gpc->gfx_frame++;
2455
2456           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2457               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2458             gpc->gfx_random = INIT_GFX_RANDOM();
2459         }
2460
2461         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2462           gfx.anim_random_frame = gpc->gfx_random;
2463
2464         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2465           gpc->gfx_frame = element_info[element].collect_score;
2466
2467         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2468                                               gpc->gfx_frame);
2469
2470         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2471           gfx.anim_random_frame = last_anim_random_frame;
2472       }
2473     }
2474   }
2475 }
2476
2477 void DisplayGameControlValues()
2478 {
2479   boolean redraw_panel = FALSE;
2480   int i;
2481
2482   for (i = 0; game_panel_controls[i].nr != -1; i++)
2483   {
2484     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2485
2486     if (PANEL_DEACTIVATED(gpc->pos))
2487       continue;
2488
2489     if (gpc->value == gpc->last_value &&
2490         gpc->frame == gpc->last_frame)
2491       continue;
2492
2493     redraw_panel = TRUE;
2494   }
2495
2496   if (!redraw_panel)
2497     return;
2498
2499   /* copy default game door content to main double buffer */
2500 #if 1
2501   /* !!! CHECK AGAIN !!! */
2502   SetPanelBackground();
2503   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2504   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2505 #else
2506   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2507              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2508 #endif
2509
2510   /* redraw game control buttons */
2511 #if 1
2512   RedrawGameButtons();
2513 #else
2514   UnmapGameButtons();
2515   MapGameButtons();
2516 #endif
2517
2518   game_status = GAME_MODE_PSEUDO_PANEL;
2519
2520 #if 1
2521   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2522 #else
2523   for (i = 0; game_panel_controls[i].nr != -1; i++)
2524 #endif
2525   {
2526 #if 1
2527     int nr = game_panel_order[i].nr;
2528     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2529 #else
2530     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2531     int nr = gpc->nr;
2532 #endif
2533     struct TextPosInfo *pos = gpc->pos;
2534     int type = gpc->type;
2535     int value = gpc->value;
2536     int frame = gpc->frame;
2537 #if 0
2538     int last_value = gpc->last_value;
2539     int last_frame = gpc->last_frame;
2540 #endif
2541     int size = pos->size;
2542     int font = pos->font;
2543     boolean draw_masked = pos->draw_masked;
2544     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2545
2546     if (PANEL_DEACTIVATED(pos))
2547       continue;
2548
2549 #if 0
2550     if (value == last_value && frame == last_frame)
2551       continue;
2552 #endif
2553
2554     gpc->last_value = value;
2555     gpc->last_frame = frame;
2556
2557 #if 0
2558     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2559 #endif
2560
2561     if (type == TYPE_INTEGER)
2562     {
2563       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564           nr == GAME_PANEL_TIME)
2565       {
2566         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2567
2568         if (use_dynamic_size)           /* use dynamic number of digits */
2569         {
2570           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572           int size2 = size1 + 1;
2573           int font1 = pos->font;
2574           int font2 = pos->font_alt;
2575
2576           size = (value < value_change ? size1 : size2);
2577           font = (value < value_change ? font1 : font2);
2578
2579 #if 0
2580           /* clear background if value just changed its size (dynamic digits) */
2581           if ((last_value < value_change) != (value < value_change))
2582           {
2583             int width1 = size1 * getFontWidth(font1);
2584             int width2 = size2 * getFontWidth(font2);
2585             int max_width = MAX(width1, width2);
2586             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2587
2588             pos->width = max_width;
2589
2590             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2591                                        max_width, max_height);
2592           }
2593 #endif
2594         }
2595       }
2596
2597 #if 1
2598       /* correct text size if "digits" is zero or less */
2599       if (size <= 0)
2600         size = strlen(int2str(value, size));
2601
2602       /* dynamically correct text alignment */
2603       pos->width = size * getFontWidth(font);
2604 #endif
2605
2606       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2607                   int2str(value, size), font, mask_mode);
2608     }
2609     else if (type == TYPE_ELEMENT)
2610     {
2611       int element, graphic;
2612       Bitmap *src_bitmap;
2613       int src_x, src_y;
2614       int width, height;
2615       int dst_x = PANEL_XPOS(pos);
2616       int dst_y = PANEL_YPOS(pos);
2617
2618 #if 1
2619       if (value != EL_UNDEFINED && value != EL_EMPTY)
2620       {
2621         element = value;
2622         graphic = el2panelimg(value);
2623
2624         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2625
2626 #if 1
2627         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2628           size = TILESIZE;
2629 #endif
2630
2631         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2632                               &src_x, &src_y);
2633
2634         width  = graphic_info[graphic].width  * size / TILESIZE;
2635         height = graphic_info[graphic].height * size / TILESIZE;
2636
2637         if (draw_masked)
2638         {
2639           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2640                         dst_x - src_x, dst_y - src_y);
2641           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2642                            dst_x, dst_y);
2643         }
2644         else
2645         {
2646           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2647                      dst_x, dst_y);
2648         }
2649       }
2650 #else
2651       if (value == EL_UNDEFINED || value == EL_EMPTY)
2652       {
2653         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2654         graphic = el2panelimg(element);
2655
2656         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2657         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2658         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2659       }
2660       else
2661       {
2662         element = value;
2663         graphic = el2panelimg(value);
2664
2665         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2666       }
2667
2668       width  = graphic_info[graphic].width  * size / TILESIZE;
2669       height = graphic_info[graphic].height * size / TILESIZE;
2670
2671       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2672 #endif
2673     }
2674     else if (type == TYPE_STRING)
2675     {
2676       boolean active = (value != 0);
2677       char *state_normal = "off";
2678       char *state_active = "on";
2679       char *state = (active ? state_active : state_normal);
2680       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2681                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2682                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2683                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2684
2685       if (nr == GAME_PANEL_GRAVITY_STATE)
2686       {
2687         int font1 = pos->font;          /* (used for normal state) */
2688         int font2 = pos->font_alt;      /* (used for active state) */
2689 #if 0
2690         int size1 = strlen(state_normal);
2691         int size2 = strlen(state_active);
2692         int width1 = size1 * getFontWidth(font1);
2693         int width2 = size2 * getFontWidth(font2);
2694         int max_width = MAX(width1, width2);
2695         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2696
2697         pos->width = max_width;
2698
2699         /* clear background for values that may have changed its size */
2700         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2701                                    max_width, max_height);
2702 #endif
2703
2704         font = (active ? font2 : font1);
2705       }
2706
2707       if (s != NULL)
2708       {
2709         char *s_cut;
2710
2711 #if 1
2712         if (size <= 0)
2713         {
2714           /* don't truncate output if "chars" is zero or less */
2715           size = strlen(s);
2716
2717           /* dynamically correct text alignment */
2718           pos->width = size * getFontWidth(font);
2719         }
2720 #endif
2721
2722         s_cut = getStringCopyN(s, size);
2723
2724         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2725                     s_cut, font, mask_mode);
2726
2727         free(s_cut);
2728       }
2729     }
2730
2731     redraw_mask |= REDRAW_DOOR_1;
2732   }
2733
2734   game_status = GAME_MODE_PLAYING;
2735 }
2736
2737 void UpdateAndDisplayGameControlValues()
2738 {
2739   if (tape.warp_forward)
2740     return;
2741
2742   UpdateGameControlValues();
2743   DisplayGameControlValues();
2744 }
2745
2746 void DrawGameValue_Emeralds(int value)
2747 {
2748   struct TextPosInfo *pos = &game.panel.gems;
2749   int font_nr = pos->font;
2750   int font_width = getFontWidth(font_nr);
2751   int chars = pos->size;
2752
2753 #if 1
2754   return;       /* !!! USE NEW STUFF !!! */
2755 #endif
2756
2757   if (PANEL_DEACTIVATED(pos))
2758     return;
2759
2760   pos->width = chars * font_width;
2761
2762   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2763 }
2764
2765 void DrawGameValue_Dynamite(int value)
2766 {
2767   struct TextPosInfo *pos = &game.panel.inventory_count;
2768   int font_nr = pos->font;
2769   int font_width = getFontWidth(font_nr);
2770   int chars = pos->size;
2771
2772 #if 1
2773   return;       /* !!! USE NEW STUFF !!! */
2774 #endif
2775
2776   if (PANEL_DEACTIVATED(pos))
2777     return;
2778
2779   pos->width = chars * font_width;
2780
2781   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2782 }
2783
2784 void DrawGameValue_Score(int value)
2785 {
2786   struct TextPosInfo *pos = &game.panel.score;
2787   int font_nr = pos->font;
2788   int font_width = getFontWidth(font_nr);
2789   int chars = pos->size;
2790
2791 #if 1
2792   return;       /* !!! USE NEW STUFF !!! */
2793 #endif
2794
2795   if (PANEL_DEACTIVATED(pos))
2796     return;
2797
2798   pos->width = chars * font_width;
2799
2800   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2801 }
2802
2803 void DrawGameValue_Time(int value)
2804 {
2805   struct TextPosInfo *pos = &game.panel.time;
2806   static int last_value = -1;
2807   int chars1 = 3;
2808   int chars2 = 4;
2809   int chars = pos->size;
2810   int font1_nr = pos->font;
2811   int font2_nr = pos->font_alt;
2812   int font_nr = font1_nr;
2813   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2814
2815 #if 1
2816   return;       /* !!! USE NEW STUFF !!! */
2817 #endif
2818
2819   if (PANEL_DEACTIVATED(pos))
2820     return;
2821
2822   if (use_dynamic_chars)                /* use dynamic number of chars */
2823   {
2824     chars   = (value < 1000 ? chars1   : chars2);
2825     font_nr = (value < 1000 ? font1_nr : font2_nr);
2826   }
2827
2828   /* clear background if value just changed its size (dynamic chars only) */
2829   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2830   {
2831     int width1 = chars1 * getFontWidth(font1_nr);
2832     int width2 = chars2 * getFontWidth(font2_nr);
2833     int max_width = MAX(width1, width2);
2834     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2835
2836     pos->width = max_width;
2837
2838     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2839                                max_width, max_height);
2840   }
2841
2842   pos->width = chars * getFontWidth(font_nr);
2843
2844   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2845
2846   last_value = value;
2847 }
2848
2849 void DrawGameValue_Level(int value)
2850 {
2851   struct TextPosInfo *pos = &game.panel.level_number;
2852   int chars1 = 2;
2853   int chars2 = 3;
2854   int chars = pos->size;
2855   int font1_nr = pos->font;
2856   int font2_nr = pos->font_alt;
2857   int font_nr = font1_nr;
2858   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2859
2860 #if 1
2861   return;       /* !!! USE NEW STUFF !!! */
2862 #endif
2863
2864   if (PANEL_DEACTIVATED(pos))
2865     return;
2866
2867   if (use_dynamic_chars)                /* use dynamic number of chars */
2868   {
2869     chars   = (level_nr < 100 ? chars1   : chars2);
2870     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2871   }
2872
2873   pos->width = chars * getFontWidth(font_nr);
2874
2875   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2876 }
2877
2878 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2879 {
2880   int i;
2881
2882 #if 1
2883   return;       /* !!! USE NEW STUFF !!! */
2884 #endif
2885
2886   for (i = 0; i < MAX_NUM_KEYS; i++)
2887   {
2888     struct TextPosInfo *pos = &game.panel.key[i];
2889     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2890     int src_y = DOOR_GFX_PAGEY1 + 123;
2891     int dst_x = PANEL_XPOS(pos);
2892     int dst_y = PANEL_YPOS(pos);
2893
2894     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2895                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2896                    EL_KEY_1) + i;
2897     int graphic = el2edimg(element);
2898
2899     if (PANEL_DEACTIVATED(pos))
2900       continue;
2901
2902 #if 0
2903     /* masked blit with tiles from half-size scaled bitmap does not work yet
2904        (no mask bitmap created for these sizes after loading and scaling) --
2905        solution: load without creating mask, scale, then create final mask */
2906
2907     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2908                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2909
2910     if (key[i])
2911     {
2912       Bitmap *src_bitmap;
2913       int src_x, src_y;
2914
2915       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2916
2917       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2918                     dst_x - src_x, dst_y - src_y);
2919       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2920                        dst_x, dst_y);
2921     }
2922 #else
2923     if (key[i])
2924       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2925     else
2926       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2927                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2928 #endif
2929   }
2930 }
2931
2932 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2933                        int key_bits)
2934 {
2935   int key[MAX_NUM_KEYS];
2936   int i;
2937
2938   /* prevent EM engine from updating time/score values parallel to GameWon() */
2939   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2940       local_player->LevelSolved)
2941     return;
2942
2943   for (i = 0; i < MAX_NUM_KEYS; i++)
2944     key[i] = key_bits & (1 << i);
2945
2946   DrawGameValue_Level(level_nr);
2947
2948   DrawGameValue_Emeralds(emeralds);
2949   DrawGameValue_Dynamite(dynamite);
2950   DrawGameValue_Score(score);
2951   DrawGameValue_Time(time);
2952
2953   DrawGameValue_Keys(key);
2954 }
2955
2956 void UpdateGameDoorValues()
2957 {
2958   UpdateGameControlValues();
2959 }
2960
2961 void DrawGameDoorValues()
2962 {
2963   DisplayGameControlValues();
2964 }
2965
2966 void DrawGameDoorValues_OLD()
2967 {
2968   int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2969   int dynamite_value = 0;
2970   int score_value = (local_player->LevelSolved ? local_player->score_final :
2971                      local_player->score);
2972   int gems_value = local_player->gems_still_needed;
2973   int key_bits = 0;
2974   int i, j;
2975
2976   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2977   {
2978     DrawGameDoorValues_EM();
2979
2980     return;
2981   }
2982
2983   if (game.centered_player_nr == -1)
2984   {
2985     for (i = 0; i < MAX_PLAYERS; i++)
2986     {
2987       for (j = 0; j < MAX_NUM_KEYS; j++)
2988         if (stored_player[i].key[j])
2989           key_bits |= (1 << j);
2990
2991       dynamite_value += stored_player[i].inventory_size;
2992     }
2993   }
2994   else
2995   {
2996     int player_nr = game.centered_player_nr;
2997
2998     for (i = 0; i < MAX_NUM_KEYS; i++)
2999       if (stored_player[player_nr].key[i])
3000         key_bits |= (1 << i);
3001
3002     dynamite_value = stored_player[player_nr].inventory_size;
3003   }
3004
3005   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3006                     key_bits);
3007 }
3008
3009
3010 /*
3011   =============================================================================
3012   InitGameEngine()
3013   -----------------------------------------------------------------------------
3014   initialize game engine due to level / tape version number
3015   =============================================================================
3016 */
3017
3018 static void InitGameEngine()
3019 {
3020   int i, j, k, l, x, y;
3021
3022   /* set game engine from tape file when re-playing, else from level file */
3023   game.engine_version = (tape.playing ? tape.engine_version :
3024                          level.game_version);
3025
3026   /* ---------------------------------------------------------------------- */
3027   /* set flags for bugs and changes according to active game engine version */
3028   /* ---------------------------------------------------------------------- */
3029
3030   /*
3031     Summary of bugfix/change:
3032     Fixed handling for custom elements that change when pushed by the player.
3033
3034     Fixed/changed in version:
3035     3.1.0
3036
3037     Description:
3038     Before 3.1.0, custom elements that "change when pushing" changed directly
3039     after the player started pushing them (until then handled in "DigField()").
3040     Since 3.1.0, these custom elements are not changed until the "pushing"
3041     move of the element is finished (now handled in "ContinueMoving()").
3042
3043     Affected levels/tapes:
3044     The first condition is generally needed for all levels/tapes before version
3045     3.1.0, which might use the old behaviour before it was changed; known tapes
3046     that are affected are some tapes from the level set "Walpurgis Gardens" by
3047     Jamie Cullen.
3048     The second condition is an exception from the above case and is needed for
3049     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3050     above (including some development versions of 3.1.0), but before it was
3051     known that this change would break tapes like the above and was fixed in
3052     3.1.1, so that the changed behaviour was active although the engine version
3053     while recording maybe was before 3.1.0. There is at least one tape that is
3054     affected by this exception, which is the tape for the one-level set "Bug
3055     Machine" by Juergen Bonhagen.
3056   */
3057
3058   game.use_change_when_pushing_bug =
3059     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3060      !(tape.playing &&
3061        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3062        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3063
3064   /*
3065     Summary of bugfix/change:
3066     Fixed handling for blocking the field the player leaves when moving.
3067
3068     Fixed/changed in version:
3069     3.1.1
3070
3071     Description:
3072     Before 3.1.1, when "block last field when moving" was enabled, the field
3073     the player is leaving when moving was blocked for the time of the move,
3074     and was directly unblocked afterwards. This resulted in the last field
3075     being blocked for exactly one less than the number of frames of one player
3076     move. Additionally, even when blocking was disabled, the last field was
3077     blocked for exactly one frame.
3078     Since 3.1.1, due to changes in player movement handling, the last field
3079     is not blocked at all when blocking is disabled. When blocking is enabled,
3080     the last field is blocked for exactly the number of frames of one player
3081     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3082     last field is blocked for exactly one more than the number of frames of
3083     one player move.
3084
3085     Affected levels/tapes:
3086     (!!! yet to be determined -- probably many !!!)
3087   */
3088
3089   game.use_block_last_field_bug =
3090     (game.engine_version < VERSION_IDENT(3,1,1,0));
3091
3092   /*
3093     Summary of bugfix/change:
3094     Changed behaviour of CE changes with multiple changes per single frame.
3095
3096     Fixed/changed in version:
3097     3.2.0-6
3098
3099     Description:
3100     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3101     This resulted in race conditions where CEs seem to behave strange in some
3102     situations (where triggered CE changes were just skipped because there was
3103     already a CE change on that tile in the playfield in that engine frame).
3104     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3105     (The number of changes per frame must be limited in any case, because else
3106     it is easily possible to define CE changes that would result in an infinite
3107     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3108     should be set large enough so that it would only be reached in cases where
3109     the corresponding CE change conditions run into a loop. Therefore, it seems
3110     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3111     maximal number of change pages for custom elements.)
3112
3113     Affected levels/tapes:
3114     Probably many.
3115   */
3116
3117 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3118   game.max_num_changes_per_frame = 1;
3119 #else
3120   game.max_num_changes_per_frame =
3121     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3122 #endif
3123
3124   /* ---------------------------------------------------------------------- */
3125
3126   /* default scan direction: scan playfield from top/left to bottom/right */
3127   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3128
3129   /* dynamically adjust element properties according to game engine version */
3130   InitElementPropertiesEngine(game.engine_version);
3131
3132 #if 0
3133   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3134   printf("          tape version == %06d [%s] [file: %06d]\n",
3135          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3136          tape.file_version);
3137   printf("       => game.engine_version == %06d\n", game.engine_version);
3138 #endif
3139
3140   /* ---------- initialize player's initial move delay --------------------- */
3141
3142   /* dynamically adjust player properties according to level information */
3143   for (i = 0; i < MAX_PLAYERS; i++)
3144     game.initial_move_delay_value[i] =
3145       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3146
3147   /* dynamically adjust player properties according to game engine version */
3148   for (i = 0; i < MAX_PLAYERS; i++)
3149     game.initial_move_delay[i] =
3150       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3151        game.initial_move_delay_value[i] : 0);
3152
3153   /* ---------- initialize player's initial push delay --------------------- */
3154
3155   /* dynamically adjust player properties according to game engine version */
3156   game.initial_push_delay_value =
3157     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3158
3159   /* ---------- initialize changing elements ------------------------------- */
3160
3161   /* initialize changing elements information */
3162   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3163   {
3164     struct ElementInfo *ei = &element_info[i];
3165
3166     /* this pointer might have been changed in the level editor */
3167     ei->change = &ei->change_page[0];
3168
3169     if (!IS_CUSTOM_ELEMENT(i))
3170     {
3171       ei->change->target_element = EL_EMPTY_SPACE;
3172       ei->change->delay_fixed = 0;
3173       ei->change->delay_random = 0;
3174       ei->change->delay_frames = 1;
3175     }
3176
3177     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3178     {
3179       ei->has_change_event[j] = FALSE;
3180
3181       ei->event_page_nr[j] = 0;
3182       ei->event_page[j] = &ei->change_page[0];
3183     }
3184   }
3185
3186   /* add changing elements from pre-defined list */
3187   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3188   {
3189     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3190     struct ElementInfo *ei = &element_info[ch_delay->element];
3191
3192     ei->change->target_element       = ch_delay->target_element;
3193     ei->change->delay_fixed          = ch_delay->change_delay;
3194
3195     ei->change->pre_change_function  = ch_delay->pre_change_function;
3196     ei->change->change_function      = ch_delay->change_function;
3197     ei->change->post_change_function = ch_delay->post_change_function;
3198
3199     ei->change->can_change = TRUE;
3200     ei->change->can_change_or_has_action = TRUE;
3201
3202     ei->has_change_event[CE_DELAY] = TRUE;
3203
3204     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3205     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3206   }
3207
3208   /* ---------- initialize internal run-time variables --------------------- */
3209
3210   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3211   {
3212     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3213
3214     for (j = 0; j < ei->num_change_pages; j++)
3215     {
3216       ei->change_page[j].can_change_or_has_action =
3217         (ei->change_page[j].can_change |
3218          ei->change_page[j].has_action);
3219     }
3220   }
3221
3222   /* add change events from custom element configuration */
3223   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3224   {
3225     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3226
3227     for (j = 0; j < ei->num_change_pages; j++)
3228     {
3229       if (!ei->change_page[j].can_change_or_has_action)
3230         continue;
3231
3232       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3233       {
3234         /* only add event page for the first page found with this event */
3235         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3236         {
3237           ei->has_change_event[k] = TRUE;
3238
3239           ei->event_page_nr[k] = j;
3240           ei->event_page[k] = &ei->change_page[j];
3241         }
3242       }
3243     }
3244   }
3245
3246 #if 1
3247   /* ---------- initialize reference elements in change conditions --------- */
3248
3249   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3250   {
3251     int element = EL_CUSTOM_START + i;
3252     struct ElementInfo *ei = &element_info[element];
3253
3254     for (j = 0; j < ei->num_change_pages; j++)
3255     {
3256       int trigger_element = ei->change_page[j].initial_trigger_element;
3257
3258       if (trigger_element >= EL_PREV_CE_8 &&
3259           trigger_element <= EL_NEXT_CE_8)
3260         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3261
3262       ei->change_page[j].trigger_element = trigger_element;
3263     }
3264   }
3265 #endif
3266
3267   /* ---------- initialize run-time trigger player and element ------------- */
3268
3269   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3270   {
3271     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3272
3273     for (j = 0; j < ei->num_change_pages; j++)
3274     {
3275       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3276       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3277       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3278       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3279       ei->change_page[j].actual_trigger_ce_value = 0;
3280       ei->change_page[j].actual_trigger_ce_score = 0;
3281     }
3282   }
3283
3284   /* ---------- initialize trigger events ---------------------------------- */
3285
3286   /* initialize trigger events information */
3287   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3288     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3289       trigger_events[i][j] = FALSE;
3290
3291   /* add trigger events from element change event properties */
3292   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3293   {
3294     struct ElementInfo *ei = &element_info[i];
3295
3296     for (j = 0; j < ei->num_change_pages; j++)
3297     {
3298       if (!ei->change_page[j].can_change_or_has_action)
3299         continue;
3300
3301       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3302       {
3303         int trigger_element = ei->change_page[j].trigger_element;
3304
3305         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3306         {
3307           if (ei->change_page[j].has_event[k])
3308           {
3309             if (IS_GROUP_ELEMENT(trigger_element))
3310             {
3311               struct ElementGroupInfo *group =
3312                 element_info[trigger_element].group;
3313
3314               for (l = 0; l < group->num_elements_resolved; l++)
3315                 trigger_events[group->element_resolved[l]][k] = TRUE;
3316             }
3317             else if (trigger_element == EL_ANY_ELEMENT)
3318               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3319                 trigger_events[l][k] = TRUE;
3320             else
3321               trigger_events[trigger_element][k] = TRUE;
3322           }
3323         }
3324       }
3325     }
3326   }
3327
3328   /* ---------- initialize push delay -------------------------------------- */
3329
3330   /* initialize push delay values to default */
3331   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3332   {
3333     if (!IS_CUSTOM_ELEMENT(i))
3334     {
3335       /* set default push delay values (corrected since version 3.0.7-1) */
3336       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3337       {
3338         element_info[i].push_delay_fixed = 2;
3339         element_info[i].push_delay_random = 8;
3340       }
3341       else
3342       {
3343         element_info[i].push_delay_fixed = 8;
3344         element_info[i].push_delay_random = 8;
3345       }
3346     }
3347   }
3348
3349   /* set push delay value for certain elements from pre-defined list */
3350   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3351   {
3352     int e = push_delay_list[i].element;
3353
3354     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3355     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3356   }
3357
3358   /* set push delay value for Supaplex elements for newer engine versions */
3359   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3360   {
3361     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3362     {
3363       if (IS_SP_ELEMENT(i))
3364       {
3365         /* set SP push delay to just enough to push under a falling zonk */
3366         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3367
3368         element_info[i].push_delay_fixed  = delay;
3369         element_info[i].push_delay_random = 0;
3370       }
3371     }
3372   }
3373
3374   /* ---------- initialize move stepsize ----------------------------------- */
3375
3376   /* initialize move stepsize values to default */
3377   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3378     if (!IS_CUSTOM_ELEMENT(i))
3379       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3380
3381   /* set move stepsize value for certain elements from pre-defined list */
3382   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3383   {
3384     int e = move_stepsize_list[i].element;
3385
3386     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3387   }
3388
3389   /* ---------- initialize collect score ----------------------------------- */
3390
3391   /* initialize collect score values for custom elements from initial value */
3392   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3393     if (IS_CUSTOM_ELEMENT(i))
3394       element_info[i].collect_score = element_info[i].collect_score_initial;
3395
3396   /* ---------- initialize collect count ----------------------------------- */
3397
3398   /* initialize collect count values for non-custom elements */
3399   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3400     if (!IS_CUSTOM_ELEMENT(i))
3401       element_info[i].collect_count_initial = 0;
3402
3403   /* add collect count values for all elements from pre-defined list */
3404   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3405     element_info[collect_count_list[i].element].collect_count_initial =
3406       collect_count_list[i].count;
3407
3408   /* ---------- initialize access direction -------------------------------- */
3409
3410   /* initialize access direction values to default (access from every side) */
3411   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3412     if (!IS_CUSTOM_ELEMENT(i))
3413       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3414
3415   /* set access direction value for certain elements from pre-defined list */
3416   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3417     element_info[access_direction_list[i].element].access_direction =
3418       access_direction_list[i].direction;
3419
3420   /* ---------- initialize explosion content ------------------------------- */
3421   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3422   {
3423     if (IS_CUSTOM_ELEMENT(i))
3424       continue;
3425
3426     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3427     {
3428       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3429
3430       element_info[i].content.e[x][y] =
3431         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3432          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3433          i == EL_PLAYER_3 ? EL_EMERALD :
3434          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3435          i == EL_MOLE ? EL_EMERALD_RED :
3436          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3437          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3438          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3439          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3440          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3441          i == EL_WALL_EMERALD ? EL_EMERALD :
3442          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3443          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3444          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3445          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3446          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3447          i == EL_WALL_PEARL ? EL_PEARL :
3448          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3449          EL_EMPTY);
3450     }
3451   }
3452
3453   /* ---------- initialize recursion detection ------------------------------ */
3454   recursion_loop_depth = 0;
3455   recursion_loop_detected = FALSE;
3456   recursion_loop_element = EL_UNDEFINED;
3457
3458   /* ---------- initialize graphics engine ---------------------------------- */
3459   game.scroll_delay_value =
3460     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3461      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3462   game.scroll_delay_value =
3463     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3464 }
3465
3466 int get_num_special_action(int element, int action_first, int action_last)
3467 {
3468   int num_special_action = 0;
3469   int i, j;
3470
3471   for (i = action_first; i <= action_last; i++)
3472   {
3473     boolean found = FALSE;
3474
3475     for (j = 0; j < NUM_DIRECTIONS; j++)
3476       if (el_act_dir2img(element, i, j) !=
3477           el_act_dir2img(element, ACTION_DEFAULT, j))
3478         found = TRUE;
3479
3480     if (found)
3481       num_special_action++;
3482     else
3483       break;
3484   }
3485
3486   return num_special_action;
3487 }
3488
3489
3490 /*
3491   =============================================================================
3492   InitGame()
3493   -----------------------------------------------------------------------------
3494   initialize and start new game
3495   =============================================================================
3496 */
3497
3498 void InitGame()
3499 {
3500   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3501   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3502   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3503 #if 0
3504   boolean do_fading = (game_status == GAME_MODE_MAIN);
3505 #endif
3506 #if 1
3507   int initial_move_dir = MV_DOWN;
3508 #else
3509   int initial_move_dir = MV_NONE;
3510 #endif
3511   int i, j, x, y;
3512
3513   game_status = GAME_MODE_PLAYING;
3514
3515 #if 1
3516   /* needed if different viewport properties defined for playing */
3517   ChangeViewportPropertiesIfNeeded();
3518 #endif
3519
3520 #if 1
3521   DrawCompleteVideoDisplay();
3522 #endif
3523
3524   InitGameEngine();
3525   InitGameControlValues();
3526
3527   /* don't play tapes over network */
3528   network_playing = (options.network && !tape.playing);
3529
3530   for (i = 0; i < MAX_PLAYERS; i++)
3531   {
3532     struct PlayerInfo *player = &stored_player[i];
3533
3534     player->index_nr = i;
3535     player->index_bit = (1 << i);
3536     player->element_nr = EL_PLAYER_1 + i;
3537
3538     player->present = FALSE;
3539     player->active = FALSE;
3540     player->mapped = FALSE;
3541
3542     player->killed = FALSE;
3543     player->reanimated = FALSE;
3544
3545     player->action = 0;
3546     player->effective_action = 0;
3547     player->programmed_action = 0;
3548
3549     player->score = 0;
3550     player->score_final = 0;
3551
3552     player->gems_still_needed = level.gems_needed;
3553     player->sokobanfields_still_needed = 0;
3554     player->lights_still_needed = 0;
3555     player->friends_still_needed = 0;
3556
3557     for (j = 0; j < MAX_NUM_KEYS; j++)
3558       player->key[j] = FALSE;
3559
3560     player->num_white_keys = 0;
3561
3562     player->dynabomb_count = 0;
3563     player->dynabomb_size = 1;
3564     player->dynabombs_left = 0;
3565     player->dynabomb_xl = FALSE;
3566
3567     player->MovDir = initial_move_dir;
3568     player->MovPos = 0;
3569     player->GfxPos = 0;
3570     player->GfxDir = initial_move_dir;
3571     player->GfxAction = ACTION_DEFAULT;
3572     player->Frame = 0;
3573     player->StepFrame = 0;
3574
3575     player->initial_element = player->element_nr;
3576     player->artwork_element =
3577       (level.use_artwork_element[i] ? level.artwork_element[i] :
3578        player->element_nr);
3579     player->use_murphy = FALSE;
3580
3581     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3582     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3583
3584     player->gravity = level.initial_player_gravity[i];
3585
3586     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3587
3588     player->actual_frame_counter = 0;
3589
3590     player->step_counter = 0;
3591
3592     player->last_move_dir = initial_move_dir;
3593
3594     player->is_active = FALSE;
3595
3596     player->is_waiting = FALSE;
3597     player->is_moving = FALSE;
3598     player->is_auto_moving = FALSE;
3599     player->is_digging = FALSE;
3600     player->is_snapping = FALSE;
3601     player->is_collecting = FALSE;
3602     player->is_pushing = FALSE;
3603     player->is_switching = FALSE;
3604     player->is_dropping = FALSE;
3605     player->is_dropping_pressed = FALSE;
3606
3607     player->is_bored = FALSE;
3608     player->is_sleeping = FALSE;
3609
3610     player->frame_counter_bored = -1;
3611     player->frame_counter_sleeping = -1;
3612
3613     player->anim_delay_counter = 0;
3614     player->post_delay_counter = 0;
3615
3616     player->dir_waiting = initial_move_dir;
3617     player->action_waiting = ACTION_DEFAULT;
3618     player->last_action_waiting = ACTION_DEFAULT;
3619     player->special_action_bored = ACTION_DEFAULT;
3620     player->special_action_sleeping = ACTION_DEFAULT;
3621
3622     player->switch_x = -1;
3623     player->switch_y = -1;
3624
3625     player->drop_x = -1;
3626     player->drop_y = -1;
3627
3628     player->show_envelope = 0;
3629
3630     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3631
3632     player->push_delay       = -1;      /* initialized when pushing starts */
3633     player->push_delay_value = game.initial_push_delay_value;
3634
3635     player->drop_delay = 0;
3636     player->drop_pressed_delay = 0;
3637
3638     player->last_jx = -1;
3639     player->last_jy = -1;
3640     player->jx = -1;
3641     player->jy = -1;
3642
3643     player->shield_normal_time_left = 0;
3644     player->shield_deadly_time_left = 0;
3645
3646     player->inventory_infinite_element = EL_UNDEFINED;
3647     player->inventory_size = 0;
3648
3649     if (level.use_initial_inventory[i])
3650     {
3651       for (j = 0; j < level.initial_inventory_size[i]; j++)
3652       {
3653         int element = level.initial_inventory_content[i][j];
3654         int collect_count = element_info[element].collect_count_initial;
3655         int k;
3656
3657         if (!IS_CUSTOM_ELEMENT(element))
3658           collect_count = 1;
3659
3660         if (collect_count == 0)
3661           player->inventory_infinite_element = element;
3662         else
3663           for (k = 0; k < collect_count; k++)
3664             if (player->inventory_size < MAX_INVENTORY_SIZE)
3665               player->inventory_element[player->inventory_size++] = element;
3666       }
3667     }
3668
3669     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3670     SnapField(player, 0, 0);
3671
3672     player->LevelSolved = FALSE;
3673     player->GameOver = FALSE;
3674
3675     player->LevelSolved_GameWon = FALSE;
3676     player->LevelSolved_GameEnd = FALSE;
3677     player->LevelSolved_PanelOff = FALSE;
3678     player->LevelSolved_SaveTape = FALSE;
3679     player->LevelSolved_SaveScore = FALSE;
3680     player->LevelSolved_CountingTime = 0;
3681     player->LevelSolved_CountingScore = 0;
3682
3683     map_player_action[i] = i;
3684   }
3685
3686   network_player_action_received = FALSE;
3687
3688 #if defined(NETWORK_AVALIABLE)
3689   /* initial null action */
3690   if (network_playing)
3691     SendToServer_MovePlayer(MV_NONE);
3692 #endif
3693
3694   ZX = ZY = -1;
3695   ExitX = ExitY = -1;
3696
3697   FrameCounter = 0;
3698   TimeFrames = 0;
3699   TimePlayed = 0;
3700   TimeLeft = level.time;
3701   TapeTime = 0;
3702
3703   ScreenMovDir = MV_NONE;
3704   ScreenMovPos = 0;
3705   ScreenGfxPos = 0;
3706
3707   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3708
3709   AllPlayersGone = FALSE;
3710
3711   game.no_time_limit = (level.time == 0);
3712
3713   game.yamyam_content_nr = 0;
3714   game.robot_wheel_active = FALSE;
3715   game.magic_wall_active = FALSE;
3716   game.magic_wall_time_left = 0;
3717   game.light_time_left = 0;
3718   game.timegate_time_left = 0;
3719   game.switchgate_pos = 0;
3720   game.wind_direction = level.wind_direction_initial;
3721
3722 #if !USE_PLAYER_GRAVITY
3723   game.gravity = FALSE;
3724   game.explosions_delayed = TRUE;
3725 #endif
3726
3727   game.lenses_time_left = 0;
3728   game.magnify_time_left = 0;
3729
3730   game.ball_state = level.ball_state_initial;
3731   game.ball_content_nr = 0;
3732
3733   game.envelope_active = FALSE;
3734
3735   /* set focus to local player for network games, else to all players */
3736   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3737   game.centered_player_nr_next = game.centered_player_nr;
3738   game.set_centered_player = FALSE;
3739
3740   if (network_playing && tape.recording)
3741   {
3742     /* store client dependent player focus when recording network games */
3743     tape.centered_player_nr_next = game.centered_player_nr_next;
3744     tape.set_centered_player = TRUE;
3745   }
3746
3747   for (i = 0; i < NUM_BELTS; i++)
3748   {
3749     game.belt_dir[i] = MV_NONE;
3750     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3751   }
3752
3753   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3754     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3755
3756   SCAN_PLAYFIELD(x, y)
3757   {
3758     Feld[x][y] = level.field[x][y];
3759     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3760     ChangeDelay[x][y] = 0;
3761     ChangePage[x][y] = -1;
3762 #if USE_NEW_CUSTOM_VALUE
3763     CustomValue[x][y] = 0;              /* initialized in InitField() */
3764 #endif
3765     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3766     AmoebaNr[x][y] = 0;
3767     WasJustMoving[x][y] = 0;
3768     WasJustFalling[x][y] = 0;
3769     CheckCollision[x][y] = 0;
3770     CheckImpact[x][y] = 0;
3771     Stop[x][y] = FALSE;
3772     Pushed[x][y] = FALSE;
3773
3774     ChangeCount[x][y] = 0;
3775     ChangeEvent[x][y] = -1;
3776
3777     ExplodePhase[x][y] = 0;
3778     ExplodeDelay[x][y] = 0;
3779     ExplodeField[x][y] = EX_TYPE_NONE;
3780
3781     RunnerVisit[x][y] = 0;
3782     PlayerVisit[x][y] = 0;
3783
3784     GfxFrame[x][y] = 0;
3785     GfxRandom[x][y] = INIT_GFX_RANDOM();
3786     GfxElement[x][y] = EL_UNDEFINED;
3787     GfxAction[x][y] = ACTION_DEFAULT;
3788     GfxDir[x][y] = MV_NONE;
3789     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3790   }
3791
3792   SCAN_PLAYFIELD(x, y)
3793   {
3794     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3795       emulate_bd = FALSE;
3796     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3797       emulate_sb = FALSE;
3798     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3799       emulate_sp = FALSE;
3800
3801     InitField(x, y, TRUE);
3802
3803     ResetGfxAnimation(x, y);
3804   }
3805
3806   InitBeltMovement();
3807
3808   for (i = 0; i < MAX_PLAYERS; i++)
3809   {
3810     struct PlayerInfo *player = &stored_player[i];
3811
3812     /* set number of special actions for bored and sleeping animation */
3813     player->num_special_action_bored =
3814       get_num_special_action(player->artwork_element,
3815                              ACTION_BORING_1, ACTION_BORING_LAST);
3816     player->num_special_action_sleeping =
3817       get_num_special_action(player->artwork_element,
3818                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3819   }
3820
3821   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3822                     emulate_sb ? EMU_SOKOBAN :
3823                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3824
3825 #if USE_NEW_ALL_SLIPPERY
3826   /* initialize type of slippery elements */
3827   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3828   {
3829     if (!IS_CUSTOM_ELEMENT(i))
3830     {
3831       /* default: elements slip down either to the left or right randomly */
3832       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3833
3834       /* SP style elements prefer to slip down on the left side */
3835       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3836         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3837
3838       /* BD style elements prefer to slip down on the left side */
3839       if (game.emulation == EMU_BOULDERDASH)
3840         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3841     }
3842   }
3843 #endif
3844
3845   /* initialize explosion and ignition delay */
3846   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3847   {
3848     if (!IS_CUSTOM_ELEMENT(i))
3849     {
3850       int num_phase = 8;
3851       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3852                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3853                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3854       int last_phase = (num_phase + 1) * delay;
3855       int half_phase = (num_phase / 2) * delay;
3856
3857       element_info[i].explosion_delay = last_phase - 1;
3858       element_info[i].ignition_delay = half_phase;
3859
3860       if (i == EL_BLACK_ORB)
3861         element_info[i].ignition_delay = 1;
3862     }
3863
3864 #if 0
3865     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3866       element_info[i].explosion_delay = 1;
3867
3868     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3869       element_info[i].ignition_delay = 1;
3870 #endif
3871   }
3872
3873   /* correct non-moving belts to start moving left */
3874   for (i = 0; i < NUM_BELTS; i++)
3875     if (game.belt_dir[i] == MV_NONE)
3876       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3877
3878 #if USE_NEW_PLAYER_ASSIGNMENTS
3879   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3880   /* choose default local player */
3881   local_player = &stored_player[0];
3882
3883   for (i = 0; i < MAX_PLAYERS; i++)
3884     stored_player[i].connected = FALSE;
3885
3886   local_player->connected = TRUE;
3887   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3888
3889   if (tape.playing)
3890   {
3891     /* try to guess locally connected team mode players (needed for correct
3892        assignment of player figures from level to locally playing players) */
3893
3894     for (i = 0; i < MAX_PLAYERS; i++)
3895       if (tape.player_participates[i])
3896         stored_player[i].connected = TRUE;
3897   }
3898   else if (setup.team_mode && !options.network)
3899   {
3900     /* try to guess locally connected team mode players (needed for correct
3901        assignment of player figures from level to locally playing players) */
3902
3903     for (i = 0; i < MAX_PLAYERS; i++)
3904       if (setup.input[i].use_joystick ||
3905           setup.input[i].key.left != KSYM_UNDEFINED)
3906         stored_player[i].connected = TRUE;
3907   }
3908
3909 #if 0
3910   for (i = 0; i < MAX_PLAYERS; i++)
3911     printf("::: player %d: %s\n", i,
3912            (stored_player[i].connected ? "connected" : "not connected"));
3913
3914   for (i = 0; i < MAX_PLAYERS; i++)
3915     printf("::: player %d: %s\n", i,
3916            (stored_player[i].present ? "present" : "not present"));
3917 #endif
3918
3919   /* check if any connected player was not found in playfield */
3920   for (i = 0; i < MAX_PLAYERS; i++)
3921   {
3922     struct PlayerInfo *player = &stored_player[i];
3923
3924     if (player->connected && !player->present)
3925     {
3926       struct PlayerInfo *field_player = NULL;
3927
3928 #if 0
3929       printf("::: looking for field player for player %d ...\n", i);
3930 #endif
3931
3932       /* assign first free player found that is present in the playfield */
3933
3934       /* first try: look for unmapped playfield player that is not connected */
3935       if (field_player == NULL)
3936         for (j = 0; j < MAX_PLAYERS; j++)
3937           if (stored_player[j].present &&
3938               !stored_player[j].mapped &&
3939               !stored_player[j].connected)
3940             field_player = &stored_player[j];
3941
3942       /* second try: look for *any* unmapped playfield player */
3943       if (field_player == NULL)
3944         for (j = 0; j < MAX_PLAYERS; j++)
3945           if (stored_player[j].present &&
3946               !stored_player[j].mapped)
3947             field_player = &stored_player[j];
3948
3949       if (field_player != NULL)
3950       {
3951         int jx = field_player->jx, jy = field_player->jy;
3952
3953 #if 0
3954         printf("::: found player figure %d\n", field_player->index_nr);
3955 #endif
3956
3957         player->present = FALSE;
3958         player->active = FALSE;
3959
3960         field_player->present = TRUE;
3961         field_player->active = TRUE;
3962
3963         /*
3964         player->initial_element = field_player->initial_element;
3965         player->artwork_element = field_player->artwork_element;
3966
3967         player->block_last_field       = field_player->block_last_field;
3968         player->block_delay_adjustment = field_player->block_delay_adjustment;
3969         */
3970
3971         StorePlayer[jx][jy] = field_player->element_nr;
3972
3973         field_player->jx = field_player->last_jx = jx;
3974         field_player->jy = field_player->last_jy = jy;
3975
3976         if (local_player == player)
3977           local_player = field_player;
3978
3979         map_player_action[field_player->index_nr] = i;
3980
3981         field_player->mapped = TRUE;
3982
3983 #if 0
3984         printf("::: map_player_action[%d] == %d\n",
3985                field_player->index_nr, i);
3986 #endif
3987       }
3988     }
3989
3990     if (player->connected && player->present)
3991       player->mapped = TRUE;
3992   }
3993
3994 #else
3995
3996   /* check if any connected player was not found in playfield */
3997   for (i = 0; i < MAX_PLAYERS; i++)
3998   {
3999     struct PlayerInfo *player = &stored_player[i];
4000
4001     if (player->connected && !player->present)
4002     {
4003       for (j = 0; j < MAX_PLAYERS; j++)
4004       {
4005         struct PlayerInfo *field_player = &stored_player[j];
4006         int jx = field_player->jx, jy = field_player->jy;
4007
4008         /* assign first free player found that is present in the playfield */
4009         if (field_player->present && !field_player->connected)
4010         {
4011           player->present = TRUE;
4012           player->active = TRUE;
4013
4014           field_player->present = FALSE;
4015           field_player->active = FALSE;
4016
4017           player->initial_element = field_player->initial_element;
4018           player->artwork_element = field_player->artwork_element;
4019
4020           player->block_last_field       = field_player->block_last_field;
4021           player->block_delay_adjustment = field_player->block_delay_adjustment;
4022
4023           StorePlayer[jx][jy] = player->element_nr;
4024
4025           player->jx = player->last_jx = jx;
4026           player->jy = player->last_jy = jy;
4027
4028           break;
4029         }
4030       }
4031     }
4032   }
4033 #endif
4034
4035 #if 0
4036   printf("::: local_player->present == %d\n", local_player->present);
4037 #endif
4038
4039   if (tape.playing)
4040   {
4041     /* when playing a tape, eliminate all players who do not participate */
4042
4043 #if USE_NEW_PLAYER_ASSIGNMENTS
4044     for (i = 0; i < MAX_PLAYERS; i++)
4045     {
4046       if (stored_player[i].active &&
4047           !tape.player_participates[map_player_action[i]])
4048       {
4049         struct PlayerInfo *player = &stored_player[i];
4050         int jx = player->jx, jy = player->jy;
4051
4052         player->active = FALSE;
4053         StorePlayer[jx][jy] = 0;
4054         Feld[jx][jy] = EL_EMPTY;
4055       }
4056     }
4057 #else
4058     for (i = 0; i < MAX_PLAYERS; i++)
4059     {
4060       if (stored_player[i].active &&
4061           !tape.player_participates[i])
4062       {
4063         struct PlayerInfo *player = &stored_player[i];
4064         int jx = player->jx, jy = player->jy;
4065
4066         player->active = FALSE;
4067         StorePlayer[jx][jy] = 0;
4068         Feld[jx][jy] = EL_EMPTY;
4069       }
4070     }
4071 #endif
4072   }
4073   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4074   {
4075     /* when in single player mode, eliminate all but the first active player */
4076
4077     for (i = 0; i < MAX_PLAYERS; i++)
4078     {
4079       if (stored_player[i].active)
4080       {
4081         for (j = i + 1; j < MAX_PLAYERS; j++)
4082         {
4083           if (stored_player[j].active)
4084           {
4085             struct PlayerInfo *player = &stored_player[j];
4086             int jx = player->jx, jy = player->jy;
4087
4088             player->active = FALSE;
4089             player->present = FALSE;
4090
4091             StorePlayer[jx][jy] = 0;
4092             Feld[jx][jy] = EL_EMPTY;
4093           }
4094         }
4095       }
4096     }
4097   }
4098
4099   /* when recording the game, store which players take part in the game */
4100   if (tape.recording)
4101   {
4102 #if USE_NEW_PLAYER_ASSIGNMENTS
4103     for (i = 0; i < MAX_PLAYERS; i++)
4104       if (stored_player[i].connected)
4105         tape.player_participates[i] = TRUE;
4106 #else
4107     for (i = 0; i < MAX_PLAYERS; i++)
4108       if (stored_player[i].active)
4109         tape.player_participates[i] = TRUE;
4110 #endif
4111   }
4112
4113   if (options.debug)
4114   {
4115     for (i = 0; i < MAX_PLAYERS; i++)
4116     {
4117       struct PlayerInfo *player = &stored_player[i];
4118
4119       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4120              i+1,
4121              player->present,
4122              player->connected,
4123              player->active);
4124       if (local_player == player)
4125         printf("Player  %d is local player.\n", i+1);
4126     }
4127   }
4128
4129   if (BorderElement == EL_EMPTY)
4130   {
4131     SBX_Left = 0;
4132     SBX_Right = lev_fieldx - SCR_FIELDX;
4133     SBY_Upper = 0;
4134     SBY_Lower = lev_fieldy - SCR_FIELDY;
4135   }
4136   else
4137   {
4138     SBX_Left = -1;
4139     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4140     SBY_Upper = -1;
4141     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4142   }
4143
4144 #if NEW_TILESIZE
4145
4146   if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4147     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4148
4149   if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4150     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4151
4152   if (EVEN(SCR_FIELDX))
4153     SBX_Left--;
4154   if (EVEN(SCR_FIELDY))
4155     SBY_Upper--;
4156
4157 #else
4158
4159   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4160     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4161
4162   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4163     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4164 #endif
4165
4166   /* if local player not found, look for custom element that might create
4167      the player (make some assumptions about the right custom element) */
4168   if (!local_player->present)
4169   {
4170     int start_x = 0, start_y = 0;
4171     int found_rating = 0;
4172     int found_element = EL_UNDEFINED;
4173     int player_nr = local_player->index_nr;
4174
4175     SCAN_PLAYFIELD(x, y)
4176     {
4177       int element = Feld[x][y];
4178       int content;
4179       int xx, yy;
4180       boolean is_player;
4181
4182       if (level.use_start_element[player_nr] &&
4183           level.start_element[player_nr] == element &&
4184           found_rating < 4)
4185       {
4186         start_x = x;
4187         start_y = y;
4188
4189         found_rating = 4;
4190         found_element = element;
4191       }
4192
4193       if (!IS_CUSTOM_ELEMENT(element))
4194         continue;
4195
4196       if (CAN_CHANGE(element))
4197       {
4198         for (i = 0; i < element_info[element].num_change_pages; i++)
4199         {
4200           /* check for player created from custom element as single target */
4201           content = element_info[element].change_page[i].target_element;
4202           is_player = ELEM_IS_PLAYER(content);
4203
4204           if (is_player && (found_rating < 3 ||
4205                             (found_rating == 3 && element < found_element)))
4206           {
4207             start_x = x;
4208             start_y = y;
4209
4210             found_rating = 3;
4211             found_element = element;
4212           }
4213         }
4214       }
4215
4216       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4217       {
4218         /* check for player created from custom element as explosion content */
4219         content = element_info[element].content.e[xx][yy];
4220         is_player = ELEM_IS_PLAYER(content);
4221
4222         if (is_player && (found_rating < 2 ||
4223                           (found_rating == 2 && element < found_element)))
4224         {
4225           start_x = x + xx - 1;
4226           start_y = y + yy - 1;
4227
4228           found_rating = 2;
4229           found_element = element;
4230         }
4231
4232         if (!CAN_CHANGE(element))
4233           continue;
4234
4235         for (i = 0; i < element_info[element].num_change_pages; i++)
4236         {
4237           /* check for player created from custom element as extended target */
4238           content =
4239             element_info[element].change_page[i].target_content.e[xx][yy];
4240
4241           is_player = ELEM_IS_PLAYER(content);
4242
4243           if (is_player && (found_rating < 1 ||
4244                             (found_rating == 1 && element < found_element)))
4245           {
4246             start_x = x + xx - 1;
4247             start_y = y + yy - 1;
4248
4249             found_rating = 1;
4250             found_element = element;
4251           }
4252         }
4253       }
4254     }
4255
4256     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4257                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4258                 start_x - MIDPOSX);
4259
4260     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4261                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4262                 start_y - MIDPOSY);
4263   }
4264   else
4265   {
4266     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4267                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4268                 local_player->jx - MIDPOSX);
4269
4270     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4271                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4272                 local_player->jy - MIDPOSY);
4273   }
4274
4275 #if 0
4276   printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4277 #endif
4278
4279 #if 0
4280   /* do not use PLAYING mask for fading out from main screen */
4281   game_status = GAME_MODE_MAIN;
4282 #endif
4283
4284   StopAnimation();
4285
4286   if (!game.restart_level)
4287     CloseDoor(DOOR_CLOSE_1);
4288
4289 #if 1
4290   if (level_editor_test_game)
4291     FadeSkipNextFadeIn();
4292   else
4293     FadeSetEnterScreen();
4294 #else
4295   if (level_editor_test_game)
4296     fading = fading_none;
4297   else
4298     fading = menu.destination;
4299 #endif
4300
4301 #if 1
4302   FadeOut(REDRAW_FIELD);
4303 #else
4304   if (do_fading)
4305     FadeOut(REDRAW_FIELD);
4306 #endif
4307
4308 #if 0
4309   game_status = GAME_MODE_PLAYING;
4310 #endif
4311
4312   /* !!! FIX THIS (START) !!! */
4313   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4314   {
4315     InitGameEngine_EM();
4316
4317     /* blit playfield from scroll buffer to normal back buffer for fading in */
4318     BlitScreenToBitmap_EM(backbuffer);
4319   }
4320   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4321   {
4322     InitGameEngine_SP();
4323
4324     /* blit playfield from scroll buffer to normal back buffer for fading in */
4325     BlitScreenToBitmap_SP(backbuffer);
4326   }
4327   else
4328   {
4329     DrawLevel();
4330     DrawAllPlayers();
4331
4332     /* after drawing the level, correct some elements */
4333     if (game.timegate_time_left == 0)
4334       CloseAllOpenTimegates();
4335
4336 #if NEW_TILESIZE
4337     BlitScreenToBitmap(backbuffer);
4338 #else
4339     /* blit playfield from scroll buffer to normal back buffer for fading in */
4340     if (setup.soft_scrolling)
4341       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4342 #endif
4343
4344     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4345   }
4346   /* !!! FIX THIS (END) !!! */
4347
4348 #if 1
4349   FadeIn(REDRAW_FIELD);
4350 #else
4351   if (do_fading)
4352     FadeIn(REDRAW_FIELD);
4353
4354   BackToFront();
4355 #endif
4356
4357   if (!game.restart_level)
4358   {
4359     /* copy default game door content to main double buffer */
4360 #if 1
4361 #if 1
4362     /* !!! CHECK AGAIN !!! */
4363     SetPanelBackground();
4364     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4365     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4366 #else
4367     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4368
4369     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4370     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4371     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4372                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4373 #endif
4374 #else
4375     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4376                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4377 #endif
4378   }
4379
4380   SetPanelBackground();
4381   SetDrawBackgroundMask(REDRAW_DOOR_1);
4382
4383 #if 1
4384   UpdateAndDisplayGameControlValues();
4385 #else
4386   UpdateGameDoorValues();
4387   DrawGameDoorValues();
4388 #endif
4389
4390   if (!game.restart_level)
4391   {
4392     UnmapGameButtons();
4393     UnmapTapeButtons();
4394     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4395     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4396     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4397     MapGameButtons();
4398     MapTapeButtons();
4399
4400     /* copy actual game door content to door double buffer for OpenDoor() */
4401     BlitBitmap(drawto, bitmap_db_door,
4402                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4403
4404     OpenDoor(DOOR_OPEN_ALL);
4405
4406     PlaySound(SND_GAME_STARTING);
4407
4408     if (setup.sound_music)
4409       PlayLevelMusic();
4410
4411     KeyboardAutoRepeatOffUnlessAutoplay();
4412
4413     if (options.debug)
4414     {
4415       for (i = 0; i < MAX_PLAYERS; i++)
4416         printf("Player %d %sactive.\n",
4417                i + 1, (stored_player[i].active ? "" : "not "));
4418     }
4419   }
4420
4421 #if 1
4422   UnmapAllGadgets();
4423
4424   MapGameButtons();
4425   MapTapeButtons();
4426 #endif
4427
4428   if (!game.restart_level && !tape.playing)
4429   {
4430     LevelStats_incPlayed(level_nr);
4431
4432     SaveLevelSetup_SeriesInfo();
4433
4434 #if 0
4435     printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4436 #endif
4437   }
4438
4439   game.restart_level = FALSE;
4440 }
4441
4442 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4443 {
4444   /* this is used for non-R'n'D game engines to update certain engine values */
4445
4446   /* needed to determine if sounds are played within the visible screen area */
4447   scroll_x = actual_scroll_x;
4448   scroll_y = actual_scroll_y;
4449 }
4450
4451 void InitMovDir(int x, int y)
4452 {
4453   int i, element = Feld[x][y];
4454   static int xy[4][2] =
4455   {
4456     {  0, +1 },
4457     { +1,  0 },
4458     {  0, -1 },
4459     { -1,  0 }
4460   };
4461   static int direction[3][4] =
4462   {
4463     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4464     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4465     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4466   };
4467
4468   switch (element)
4469   {
4470     case EL_BUG_RIGHT:
4471     case EL_BUG_UP:
4472     case EL_BUG_LEFT:
4473     case EL_BUG_DOWN:
4474       Feld[x][y] = EL_BUG;
4475       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4476       break;
4477
4478     case EL_SPACESHIP_RIGHT:
4479     case EL_SPACESHIP_UP:
4480     case EL_SPACESHIP_LEFT:
4481     case EL_SPACESHIP_DOWN:
4482       Feld[x][y] = EL_SPACESHIP;
4483       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4484       break;
4485
4486     case EL_BD_BUTTERFLY_RIGHT:
4487     case EL_BD_BUTTERFLY_UP:
4488     case EL_BD_BUTTERFLY_LEFT:
4489     case EL_BD_BUTTERFLY_DOWN:
4490       Feld[x][y] = EL_BD_BUTTERFLY;
4491       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4492       break;
4493
4494     case EL_BD_FIREFLY_RIGHT:
4495     case EL_BD_FIREFLY_UP:
4496     case EL_BD_FIREFLY_LEFT:
4497     case EL_BD_FIREFLY_DOWN:
4498       Feld[x][y] = EL_BD_FIREFLY;
4499       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4500       break;
4501
4502     case EL_PACMAN_RIGHT:
4503     case EL_PACMAN_UP:
4504     case EL_PACMAN_LEFT:
4505     case EL_PACMAN_DOWN:
4506       Feld[x][y] = EL_PACMAN;
4507       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4508       break;
4509
4510     case EL_YAMYAM_LEFT:
4511     case EL_YAMYAM_RIGHT:
4512     case EL_YAMYAM_UP:
4513     case EL_YAMYAM_DOWN:
4514       Feld[x][y] = EL_YAMYAM;
4515       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4516       break;
4517
4518     case EL_SP_SNIKSNAK:
4519       MovDir[x][y] = MV_UP;
4520       break;
4521
4522     case EL_SP_ELECTRON:
4523       MovDir[x][y] = MV_LEFT;
4524       break;
4525
4526     case EL_MOLE_LEFT:
4527     case EL_MOLE_RIGHT:
4528     case EL_MOLE_UP:
4529     case EL_MOLE_DOWN:
4530       Feld[x][y] = EL_MOLE;
4531       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4532       break;
4533
4534     default:
4535       if (IS_CUSTOM_ELEMENT(element))
4536       {
4537         struct ElementInfo *ei = &element_info[element];
4538         int move_direction_initial = ei->move_direction_initial;
4539         int move_pattern = ei->move_pattern;
4540
4541         if (move_direction_initial == MV_START_PREVIOUS)
4542         {
4543           if (MovDir[x][y] != MV_NONE)
4544             return;
4545
4546           move_direction_initial = MV_START_AUTOMATIC;
4547         }
4548
4549         if (move_direction_initial == MV_START_RANDOM)
4550           MovDir[x][y] = 1 << RND(4);
4551         else if (move_direction_initial & MV_ANY_DIRECTION)
4552           MovDir[x][y] = move_direction_initial;
4553         else if (move_pattern == MV_ALL_DIRECTIONS ||
4554                  move_pattern == MV_TURNING_LEFT ||
4555                  move_pattern == MV_TURNING_RIGHT ||
4556                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4557                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4558                  move_pattern == MV_TURNING_RANDOM)
4559           MovDir[x][y] = 1 << RND(4);
4560         else if (move_pattern == MV_HORIZONTAL)
4561           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4562         else if (move_pattern == MV_VERTICAL)
4563           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4564         else if (move_pattern & MV_ANY_DIRECTION)
4565           MovDir[x][y] = element_info[element].move_pattern;
4566         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4567                  move_pattern == MV_ALONG_RIGHT_SIDE)
4568         {
4569           /* use random direction as default start direction */
4570           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4571             MovDir[x][y] = 1 << RND(4);
4572
4573           for (i = 0; i < NUM_DIRECTIONS; i++)
4574           {
4575             int x1 = x + xy[i][0];
4576             int y1 = y + xy[i][1];
4577
4578             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4579             {
4580               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4581                 MovDir[x][y] = direction[0][i];
4582               else
4583                 MovDir[x][y] = direction[1][i];
4584
4585               break;
4586             }
4587           }
4588         }                
4589       }
4590       else
4591       {
4592         MovDir[x][y] = 1 << RND(4);
4593
4594         if (element != EL_BUG &&
4595             element != EL_SPACESHIP &&
4596             element != EL_BD_BUTTERFLY &&
4597             element != EL_BD_FIREFLY)
4598           break;
4599
4600         for (i = 0; i < NUM_DIRECTIONS; i++)
4601         {
4602           int x1 = x + xy[i][0];
4603           int y1 = y + xy[i][1];
4604
4605           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4606           {
4607             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4608             {
4609               MovDir[x][y] = direction[0][i];
4610               break;
4611             }
4612             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4613                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4614             {
4615               MovDir[x][y] = direction[1][i];
4616               break;
4617             }
4618           }
4619         }
4620       }
4621       break;
4622   }
4623
4624   GfxDir[x][y] = MovDir[x][y];
4625 }
4626
4627 void InitAmoebaNr(int x, int y)
4628 {
4629   int i;
4630   int group_nr = AmoebeNachbarNr(x, y);
4631
4632   if (group_nr == 0)
4633   {
4634     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4635     {
4636       if (AmoebaCnt[i] == 0)
4637       {
4638         group_nr = i;
4639         break;
4640       }
4641     }
4642   }
4643
4644   AmoebaNr[x][y] = group_nr;
4645   AmoebaCnt[group_nr]++;
4646   AmoebaCnt2[group_nr]++;
4647 }
4648
4649 static void PlayerWins(struct PlayerInfo *player)
4650 {
4651   player->LevelSolved = TRUE;
4652   player->GameOver = TRUE;
4653
4654   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4655                          level.native_em_level->lev->score : player->score);
4656
4657   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4658                                       TimeLeft);
4659   player->LevelSolved_CountingScore = player->score_final;
4660 }
4661
4662 void GameWon()
4663 {
4664   static int time, time_final;
4665   static int score, score_final;
4666   static int game_over_delay_1 = 0;
4667   static int game_over_delay_2 = 0;
4668   int game_over_delay_value_1 = 50;
4669   int game_over_delay_value_2 = 50;
4670
4671   if (!local_player->LevelSolved_GameWon)
4672   {
4673     int i;
4674
4675     /* do not start end game actions before the player stops moving (to exit) */
4676     if (local_player->MovPos)
4677       return;
4678
4679     local_player->LevelSolved_GameWon = TRUE;
4680     local_player->LevelSolved_SaveTape = tape.recording;
4681     local_player->LevelSolved_SaveScore = !tape.playing;
4682
4683     if (!tape.playing)
4684     {
4685       LevelStats_incSolved(level_nr);
4686
4687       SaveLevelSetup_SeriesInfo();
4688
4689 #if 0
4690       printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4691 #endif
4692     }
4693
4694     if (tape.auto_play)         /* tape might already be stopped here */
4695       tape.auto_play_level_solved = TRUE;
4696
4697 #if 1
4698     TapeStop();
4699 #endif
4700
4701     game_over_delay_1 = game_over_delay_value_1;
4702     game_over_delay_2 = game_over_delay_value_2;
4703
4704     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4705     score = score_final = local_player->score_final;
4706
4707     if (TimeLeft > 0)
4708     {
4709       time_final = 0;
4710       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4711     }
4712     else if (game.no_time_limit && TimePlayed < 999)
4713     {
4714       time_final = 999;
4715       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4716     }
4717
4718     local_player->score_final = score_final;
4719
4720     if (level_editor_test_game)
4721     {
4722       time = time_final;
4723       score = score_final;
4724
4725 #if 1
4726       local_player->LevelSolved_CountingTime = time;
4727       local_player->LevelSolved_CountingScore = score;
4728
4729       game_panel_controls[GAME_PANEL_TIME].value = time;
4730       game_panel_controls[GAME_PANEL_SCORE].value = score;
4731
4732       DisplayGameControlValues();
4733 #else
4734       DrawGameValue_Time(time);
4735       DrawGameValue_Score(score);
4736 #endif
4737     }
4738
4739     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4740     {
4741       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4742       {
4743         /* close exit door after last player */
4744         if ((AllPlayersGone &&
4745              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4746               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4747               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4748             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4749             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4750         {
4751           int element = Feld[ExitX][ExitY];
4752
4753 #if 0
4754           if (element == EL_EM_EXIT_OPEN ||
4755               element == EL_EM_STEEL_EXIT_OPEN)
4756           {
4757             Bang(ExitX, ExitY);
4758           }
4759           else
4760 #endif
4761           {
4762             Feld[ExitX][ExitY] =
4763               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4764                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4765                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4766                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4767                EL_EM_STEEL_EXIT_CLOSING);
4768
4769             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4770           }
4771         }
4772
4773         /* player disappears */
4774         DrawLevelField(ExitX, ExitY);
4775       }
4776
4777       for (i = 0; i < MAX_PLAYERS; i++)
4778       {
4779         struct PlayerInfo *player = &stored_player[i];
4780
4781         if (player->present)
4782         {
4783           RemovePlayer(player);
4784
4785           /* player disappears */
4786           DrawLevelField(player->jx, player->jy);
4787         }
4788       }
4789     }
4790
4791     PlaySound(SND_GAME_WINNING);
4792   }
4793
4794   if (game_over_delay_1 > 0)
4795   {
4796     game_over_delay_1--;
4797
4798     return;
4799   }
4800
4801   if (time != time_final)
4802   {
4803     int time_to_go = ABS(time_final - time);
4804     int time_count_dir = (time < time_final ? +1 : -1);
4805     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4806
4807     time  += time_count_steps * time_count_dir;
4808     score += time_count_steps * level.score[SC_TIME_BONUS];
4809
4810 #if 1
4811     local_player->LevelSolved_CountingTime = time;
4812     local_player->LevelSolved_CountingScore = score;
4813
4814     game_panel_controls[GAME_PANEL_TIME].value = time;
4815     game_panel_controls[GAME_PANEL_SCORE].value = score;
4816
4817     DisplayGameControlValues();
4818 #else
4819     DrawGameValue_Time(time);
4820     DrawGameValue_Score(score);
4821 #endif
4822
4823     if (time == time_final)
4824       StopSound(SND_GAME_LEVELTIME_BONUS);
4825     else if (setup.sound_loops)
4826       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4827     else
4828       PlaySound(SND_GAME_LEVELTIME_BONUS);
4829
4830     return;
4831   }
4832
4833   local_player->LevelSolved_PanelOff = TRUE;
4834
4835   if (game_over_delay_2 > 0)
4836   {
4837     game_over_delay_2--;
4838
4839     return;
4840   }
4841
4842 #if 1
4843   GameEnd();
4844 #endif
4845 }
4846
4847 void GameEnd()
4848 {
4849   int hi_pos;
4850   boolean raise_level = FALSE;
4851
4852   local_player->LevelSolved_GameEnd = TRUE;
4853
4854   CloseDoor(DOOR_CLOSE_1);
4855
4856   if (local_player->LevelSolved_SaveTape)
4857   {
4858 #if 0
4859     TapeStop();
4860 #endif
4861
4862 #if 1
4863     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4864 #else
4865     SaveTape(tape.level_nr);            /* ask to save tape */
4866 #endif
4867   }
4868
4869   if (level_editor_test_game)
4870   {
4871     game_status = GAME_MODE_MAIN;
4872
4873 #if 1
4874     DrawAndFadeInMainMenu(REDRAW_FIELD);
4875 #else
4876     DrawMainMenu();
4877 #endif
4878
4879     return;
4880   }
4881
4882   if (!local_player->LevelSolved_SaveScore)
4883   {
4884 #if 1
4885     FadeOut(REDRAW_FIELD);
4886 #endif
4887
4888     game_status = GAME_MODE_MAIN;
4889
4890     DrawAndFadeInMainMenu(REDRAW_FIELD);
4891
4892     return;
4893   }
4894
4895   if (level_nr == leveldir_current->handicap_level)
4896   {
4897     leveldir_current->handicap_level++;
4898
4899     SaveLevelSetup_SeriesInfo();
4900   }
4901
4902   if (level_nr < leveldir_current->last_level)
4903     raise_level = TRUE;                 /* advance to next level */
4904
4905   if ((hi_pos = NewHiScore()) >= 0) 
4906   {
4907     game_status = GAME_MODE_SCORES;
4908
4909     DrawHallOfFame(hi_pos);
4910
4911     if (raise_level)
4912     {
4913       level_nr++;
4914       TapeErase();
4915     }
4916   }
4917   else
4918   {
4919 #if 1
4920     FadeOut(REDRAW_FIELD);
4921 #endif
4922
4923     game_status = GAME_MODE_MAIN;
4924
4925     if (raise_level)
4926     {
4927       level_nr++;
4928       TapeErase();
4929     }
4930
4931     DrawAndFadeInMainMenu(REDRAW_FIELD);
4932   }
4933 }
4934
4935 int NewHiScore()
4936 {
4937   int k, l;
4938   int position = -1;
4939
4940   LoadScore(level_nr);
4941
4942   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4943       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4944     return -1;
4945
4946   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4947   {
4948     if (local_player->score_final > highscore[k].Score)
4949     {
4950       /* player has made it to the hall of fame */
4951
4952       if (k < MAX_SCORE_ENTRIES - 1)
4953       {
4954         int m = MAX_SCORE_ENTRIES - 1;
4955
4956 #ifdef ONE_PER_NAME
4957         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4958           if (strEqual(setup.player_name, highscore[l].Name))
4959             m = l;
4960         if (m == k)     /* player's new highscore overwrites his old one */
4961           goto put_into_list;
4962 #endif
4963
4964         for (l = m; l > k; l--)
4965         {
4966           strcpy(highscore[l].Name, highscore[l - 1].Name);
4967           highscore[l].Score = highscore[l - 1].Score;
4968         }
4969       }
4970
4971 #ifdef ONE_PER_NAME
4972       put_into_list:
4973 #endif
4974       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4975       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4976       highscore[k].Score = local_player->score_final; 
4977       position = k;
4978       break;
4979     }
4980
4981 #ifdef ONE_PER_NAME
4982     else if (!strncmp(setup.player_name, highscore[k].Name,
4983                       MAX_PLAYER_NAME_LEN))
4984       break;    /* player already there with a higher score */
4985 #endif
4986
4987   }
4988
4989   if (position >= 0) 
4990     SaveScore(level_nr);
4991
4992   return position;
4993 }
4994
4995 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4996 {
4997   int element = Feld[x][y];
4998   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4999   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5000   int horiz_move = (dx != 0);
5001   int sign = (horiz_move ? dx : dy);
5002   int step = sign * element_info[element].move_stepsize;
5003
5004   /* special values for move stepsize for spring and things on conveyor belt */
5005   if (horiz_move)
5006   {
5007     if (CAN_FALL(element) &&
5008         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5009       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5010     else if (element == EL_SPRING)
5011       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5012   }
5013
5014   return step;
5015 }
5016
5017 inline static int getElementMoveStepsize(int x, int y)
5018 {
5019   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5020 }
5021
5022 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5023 {
5024   if (player->GfxAction != action || player->GfxDir != dir)
5025   {
5026 #if 0
5027     printf("Player frame reset! (%d => %d, %d => %d)\n",
5028            player->GfxAction, action, player->GfxDir, dir);
5029 #endif
5030
5031     player->GfxAction = action;
5032     player->GfxDir = dir;
5033     player->Frame = 0;
5034     player->StepFrame = 0;
5035   }
5036 }
5037
5038 #if USE_GFX_RESET_GFX_ANIMATION
5039 static void ResetGfxFrame(int x, int y, boolean redraw)
5040 {
5041   int element = Feld[x][y];
5042   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5043   int last_gfx_frame = GfxFrame[x][y];
5044
5045   if (graphic_info[graphic].anim_global_sync)
5046     GfxFrame[x][y] = FrameCounter;
5047   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5048     GfxFrame[x][y] = CustomValue[x][y];
5049   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5050     GfxFrame[x][y] = element_info[element].collect_score;
5051   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5052     GfxFrame[x][y] = ChangeDelay[x][y];
5053
5054   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5055     DrawLevelGraphicAnimation(x, y, graphic);
5056 }
5057 #endif
5058
5059 static void ResetGfxAnimation(int x, int y)
5060 {
5061   GfxAction[x][y] = ACTION_DEFAULT;
5062   GfxDir[x][y] = MovDir[x][y];
5063   GfxFrame[x][y] = 0;
5064
5065 #if USE_GFX_RESET_GFX_ANIMATION
5066   ResetGfxFrame(x, y, FALSE);
5067 #endif
5068 }
5069
5070 static void ResetRandomAnimationValue(int x, int y)
5071 {
5072   GfxRandom[x][y] = INIT_GFX_RANDOM();
5073 }
5074
5075 void InitMovingField(int x, int y, int direction)
5076 {
5077   int element = Feld[x][y];
5078   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5079   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5080   int newx = x + dx;
5081   int newy = y + dy;
5082   boolean is_moving_before, is_moving_after;
5083 #if 0
5084   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5085 #endif
5086
5087   /* check if element was/is moving or being moved before/after mode change */
5088 #if 1
5089 #if 1
5090   is_moving_before = (WasJustMoving[x][y] != 0);
5091 #else
5092   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5093   is_moving_before = WasJustMoving[x][y];
5094 #endif
5095 #else
5096   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5097 #endif
5098   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5099
5100   /* reset animation only for moving elements which change direction of moving
5101      or which just started or stopped moving
5102      (else CEs with property "can move" / "not moving" are reset each frame) */
5103 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5104 #if 1
5105   if (is_moving_before != is_moving_after ||
5106       direction != MovDir[x][y])
5107     ResetGfxAnimation(x, y);
5108 #else
5109   if ((is_moving_before || is_moving_after) && !continues_moving)
5110     ResetGfxAnimation(x, y);
5111 #endif
5112 #else
5113   if (!continues_moving)
5114     ResetGfxAnimation(x, y);
5115 #endif
5116
5117   MovDir[x][y] = direction;
5118   GfxDir[x][y] = direction;
5119
5120 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5121   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5122                      direction == MV_DOWN && CAN_FALL(element) ?
5123                      ACTION_FALLING : ACTION_MOVING);
5124 #else
5125   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5126                      ACTION_FALLING : ACTION_MOVING);
5127 #endif
5128
5129   /* this is needed for CEs with property "can move" / "not moving" */
5130
5131   if (is_moving_after)
5132   {
5133     if (Feld[newx][newy] == EL_EMPTY)
5134       Feld[newx][newy] = EL_BLOCKED;
5135
5136     MovDir[newx][newy] = MovDir[x][y];
5137
5138 #if USE_NEW_CUSTOM_VALUE
5139     CustomValue[newx][newy] = CustomValue[x][y];
5140 #endif
5141
5142     GfxFrame[newx][newy] = GfxFrame[x][y];
5143     GfxRandom[newx][newy] = GfxRandom[x][y];
5144     GfxAction[newx][newy] = GfxAction[x][y];
5145     GfxDir[newx][newy] = GfxDir[x][y];
5146   }
5147 }
5148
5149 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5150 {
5151   int direction = MovDir[x][y];
5152   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5153   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5154
5155   *goes_to_x = newx;
5156   *goes_to_y = newy;
5157 }
5158
5159 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5160 {
5161   int oldx = x, oldy = y;
5162   int direction = MovDir[x][y];
5163
5164   if (direction == MV_LEFT)
5165     oldx++;
5166   else if (direction == MV_RIGHT)
5167     oldx--;
5168   else if (direction == MV_UP)
5169     oldy++;
5170   else if (direction == MV_DOWN)
5171     oldy--;
5172
5173   *comes_from_x = oldx;
5174   *comes_from_y = oldy;
5175 }
5176
5177 int MovingOrBlocked2Element(int x, int y)
5178 {
5179   int element = Feld[x][y];
5180
5181   if (element == EL_BLOCKED)
5182   {
5183     int oldx, oldy;
5184
5185     Blocked2Moving(x, y, &oldx, &oldy);
5186     return Feld[oldx][oldy];
5187   }
5188   else
5189     return element;
5190 }
5191
5192 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5193 {
5194   /* like MovingOrBlocked2Element(), but if element is moving
5195      and (x,y) is the field the moving element is just leaving,
5196      return EL_BLOCKED instead of the element value */
5197   int element = Feld[x][y];
5198
5199   if (IS_MOVING(x, y))
5200   {
5201     if (element == EL_BLOCKED)
5202     {
5203       int oldx, oldy;
5204
5205       Blocked2Moving(x, y, &oldx, &oldy);
5206       return Feld[oldx][oldy];
5207     }
5208     else
5209       return EL_BLOCKED;
5210   }
5211   else
5212     return element;
5213 }
5214
5215 static void RemoveField(int x, int y)
5216 {
5217   Feld[x][y] = EL_EMPTY;
5218
5219   MovPos[x][y] = 0;
5220   MovDir[x][y] = 0;
5221   MovDelay[x][y] = 0;
5222
5223 #if USE_NEW_CUSTOM_VALUE
5224   CustomValue[x][y] = 0;
5225 #endif
5226
5227   AmoebaNr[x][y] = 0;
5228   ChangeDelay[x][y] = 0;
5229   ChangePage[x][y] = -1;
5230   Pushed[x][y] = FALSE;
5231
5232 #if 0
5233   ExplodeField[x][y] = EX_TYPE_NONE;
5234 #endif
5235
5236   GfxElement[x][y] = EL_UNDEFINED;
5237   GfxAction[x][y] = ACTION_DEFAULT;
5238   GfxDir[x][y] = MV_NONE;
5239 #if 0
5240   /* !!! this would prevent the removed tile from being redrawn !!! */
5241   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5242 #endif
5243 }
5244
5245 void RemoveMovingField(int x, int y)
5246 {
5247   int oldx = x, oldy = y, newx = x, newy = y;
5248   int element = Feld[x][y];
5249   int next_element = EL_UNDEFINED;
5250
5251   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5252     return;
5253
5254   if (IS_MOVING(x, y))
5255   {
5256     Moving2Blocked(x, y, &newx, &newy);
5257
5258     if (Feld[newx][newy] != EL_BLOCKED)
5259     {
5260       /* element is moving, but target field is not free (blocked), but
5261          already occupied by something different (example: acid pool);
5262          in this case, only remove the moving field, but not the target */
5263
5264       RemoveField(oldx, oldy);
5265
5266       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5267
5268       TEST_DrawLevelField(oldx, oldy);
5269
5270       return;
5271     }
5272   }
5273   else if (element == EL_BLOCKED)
5274   {
5275     Blocked2Moving(x, y, &oldx, &oldy);
5276     if (!IS_MOVING(oldx, oldy))
5277       return;
5278   }
5279
5280   if (element == EL_BLOCKED &&
5281       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5282        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5283        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5284        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5285        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5286        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5287     next_element = get_next_element(Feld[oldx][oldy]);
5288
5289   RemoveField(oldx, oldy);
5290   RemoveField(newx, newy);
5291
5292   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5293
5294   if (next_element != EL_UNDEFINED)
5295     Feld[oldx][oldy] = next_element;
5296
5297   TEST_DrawLevelField(oldx, oldy);
5298   TEST_DrawLevelField(newx, newy);
5299 }
5300
5301 void DrawDynamite(int x, int y)
5302 {
5303   int sx = SCREENX(x), sy = SCREENY(y);
5304   int graphic = el2img(Feld[x][y]);
5305   int frame;
5306
5307   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5308     return;
5309
5310   if (IS_WALKABLE_INSIDE(Back[x][y]))
5311     return;
5312
5313   if (Back[x][y])
5314     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5315   else if (Store[x][y])
5316     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5317
5318   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5319
5320   if (Back[x][y] || Store[x][y])
5321     DrawGraphicThruMask(sx, sy, graphic, frame);
5322   else
5323     DrawGraphic(sx, sy, graphic, frame);
5324 }
5325
5326 void CheckDynamite(int x, int y)
5327 {
5328   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5329   {
5330     MovDelay[x][y]--;
5331
5332     if (MovDelay[x][y] != 0)
5333     {
5334       DrawDynamite(x, y);
5335       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5336
5337       return;
5338     }
5339   }
5340
5341   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5342
5343   Bang(x, y);
5344 }
5345
5346 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5347 {
5348   boolean num_checked_players = 0;
5349   int i;
5350
5351   for (i = 0; i < MAX_PLAYERS; i++)
5352   {
5353     if (stored_player[i].active)
5354     {
5355       int sx = stored_player[i].jx;
5356       int sy = stored_player[i].jy;
5357
5358       if (num_checked_players == 0)
5359       {
5360         *sx1 = *sx2 = sx;
5361         *sy1 = *sy2 = sy;
5362       }
5363       else
5364       {
5365         *sx1 = MIN(*sx1, sx);
5366         *sy1 = MIN(*sy1, sy);
5367         *sx2 = MAX(*sx2, sx);
5368         *sy2 = MAX(*sy2, sy);
5369       }
5370
5371       num_checked_players++;
5372     }
5373   }
5374 }
5375
5376 static boolean checkIfAllPlayersFitToScreen_RND()
5377 {
5378   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5379
5380   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5381
5382   return (sx2 - sx1 < SCR_FIELDX &&
5383           sy2 - sy1 < SCR_FIELDY);
5384 }
5385
5386 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5387 {
5388   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5389
5390   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5391
5392   *sx = (sx1 + sx2) / 2;
5393   *sy = (sy1 + sy2) / 2;
5394 }
5395
5396 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5397                         boolean center_screen, boolean quick_relocation)
5398 {
5399   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5400   boolean no_delay = (tape.warp_forward);
5401   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5402   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5403
5404   if (quick_relocation)
5405   {
5406     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5407     {
5408       if (!level.shifted_relocation || center_screen)
5409       {
5410         /* quick relocation (without scrolling), with centering of screen */
5411
5412         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5413                     x > SBX_Right + MIDPOSX ? SBX_Right :
5414                     x - MIDPOSX);
5415
5416         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5417                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5418                     y - MIDPOSY);
5419       }
5420       else
5421       {
5422         /* quick relocation (without scrolling), but do not center screen */
5423
5424         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5425                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5426                                old_x - MIDPOSX);
5427
5428         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5429                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5430                                old_y - MIDPOSY);
5431
5432         int offset_x = x + (scroll_x - center_scroll_x);
5433         int offset_y = y + (scroll_y - center_scroll_y);
5434
5435         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5436                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5437                     offset_x - MIDPOSX);
5438
5439         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5440                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5441                     offset_y - MIDPOSY);
5442       }
5443     }
5444     else
5445     {
5446 #if 1
5447       if (!level.shifted_relocation || center_screen)
5448       {
5449         /* quick relocation (without scrolling), with centering of screen */
5450
5451         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5452                     x > SBX_Right + MIDPOSX ? SBX_Right :
5453                     x - MIDPOSX);
5454
5455         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5456                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5457                     y - MIDPOSY);
5458       }
5459       else
5460       {
5461         /* quick relocation (without scrolling), but do not center screen */
5462
5463         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5464                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5465                                old_x - MIDPOSX);
5466
5467         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5468                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5469                                old_y - MIDPOSY);
5470
5471         int offset_x = x + (scroll_x - center_scroll_x);
5472         int offset_y = y + (scroll_y - center_scroll_y);
5473
5474         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5475                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5476                     offset_x - MIDPOSX);
5477
5478         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5479                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5480                     offset_y - MIDPOSY);
5481       }
5482 #else
5483       /* quick relocation (without scrolling), inside visible screen area */
5484
5485       int offset = game.scroll_delay_value;
5486
5487       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5488           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5489         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5490
5491       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5492           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5493         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5494
5495       /* don't scroll over playfield boundaries */
5496       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5497         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5498
5499       /* don't scroll over playfield boundaries */
5500       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5501         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5502 #endif
5503     }
5504
5505     RedrawPlayfield(TRUE, 0,0,0,0);
5506   }
5507   else
5508   {
5509 #if 1
5510     int scroll_xx, scroll_yy;
5511
5512     if (!level.shifted_relocation || center_screen)
5513     {
5514       /* visible relocation (with scrolling), with centering of screen */
5515
5516       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5517                    x > SBX_Right + MIDPOSX ? SBX_Right :
5518                    x - MIDPOSX);
5519
5520       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5521                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5522                    y - MIDPOSY);
5523     }
5524     else
5525     {
5526       /* visible relocation (with scrolling), but do not center screen */
5527
5528       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5529                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5530                              old_x - MIDPOSX);
5531
5532       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5533                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5534                              old_y - MIDPOSY);
5535
5536       int offset_x = x + (scroll_x - center_scroll_x);
5537       int offset_y = y + (scroll_y - center_scroll_y);
5538
5539       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5540                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5541                    offset_x - MIDPOSX);
5542
5543       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5544                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5545                    offset_y - MIDPOSY);
5546     }
5547
5548 #else
5549
5550     /* visible relocation (with scrolling), with centering of screen */
5551
5552     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5553                      x > SBX_Right + MIDPOSX ? SBX_Right :
5554                      x - MIDPOSX);
5555
5556     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5557                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5558                      y - MIDPOSY);
5559 #endif
5560
5561     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5562
5563     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5564     {
5565       int dx = 0, dy = 0;
5566       int fx = FX, fy = FY;
5567
5568       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5569       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5570
5571       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5572         break;
5573
5574       scroll_x -= dx;
5575       scroll_y -= dy;
5576
5577       fx += dx * TILEX / 2;
5578       fy += dy * TILEY / 2;
5579
5580       ScrollLevel(dx, dy);
5581       DrawAllPlayers();
5582
5583       /* scroll in two steps of half tile size to make things smoother */
5584       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5585       FlushDisplay();
5586       Delay(wait_delay_value);
5587
5588       /* scroll second step to align at full tile size */
5589       BackToFront();
5590       Delay(wait_delay_value);
5591     }
5592
5593     DrawAllPlayers();
5594     BackToFront();
5595     Delay(wait_delay_value);
5596   }
5597 }
5598
5599 void RelocatePlayer(int jx, int jy, int el_player_raw)
5600 {
5601   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5602   int player_nr = GET_PLAYER_NR(el_player);
5603   struct PlayerInfo *player = &stored_player[player_nr];
5604   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5605   boolean no_delay = (tape.warp_forward);
5606   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5607   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5608   int old_jx = player->jx;
5609   int old_jy = player->jy;
5610   int old_element = Feld[old_jx][old_jy];
5611   int element = Feld[jx][jy];
5612   boolean player_relocated = (old_jx != jx || old_jy != jy);
5613
5614   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5615   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5616   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5617   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5618   int leave_side_horiz = move_dir_horiz;
5619   int leave_side_vert  = move_dir_vert;
5620   int enter_side = enter_side_horiz | enter_side_vert;
5621   int leave_side = leave_side_horiz | leave_side_vert;
5622
5623   if (player->GameOver)         /* do not reanimate dead player */
5624     return;
5625
5626   if (!player_relocated)        /* no need to relocate the player */
5627     return;
5628
5629   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5630   {
5631     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5632     DrawLevelField(jx, jy);
5633   }
5634
5635   if (player->present)
5636   {
5637     while (player->MovPos)
5638     {
5639       ScrollPlayer(player, SCROLL_GO_ON);
5640       ScrollScreen(NULL, SCROLL_GO_ON);
5641
5642       AdvanceFrameAndPlayerCounters(player->index_nr);
5643
5644       DrawPlayer(player);
5645
5646       BackToFront();
5647       Delay(wait_delay_value);
5648     }
5649
5650     DrawPlayer(player);         /* needed here only to cleanup last field */
5651     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5652
5653     player->is_moving = FALSE;
5654   }
5655
5656   if (IS_CUSTOM_ELEMENT(old_element))
5657     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5658                                CE_LEFT_BY_PLAYER,
5659                                player->index_bit, leave_side);
5660
5661   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5662                                       CE_PLAYER_LEAVES_X,
5663                                       player->index_bit, leave_side);
5664
5665   Feld[jx][jy] = el_player;
5666   InitPlayerField(jx, jy, el_player, TRUE);
5667
5668   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5669      possible that the relocation target field did not contain a player element,
5670      but a walkable element, to which the new player was relocated -- in this
5671      case, restore that (already initialized!) element on the player field */
5672   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5673   {
5674     Feld[jx][jy] = element;     /* restore previously existing element */
5675 #if 0
5676     /* !!! do not initialize already initialized element a second time !!! */
5677     /* (this causes at least problems with "element creation" CE trigger for
5678        already existing elements, and existing Sokoban fields counted twice) */
5679     InitField(jx, jy, FALSE);
5680 #endif
5681   }
5682
5683   /* only visually relocate centered player */
5684   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5685                      FALSE, level.instant_relocation);
5686
5687   TestIfPlayerTouchesBadThing(jx, jy);
5688   TestIfPlayerTouchesCustomElement(jx, jy);
5689
5690   if (IS_CUSTOM_ELEMENT(element))
5691     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5692                                player->index_bit, enter_side);
5693
5694   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5695                                       player->index_bit, enter_side);
5696
5697 #if 1
5698   if (player->is_switching)
5699   {
5700     /* ensure that relocation while still switching an element does not cause
5701        a new element to be treated as also switched directly after relocation
5702        (this is important for teleporter switches that teleport the player to
5703        a place where another teleporter switch is in the same direction, which
5704        would then incorrectly be treated as immediately switched before the
5705        direction key that caused the switch was released) */
5706
5707     player->switch_x += jx - old_jx;
5708     player->switch_y += jy - old_jy;
5709   }
5710 #endif
5711 }
5712
5713 void Explode(int ex, int ey, int phase, int mode)
5714 {
5715   int x, y;
5716   int last_phase;
5717   int border_element;
5718
5719   /* !!! eliminate this variable !!! */
5720   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5721
5722   if (game.explosions_delayed)
5723   {
5724     ExplodeField[ex][ey] = mode;
5725     return;
5726   }
5727
5728   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5729   {
5730     int center_element = Feld[ex][ey];
5731     int artwork_element, explosion_element;     /* set these values later */
5732
5733 #if 0
5734     /* --- This is only really needed (and now handled) in "Impact()". --- */
5735     /* do not explode moving elements that left the explode field in time */
5736     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5737         center_element == EL_EMPTY &&
5738         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5739       return;
5740 #endif
5741
5742 #if 0
5743     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5744     if (mode == EX_TYPE_NORMAL ||
5745         mode == EX_TYPE_CENTER ||
5746         mode == EX_TYPE_CROSS)
5747       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5748 #endif
5749
5750     /* remove things displayed in background while burning dynamite */
5751     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5752       Back[ex][ey] = 0;
5753
5754     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5755     {
5756       /* put moving element to center field (and let it explode there) */
5757       center_element = MovingOrBlocked2Element(ex, ey);
5758       RemoveMovingField(ex, ey);
5759       Feld[ex][ey] = center_element;
5760     }
5761
5762     /* now "center_element" is finally determined -- set related values now */
5763     artwork_element = center_element;           /* for custom player artwork */
5764     explosion_element = center_element;         /* for custom player artwork */
5765
5766     if (IS_PLAYER(ex, ey))
5767     {
5768       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5769
5770       artwork_element = stored_player[player_nr].artwork_element;
5771
5772       if (level.use_explosion_element[player_nr])
5773       {
5774         explosion_element = level.explosion_element[player_nr];
5775         artwork_element = explosion_element;
5776       }
5777     }
5778
5779 #if 1
5780     if (mode == EX_TYPE_NORMAL ||
5781         mode == EX_TYPE_CENTER ||
5782         mode == EX_TYPE_CROSS)
5783       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5784 #endif
5785
5786     last_phase = element_info[explosion_element].explosion_delay + 1;
5787
5788     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5789     {
5790       int xx = x - ex + 1;
5791       int yy = y - ey + 1;
5792       int element;
5793
5794       if (!IN_LEV_FIELD(x, y) ||
5795           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5796           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5797         continue;
5798
5799       element = Feld[x][y];
5800
5801       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5802       {
5803         element = MovingOrBlocked2Element(x, y);
5804
5805         if (!IS_EXPLOSION_PROOF(element))
5806           RemoveMovingField(x, y);
5807       }
5808
5809       /* indestructible elements can only explode in center (but not flames) */
5810       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5811                                            mode == EX_TYPE_BORDER)) ||
5812           element == EL_FLAMES)
5813         continue;
5814
5815       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5816          behaviour, for example when touching a yamyam that explodes to rocks
5817          with active deadly shield, a rock is created under the player !!! */
5818       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5819 #if 0
5820       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5821           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5822            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5823 #else
5824       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5825 #endif
5826       {
5827         if (IS_ACTIVE_BOMB(element))
5828         {
5829           /* re-activate things under the bomb like gate or penguin */
5830           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5831           Back[x][y] = 0;
5832         }
5833
5834         continue;
5835       }
5836
5837       /* save walkable background elements while explosion on same tile */
5838       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5839           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5840         Back[x][y] = element;
5841
5842       /* ignite explodable elements reached by other explosion */
5843       if (element == EL_EXPLOSION)
5844         element = Store2[x][y];
5845
5846       if (AmoebaNr[x][y] &&
5847           (element == EL_AMOEBA_FULL ||
5848            element == EL_BD_AMOEBA ||
5849            element == EL_AMOEBA_GROWING))
5850       {
5851         AmoebaCnt[AmoebaNr[x][y]]--;
5852         AmoebaCnt2[AmoebaNr[x][y]]--;
5853       }
5854
5855       RemoveField(x, y);
5856
5857       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5858       {
5859         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5860
5861         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5862
5863         if (PLAYERINFO(ex, ey)->use_murphy)
5864           Store[x][y] = EL_EMPTY;
5865       }
5866
5867       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5868          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5869       else if (ELEM_IS_PLAYER(center_element))
5870         Store[x][y] = EL_EMPTY;
5871       else if (center_element == EL_YAMYAM)
5872         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5873       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5874         Store[x][y] = element_info[center_element].content.e[xx][yy];
5875 #if 1
5876       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5877          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5878          otherwise) -- FIX THIS !!! */
5879       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5880         Store[x][y] = element_info[element].content.e[1][1];
5881 #else
5882       else if (!CAN_EXPLODE(element))
5883         Store[x][y] = element_info[element].content.e[1][1];
5884 #endif
5885       else
5886         Store[x][y] = EL_EMPTY;
5887
5888       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5889           center_element == EL_AMOEBA_TO_DIAMOND)
5890         Store2[x][y] = element;
5891
5892       Feld[x][y] = EL_EXPLOSION;
5893       GfxElement[x][y] = artwork_element;
5894
5895       ExplodePhase[x][y] = 1;
5896       ExplodeDelay[x][y] = last_phase;
5897
5898       Stop[x][y] = TRUE;
5899     }
5900
5901     if (center_element == EL_YAMYAM)
5902       game.yamyam_content_nr =
5903         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5904
5905     return;
5906   }
5907
5908   if (Stop[ex][ey])
5909     return;
5910
5911   x = ex;
5912   y = ey;
5913
5914   if (phase == 1)
5915     GfxFrame[x][y] = 0;         /* restart explosion animation */
5916
5917   last_phase = ExplodeDelay[x][y];
5918
5919   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5920
5921 #ifdef DEBUG
5922
5923   /* activate this even in non-DEBUG version until cause for crash in
5924      getGraphicAnimationFrame() (see below) is found and eliminated */
5925
5926 #endif
5927 #if 1
5928
5929 #if 1
5930   /* this can happen if the player leaves an explosion just in time */
5931   if (GfxElement[x][y] == EL_UNDEFINED)
5932     GfxElement[x][y] = EL_EMPTY;
5933 #else
5934   if (GfxElement[x][y] == EL_UNDEFINED)
5935   {
5936     printf("\n\n");
5937     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5938     printf("Explode(): This should never happen!\n");
5939     printf("\n\n");
5940
5941     GfxElement[x][y] = EL_EMPTY;
5942   }
5943 #endif
5944
5945 #endif
5946
5947   border_element = Store2[x][y];
5948   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5949     border_element = StorePlayer[x][y];
5950
5951   if (phase == element_info[border_element].ignition_delay ||
5952       phase == last_phase)
5953   {
5954     boolean border_explosion = FALSE;
5955
5956     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5957         !PLAYER_EXPLOSION_PROTECTED(x, y))
5958     {
5959       KillPlayerUnlessExplosionProtected(x, y);
5960       border_explosion = TRUE;
5961     }
5962     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5963     {
5964       Feld[x][y] = Store2[x][y];
5965       Store2[x][y] = 0;
5966       Bang(x, y);
5967       border_explosion = TRUE;
5968     }
5969     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5970     {
5971       AmoebeUmwandeln(x, y);
5972       Store2[x][y] = 0;
5973       border_explosion = TRUE;
5974     }
5975
5976     /* if an element just explodes due to another explosion (chain-reaction),
5977        do not immediately end the new explosion when it was the last frame of
5978        the explosion (as it would be done in the following "if"-statement!) */
5979     if (border_explosion && phase == last_phase)
5980       return;
5981   }
5982
5983   if (phase == last_phase)
5984   {
5985     int element;
5986
5987     element = Feld[x][y] = Store[x][y];
5988     Store[x][y] = Store2[x][y] = 0;
5989     GfxElement[x][y] = EL_UNDEFINED;
5990
5991     /* player can escape from explosions and might therefore be still alive */
5992     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5993         element <= EL_PLAYER_IS_EXPLODING_4)
5994     {
5995       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5996       int explosion_element = EL_PLAYER_1 + player_nr;
5997       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5998       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5999
6000       if (level.use_explosion_element[player_nr])
6001         explosion_element = level.explosion_element[player_nr];
6002
6003       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6004                     element_info[explosion_element].content.e[xx][yy]);
6005     }
6006
6007     /* restore probably existing indestructible background element */
6008     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6009       element = Feld[x][y] = Back[x][y];
6010     Back[x][y] = 0;
6011
6012     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6013     GfxDir[x][y] = MV_NONE;
6014     ChangeDelay[x][y] = 0;
6015     ChangePage[x][y] = -1;
6016
6017 #if USE_NEW_CUSTOM_VALUE
6018     CustomValue[x][y] = 0;
6019 #endif
6020
6021     InitField_WithBug2(x, y, FALSE);
6022
6023     TEST_DrawLevelField(x, y);
6024
6025     TestIfElementTouchesCustomElement(x, y);
6026
6027     if (GFX_CRUMBLED(element))
6028       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6029
6030     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6031       StorePlayer[x][y] = 0;
6032
6033     if (ELEM_IS_PLAYER(element))
6034       RelocatePlayer(x, y, element);
6035   }
6036   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6037   {
6038     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6039     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6040
6041     if (phase == delay)
6042       TEST_DrawLevelFieldCrumbled(x, y);
6043
6044     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6045     {
6046       DrawLevelElement(x, y, Back[x][y]);
6047       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6048     }
6049     else if (IS_WALKABLE_UNDER(Back[x][y]))
6050     {
6051       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6052       DrawLevelElementThruMask(x, y, Back[x][y]);
6053     }
6054     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6055       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6056   }
6057 }
6058
6059 void DynaExplode(int ex, int ey)
6060 {
6061   int i, j;
6062   int dynabomb_element = Feld[ex][ey];
6063   int dynabomb_size = 1;
6064   boolean dynabomb_xl = FALSE;
6065   struct PlayerInfo *player;
6066   static int xy[4][2] =
6067   {
6068     { 0, -1 },
6069     { -1, 0 },
6070     { +1, 0 },
6071     { 0, +1 }
6072   };
6073
6074   if (IS_ACTIVE_BOMB(dynabomb_element))
6075   {
6076     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6077     dynabomb_size = player->dynabomb_size;
6078     dynabomb_xl = player->dynabomb_xl;
6079     player->dynabombs_left++;
6080   }
6081
6082   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6083
6084   for (i = 0; i < NUM_DIRECTIONS; i++)
6085   {
6086     for (j = 1; j <= dynabomb_size; j++)
6087     {
6088       int x = ex + j * xy[i][0];
6089       int y = ey + j * xy[i][1];
6090       int element;
6091
6092       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6093         break;
6094
6095       element = Feld[x][y];
6096
6097       /* do not restart explosions of fields with active bombs */
6098       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6099         continue;
6100
6101       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6102
6103       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6104           !IS_DIGGABLE(element) && !dynabomb_xl)
6105         break;
6106     }
6107   }
6108 }
6109
6110 void Bang(int x, int y)
6111 {
6112   int element = MovingOrBlocked2Element(x, y);
6113   int explosion_type = EX_TYPE_NORMAL;
6114
6115   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6116   {
6117     struct PlayerInfo *player = PLAYERINFO(x, y);
6118
6119 #if USE_FIX_CE_ACTION_WITH_PLAYER
6120     element = Feld[x][y] = player->initial_element;
6121 #else
6122     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6123                             player->element_nr);
6124 #endif
6125
6126     if (level.use_explosion_element[player->index_nr])
6127     {
6128       int explosion_element = level.explosion_element[player->index_nr];
6129
6130       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6131         explosion_type = EX_TYPE_CROSS;
6132       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6133         explosion_type = EX_TYPE_CENTER;
6134     }
6135   }
6136
6137   switch (element)
6138   {
6139     case EL_BUG:
6140     case EL_SPACESHIP:
6141     case EL_BD_BUTTERFLY:
6142     case EL_BD_FIREFLY:
6143     case EL_YAMYAM:
6144     case EL_DARK_YAMYAM:
6145     case EL_ROBOT:
6146     case EL_PACMAN:
6147     case EL_MOLE:
6148       RaiseScoreElement(element);
6149       break;
6150
6151     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6152     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6153     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6154     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6155     case EL_DYNABOMB_INCREASE_NUMBER:
6156     case EL_DYNABOMB_INCREASE_SIZE:
6157     case EL_DYNABOMB_INCREASE_POWER:
6158       explosion_type = EX_TYPE_DYNA;
6159       break;
6160
6161     case EL_DC_LANDMINE:
6162 #if 0
6163     case EL_EM_EXIT_OPEN:
6164     case EL_EM_STEEL_EXIT_OPEN:
6165 #endif
6166       explosion_type = EX_TYPE_CENTER;
6167       break;
6168
6169     case EL_PENGUIN:
6170     case EL_LAMP:
6171     case EL_LAMP_ACTIVE:
6172     case EL_AMOEBA_TO_DIAMOND:
6173       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6174         explosion_type = EX_TYPE_CENTER;
6175       break;
6176
6177     default:
6178       if (element_info[element].explosion_type == EXPLODES_CROSS)
6179         explosion_type = EX_TYPE_CROSS;
6180       else if (element_info[element].explosion_type == EXPLODES_1X1)
6181         explosion_type = EX_TYPE_CENTER;
6182       break;
6183   }
6184
6185   if (explosion_type == EX_TYPE_DYNA)
6186     DynaExplode(x, y);
6187   else
6188     Explode(x, y, EX_PHASE_START, explosion_type);
6189
6190   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6191 }
6192
6193 void SplashAcid(int x, int y)
6194 {
6195   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6196       (!IN_LEV_FIELD(x - 1, y - 2) ||
6197        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6198     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6199
6200   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6201       (!IN_LEV_FIELD(x + 1, y - 2) ||
6202        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6203     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6204
6205   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6206 }
6207
6208 static void InitBeltMovement()
6209 {
6210   static int belt_base_element[4] =
6211   {
6212     EL_CONVEYOR_BELT_1_LEFT,
6213     EL_CONVEYOR_BELT_2_LEFT,
6214     EL_CONVEYOR_BELT_3_LEFT,
6215     EL_CONVEYOR_BELT_4_LEFT
6216   };
6217   static int belt_base_active_element[4] =
6218   {
6219     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6220     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6221     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6222     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6223   };
6224
6225   int x, y, i, j;
6226
6227   /* set frame order for belt animation graphic according to belt direction */
6228   for (i = 0; i < NUM_BELTS; i++)
6229   {
6230     int belt_nr = i;
6231
6232     for (j = 0; j < NUM_BELT_PARTS; j++)
6233     {
6234       int element = belt_base_active_element[belt_nr] + j;
6235       int graphic_1 = el2img(element);
6236       int graphic_2 = el2panelimg(element);
6237
6238       if (game.belt_dir[i] == MV_LEFT)
6239       {
6240         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6241         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6242       }
6243       else
6244       {
6245         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6246         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6247       }
6248     }
6249   }
6250
6251   SCAN_PLAYFIELD(x, y)
6252   {
6253     int element = Feld[x][y];
6254
6255     for (i = 0; i < NUM_BELTS; i++)
6256     {
6257       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6258       {
6259         int e_belt_nr = getBeltNrFromBeltElement(element);
6260         int belt_nr = i;
6261
6262         if (e_belt_nr == belt_nr)
6263         {
6264           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6265
6266           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6267         }
6268       }
6269     }
6270   }
6271 }
6272
6273 static void ToggleBeltSwitch(int x, int y)
6274 {
6275   static int belt_base_element[4] =
6276   {
6277     EL_CONVEYOR_BELT_1_LEFT,
6278     EL_CONVEYOR_BELT_2_LEFT,
6279     EL_CONVEYOR_BELT_3_LEFT,
6280     EL_CONVEYOR_BELT_4_LEFT
6281   };
6282   static int belt_base_active_element[4] =
6283   {
6284     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6285     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6286     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6287     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6288   };
6289   static int belt_base_switch_element[4] =
6290   {
6291     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6292     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6293     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6294     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6295   };
6296   static int belt_move_dir[4] =
6297   {
6298     MV_LEFT,
6299     MV_NONE,
6300     MV_RIGHT,
6301     MV_NONE,
6302   };
6303
6304   int element = Feld[x][y];
6305   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6306   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6307   int belt_dir = belt_move_dir[belt_dir_nr];
6308   int xx, yy, i;
6309
6310   if (!IS_BELT_SWITCH(element))
6311     return;
6312
6313   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6314   game.belt_dir[belt_nr] = belt_dir;
6315
6316   if (belt_dir_nr == 3)
6317     belt_dir_nr = 1;
6318
6319   /* set frame order for belt animation graphic according to belt direction */
6320   for (i = 0; i < NUM_BELT_PARTS; i++)
6321   {
6322     int element = belt_base_active_element[belt_nr] + i;
6323     int graphic_1 = el2img(element);
6324     int graphic_2 = el2panelimg(element);
6325
6326     if (belt_dir == MV_LEFT)
6327     {
6328       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6329       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6330     }
6331     else
6332     {
6333       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6334       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6335     }
6336   }
6337
6338   SCAN_PLAYFIELD(xx, yy)
6339   {
6340     int element = Feld[xx][yy];
6341
6342     if (IS_BELT_SWITCH(element))
6343     {
6344       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6345
6346       if (e_belt_nr == belt_nr)
6347       {
6348         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6349         TEST_DrawLevelField(xx, yy);
6350       }
6351     }
6352     else if (IS_BELT(element) && belt_dir != MV_NONE)
6353     {
6354       int e_belt_nr = getBeltNrFromBeltElement(element);
6355
6356       if (e_belt_nr == belt_nr)
6357       {
6358         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6359
6360         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6361         TEST_DrawLevelField(xx, yy);
6362       }
6363     }
6364     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6365     {
6366       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6367
6368       if (e_belt_nr == belt_nr)
6369       {
6370         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6371
6372         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6373         TEST_DrawLevelField(xx, yy);
6374       }
6375     }
6376   }
6377 }
6378
6379 static void ToggleSwitchgateSwitch(int x, int y)
6380 {
6381   int xx, yy;
6382
6383   game.switchgate_pos = !game.switchgate_pos;
6384
6385   SCAN_PLAYFIELD(xx, yy)
6386   {
6387     int element = Feld[xx][yy];
6388
6389 #if !USE_BOTH_SWITCHGATE_SWITCHES
6390     if (element == EL_SWITCHGATE_SWITCH_UP ||
6391         element == EL_SWITCHGATE_SWITCH_DOWN)
6392     {
6393       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6394       TEST_DrawLevelField(xx, yy);
6395     }
6396     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6397              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6398     {
6399       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6400       TEST_DrawLevelField(xx, yy);
6401     }
6402 #else
6403     if (element == EL_SWITCHGATE_SWITCH_UP)
6404     {
6405       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6406       TEST_DrawLevelField(xx, yy);
6407     }
6408     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6409     {
6410       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6411       TEST_DrawLevelField(xx, yy);
6412     }
6413     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6414     {
6415       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6416       TEST_DrawLevelField(xx, yy);
6417     }
6418     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6419     {
6420       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6421       TEST_DrawLevelField(xx, yy);
6422     }
6423 #endif
6424     else if (element == EL_SWITCHGATE_OPEN ||
6425              element == EL_SWITCHGATE_OPENING)
6426     {
6427       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6428
6429       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6430     }
6431     else if (element == EL_SWITCHGATE_CLOSED ||
6432              element == EL_SWITCHGATE_CLOSING)
6433     {
6434       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6435
6436       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6437     }
6438   }
6439 }
6440
6441 static int getInvisibleActiveFromInvisibleElement(int element)
6442 {
6443   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6444           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6445           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6446           element);
6447 }
6448
6449 static int getInvisibleFromInvisibleActiveElement(int element)
6450 {
6451   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6452           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6453           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6454           element);
6455 }
6456
6457 static void RedrawAllLightSwitchesAndInvisibleElements()
6458 {
6459   int x, y;
6460
6461   SCAN_PLAYFIELD(x, y)
6462   {
6463     int element = Feld[x][y];
6464
6465     if (element == EL_LIGHT_SWITCH &&
6466         game.light_time_left > 0)
6467     {
6468       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6469       TEST_DrawLevelField(x, y);
6470     }
6471     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6472              game.light_time_left == 0)
6473     {
6474       Feld[x][y] = EL_LIGHT_SWITCH;
6475       TEST_DrawLevelField(x, y);
6476     }
6477     else if (element == EL_EMC_DRIPPER &&
6478              game.light_time_left > 0)
6479     {
6480       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6481       TEST_DrawLevelField(x, y);
6482     }
6483     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6484              game.light_time_left == 0)
6485     {
6486       Feld[x][y] = EL_EMC_DRIPPER;
6487       TEST_DrawLevelField(x, y);
6488     }
6489     else if (element == EL_INVISIBLE_STEELWALL ||
6490              element == EL_INVISIBLE_WALL ||
6491              element == EL_INVISIBLE_SAND)
6492     {
6493       if (game.light_time_left > 0)
6494         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6495
6496       TEST_DrawLevelField(x, y);
6497
6498       /* uncrumble neighbour fields, if needed */
6499       if (element == EL_INVISIBLE_SAND)
6500         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6501     }
6502     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6503              element == EL_INVISIBLE_WALL_ACTIVE ||
6504              element == EL_INVISIBLE_SAND_ACTIVE)
6505     {
6506       if (game.light_time_left == 0)
6507         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6508
6509       TEST_DrawLevelField(x, y);
6510
6511       /* re-crumble neighbour fields, if needed */
6512       if (element == EL_INVISIBLE_SAND)
6513         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6514     }
6515   }
6516 }
6517
6518 static void RedrawAllInvisibleElementsForLenses()
6519 {
6520   int x, y;
6521
6522   SCAN_PLAYFIELD(x, y)
6523   {
6524     int element = Feld[x][y];
6525
6526     if (element == EL_EMC_DRIPPER &&
6527         game.lenses_time_left > 0)
6528     {
6529       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6530       TEST_DrawLevelField(x, y);
6531     }
6532     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6533              game.lenses_time_left == 0)
6534     {
6535       Feld[x][y] = EL_EMC_DRIPPER;
6536       TEST_DrawLevelField(x, y);
6537     }
6538     else if (element == EL_INVISIBLE_STEELWALL ||
6539              element == EL_INVISIBLE_WALL ||
6540              element == EL_INVISIBLE_SAND)
6541     {
6542       if (game.lenses_time_left > 0)
6543         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6544
6545       TEST_DrawLevelField(x, y);
6546
6547       /* uncrumble neighbour fields, if needed */
6548       if (element == EL_INVISIBLE_SAND)
6549         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6550     }
6551     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6552              element == EL_INVISIBLE_WALL_ACTIVE ||
6553              element == EL_INVISIBLE_SAND_ACTIVE)
6554     {
6555       if (game.lenses_time_left == 0)
6556         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6557
6558       TEST_DrawLevelField(x, y);
6559
6560       /* re-crumble neighbour fields, if needed */
6561       if (element == EL_INVISIBLE_SAND)
6562         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6563     }
6564   }
6565 }
6566
6567 static void RedrawAllInvisibleElementsForMagnifier()
6568 {
6569   int x, y;
6570
6571   SCAN_PLAYFIELD(x, y)
6572   {
6573     int element = Feld[x][y];
6574
6575     if (element == EL_EMC_FAKE_GRASS &&
6576         game.magnify_time_left > 0)
6577     {
6578       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6579       TEST_DrawLevelField(x, y);
6580     }
6581     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6582              game.magnify_time_left == 0)
6583     {
6584       Feld[x][y] = EL_EMC_FAKE_GRASS;
6585       TEST_DrawLevelField(x, y);
6586     }
6587     else if (IS_GATE_GRAY(element) &&
6588              game.magnify_time_left > 0)
6589     {
6590       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6591                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6592                     IS_EM_GATE_GRAY(element) ?
6593                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6594                     IS_EMC_GATE_GRAY(element) ?
6595                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6596                     IS_DC_GATE_GRAY(element) ?
6597                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6598                     element);
6599       TEST_DrawLevelField(x, y);
6600     }
6601     else if (IS_GATE_GRAY_ACTIVE(element) &&
6602              game.magnify_time_left == 0)
6603     {
6604       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6605                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6606                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6607                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6608                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6609                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6610                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6611                     EL_DC_GATE_WHITE_GRAY :
6612                     element);
6613       TEST_DrawLevelField(x, y);
6614     }
6615   }
6616 }
6617
6618 static void ToggleLightSwitch(int x, int y)
6619 {
6620   int element = Feld[x][y];
6621
6622   game.light_time_left =
6623     (element == EL_LIGHT_SWITCH ?
6624      level.time_light * FRAMES_PER_SECOND : 0);
6625
6626   RedrawAllLightSwitchesAndInvisibleElements();
6627 }
6628
6629 static void ActivateTimegateSwitch(int x, int y)
6630 {
6631   int xx, yy;
6632
6633   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6634
6635   SCAN_PLAYFIELD(xx, yy)
6636   {
6637     int element = Feld[xx][yy];
6638
6639     if (element == EL_TIMEGATE_CLOSED ||
6640         element == EL_TIMEGATE_CLOSING)
6641     {
6642       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6643       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6644     }
6645
6646     /*
6647     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6648     {
6649       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6650       TEST_DrawLevelField(xx, yy);
6651     }
6652     */
6653
6654   }
6655
6656 #if 1
6657   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6658                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6659 #else
6660   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6661 #endif
6662 }
6663
6664 void Impact(int x, int y)
6665 {
6666   boolean last_line = (y == lev_fieldy - 1);
6667   boolean object_hit = FALSE;
6668   boolean impact = (last_line || object_hit);
6669   int element = Feld[x][y];
6670   int smashed = EL_STEELWALL;
6671
6672   if (!last_line)       /* check if element below was hit */
6673   {
6674     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6675       return;
6676
6677     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6678                                          MovDir[x][y + 1] != MV_DOWN ||
6679                                          MovPos[x][y + 1] <= TILEY / 2));
6680
6681     /* do not smash moving elements that left the smashed field in time */
6682     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6683         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6684       object_hit = FALSE;
6685
6686 #if USE_QUICKSAND_IMPACT_BUGFIX
6687     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6688     {
6689       RemoveMovingField(x, y + 1);
6690       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6691       Feld[x][y + 2] = EL_ROCK;
6692       TEST_DrawLevelField(x, y + 2);
6693
6694       object_hit = TRUE;
6695     }
6696
6697     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6698     {
6699       RemoveMovingField(x, y + 1);
6700       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6701       Feld[x][y + 2] = EL_ROCK;
6702       TEST_DrawLevelField(x, y + 2);
6703
6704       object_hit = TRUE;
6705     }
6706 #endif
6707
6708     if (object_hit)
6709       smashed = MovingOrBlocked2Element(x, y + 1);
6710
6711     impact = (last_line || object_hit);
6712   }
6713
6714   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6715   {
6716     SplashAcid(x, y + 1);
6717     return;
6718   }
6719
6720   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6721   /* only reset graphic animation if graphic really changes after impact */
6722   if (impact &&
6723       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6724   {
6725     ResetGfxAnimation(x, y);
6726     TEST_DrawLevelField(x, y);
6727   }
6728
6729   if (impact && CAN_EXPLODE_IMPACT(element))
6730   {
6731     Bang(x, y);
6732     return;
6733   }
6734   else if (impact && element == EL_PEARL &&
6735            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6736   {
6737     ResetGfxAnimation(x, y);
6738
6739     Feld[x][y] = EL_PEARL_BREAKING;
6740     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6741     return;
6742   }
6743   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6744   {
6745     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6746
6747     return;
6748   }
6749
6750   if (impact && element == EL_AMOEBA_DROP)
6751   {
6752     if (object_hit && IS_PLAYER(x, y + 1))
6753       KillPlayerUnlessEnemyProtected(x, y + 1);
6754     else if (object_hit && smashed == EL_PENGUIN)
6755       Bang(x, y + 1);
6756     else
6757     {
6758       Feld[x][y] = EL_AMOEBA_GROWING;
6759       Store[x][y] = EL_AMOEBA_WET;
6760
6761       ResetRandomAnimationValue(x, y);
6762     }
6763     return;
6764   }
6765
6766   if (object_hit)               /* check which object was hit */
6767   {
6768     if ((CAN_PASS_MAGIC_WALL(element) && 
6769          (smashed == EL_MAGIC_WALL ||
6770           smashed == EL_BD_MAGIC_WALL)) ||
6771         (CAN_PASS_DC_MAGIC_WALL(element) &&
6772          smashed == EL_DC_MAGIC_WALL))
6773     {
6774       int xx, yy;
6775       int activated_magic_wall =
6776         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6777          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6778          EL_DC_MAGIC_WALL_ACTIVE);
6779
6780       /* activate magic wall / mill */
6781       SCAN_PLAYFIELD(xx, yy)
6782       {
6783         if (Feld[xx][yy] == smashed)
6784           Feld[xx][yy] = activated_magic_wall;
6785       }
6786
6787       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6788       game.magic_wall_active = TRUE;
6789
6790       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6791                             SND_MAGIC_WALL_ACTIVATING :
6792                             smashed == EL_BD_MAGIC_WALL ?
6793                             SND_BD_MAGIC_WALL_ACTIVATING :
6794                             SND_DC_MAGIC_WALL_ACTIVATING));
6795     }
6796
6797     if (IS_PLAYER(x, y + 1))
6798     {
6799       if (CAN_SMASH_PLAYER(element))
6800       {
6801         KillPlayerUnlessEnemyProtected(x, y + 1);
6802         return;
6803       }
6804     }
6805     else if (smashed == EL_PENGUIN)
6806     {
6807       if (CAN_SMASH_PLAYER(element))
6808       {
6809         Bang(x, y + 1);
6810         return;
6811       }
6812     }
6813     else if (element == EL_BD_DIAMOND)
6814     {
6815       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6816       {
6817         Bang(x, y + 1);
6818         return;
6819       }
6820     }
6821     else if (((element == EL_SP_INFOTRON ||
6822                element == EL_SP_ZONK) &&
6823               (smashed == EL_SP_SNIKSNAK ||
6824                smashed == EL_SP_ELECTRON ||
6825                smashed == EL_SP_DISK_ORANGE)) ||
6826              (element == EL_SP_INFOTRON &&
6827               smashed == EL_SP_DISK_YELLOW))
6828     {
6829       Bang(x, y + 1);
6830       return;
6831     }
6832     else if (CAN_SMASH_EVERYTHING(element))
6833     {
6834       if (IS_CLASSIC_ENEMY(smashed) ||
6835           CAN_EXPLODE_SMASHED(smashed))
6836       {
6837         Bang(x, y + 1);
6838         return;
6839       }
6840       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6841       {
6842         if (smashed == EL_LAMP ||
6843             smashed == EL_LAMP_ACTIVE)
6844         {
6845           Bang(x, y + 1);
6846           return;
6847         }
6848         else if (smashed == EL_NUT)
6849         {
6850           Feld[x][y + 1] = EL_NUT_BREAKING;
6851           PlayLevelSound(x, y, SND_NUT_BREAKING);
6852           RaiseScoreElement(EL_NUT);
6853           return;
6854         }
6855         else if (smashed == EL_PEARL)
6856         {
6857           ResetGfxAnimation(x, y);
6858
6859           Feld[x][y + 1] = EL_PEARL_BREAKING;
6860           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6861           return;
6862         }
6863         else if (smashed == EL_DIAMOND)
6864         {
6865           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6866           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6867           return;
6868         }
6869         else if (IS_BELT_SWITCH(smashed))
6870         {
6871           ToggleBeltSwitch(x, y + 1);
6872         }
6873         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6874                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6875                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6876                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6877         {
6878           ToggleSwitchgateSwitch(x, y + 1);
6879         }
6880         else if (smashed == EL_LIGHT_SWITCH ||
6881                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6882         {
6883           ToggleLightSwitch(x, y + 1);
6884         }
6885         else
6886         {
6887 #if 0
6888           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6889 #endif
6890
6891           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6892
6893           CheckElementChangeBySide(x, y + 1, smashed, element,
6894                                    CE_SWITCHED, CH_SIDE_TOP);
6895           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6896                                             CH_SIDE_TOP);
6897         }
6898       }
6899       else
6900       {
6901         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6902       }
6903     }
6904   }
6905
6906   /* play sound of magic wall / mill */
6907   if (!last_line &&
6908       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6909        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6910        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6911   {
6912     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6913       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6914     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6915       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6916     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6917       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6918
6919     return;
6920   }
6921
6922   /* play sound of object that hits the ground */
6923   if (last_line || object_hit)
6924     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6925 }
6926
6927 inline static void TurnRoundExt(int x, int y)
6928 {
6929   static struct
6930   {
6931     int dx, dy;
6932   } move_xy[] =
6933   {
6934     {  0,  0 },
6935     { -1,  0 },
6936     { +1,  0 },
6937     {  0,  0 },
6938     {  0, -1 },
6939     {  0,  0 }, { 0, 0 }, { 0, 0 },
6940     {  0, +1 }
6941   };
6942   static struct
6943   {
6944     int left, right, back;
6945   } turn[] =
6946   {
6947     { 0,        0,              0        },
6948     { MV_DOWN,  MV_UP,          MV_RIGHT },
6949     { MV_UP,    MV_DOWN,        MV_LEFT  },
6950     { 0,        0,              0        },
6951     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6952     { 0,        0,              0        },
6953     { 0,        0,              0        },
6954     { 0,        0,              0        },
6955     { MV_RIGHT, MV_LEFT,        MV_UP    }
6956   };
6957
6958   int element = Feld[x][y];
6959   int move_pattern = element_info[element].move_pattern;
6960
6961   int old_move_dir = MovDir[x][y];
6962   int left_dir  = turn[old_move_dir].left;
6963   int right_dir = turn[old_move_dir].right;
6964   int back_dir  = turn[old_move_dir].back;
6965
6966   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6967   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6968   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6969   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6970
6971   int left_x  = x + left_dx,  left_y  = y + left_dy;
6972   int right_x = x + right_dx, right_y = y + right_dy;
6973   int move_x  = x + move_dx,  move_y  = y + move_dy;
6974
6975   int xx, yy;
6976
6977   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6978   {
6979     TestIfBadThingTouchesOtherBadThing(x, y);
6980
6981     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6982       MovDir[x][y] = right_dir;
6983     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6984       MovDir[x][y] = left_dir;
6985
6986     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6987       MovDelay[x][y] = 9;
6988     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6989       MovDelay[x][y] = 1;
6990   }
6991   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6992   {
6993     TestIfBadThingTouchesOtherBadThing(x, y);
6994
6995     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6996       MovDir[x][y] = left_dir;
6997     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6998       MovDir[x][y] = right_dir;
6999
7000     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7001       MovDelay[x][y] = 9;
7002     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7003       MovDelay[x][y] = 1;
7004   }
7005   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7006   {
7007     TestIfBadThingTouchesOtherBadThing(x, y);
7008
7009     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7010       MovDir[x][y] = left_dir;
7011     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7012       MovDir[x][y] = right_dir;
7013
7014     if (MovDir[x][y] != old_move_dir)
7015       MovDelay[x][y] = 9;
7016   }
7017   else if (element == EL_YAMYAM)
7018   {
7019     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7020     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7021
7022     if (can_turn_left && can_turn_right)
7023       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7024     else if (can_turn_left)
7025       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7026     else if (can_turn_right)
7027       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7028     else
7029       MovDir[x][y] = back_dir;
7030
7031     MovDelay[x][y] = 16 + 16 * RND(3);
7032   }
7033   else if (element == EL_DARK_YAMYAM)
7034   {
7035     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7036                                                          left_x, left_y);
7037     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7038                                                          right_x, right_y);
7039
7040     if (can_turn_left && can_turn_right)
7041       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7042     else if (can_turn_left)
7043       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7044     else if (can_turn_right)
7045       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7046     else
7047       MovDir[x][y] = back_dir;
7048
7049     MovDelay[x][y] = 16 + 16 * RND(3);
7050   }
7051   else if (element == EL_PACMAN)
7052   {
7053     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7054     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7055
7056     if (can_turn_left && can_turn_right)
7057       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7058     else if (can_turn_left)
7059       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7060     else if (can_turn_right)
7061       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7062     else
7063       MovDir[x][y] = back_dir;
7064
7065     MovDelay[x][y] = 6 + RND(40);
7066   }
7067   else if (element == EL_PIG)
7068   {
7069     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7070     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7071     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7072     boolean should_turn_left, should_turn_right, should_move_on;
7073     int rnd_value = 24;
7074     int rnd = RND(rnd_value);
7075
7076     should_turn_left = (can_turn_left &&
7077                         (!can_move_on ||
7078                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7079                                                    y + back_dy + left_dy)));
7080     should_turn_right = (can_turn_right &&
7081                          (!can_move_on ||
7082                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7083                                                     y + back_dy + right_dy)));
7084     should_move_on = (can_move_on &&
7085                       (!can_turn_left ||
7086                        !can_turn_right ||
7087                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7088                                                  y + move_dy + left_dy) ||
7089                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7090                                                  y + move_dy + right_dy)));
7091
7092     if (should_turn_left || should_turn_right || should_move_on)
7093     {
7094       if (should_turn_left && should_turn_right && should_move_on)
7095         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7096                         rnd < 2 * rnd_value / 3 ? right_dir :
7097                         old_move_dir);
7098       else if (should_turn_left && should_turn_right)
7099         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7100       else if (should_turn_left && should_move_on)
7101         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7102       else if (should_turn_right && should_move_on)
7103         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7104       else if (should_turn_left)
7105         MovDir[x][y] = left_dir;
7106       else if (should_turn_right)
7107         MovDir[x][y] = right_dir;
7108       else if (should_move_on)
7109         MovDir[x][y] = old_move_dir;
7110     }
7111     else if (can_move_on && rnd > rnd_value / 8)
7112       MovDir[x][y] = old_move_dir;
7113     else if (can_turn_left && can_turn_right)
7114       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7115     else if (can_turn_left && rnd > rnd_value / 8)
7116       MovDir[x][y] = left_dir;
7117     else if (can_turn_right && rnd > rnd_value/8)
7118       MovDir[x][y] = right_dir;
7119     else
7120       MovDir[x][y] = back_dir;
7121
7122     xx = x + move_xy[MovDir[x][y]].dx;
7123     yy = y + move_xy[MovDir[x][y]].dy;
7124
7125     if (!IN_LEV_FIELD(xx, yy) ||
7126         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7127       MovDir[x][y] = old_move_dir;
7128
7129     MovDelay[x][y] = 0;
7130   }
7131   else if (element == EL_DRAGON)
7132   {
7133     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7134     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7135     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7136     int rnd_value = 24;
7137     int rnd = RND(rnd_value);
7138
7139     if (can_move_on && rnd > rnd_value / 8)
7140       MovDir[x][y] = old_move_dir;
7141     else if (can_turn_left && can_turn_right)
7142       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7143     else if (can_turn_left && rnd > rnd_value / 8)
7144       MovDir[x][y] = left_dir;
7145     else if (can_turn_right && rnd > rnd_value / 8)
7146       MovDir[x][y] = right_dir;
7147     else
7148       MovDir[x][y] = back_dir;
7149
7150     xx = x + move_xy[MovDir[x][y]].dx;
7151     yy = y + move_xy[MovDir[x][y]].dy;
7152
7153     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7154       MovDir[x][y] = old_move_dir;
7155
7156     MovDelay[x][y] = 0;
7157   }
7158   else if (element == EL_MOLE)
7159   {
7160     boolean can_move_on =
7161       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7162                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7163                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7164     if (!can_move_on)
7165     {
7166       boolean can_turn_left =
7167         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7168                               IS_AMOEBOID(Feld[left_x][left_y])));
7169
7170       boolean can_turn_right =
7171         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7172                               IS_AMOEBOID(Feld[right_x][right_y])));
7173
7174       if (can_turn_left && can_turn_right)
7175         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7176       else if (can_turn_left)
7177         MovDir[x][y] = left_dir;
7178       else
7179         MovDir[x][y] = right_dir;
7180     }
7181
7182     if (MovDir[x][y] != old_move_dir)
7183       MovDelay[x][y] = 9;
7184   }
7185   else if (element == EL_BALLOON)
7186   {
7187     MovDir[x][y] = game.wind_direction;
7188     MovDelay[x][y] = 0;
7189   }
7190   else if (element == EL_SPRING)
7191   {
7192 #if USE_NEW_SPRING_BUMPER
7193     if (MovDir[x][y] & MV_HORIZONTAL)
7194     {
7195       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7196           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7197       {
7198         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7199         ResetGfxAnimation(move_x, move_y);
7200         TEST_DrawLevelField(move_x, move_y);
7201
7202         MovDir[x][y] = back_dir;
7203       }
7204       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7205                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7206         MovDir[x][y] = MV_NONE;
7207     }
7208 #else
7209     if (MovDir[x][y] & MV_HORIZONTAL &&
7210         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7211          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7212       MovDir[x][y] = MV_NONE;
7213 #endif
7214
7215     MovDelay[x][y] = 0;
7216   }
7217   else if (element == EL_ROBOT ||
7218            element == EL_SATELLITE ||
7219            element == EL_PENGUIN ||
7220            element == EL_EMC_ANDROID)
7221   {
7222     int attr_x = -1, attr_y = -1;
7223
7224     if (AllPlayersGone)
7225     {
7226       attr_x = ExitX;
7227       attr_y = ExitY;
7228     }
7229     else
7230     {
7231       int i;
7232
7233       for (i = 0; i < MAX_PLAYERS; i++)
7234       {
7235         struct PlayerInfo *player = &stored_player[i];
7236         int jx = player->jx, jy = player->jy;
7237
7238         if (!player->active)
7239           continue;
7240
7241         if (attr_x == -1 ||
7242             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7243         {
7244           attr_x = jx;
7245           attr_y = jy;
7246         }
7247       }
7248     }
7249
7250     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7251         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7252          game.engine_version < VERSION_IDENT(3,1,0,0)))
7253     {
7254       attr_x = ZX;
7255       attr_y = ZY;
7256     }
7257
7258     if (element == EL_PENGUIN)
7259     {
7260       int i;
7261       static int xy[4][2] =
7262       {
7263         { 0, -1 },
7264         { -1, 0 },
7265         { +1, 0 },
7266         { 0, +1 }
7267       };
7268
7269       for (i = 0; i < NUM_DIRECTIONS; i++)
7270       {
7271         int ex = x + xy[i][0];
7272         int ey = y + xy[i][1];
7273
7274         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7275                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7276                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7277                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7278         {
7279           attr_x = ex;
7280           attr_y = ey;
7281           break;
7282         }
7283       }
7284     }
7285
7286     MovDir[x][y] = MV_NONE;
7287     if (attr_x < x)
7288       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7289     else if (attr_x > x)
7290       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7291     if (attr_y < y)
7292       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7293     else if (attr_y > y)
7294       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7295
7296     if (element == EL_ROBOT)
7297     {
7298       int newx, newy;
7299
7300       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7301         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7302       Moving2Blocked(x, y, &newx, &newy);
7303
7304       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7305         MovDelay[x][y] = 8 + 8 * !RND(3);
7306       else
7307         MovDelay[x][y] = 16;
7308     }
7309     else if (element == EL_PENGUIN)
7310     {
7311       int newx, newy;
7312
7313       MovDelay[x][y] = 1;
7314
7315       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7316       {
7317         boolean first_horiz = RND(2);
7318         int new_move_dir = MovDir[x][y];
7319
7320         MovDir[x][y] =
7321           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7322         Moving2Blocked(x, y, &newx, &newy);
7323
7324         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7325           return;
7326
7327         MovDir[x][y] =
7328           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7329         Moving2Blocked(x, y, &newx, &newy);
7330
7331         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7332           return;
7333
7334         MovDir[x][y] = old_move_dir;
7335         return;
7336       }
7337     }
7338     else if (element == EL_SATELLITE)
7339     {
7340       int newx, newy;
7341
7342       MovDelay[x][y] = 1;
7343
7344       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7345       {
7346         boolean first_horiz = RND(2);
7347         int new_move_dir = MovDir[x][y];
7348
7349         MovDir[x][y] =
7350           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7351         Moving2Blocked(x, y, &newx, &newy);
7352
7353         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7354           return;
7355
7356         MovDir[x][y] =
7357           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7358         Moving2Blocked(x, y, &newx, &newy);
7359
7360         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7361           return;
7362
7363         MovDir[x][y] = old_move_dir;
7364         return;
7365       }
7366     }
7367     else if (element == EL_EMC_ANDROID)
7368     {
7369       static int check_pos[16] =
7370       {
7371         -1,             /*  0 => (invalid)          */
7372         7,              /*  1 => MV_LEFT            */
7373         3,              /*  2 => MV_RIGHT           */
7374         -1,             /*  3 => (invalid)          */
7375         1,              /*  4 =>            MV_UP   */
7376         0,              /*  5 => MV_LEFT  | MV_UP   */
7377         2,              /*  6 => MV_RIGHT | MV_UP   */
7378         -1,             /*  7 => (invalid)          */
7379         5,              /*  8 =>            MV_DOWN */
7380         6,              /*  9 => MV_LEFT  | MV_DOWN */
7381         4,              /* 10 => MV_RIGHT | MV_DOWN */
7382         -1,             /* 11 => (invalid)          */
7383         -1,             /* 12 => (invalid)          */
7384         -1,             /* 13 => (invalid)          */
7385         -1,             /* 14 => (invalid)          */
7386         -1,             /* 15 => (invalid)          */
7387       };
7388       static struct
7389       {
7390         int dx, dy;
7391         int dir;
7392       } check_xy[8] =
7393       {
7394         { -1, -1,       MV_LEFT  | MV_UP   },
7395         {  0, -1,                  MV_UP   },
7396         { +1, -1,       MV_RIGHT | MV_UP   },
7397         { +1,  0,       MV_RIGHT           },
7398         { +1, +1,       MV_RIGHT | MV_DOWN },
7399         {  0, +1,                  MV_DOWN },
7400         { -1, +1,       MV_LEFT  | MV_DOWN },
7401         { -1,  0,       MV_LEFT            },
7402       };
7403       int start_pos, check_order;
7404       boolean can_clone = FALSE;
7405       int i;
7406
7407       /* check if there is any free field around current position */
7408       for (i = 0; i < 8; i++)
7409       {
7410         int newx = x + check_xy[i].dx;
7411         int newy = y + check_xy[i].dy;
7412
7413         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7414         {
7415           can_clone = TRUE;
7416
7417           break;
7418         }
7419       }
7420
7421       if (can_clone)            /* randomly find an element to clone */
7422       {
7423         can_clone = FALSE;
7424
7425         start_pos = check_pos[RND(8)];
7426         check_order = (RND(2) ? -1 : +1);
7427
7428         for (i = 0; i < 8; i++)
7429         {
7430           int pos_raw = start_pos + i * check_order;
7431           int pos = (pos_raw + 8) % 8;
7432           int newx = x + check_xy[pos].dx;
7433           int newy = y + check_xy[pos].dy;
7434
7435           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7436           {
7437             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7438             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7439
7440             Store[x][y] = Feld[newx][newy];
7441
7442             can_clone = TRUE;
7443
7444             break;
7445           }
7446         }
7447       }
7448
7449       if (can_clone)            /* randomly find a direction to move */
7450       {
7451         can_clone = FALSE;
7452
7453         start_pos = check_pos[RND(8)];
7454         check_order = (RND(2) ? -1 : +1);
7455
7456         for (i = 0; i < 8; i++)
7457         {
7458           int pos_raw = start_pos + i * check_order;
7459           int pos = (pos_raw + 8) % 8;
7460           int newx = x + check_xy[pos].dx;
7461           int newy = y + check_xy[pos].dy;
7462           int new_move_dir = check_xy[pos].dir;
7463
7464           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7465           {
7466             MovDir[x][y] = new_move_dir;
7467             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7468
7469             can_clone = TRUE;
7470
7471             break;
7472           }
7473         }
7474       }
7475
7476       if (can_clone)            /* cloning and moving successful */
7477         return;
7478
7479       /* cannot clone -- try to move towards player */
7480
7481       start_pos = check_pos[MovDir[x][y] & 0x0f];
7482       check_order = (RND(2) ? -1 : +1);
7483
7484       for (i = 0; i < 3; i++)
7485       {
7486         /* first check start_pos, then previous/next or (next/previous) pos */
7487         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7488         int pos = (pos_raw + 8) % 8;
7489         int newx = x + check_xy[pos].dx;
7490         int newy = y + check_xy[pos].dy;
7491         int new_move_dir = check_xy[pos].dir;
7492
7493         if (IS_PLAYER(newx, newy))
7494           break;
7495
7496         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7497         {
7498           MovDir[x][y] = new_move_dir;
7499           MovDelay[x][y] = level.android_move_time * 8 + 1;
7500
7501           break;
7502         }
7503       }
7504     }
7505   }
7506   else if (move_pattern == MV_TURNING_LEFT ||
7507            move_pattern == MV_TURNING_RIGHT ||
7508            move_pattern == MV_TURNING_LEFT_RIGHT ||
7509            move_pattern == MV_TURNING_RIGHT_LEFT ||
7510            move_pattern == MV_TURNING_RANDOM ||
7511            move_pattern == MV_ALL_DIRECTIONS)
7512   {
7513     boolean can_turn_left =
7514       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7515     boolean can_turn_right =
7516       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7517
7518     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7519       return;
7520
7521     if (move_pattern == MV_TURNING_LEFT)
7522       MovDir[x][y] = left_dir;
7523     else if (move_pattern == MV_TURNING_RIGHT)
7524       MovDir[x][y] = right_dir;
7525     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7526       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7527     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7528       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7529     else if (move_pattern == MV_TURNING_RANDOM)
7530       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7531                       can_turn_right && !can_turn_left ? right_dir :
7532                       RND(2) ? left_dir : right_dir);
7533     else if (can_turn_left && can_turn_right)
7534       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7535     else if (can_turn_left)
7536       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7537     else if (can_turn_right)
7538       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7539     else
7540       MovDir[x][y] = back_dir;
7541
7542     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7543   }
7544   else if (move_pattern == MV_HORIZONTAL ||
7545            move_pattern == MV_VERTICAL)
7546   {
7547     if (move_pattern & old_move_dir)
7548       MovDir[x][y] = back_dir;
7549     else if (move_pattern == MV_HORIZONTAL)
7550       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7551     else if (move_pattern == MV_VERTICAL)
7552       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7553
7554     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7555   }
7556   else if (move_pattern & MV_ANY_DIRECTION)
7557   {
7558     MovDir[x][y] = move_pattern;
7559     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7560   }
7561   else if (move_pattern & MV_WIND_DIRECTION)
7562   {
7563     MovDir[x][y] = game.wind_direction;
7564     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7565   }
7566   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7567   {
7568     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7569       MovDir[x][y] = left_dir;
7570     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7571       MovDir[x][y] = right_dir;
7572
7573     if (MovDir[x][y] != old_move_dir)
7574       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7575   }
7576   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7577   {
7578     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7579       MovDir[x][y] = right_dir;
7580     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7581       MovDir[x][y] = left_dir;
7582
7583     if (MovDir[x][y] != old_move_dir)
7584       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7585   }
7586   else if (move_pattern == MV_TOWARDS_PLAYER ||
7587            move_pattern == MV_AWAY_FROM_PLAYER)
7588   {
7589     int attr_x = -1, attr_y = -1;
7590     int newx, newy;
7591     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7592
7593     if (AllPlayersGone)
7594     {
7595       attr_x = ExitX;
7596       attr_y = ExitY;
7597     }
7598     else
7599     {
7600       int i;
7601
7602       for (i = 0; i < MAX_PLAYERS; i++)
7603       {
7604         struct PlayerInfo *player = &stored_player[i];
7605         int jx = player->jx, jy = player->jy;
7606
7607         if (!player->active)
7608           continue;
7609
7610         if (attr_x == -1 ||
7611             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7612         {
7613           attr_x = jx;
7614           attr_y = jy;
7615         }
7616       }
7617     }
7618
7619     MovDir[x][y] = MV_NONE;
7620     if (attr_x < x)
7621       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7622     else if (attr_x > x)
7623       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7624     if (attr_y < y)
7625       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7626     else if (attr_y > y)
7627       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7628
7629     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7630
7631     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7632     {
7633       boolean first_horiz = RND(2);
7634       int new_move_dir = MovDir[x][y];
7635
7636       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7637       {
7638         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7639         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7640
7641         return;
7642       }
7643
7644       MovDir[x][y] =
7645         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7646       Moving2Blocked(x, y, &newx, &newy);
7647
7648       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7649         return;
7650
7651       MovDir[x][y] =
7652         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7653       Moving2Blocked(x, y, &newx, &newy);
7654
7655       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7656         return;
7657
7658       MovDir[x][y] = old_move_dir;
7659     }
7660   }
7661   else if (move_pattern == MV_WHEN_PUSHED ||
7662            move_pattern == MV_WHEN_DROPPED)
7663   {
7664     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7665       MovDir[x][y] = MV_NONE;
7666
7667     MovDelay[x][y] = 0;
7668   }
7669   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7670   {
7671     static int test_xy[7][2] =
7672     {
7673       { 0, -1 },
7674       { -1, 0 },
7675       { +1, 0 },
7676       { 0, +1 },
7677       { 0, -1 },
7678       { -1, 0 },
7679       { +1, 0 },
7680     };
7681     static int test_dir[7] =
7682     {
7683       MV_UP,
7684       MV_LEFT,
7685       MV_RIGHT,
7686       MV_DOWN,
7687       MV_UP,
7688       MV_LEFT,
7689       MV_RIGHT,
7690     };
7691     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7692     int move_preference = -1000000;     /* start with very low preference */
7693     int new_move_dir = MV_NONE;
7694     int start_test = RND(4);
7695     int i;
7696
7697     for (i = 0; i < NUM_DIRECTIONS; i++)
7698     {
7699       int move_dir = test_dir[start_test + i];
7700       int move_dir_preference;
7701
7702       xx = x + test_xy[start_test + i][0];
7703       yy = y + test_xy[start_test + i][1];
7704
7705       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7706           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7707       {
7708         new_move_dir = move_dir;
7709
7710         break;
7711       }
7712
7713       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7714         continue;
7715
7716       move_dir_preference = -1 * RunnerVisit[xx][yy];
7717       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7718         move_dir_preference = PlayerVisit[xx][yy];
7719
7720       if (move_dir_preference > move_preference)
7721       {
7722         /* prefer field that has not been visited for the longest time */
7723         move_preference = move_dir_preference;
7724         new_move_dir = move_dir;
7725       }
7726       else if (move_dir_preference == move_preference &&
7727                move_dir == old_move_dir)
7728       {
7729         /* prefer last direction when all directions are preferred equally */
7730         move_preference = move_dir_preference;
7731         new_move_dir = move_dir;
7732       }
7733     }
7734
7735     MovDir[x][y] = new_move_dir;
7736     if (old_move_dir != new_move_dir)
7737       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7738   }
7739 }
7740
7741 static void TurnRound(int x, int y)
7742 {
7743   int direction = MovDir[x][y];
7744
7745   TurnRoundExt(x, y);
7746
7747   GfxDir[x][y] = MovDir[x][y];
7748
7749   if (direction != MovDir[x][y])
7750     GfxFrame[x][y] = 0;
7751
7752   if (MovDelay[x][y])
7753     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7754
7755   ResetGfxFrame(x, y, FALSE);
7756 }
7757
7758 static boolean JustBeingPushed(int x, int y)
7759 {
7760   int i;
7761
7762   for (i = 0; i < MAX_PLAYERS; i++)
7763   {
7764     struct PlayerInfo *player = &stored_player[i];
7765
7766     if (player->active && player->is_pushing && player->MovPos)
7767     {
7768       int next_jx = player->jx + (player->jx - player->last_jx);
7769       int next_jy = player->jy + (player->jy - player->last_jy);
7770
7771       if (x == next_jx && y == next_jy)
7772         return TRUE;
7773     }
7774   }
7775
7776   return FALSE;
7777 }
7778
7779 void StartMoving(int x, int y)
7780 {
7781   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7782   int element = Feld[x][y];
7783
7784   if (Stop[x][y])
7785     return;
7786
7787   if (MovDelay[x][y] == 0)
7788     GfxAction[x][y] = ACTION_DEFAULT;
7789
7790   if (CAN_FALL(element) && y < lev_fieldy - 1)
7791   {
7792     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7793         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7794       if (JustBeingPushed(x, y))
7795         return;
7796
7797     if (element == EL_QUICKSAND_FULL)
7798     {
7799       if (IS_FREE(x, y + 1))
7800       {
7801         InitMovingField(x, y, MV_DOWN);
7802         started_moving = TRUE;
7803
7804         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7805 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7806         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7807           Store[x][y] = EL_ROCK;
7808 #else
7809         Store[x][y] = EL_ROCK;
7810 #endif
7811
7812         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7813       }
7814       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7815       {
7816         if (!MovDelay[x][y])
7817         {
7818           MovDelay[x][y] = TILEY + 1;
7819
7820           ResetGfxAnimation(x, y);
7821           ResetGfxAnimation(x, y + 1);
7822         }
7823
7824         if (MovDelay[x][y])
7825         {
7826           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7827           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7828
7829           MovDelay[x][y]--;
7830           if (MovDelay[x][y])
7831             return;
7832         }
7833
7834         Feld[x][y] = EL_QUICKSAND_EMPTY;
7835         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7836         Store[x][y + 1] = Store[x][y];
7837         Store[x][y] = 0;
7838
7839         PlayLevelSoundAction(x, y, ACTION_FILLING);
7840       }
7841       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7842       {
7843         if (!MovDelay[x][y])
7844         {
7845           MovDelay[x][y] = TILEY + 1;
7846
7847           ResetGfxAnimation(x, y);
7848           ResetGfxAnimation(x, y + 1);
7849         }
7850
7851         if (MovDelay[x][y])
7852         {
7853           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7854           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7855
7856           MovDelay[x][y]--;
7857           if (MovDelay[x][y])
7858             return;
7859         }
7860
7861         Feld[x][y] = EL_QUICKSAND_EMPTY;
7862         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7863         Store[x][y + 1] = Store[x][y];
7864         Store[x][y] = 0;
7865
7866         PlayLevelSoundAction(x, y, ACTION_FILLING);
7867       }
7868     }
7869     else if (element == EL_QUICKSAND_FAST_FULL)
7870     {
7871       if (IS_FREE(x, y + 1))
7872       {
7873         InitMovingField(x, y, MV_DOWN);
7874         started_moving = TRUE;
7875
7876         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7877 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7878         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7879           Store[x][y] = EL_ROCK;
7880 #else
7881         Store[x][y] = EL_ROCK;
7882 #endif
7883
7884         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7885       }
7886       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7887       {
7888         if (!MovDelay[x][y])
7889         {
7890           MovDelay[x][y] = TILEY + 1;
7891
7892           ResetGfxAnimation(x, y);
7893           ResetGfxAnimation(x, y + 1);
7894         }
7895
7896         if (MovDelay[x][y])
7897         {
7898           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7899           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7900
7901           MovDelay[x][y]--;
7902           if (MovDelay[x][y])
7903             return;
7904         }
7905
7906         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7907         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7908         Store[x][y + 1] = Store[x][y];
7909         Store[x][y] = 0;
7910
7911         PlayLevelSoundAction(x, y, ACTION_FILLING);
7912       }
7913       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7914       {
7915         if (!MovDelay[x][y])
7916         {
7917           MovDelay[x][y] = TILEY + 1;
7918
7919           ResetGfxAnimation(x, y);
7920           ResetGfxAnimation(x, y + 1);
7921         }
7922
7923         if (MovDelay[x][y])
7924         {
7925           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7926           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7927
7928           MovDelay[x][y]--;
7929           if (MovDelay[x][y])
7930             return;
7931         }
7932
7933         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7934         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7935         Store[x][y + 1] = Store[x][y];
7936         Store[x][y] = 0;
7937
7938         PlayLevelSoundAction(x, y, ACTION_FILLING);
7939       }
7940     }
7941     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7942              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7943     {
7944       InitMovingField(x, y, MV_DOWN);
7945       started_moving = TRUE;
7946
7947       Feld[x][y] = EL_QUICKSAND_FILLING;
7948       Store[x][y] = element;
7949
7950       PlayLevelSoundAction(x, y, ACTION_FILLING);
7951     }
7952     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7953              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7954     {
7955       InitMovingField(x, y, MV_DOWN);
7956       started_moving = TRUE;
7957
7958       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7959       Store[x][y] = element;
7960
7961       PlayLevelSoundAction(x, y, ACTION_FILLING);
7962     }
7963     else if (element == EL_MAGIC_WALL_FULL)
7964     {
7965       if (IS_FREE(x, y + 1))
7966       {
7967         InitMovingField(x, y, MV_DOWN);
7968         started_moving = TRUE;
7969
7970         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7971         Store[x][y] = EL_CHANGED(Store[x][y]);
7972       }
7973       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7974       {
7975         if (!MovDelay[x][y])
7976           MovDelay[x][y] = TILEY / 4 + 1;
7977
7978         if (MovDelay[x][y])
7979         {
7980           MovDelay[x][y]--;
7981           if (MovDelay[x][y])
7982             return;
7983         }
7984
7985         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7986         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7987         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7988         Store[x][y] = 0;
7989       }
7990     }
7991     else if (element == EL_BD_MAGIC_WALL_FULL)
7992     {
7993       if (IS_FREE(x, y + 1))
7994       {
7995         InitMovingField(x, y, MV_DOWN);
7996         started_moving = TRUE;
7997
7998         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7999         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8000       }
8001       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8002       {
8003         if (!MovDelay[x][y])
8004           MovDelay[x][y] = TILEY / 4 + 1;
8005
8006         if (MovDelay[x][y])
8007         {
8008           MovDelay[x][y]--;
8009           if (MovDelay[x][y])
8010             return;
8011         }
8012
8013         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8014         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8015         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8016         Store[x][y] = 0;
8017       }
8018     }
8019     else if (element == EL_DC_MAGIC_WALL_FULL)
8020     {
8021       if (IS_FREE(x, y + 1))
8022       {
8023         InitMovingField(x, y, MV_DOWN);
8024         started_moving = TRUE;
8025
8026         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8027         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8028       }
8029       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8030       {
8031         if (!MovDelay[x][y])
8032           MovDelay[x][y] = TILEY / 4 + 1;
8033
8034         if (MovDelay[x][y])
8035         {
8036           MovDelay[x][y]--;
8037           if (MovDelay[x][y])
8038             return;
8039         }
8040
8041         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8042         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8043         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8044         Store[x][y] = 0;
8045       }
8046     }
8047     else if ((CAN_PASS_MAGIC_WALL(element) &&
8048               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8049                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8050              (CAN_PASS_DC_MAGIC_WALL(element) &&
8051               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8052
8053     {
8054       InitMovingField(x, y, MV_DOWN);
8055       started_moving = TRUE;
8056
8057       Feld[x][y] =
8058         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8059          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8060          EL_DC_MAGIC_WALL_FILLING);
8061       Store[x][y] = element;
8062     }
8063     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8064     {
8065       SplashAcid(x, y + 1);
8066
8067       InitMovingField(x, y, MV_DOWN);
8068       started_moving = TRUE;
8069
8070       Store[x][y] = EL_ACID;
8071     }
8072     else if (
8073 #if USE_FIX_IMPACT_COLLISION
8074              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8075               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8076 #else
8077              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8078               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8079 #endif
8080              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8081               CAN_FALL(element) && WasJustFalling[x][y] &&
8082               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8083
8084              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8085               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8086               (Feld[x][y + 1] == EL_BLOCKED)))
8087     {
8088       /* this is needed for a special case not covered by calling "Impact()"
8089          from "ContinueMoving()": if an element moves to a tile directly below
8090          another element which was just falling on that tile (which was empty
8091          in the previous frame), the falling element above would just stop
8092          instead of smashing the element below (in previous version, the above
8093          element was just checked for "moving" instead of "falling", resulting
8094          in incorrect smashes caused by horizontal movement of the above
8095          element; also, the case of the player being the element to smash was
8096          simply not covered here... :-/ ) */
8097
8098       CheckCollision[x][y] = 0;
8099       CheckImpact[x][y] = 0;
8100
8101       Impact(x, y);
8102     }
8103     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8104     {
8105       if (MovDir[x][y] == MV_NONE)
8106       {
8107         InitMovingField(x, y, MV_DOWN);
8108         started_moving = TRUE;
8109       }
8110     }
8111     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8112     {
8113       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8114         MovDir[x][y] = MV_DOWN;
8115
8116       InitMovingField(x, y, MV_DOWN);
8117       started_moving = TRUE;
8118     }
8119     else if (element == EL_AMOEBA_DROP)
8120     {
8121       Feld[x][y] = EL_AMOEBA_GROWING;
8122       Store[x][y] = EL_AMOEBA_WET;
8123     }
8124     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8125               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8126              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8127              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8128     {
8129       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8130                                 (IS_FREE(x - 1, y + 1) ||
8131                                  Feld[x - 1][y + 1] == EL_ACID));
8132       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8133                                 (IS_FREE(x + 1, y + 1) ||
8134                                  Feld[x + 1][y + 1] == EL_ACID));
8135       boolean can_fall_any  = (can_fall_left || can_fall_right);
8136       boolean can_fall_both = (can_fall_left && can_fall_right);
8137       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8138
8139 #if USE_NEW_ALL_SLIPPERY
8140       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8141       {
8142         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8143           can_fall_right = FALSE;
8144         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8145           can_fall_left = FALSE;
8146         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8147           can_fall_right = FALSE;
8148         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8149           can_fall_left = FALSE;
8150
8151         can_fall_any  = (can_fall_left || can_fall_right);
8152         can_fall_both = FALSE;
8153       }
8154 #else
8155       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8156       {
8157         if (slippery_type == SLIPPERY_ONLY_LEFT)
8158           can_fall_right = FALSE;
8159         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8160           can_fall_left = FALSE;
8161         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8162           can_fall_right = FALSE;
8163         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8164           can_fall_left = FALSE;
8165
8166         can_fall_any  = (can_fall_left || can_fall_right);
8167         can_fall_both = (can_fall_left && can_fall_right);
8168       }
8169 #endif
8170
8171 #if USE_NEW_ALL_SLIPPERY
8172 #else
8173 #if USE_NEW_SP_SLIPPERY
8174       /* !!! better use the same properties as for custom elements here !!! */
8175       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8176                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8177       {
8178         can_fall_right = FALSE;         /* slip down on left side */
8179         can_fall_both = FALSE;
8180       }
8181 #endif
8182 #endif
8183
8184 #if USE_NEW_ALL_SLIPPERY
8185       if (can_fall_both)
8186       {
8187         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8188           can_fall_right = FALSE;       /* slip down on left side */
8189         else
8190           can_fall_left = !(can_fall_right = RND(2));
8191
8192         can_fall_both = FALSE;
8193       }
8194 #else
8195       if (can_fall_both)
8196       {
8197         if (game.emulation == EMU_BOULDERDASH ||
8198             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8199           can_fall_right = FALSE;       /* slip down on left side */
8200         else
8201           can_fall_left = !(can_fall_right = RND(2));
8202
8203         can_fall_both = FALSE;
8204       }
8205 #endif
8206
8207       if (can_fall_any)
8208       {
8209         /* if not determined otherwise, prefer left side for slipping down */
8210         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8211         started_moving = TRUE;
8212       }
8213     }
8214 #if 0
8215     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8216 #else
8217     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8218 #endif
8219     {
8220       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8221       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8222       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8223       int belt_dir = game.belt_dir[belt_nr];
8224
8225       if ((belt_dir == MV_LEFT  && left_is_free) ||
8226           (belt_dir == MV_RIGHT && right_is_free))
8227       {
8228         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8229
8230         InitMovingField(x, y, belt_dir);
8231         started_moving = TRUE;
8232
8233         Pushed[x][y] = TRUE;
8234         Pushed[nextx][y] = TRUE;
8235
8236         GfxAction[x][y] = ACTION_DEFAULT;
8237       }
8238       else
8239       {
8240         MovDir[x][y] = 0;       /* if element was moving, stop it */
8241       }
8242     }
8243   }
8244
8245   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8246 #if 0
8247   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8248 #else
8249   if (CAN_MOVE(element) && !started_moving)
8250 #endif
8251   {
8252     int move_pattern = element_info[element].move_pattern;
8253     int newx, newy;
8254
8255 #if 0
8256 #if DEBUG
8257     if (MovDir[x][y] == MV_NONE)
8258     {
8259       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8260              x, y, element, element_info[element].token_name);
8261       printf("StartMoving(): This should never happen!\n");
8262     }
8263 #endif
8264 #endif
8265
8266     Moving2Blocked(x, y, &newx, &newy);
8267
8268     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8269       return;
8270
8271     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8272         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8273     {
8274       WasJustMoving[x][y] = 0;
8275       CheckCollision[x][y] = 0;
8276
8277       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8278
8279       if (Feld[x][y] != element)        /* element has changed */
8280         return;
8281     }
8282
8283     if (!MovDelay[x][y])        /* start new movement phase */
8284     {
8285       /* all objects that can change their move direction after each step
8286          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8287
8288       if (element != EL_YAMYAM &&
8289           element != EL_DARK_YAMYAM &&
8290           element != EL_PACMAN &&
8291           !(move_pattern & MV_ANY_DIRECTION) &&
8292           move_pattern != MV_TURNING_LEFT &&
8293           move_pattern != MV_TURNING_RIGHT &&
8294           move_pattern != MV_TURNING_LEFT_RIGHT &&
8295           move_pattern != MV_TURNING_RIGHT_LEFT &&
8296           move_pattern != MV_TURNING_RANDOM)
8297       {
8298         TurnRound(x, y);
8299
8300         if (MovDelay[x][y] && (element == EL_BUG ||
8301                                element == EL_SPACESHIP ||
8302                                element == EL_SP_SNIKSNAK ||
8303                                element == EL_SP_ELECTRON ||
8304                                element == EL_MOLE))
8305           TEST_DrawLevelField(x, y);
8306       }
8307     }
8308
8309     if (MovDelay[x][y])         /* wait some time before next movement */
8310     {
8311       MovDelay[x][y]--;
8312
8313       if (element == EL_ROBOT ||
8314           element == EL_YAMYAM ||
8315           element == EL_DARK_YAMYAM)
8316       {
8317         DrawLevelElementAnimationIfNeeded(x, y, element);
8318         PlayLevelSoundAction(x, y, ACTION_WAITING);
8319       }
8320       else if (element == EL_SP_ELECTRON)
8321         DrawLevelElementAnimationIfNeeded(x, y, element);
8322       else if (element == EL_DRAGON)
8323       {
8324         int i;
8325         int dir = MovDir[x][y];
8326         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8327         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8328         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8329                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8330                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8331                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8332         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8333
8334         GfxAction[x][y] = ACTION_ATTACKING;
8335
8336         if (IS_PLAYER(x, y))
8337           DrawPlayerField(x, y);
8338         else
8339           TEST_DrawLevelField(x, y);
8340
8341         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8342
8343         for (i = 1; i <= 3; i++)
8344         {
8345           int xx = x + i * dx;
8346           int yy = y + i * dy;
8347           int sx = SCREENX(xx);
8348           int sy = SCREENY(yy);
8349           int flame_graphic = graphic + (i - 1);
8350
8351           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8352             break;
8353
8354           if (MovDelay[x][y])
8355           {
8356             int flamed = MovingOrBlocked2Element(xx, yy);
8357
8358             /* !!! */
8359 #if 0
8360             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8361               Bang(xx, yy);
8362             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8363               RemoveMovingField(xx, yy);
8364             else
8365               RemoveField(xx, yy);
8366 #else
8367             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8368               Bang(xx, yy);
8369             else
8370               RemoveMovingField(xx, yy);
8371 #endif
8372
8373             ChangeDelay[xx][yy] = 0;
8374
8375             Feld[xx][yy] = EL_FLAMES;
8376
8377             if (IN_SCR_FIELD(sx, sy))
8378             {
8379               TEST_DrawLevelFieldCrumbled(xx, yy);
8380               DrawGraphic(sx, sy, flame_graphic, frame);
8381             }
8382           }
8383           else
8384           {
8385             if (Feld[xx][yy] == EL_FLAMES)
8386               Feld[xx][yy] = EL_EMPTY;
8387             TEST_DrawLevelField(xx, yy);
8388           }
8389         }
8390       }
8391
8392       if (MovDelay[x][y])       /* element still has to wait some time */
8393       {
8394         PlayLevelSoundAction(x, y, ACTION_WAITING);
8395
8396         return;
8397       }
8398     }
8399
8400     /* now make next step */
8401
8402     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8403
8404     if (DONT_COLLIDE_WITH(element) &&
8405         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8406         !PLAYER_ENEMY_PROTECTED(newx, newy))
8407     {
8408       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8409
8410       return;
8411     }
8412
8413     else if (CAN_MOVE_INTO_ACID(element) &&
8414              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8415              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8416              (MovDir[x][y] == MV_DOWN ||
8417               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8418     {
8419       SplashAcid(newx, newy);
8420       Store[x][y] = EL_ACID;
8421     }
8422     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8423     {
8424       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8425           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8426           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8427           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8428       {
8429         RemoveField(x, y);
8430         TEST_DrawLevelField(x, y);
8431
8432         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8433         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8434           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8435
8436         local_player->friends_still_needed--;
8437         if (!local_player->friends_still_needed &&
8438             !local_player->GameOver && AllPlayersGone)
8439           PlayerWins(local_player);
8440
8441         return;
8442       }
8443       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8444       {
8445         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8446           TEST_DrawLevelField(newx, newy);
8447         else
8448           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8449       }
8450       else if (!IS_FREE(newx, newy))
8451       {
8452         GfxAction[x][y] = ACTION_WAITING;
8453
8454         if (IS_PLAYER(x, y))
8455           DrawPlayerField(x, y);
8456         else
8457           TEST_DrawLevelField(x, y);
8458
8459         return;
8460       }
8461     }
8462     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8463     {
8464       if (IS_FOOD_PIG(Feld[newx][newy]))
8465       {
8466         if (IS_MOVING(newx, newy))
8467           RemoveMovingField(newx, newy);
8468         else
8469         {
8470           Feld[newx][newy] = EL_EMPTY;
8471           TEST_DrawLevelField(newx, newy);
8472         }
8473
8474         PlayLevelSound(x, y, SND_PIG_DIGGING);
8475       }
8476       else if (!IS_FREE(newx, newy))
8477       {
8478         if (IS_PLAYER(x, y))
8479           DrawPlayerField(x, y);
8480         else
8481           TEST_DrawLevelField(x, y);
8482
8483         return;
8484       }
8485     }
8486     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8487     {
8488       if (Store[x][y] != EL_EMPTY)
8489       {
8490         boolean can_clone = FALSE;
8491         int xx, yy;
8492
8493         /* check if element to clone is still there */
8494         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8495         {
8496           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8497           {
8498             can_clone = TRUE;
8499
8500             break;
8501           }
8502         }
8503
8504         /* cannot clone or target field not free anymore -- do not clone */
8505         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8506           Store[x][y] = EL_EMPTY;
8507       }
8508
8509       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8510       {
8511         if (IS_MV_DIAGONAL(MovDir[x][y]))
8512         {
8513           int diagonal_move_dir = MovDir[x][y];
8514           int stored = Store[x][y];
8515           int change_delay = 8;
8516           int graphic;
8517
8518           /* android is moving diagonally */
8519
8520           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8521
8522           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8523           GfxElement[x][y] = EL_EMC_ANDROID;
8524           GfxAction[x][y] = ACTION_SHRINKING;
8525           GfxDir[x][y] = diagonal_move_dir;
8526           ChangeDelay[x][y] = change_delay;
8527
8528           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8529                                    GfxDir[x][y]);
8530
8531           DrawLevelGraphicAnimation(x, y, graphic);
8532           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8533
8534           if (Feld[newx][newy] == EL_ACID)
8535           {
8536             SplashAcid(newx, newy);
8537
8538             return;
8539           }
8540
8541           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8542
8543           Store[newx][newy] = EL_EMC_ANDROID;
8544           GfxElement[newx][newy] = EL_EMC_ANDROID;
8545           GfxAction[newx][newy] = ACTION_GROWING;
8546           GfxDir[newx][newy] = diagonal_move_dir;
8547           ChangeDelay[newx][newy] = change_delay;
8548
8549           graphic = el_act_dir2img(GfxElement[newx][newy],
8550                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8551
8552           DrawLevelGraphicAnimation(newx, newy, graphic);
8553           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8554
8555           return;
8556         }
8557         else
8558         {
8559           Feld[newx][newy] = EL_EMPTY;
8560           TEST_DrawLevelField(newx, newy);
8561
8562           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8563         }
8564       }
8565       else if (!IS_FREE(newx, newy))
8566       {
8567 #if 0
8568         if (IS_PLAYER(x, y))
8569           DrawPlayerField(x, y);
8570         else
8571           TEST_DrawLevelField(x, y);
8572 #endif
8573
8574         return;
8575       }
8576     }
8577     else if (IS_CUSTOM_ELEMENT(element) &&
8578              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8579     {
8580 #if 1
8581       if (!DigFieldByCE(newx, newy, element))
8582         return;
8583 #else
8584       int new_element = Feld[newx][newy];
8585
8586       if (!IS_FREE(newx, newy))
8587       {
8588         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8589                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8590                       ACTION_BREAKING);
8591
8592         /* no element can dig solid indestructible elements */
8593         if (IS_INDESTRUCTIBLE(new_element) &&
8594             !IS_DIGGABLE(new_element) &&
8595             !IS_COLLECTIBLE(new_element))
8596           return;
8597
8598         if (AmoebaNr[newx][newy] &&
8599             (new_element == EL_AMOEBA_FULL ||
8600              new_element == EL_BD_AMOEBA ||
8601              new_element == EL_AMOEBA_GROWING))
8602         {
8603           AmoebaCnt[AmoebaNr[newx][newy]]--;
8604           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8605         }
8606
8607         if (IS_MOVING(newx, newy))
8608           RemoveMovingField(newx, newy);
8609         else
8610         {
8611           RemoveField(newx, newy);
8612           TEST_DrawLevelField(newx, newy);
8613         }
8614
8615         /* if digged element was about to explode, prevent the explosion */
8616         ExplodeField[newx][newy] = EX_TYPE_NONE;
8617
8618         PlayLevelSoundAction(x, y, action);
8619       }
8620
8621       Store[newx][newy] = EL_EMPTY;
8622
8623 #if 1
8624       /* this makes it possible to leave the removed element again */
8625       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8626         Store[newx][newy] = new_element;
8627 #else
8628       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8629       {
8630         int move_leave_element = element_info[element].move_leave_element;
8631
8632         /* this makes it possible to leave the removed element again */
8633         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8634                              new_element : move_leave_element);
8635       }
8636 #endif
8637
8638 #endif
8639
8640       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8641       {
8642         RunnerVisit[x][y] = FrameCounter;
8643         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8644       }
8645     }
8646     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8647     {
8648       if (!IS_FREE(newx, newy))
8649       {
8650         if (IS_PLAYER(x, y))
8651           DrawPlayerField(x, y);
8652         else
8653           TEST_DrawLevelField(x, y);
8654
8655         return;
8656       }
8657       else
8658       {
8659         boolean wanna_flame = !RND(10);
8660         int dx = newx - x, dy = newy - y;
8661         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8662         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8663         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8664                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8665         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8666                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8667
8668         if ((wanna_flame ||
8669              IS_CLASSIC_ENEMY(element1) ||
8670              IS_CLASSIC_ENEMY(element2)) &&
8671             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8672             element1 != EL_FLAMES && element2 != EL_FLAMES)
8673         {
8674           ResetGfxAnimation(x, y);
8675           GfxAction[x][y] = ACTION_ATTACKING;
8676
8677           if (IS_PLAYER(x, y))
8678             DrawPlayerField(x, y);
8679           else
8680             TEST_DrawLevelField(x, y);
8681
8682           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8683
8684           MovDelay[x][y] = 50;
8685
8686           /* !!! */
8687 #if 0
8688           RemoveField(newx, newy);
8689 #endif
8690           Feld[newx][newy] = EL_FLAMES;
8691           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8692           {
8693 #if 0
8694             RemoveField(newx1, newy1);
8695 #endif
8696             Feld[newx1][newy1] = EL_FLAMES;
8697           }
8698           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8699           {
8700 #if 0
8701             RemoveField(newx2, newy2);
8702 #endif
8703             Feld[newx2][newy2] = EL_FLAMES;
8704           }
8705
8706           return;
8707         }
8708       }
8709     }
8710     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8711              Feld[newx][newy] == EL_DIAMOND)
8712     {
8713       if (IS_MOVING(newx, newy))
8714         RemoveMovingField(newx, newy);
8715       else
8716       {
8717         Feld[newx][newy] = EL_EMPTY;
8718         TEST_DrawLevelField(newx, newy);
8719       }
8720
8721       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8722     }
8723     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8724              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8725     {
8726       if (AmoebaNr[newx][newy])
8727       {
8728         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8729         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8730             Feld[newx][newy] == EL_BD_AMOEBA)
8731           AmoebaCnt[AmoebaNr[newx][newy]]--;
8732       }
8733
8734 #if 0
8735       /* !!! test !!! */
8736       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8737       {
8738         RemoveMovingField(newx, newy);
8739       }
8740 #else
8741       if (IS_MOVING(newx, newy))
8742       {
8743         RemoveMovingField(newx, newy);
8744       }
8745 #endif
8746       else
8747       {
8748         Feld[newx][newy] = EL_EMPTY;
8749         TEST_DrawLevelField(newx, newy);
8750       }
8751
8752       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8753     }
8754     else if ((element == EL_PACMAN || element == EL_MOLE)
8755              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8756     {
8757       if (AmoebaNr[newx][newy])
8758       {
8759         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8760         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8761             Feld[newx][newy] == EL_BD_AMOEBA)
8762           AmoebaCnt[AmoebaNr[newx][newy]]--;
8763       }
8764
8765       if (element == EL_MOLE)
8766       {
8767         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8768         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8769
8770         ResetGfxAnimation(x, y);
8771         GfxAction[x][y] = ACTION_DIGGING;
8772         TEST_DrawLevelField(x, y);
8773
8774         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8775
8776         return;                         /* wait for shrinking amoeba */
8777       }
8778       else      /* element == EL_PACMAN */
8779       {
8780         Feld[newx][newy] = EL_EMPTY;
8781         TEST_DrawLevelField(newx, newy);
8782         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8783       }
8784     }
8785     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8786              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8787               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8788     {
8789       /* wait for shrinking amoeba to completely disappear */
8790       return;
8791     }
8792     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8793     {
8794       /* object was running against a wall */
8795
8796       TurnRound(x, y);
8797
8798 #if 0
8799       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8800       if (move_pattern & MV_ANY_DIRECTION &&
8801           move_pattern == MovDir[x][y])
8802       {
8803         int blocking_element =
8804           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8805
8806         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8807                                  MovDir[x][y]);
8808
8809         element = Feld[x][y];   /* element might have changed */
8810       }
8811 #endif
8812
8813       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8814         DrawLevelElementAnimation(x, y, element);
8815
8816       if (DONT_TOUCH(element))
8817         TestIfBadThingTouchesPlayer(x, y);
8818
8819       return;
8820     }
8821
8822     InitMovingField(x, y, MovDir[x][y]);
8823
8824     PlayLevelSoundAction(x, y, ACTION_MOVING);
8825   }
8826
8827   if (MovDir[x][y])
8828     ContinueMoving(x, y);
8829 }
8830
8831 void ContinueMoving(int x, int y)
8832 {
8833   int element = Feld[x][y];
8834   struct ElementInfo *ei = &element_info[element];
8835   int direction = MovDir[x][y];
8836   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8837   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8838   int newx = x + dx, newy = y + dy;
8839   int stored = Store[x][y];
8840   int stored_new = Store[newx][newy];
8841   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8842   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8843   boolean last_line = (newy == lev_fieldy - 1);
8844
8845   MovPos[x][y] += getElementMoveStepsize(x, y);
8846
8847   if (pushed_by_player) /* special case: moving object pushed by player */
8848     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8849
8850   if (ABS(MovPos[x][y]) < TILEX)
8851   {
8852 #if 0
8853     int ee = Feld[x][y];
8854     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8855     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8856
8857     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8858            x, y, ABS(MovPos[x][y]),
8859            ee, gg, ff,
8860            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8861 #endif
8862
8863     TEST_DrawLevelField(x, y);
8864
8865     return;     /* element is still moving */
8866   }
8867
8868   /* element reached destination field */
8869
8870   Feld[x][y] = EL_EMPTY;
8871   Feld[newx][newy] = element;
8872   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8873
8874   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8875   {
8876     element = Feld[newx][newy] = EL_ACID;
8877   }
8878   else if (element == EL_MOLE)
8879   {
8880     Feld[x][y] = EL_SAND;
8881
8882     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8883   }
8884   else if (element == EL_QUICKSAND_FILLING)
8885   {
8886     element = Feld[newx][newy] = get_next_element(element);
8887     Store[newx][newy] = Store[x][y];
8888   }
8889   else if (element == EL_QUICKSAND_EMPTYING)
8890   {
8891     Feld[x][y] = get_next_element(element);
8892     element = Feld[newx][newy] = Store[x][y];
8893   }
8894   else if (element == EL_QUICKSAND_FAST_FILLING)
8895   {
8896     element = Feld[newx][newy] = get_next_element(element);
8897     Store[newx][newy] = Store[x][y];
8898   }
8899   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8900   {
8901     Feld[x][y] = get_next_element(element);
8902     element = Feld[newx][newy] = Store[x][y];
8903   }
8904   else if (element == EL_MAGIC_WALL_FILLING)
8905   {
8906     element = Feld[newx][newy] = get_next_element(element);
8907     if (!game.magic_wall_active)
8908       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8909     Store[newx][newy] = Store[x][y];
8910   }
8911   else if (element == EL_MAGIC_WALL_EMPTYING)
8912   {
8913     Feld[x][y] = get_next_element(element);
8914     if (!game.magic_wall_active)
8915       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8916     element = Feld[newx][newy] = Store[x][y];
8917
8918 #if USE_NEW_CUSTOM_VALUE
8919     InitField(newx, newy, FALSE);
8920 #endif
8921   }
8922   else if (element == EL_BD_MAGIC_WALL_FILLING)
8923   {
8924     element = Feld[newx][newy] = get_next_element(element);
8925     if (!game.magic_wall_active)
8926       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8927     Store[newx][newy] = Store[x][y];
8928   }
8929   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8930   {
8931     Feld[x][y] = get_next_element(element);
8932     if (!game.magic_wall_active)
8933       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8934     element = Feld[newx][newy] = Store[x][y];
8935
8936 #if USE_NEW_CUSTOM_VALUE
8937     InitField(newx, newy, FALSE);
8938 #endif
8939   }
8940   else if (element == EL_DC_MAGIC_WALL_FILLING)
8941   {
8942     element = Feld[newx][newy] = get_next_element(element);
8943     if (!game.magic_wall_active)
8944       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8945     Store[newx][newy] = Store[x][y];
8946   }
8947   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8948   {
8949     Feld[x][y] = get_next_element(element);
8950     if (!game.magic_wall_active)
8951       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8952     element = Feld[newx][newy] = Store[x][y];
8953
8954 #if USE_NEW_CUSTOM_VALUE
8955     InitField(newx, newy, FALSE);
8956 #endif
8957   }
8958   else if (element == EL_AMOEBA_DROPPING)
8959   {
8960     Feld[x][y] = get_next_element(element);
8961     element = Feld[newx][newy] = Store[x][y];
8962   }
8963   else if (element == EL_SOKOBAN_OBJECT)
8964   {
8965     if (Back[x][y])
8966       Feld[x][y] = Back[x][y];
8967
8968     if (Back[newx][newy])
8969       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8970
8971     Back[x][y] = Back[newx][newy] = 0;
8972   }
8973
8974   Store[x][y] = EL_EMPTY;
8975   MovPos[x][y] = 0;
8976   MovDir[x][y] = 0;
8977   MovDelay[x][y] = 0;
8978
8979   MovDelay[newx][newy] = 0;
8980
8981   if (CAN_CHANGE_OR_HAS_ACTION(element))
8982   {
8983     /* copy element change control values to new field */
8984     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8985     ChangePage[newx][newy]  = ChangePage[x][y];
8986     ChangeCount[newx][newy] = ChangeCount[x][y];
8987     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8988   }
8989
8990 #if USE_NEW_CUSTOM_VALUE
8991   CustomValue[newx][newy] = CustomValue[x][y];
8992 #endif
8993
8994   ChangeDelay[x][y] = 0;
8995   ChangePage[x][y] = -1;
8996   ChangeCount[x][y] = 0;
8997   ChangeEvent[x][y] = -1;
8998
8999 #if USE_NEW_CUSTOM_VALUE
9000   CustomValue[x][y] = 0;
9001 #endif
9002
9003   /* copy animation control values to new field */
9004   GfxFrame[newx][newy]  = GfxFrame[x][y];
9005   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9006   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9007   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9008
9009   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9010
9011   /* some elements can leave other elements behind after moving */
9012 #if 1
9013   if (ei->move_leave_element != EL_EMPTY &&
9014       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9015       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9016 #else
9017   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9018       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9019       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9020 #endif
9021   {
9022     int move_leave_element = ei->move_leave_element;
9023
9024 #if 1
9025 #if 1
9026     /* this makes it possible to leave the removed element again */
9027     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9028       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9029 #else
9030     /* this makes it possible to leave the removed element again */
9031     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9032       move_leave_element = stored;
9033 #endif
9034 #else
9035     /* this makes it possible to leave the removed element again */
9036     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9037         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9038       move_leave_element = stored;
9039 #endif
9040
9041     Feld[x][y] = move_leave_element;
9042
9043     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9044       MovDir[x][y] = direction;
9045
9046     InitField(x, y, FALSE);
9047
9048     if (GFX_CRUMBLED(Feld[x][y]))
9049       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9050
9051     if (ELEM_IS_PLAYER(move_leave_element))
9052       RelocatePlayer(x, y, move_leave_element);
9053   }
9054
9055   /* do this after checking for left-behind element */
9056   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9057
9058   if (!CAN_MOVE(element) ||
9059       (CAN_FALL(element) && direction == MV_DOWN &&
9060        (element == EL_SPRING ||
9061         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9062         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9063     GfxDir[x][y] = MovDir[newx][newy] = 0;
9064
9065   TEST_DrawLevelField(x, y);
9066   TEST_DrawLevelField(newx, newy);
9067
9068   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9069
9070   /* prevent pushed element from moving on in pushed direction */
9071   if (pushed_by_player && CAN_MOVE(element) &&
9072       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9073       !(element_info[element].move_pattern & direction))
9074     TurnRound(newx, newy);
9075
9076   /* prevent elements on conveyor belt from moving on in last direction */
9077   if (pushed_by_conveyor && CAN_FALL(element) &&
9078       direction & MV_HORIZONTAL)
9079     MovDir[newx][newy] = 0;
9080
9081   if (!pushed_by_player)
9082   {
9083     int nextx = newx + dx, nexty = newy + dy;
9084     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9085
9086     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9087
9088     if (CAN_FALL(element) && direction == MV_DOWN)
9089       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9090
9091     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9092       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9093
9094 #if USE_FIX_IMPACT_COLLISION
9095     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9096       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9097 #endif
9098   }
9099
9100   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9101   {
9102     TestIfBadThingTouchesPlayer(newx, newy);
9103     TestIfBadThingTouchesFriend(newx, newy);
9104
9105     if (!IS_CUSTOM_ELEMENT(element))
9106       TestIfBadThingTouchesOtherBadThing(newx, newy);
9107   }
9108   else if (element == EL_PENGUIN)
9109     TestIfFriendTouchesBadThing(newx, newy);
9110
9111   if (DONT_GET_HIT_BY(element))
9112   {
9113     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9114   }
9115
9116   /* give the player one last chance (one more frame) to move away */
9117   if (CAN_FALL(element) && direction == MV_DOWN &&
9118       (last_line || (!IS_FREE(x, newy + 1) &&
9119                      (!IS_PLAYER(x, newy + 1) ||
9120                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9121     Impact(x, newy);
9122
9123   if (pushed_by_player && !game.use_change_when_pushing_bug)
9124   {
9125     int push_side = MV_DIR_OPPOSITE(direction);
9126     struct PlayerInfo *player = PLAYERINFO(x, y);
9127
9128     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9129                                player->index_bit, push_side);
9130     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9131                                         player->index_bit, push_side);
9132   }
9133
9134   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9135     MovDelay[newx][newy] = 1;
9136
9137   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9138
9139   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9140
9141 #if 0
9142   if (ChangePage[newx][newy] != -1)             /* delayed change */
9143   {
9144     int page = ChangePage[newx][newy];
9145     struct ElementChangeInfo *change = &ei->change_page[page];
9146
9147     ChangePage[newx][newy] = -1;
9148
9149     if (change->can_change)
9150     {
9151       if (ChangeElement(newx, newy, element, page))
9152       {
9153         if (change->post_change_function)
9154           change->post_change_function(newx, newy);
9155       }
9156     }
9157
9158     if (change->has_action)
9159       ExecuteCustomElementAction(newx, newy, element, page);
9160   }
9161 #endif
9162
9163   TestIfElementHitsCustomElement(newx, newy, direction);
9164   TestIfPlayerTouchesCustomElement(newx, newy);
9165   TestIfElementTouchesCustomElement(newx, newy);
9166
9167   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9168       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9169     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9170                              MV_DIR_OPPOSITE(direction));
9171 }
9172
9173 int AmoebeNachbarNr(int ax, int ay)
9174 {
9175   int i;
9176   int element = Feld[ax][ay];
9177   int group_nr = 0;
9178   static int xy[4][2] =
9179   {
9180     { 0, -1 },
9181     { -1, 0 },
9182     { +1, 0 },
9183     { 0, +1 }
9184   };
9185
9186   for (i = 0; i < NUM_DIRECTIONS; i++)
9187   {
9188     int x = ax + xy[i][0];
9189     int y = ay + xy[i][1];
9190
9191     if (!IN_LEV_FIELD(x, y))
9192       continue;
9193
9194     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9195       group_nr = AmoebaNr[x][y];
9196   }
9197
9198   return group_nr;
9199 }
9200
9201 void AmoebenVereinigen(int ax, int ay)
9202 {
9203   int i, x, y, xx, yy;
9204   int new_group_nr = AmoebaNr[ax][ay];
9205   static int xy[4][2] =
9206   {
9207     { 0, -1 },
9208     { -1, 0 },
9209     { +1, 0 },
9210     { 0, +1 }
9211   };
9212
9213   if (new_group_nr == 0)
9214     return;
9215
9216   for (i = 0; i < NUM_DIRECTIONS; i++)
9217   {
9218     x = ax + xy[i][0];
9219     y = ay + xy[i][1];
9220
9221     if (!IN_LEV_FIELD(x, y))
9222       continue;
9223
9224     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9225          Feld[x][y] == EL_BD_AMOEBA ||
9226          Feld[x][y] == EL_AMOEBA_DEAD) &&
9227         AmoebaNr[x][y] != new_group_nr)
9228     {
9229       int old_group_nr = AmoebaNr[x][y];
9230
9231       if (old_group_nr == 0)
9232         return;
9233
9234       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9235       AmoebaCnt[old_group_nr] = 0;
9236       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9237       AmoebaCnt2[old_group_nr] = 0;
9238
9239       SCAN_PLAYFIELD(xx, yy)
9240       {
9241         if (AmoebaNr[xx][yy] == old_group_nr)
9242           AmoebaNr[xx][yy] = new_group_nr;
9243       }
9244     }
9245   }
9246 }
9247
9248 void AmoebeUmwandeln(int ax, int ay)
9249 {
9250   int i, x, y;
9251
9252   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9253   {
9254     int group_nr = AmoebaNr[ax][ay];
9255
9256 #ifdef DEBUG
9257     if (group_nr == 0)
9258     {
9259       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9260       printf("AmoebeUmwandeln(): This should never happen!\n");
9261       return;
9262     }
9263 #endif
9264
9265     SCAN_PLAYFIELD(x, y)
9266     {
9267       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9268       {
9269         AmoebaNr[x][y] = 0;
9270         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9271       }
9272     }
9273
9274     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9275                             SND_AMOEBA_TURNING_TO_GEM :
9276                             SND_AMOEBA_TURNING_TO_ROCK));
9277     Bang(ax, ay);
9278   }
9279   else
9280   {
9281     static int xy[4][2] =
9282     {
9283       { 0, -1 },
9284       { -1, 0 },
9285       { +1, 0 },
9286       { 0, +1 }
9287     };
9288
9289     for (i = 0; i < NUM_DIRECTIONS; i++)
9290     {
9291       x = ax + xy[i][0];
9292       y = ay + xy[i][1];
9293
9294       if (!IN_LEV_FIELD(x, y))
9295         continue;
9296
9297       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9298       {
9299         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9300                               SND_AMOEBA_TURNING_TO_GEM :
9301                               SND_AMOEBA_TURNING_TO_ROCK));
9302         Bang(x, y);
9303       }
9304     }
9305   }
9306 }
9307
9308 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9309 {
9310   int x, y;
9311   int group_nr = AmoebaNr[ax][ay];
9312   boolean done = FALSE;
9313
9314 #ifdef DEBUG
9315   if (group_nr == 0)
9316   {
9317     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9318     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9319     return;
9320   }
9321 #endif
9322
9323   SCAN_PLAYFIELD(x, y)
9324   {
9325     if (AmoebaNr[x][y] == group_nr &&
9326         (Feld[x][y] == EL_AMOEBA_DEAD ||
9327          Feld[x][y] == EL_BD_AMOEBA ||
9328          Feld[x][y] == EL_AMOEBA_GROWING))
9329     {
9330       AmoebaNr[x][y] = 0;
9331       Feld[x][y] = new_element;
9332       InitField(x, y, FALSE);
9333       TEST_DrawLevelField(x, y);
9334       done = TRUE;
9335     }
9336   }
9337
9338   if (done)
9339     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9340                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9341                             SND_BD_AMOEBA_TURNING_TO_GEM));
9342 }
9343
9344 void AmoebeWaechst(int x, int y)
9345 {
9346   static unsigned int sound_delay = 0;
9347   static unsigned int sound_delay_value = 0;
9348
9349   if (!MovDelay[x][y])          /* start new growing cycle */
9350   {
9351     MovDelay[x][y] = 7;
9352
9353     if (DelayReached(&sound_delay, sound_delay_value))
9354     {
9355       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9356       sound_delay_value = 30;
9357     }
9358   }
9359
9360   if (MovDelay[x][y])           /* wait some time before growing bigger */
9361   {
9362     MovDelay[x][y]--;
9363     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9364     {
9365       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9366                                            6 - MovDelay[x][y]);
9367
9368       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9369     }
9370
9371     if (!MovDelay[x][y])
9372     {
9373       Feld[x][y] = Store[x][y];
9374       Store[x][y] = 0;
9375       TEST_DrawLevelField(x, y);
9376     }
9377   }
9378 }
9379
9380 void AmoebaDisappearing(int x, int y)
9381 {
9382   static unsigned int sound_delay = 0;
9383   static unsigned int sound_delay_value = 0;
9384
9385   if (!MovDelay[x][y])          /* start new shrinking cycle */
9386   {
9387     MovDelay[x][y] = 7;
9388
9389     if (DelayReached(&sound_delay, sound_delay_value))
9390       sound_delay_value = 30;
9391   }
9392
9393   if (MovDelay[x][y])           /* wait some time before shrinking */
9394   {
9395     MovDelay[x][y]--;
9396     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9397     {
9398       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9399                                            6 - MovDelay[x][y]);
9400
9401       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9402     }
9403
9404     if (!MovDelay[x][y])
9405     {
9406       Feld[x][y] = EL_EMPTY;
9407       TEST_DrawLevelField(x, y);
9408
9409       /* don't let mole enter this field in this cycle;
9410          (give priority to objects falling to this field from above) */
9411       Stop[x][y] = TRUE;
9412     }
9413   }
9414 }
9415
9416 void AmoebeAbleger(int ax, int ay)
9417 {
9418   int i;
9419   int element = Feld[ax][ay];
9420   int graphic = el2img(element);
9421   int newax = ax, neway = ay;
9422   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9423   static int xy[4][2] =
9424   {
9425     { 0, -1 },
9426     { -1, 0 },
9427     { +1, 0 },
9428     { 0, +1 }
9429   };
9430
9431   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9432   {
9433     Feld[ax][ay] = EL_AMOEBA_DEAD;
9434     TEST_DrawLevelField(ax, ay);
9435     return;
9436   }
9437
9438   if (IS_ANIMATED(graphic))
9439     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9440
9441   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9442     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9443
9444   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9445   {
9446     MovDelay[ax][ay]--;
9447     if (MovDelay[ax][ay])
9448       return;
9449   }
9450
9451   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9452   {
9453     int start = RND(4);
9454     int x = ax + xy[start][0];
9455     int y = ay + xy[start][1];
9456
9457     if (!IN_LEV_FIELD(x, y))
9458       return;
9459
9460     if (IS_FREE(x, y) ||
9461         CAN_GROW_INTO(Feld[x][y]) ||
9462         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9463         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9464     {
9465       newax = x;
9466       neway = y;
9467     }
9468
9469     if (newax == ax && neway == ay)
9470       return;
9471   }
9472   else                          /* normal or "filled" (BD style) amoeba */
9473   {
9474     int start = RND(4);
9475     boolean waiting_for_player = FALSE;
9476
9477     for (i = 0; i < NUM_DIRECTIONS; i++)
9478     {
9479       int j = (start + i) % 4;
9480       int x = ax + xy[j][0];
9481       int y = ay + xy[j][1];
9482
9483       if (!IN_LEV_FIELD(x, y))
9484         continue;
9485
9486       if (IS_FREE(x, y) ||
9487           CAN_GROW_INTO(Feld[x][y]) ||
9488           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9489           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9490       {
9491         newax = x;
9492         neway = y;
9493         break;
9494       }
9495       else if (IS_PLAYER(x, y))
9496         waiting_for_player = TRUE;
9497     }
9498
9499     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9500     {
9501       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9502       {
9503         Feld[ax][ay] = EL_AMOEBA_DEAD;
9504         TEST_DrawLevelField(ax, ay);
9505         AmoebaCnt[AmoebaNr[ax][ay]]--;
9506
9507         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9508         {
9509           if (element == EL_AMOEBA_FULL)
9510             AmoebeUmwandeln(ax, ay);
9511           else if (element == EL_BD_AMOEBA)
9512             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9513         }
9514       }
9515       return;
9516     }
9517     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9518     {
9519       /* amoeba gets larger by growing in some direction */
9520
9521       int new_group_nr = AmoebaNr[ax][ay];
9522
9523 #ifdef DEBUG
9524   if (new_group_nr == 0)
9525   {
9526     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9527     printf("AmoebeAbleger(): This should never happen!\n");
9528     return;
9529   }
9530 #endif
9531
9532       AmoebaNr[newax][neway] = new_group_nr;
9533       AmoebaCnt[new_group_nr]++;
9534       AmoebaCnt2[new_group_nr]++;
9535
9536       /* if amoeba touches other amoeba(s) after growing, unify them */
9537       AmoebenVereinigen(newax, neway);
9538
9539       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9540       {
9541         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9542         return;
9543       }
9544     }
9545   }
9546
9547   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9548       (neway == lev_fieldy - 1 && newax != ax))
9549   {
9550     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9551     Store[newax][neway] = element;
9552   }
9553   else if (neway == ay || element == EL_EMC_DRIPPER)
9554   {
9555     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9556
9557     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9558   }
9559   else
9560   {
9561     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9562     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9563     Store[ax][ay] = EL_AMOEBA_DROP;
9564     ContinueMoving(ax, ay);
9565     return;
9566   }
9567
9568   TEST_DrawLevelField(newax, neway);
9569 }
9570
9571 void Life(int ax, int ay)
9572 {
9573   int x1, y1, x2, y2;
9574   int life_time = 40;
9575   int element = Feld[ax][ay];
9576   int graphic = el2img(element);
9577   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9578                          level.biomaze);
9579   boolean changed = FALSE;
9580
9581   if (IS_ANIMATED(graphic))
9582     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9583
9584   if (Stop[ax][ay])
9585     return;
9586
9587   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9588     MovDelay[ax][ay] = life_time;
9589
9590   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9591   {
9592     MovDelay[ax][ay]--;
9593     if (MovDelay[ax][ay])
9594       return;
9595   }
9596
9597   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9598   {
9599     int xx = ax+x1, yy = ay+y1;
9600     int nachbarn = 0;
9601
9602     if (!IN_LEV_FIELD(xx, yy))
9603       continue;
9604
9605     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9606     {
9607       int x = xx+x2, y = yy+y2;
9608
9609       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9610         continue;
9611
9612       if (((Feld[x][y] == element ||
9613             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9614            !Stop[x][y]) ||
9615           (IS_FREE(x, y) && Stop[x][y]))
9616         nachbarn++;
9617     }
9618
9619     if (xx == ax && yy == ay)           /* field in the middle */
9620     {
9621       if (nachbarn < life_parameter[0] ||
9622           nachbarn > life_parameter[1])
9623       {
9624         Feld[xx][yy] = EL_EMPTY;
9625         if (!Stop[xx][yy])
9626           TEST_DrawLevelField(xx, yy);
9627         Stop[xx][yy] = TRUE;
9628         changed = TRUE;
9629       }
9630     }
9631     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9632     {                                   /* free border field */
9633       if (nachbarn >= life_parameter[2] &&
9634           nachbarn <= life_parameter[3])
9635       {
9636         Feld[xx][yy] = element;
9637         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9638         if (!Stop[xx][yy])
9639           TEST_DrawLevelField(xx, yy);
9640         Stop[xx][yy] = TRUE;
9641         changed = TRUE;
9642       }
9643     }
9644   }
9645
9646   if (changed)
9647     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9648                    SND_GAME_OF_LIFE_GROWING);
9649 }
9650
9651 static void InitRobotWheel(int x, int y)
9652 {
9653   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9654 }
9655
9656 static void RunRobotWheel(int x, int y)
9657 {
9658   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9659 }
9660
9661 static void StopRobotWheel(int x, int y)
9662 {
9663   if (ZX == x && ZY == y)
9664   {
9665     ZX = ZY = -1;
9666
9667     game.robot_wheel_active = FALSE;
9668   }
9669 }
9670
9671 static void InitTimegateWheel(int x, int y)
9672 {
9673   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9674 }
9675
9676 static void RunTimegateWheel(int x, int y)
9677 {
9678   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9679 }
9680
9681 static void InitMagicBallDelay(int x, int y)
9682 {
9683 #if 1
9684   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9685 #else
9686   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9687 #endif
9688 }
9689
9690 static void ActivateMagicBall(int bx, int by)
9691 {
9692   int x, y;
9693
9694   if (level.ball_random)
9695   {
9696     int pos_border = RND(8);    /* select one of the eight border elements */
9697     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9698     int xx = pos_content % 3;
9699     int yy = pos_content / 3;
9700
9701     x = bx - 1 + xx;
9702     y = by - 1 + yy;
9703
9704     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9705       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9706   }
9707   else
9708   {
9709     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9710     {
9711       int xx = x - bx + 1;
9712       int yy = y - by + 1;
9713
9714       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9715         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9716     }
9717   }
9718
9719   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9720 }
9721
9722 void CheckExit(int x, int y)
9723 {
9724   if (local_player->gems_still_needed > 0 ||
9725       local_player->sokobanfields_still_needed > 0 ||
9726       local_player->lights_still_needed > 0)
9727   {
9728     int element = Feld[x][y];
9729     int graphic = el2img(element);
9730
9731     if (IS_ANIMATED(graphic))
9732       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9733
9734     return;
9735   }
9736
9737   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9738     return;
9739
9740   Feld[x][y] = EL_EXIT_OPENING;
9741
9742   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9743 }
9744
9745 void CheckExitEM(int x, int y)
9746 {
9747   if (local_player->gems_still_needed > 0 ||
9748       local_player->sokobanfields_still_needed > 0 ||
9749       local_player->lights_still_needed > 0)
9750   {
9751     int element = Feld[x][y];
9752     int graphic = el2img(element);
9753
9754     if (IS_ANIMATED(graphic))
9755       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9756
9757     return;
9758   }
9759
9760   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9761     return;
9762
9763   Feld[x][y] = EL_EM_EXIT_OPENING;
9764
9765   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9766 }
9767
9768 void CheckExitSteel(int x, int y)
9769 {
9770   if (local_player->gems_still_needed > 0 ||
9771       local_player->sokobanfields_still_needed > 0 ||
9772       local_player->lights_still_needed > 0)
9773   {
9774     int element = Feld[x][y];
9775     int graphic = el2img(element);
9776
9777     if (IS_ANIMATED(graphic))
9778       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9779
9780     return;
9781   }
9782
9783   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9784     return;
9785
9786   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9787
9788   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9789 }
9790
9791 void CheckExitSteelEM(int x, int y)
9792 {
9793   if (local_player->gems_still_needed > 0 ||
9794       local_player->sokobanfields_still_needed > 0 ||
9795       local_player->lights_still_needed > 0)
9796   {
9797     int element = Feld[x][y];
9798     int graphic = el2img(element);
9799
9800     if (IS_ANIMATED(graphic))
9801       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9802
9803     return;
9804   }
9805
9806   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9807     return;
9808
9809   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9810
9811   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9812 }
9813
9814 void CheckExitSP(int x, int y)
9815 {
9816   if (local_player->gems_still_needed > 0)
9817   {
9818     int element = Feld[x][y];
9819     int graphic = el2img(element);
9820
9821     if (IS_ANIMATED(graphic))
9822       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9823
9824     return;
9825   }
9826
9827   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9828     return;
9829
9830   Feld[x][y] = EL_SP_EXIT_OPENING;
9831
9832   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9833 }
9834
9835 static void CloseAllOpenTimegates()
9836 {
9837   int x, y;
9838
9839   SCAN_PLAYFIELD(x, y)
9840   {
9841     int element = Feld[x][y];
9842
9843     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9844     {
9845       Feld[x][y] = EL_TIMEGATE_CLOSING;
9846
9847       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9848     }
9849   }
9850 }
9851
9852 void DrawTwinkleOnField(int x, int y)
9853 {
9854   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9855     return;
9856
9857   if (Feld[x][y] == EL_BD_DIAMOND)
9858     return;
9859
9860   if (MovDelay[x][y] == 0)      /* next animation frame */
9861     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9862
9863   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9864   {
9865     MovDelay[x][y]--;
9866
9867     DrawLevelElementAnimation(x, y, Feld[x][y]);
9868
9869     if (MovDelay[x][y] != 0)
9870     {
9871       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9872                                            10 - MovDelay[x][y]);
9873
9874       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9875     }
9876   }
9877 }
9878
9879 void MauerWaechst(int x, int y)
9880 {
9881   int delay = 6;
9882
9883   if (!MovDelay[x][y])          /* next animation frame */
9884     MovDelay[x][y] = 3 * delay;
9885
9886   if (MovDelay[x][y])           /* wait some time before next frame */
9887   {
9888     MovDelay[x][y]--;
9889
9890     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9891     {
9892       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9893       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9894
9895       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9896     }
9897
9898     if (!MovDelay[x][y])
9899     {
9900       if (MovDir[x][y] == MV_LEFT)
9901       {
9902         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9903           TEST_DrawLevelField(x - 1, y);
9904       }
9905       else if (MovDir[x][y] == MV_RIGHT)
9906       {
9907         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9908           TEST_DrawLevelField(x + 1, y);
9909       }
9910       else if (MovDir[x][y] == MV_UP)
9911       {
9912         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9913           TEST_DrawLevelField(x, y - 1);
9914       }
9915       else
9916       {
9917         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9918           TEST_DrawLevelField(x, y + 1);
9919       }
9920
9921       Feld[x][y] = Store[x][y];
9922       Store[x][y] = 0;
9923       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9924       TEST_DrawLevelField(x, y);
9925     }
9926   }
9927 }
9928
9929 void MauerAbleger(int ax, int ay)
9930 {
9931   int element = Feld[ax][ay];
9932   int graphic = el2img(element);
9933   boolean oben_frei = FALSE, unten_frei = FALSE;
9934   boolean links_frei = FALSE, rechts_frei = FALSE;
9935   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9936   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9937   boolean new_wall = FALSE;
9938
9939   if (IS_ANIMATED(graphic))
9940     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9941
9942   if (!MovDelay[ax][ay])        /* start building new wall */
9943     MovDelay[ax][ay] = 6;
9944
9945   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9946   {
9947     MovDelay[ax][ay]--;
9948     if (MovDelay[ax][ay])
9949       return;
9950   }
9951
9952   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9953     oben_frei = TRUE;
9954   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9955     unten_frei = TRUE;
9956   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9957     links_frei = TRUE;
9958   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9959     rechts_frei = TRUE;
9960
9961   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9962       element == EL_EXPANDABLE_WALL_ANY)
9963   {
9964     if (oben_frei)
9965     {
9966       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9967       Store[ax][ay-1] = element;
9968       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9969       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9970         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9971                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9972       new_wall = TRUE;
9973     }
9974     if (unten_frei)
9975     {
9976       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9977       Store[ax][ay+1] = element;
9978       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9979       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9980         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9981                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9982       new_wall = TRUE;
9983     }
9984   }
9985
9986   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9987       element == EL_EXPANDABLE_WALL_ANY ||
9988       element == EL_EXPANDABLE_WALL ||
9989       element == EL_BD_EXPANDABLE_WALL)
9990   {
9991     if (links_frei)
9992     {
9993       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9994       Store[ax-1][ay] = element;
9995       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9996       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9997         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9998                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9999       new_wall = TRUE;
10000     }
10001
10002     if (rechts_frei)
10003     {
10004       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10005       Store[ax+1][ay] = element;
10006       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10007       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10008         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10009                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10010       new_wall = TRUE;
10011     }
10012   }
10013
10014   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10015     TEST_DrawLevelField(ax, ay);
10016
10017   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10018     oben_massiv = TRUE;
10019   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10020     unten_massiv = TRUE;
10021   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10022     links_massiv = TRUE;
10023   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10024     rechts_massiv = TRUE;
10025
10026   if (((oben_massiv && unten_massiv) ||
10027        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10028        element == EL_EXPANDABLE_WALL) &&
10029       ((links_massiv && rechts_massiv) ||
10030        element == EL_EXPANDABLE_WALL_VERTICAL))
10031     Feld[ax][ay] = EL_WALL;
10032
10033   if (new_wall)
10034     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10035 }
10036
10037 void MauerAblegerStahl(int ax, int ay)
10038 {
10039   int element = Feld[ax][ay];
10040   int graphic = el2img(element);
10041   boolean oben_frei = FALSE, unten_frei = FALSE;
10042   boolean links_frei = FALSE, rechts_frei = FALSE;
10043   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10044   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10045   boolean new_wall = FALSE;
10046
10047   if (IS_ANIMATED(graphic))
10048     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10049
10050   if (!MovDelay[ax][ay])        /* start building new wall */
10051     MovDelay[ax][ay] = 6;
10052
10053   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10054   {
10055     MovDelay[ax][ay]--;
10056     if (MovDelay[ax][ay])
10057       return;
10058   }
10059
10060   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10061     oben_frei = TRUE;
10062   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10063     unten_frei = TRUE;
10064   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10065     links_frei = TRUE;
10066   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10067     rechts_frei = TRUE;
10068
10069   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10070       element == EL_EXPANDABLE_STEELWALL_ANY)
10071   {
10072     if (oben_frei)
10073     {
10074       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10075       Store[ax][ay-1] = element;
10076       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10077       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10078         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10079                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10080       new_wall = TRUE;
10081     }
10082     if (unten_frei)
10083     {
10084       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10085       Store[ax][ay+1] = element;
10086       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10087       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10088         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10089                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10090       new_wall = TRUE;
10091     }
10092   }
10093
10094   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10095       element == EL_EXPANDABLE_STEELWALL_ANY)
10096   {
10097     if (links_frei)
10098     {
10099       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10100       Store[ax-1][ay] = element;
10101       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10102       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10103         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10104                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10105       new_wall = TRUE;
10106     }
10107
10108     if (rechts_frei)
10109     {
10110       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10111       Store[ax+1][ay] = element;
10112       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10113       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10114         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10115                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10116       new_wall = TRUE;
10117     }
10118   }
10119
10120   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10121     oben_massiv = TRUE;
10122   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10123     unten_massiv = TRUE;
10124   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10125     links_massiv = TRUE;
10126   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10127     rechts_massiv = TRUE;
10128
10129   if (((oben_massiv && unten_massiv) ||
10130        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10131       ((links_massiv && rechts_massiv) ||
10132        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10133     Feld[ax][ay] = EL_STEELWALL;
10134
10135   if (new_wall)
10136     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10137 }
10138
10139 void CheckForDragon(int x, int y)
10140 {
10141   int i, j;
10142   boolean dragon_found = FALSE;
10143   static int xy[4][2] =
10144   {
10145     { 0, -1 },
10146     { -1, 0 },
10147     { +1, 0 },
10148     { 0, +1 }
10149   };
10150
10151   for (i = 0; i < NUM_DIRECTIONS; i++)
10152   {
10153     for (j = 0; j < 4; j++)
10154     {
10155       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10156
10157       if (IN_LEV_FIELD(xx, yy) &&
10158           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10159       {
10160         if (Feld[xx][yy] == EL_DRAGON)
10161           dragon_found = TRUE;
10162       }
10163       else
10164         break;
10165     }
10166   }
10167
10168   if (!dragon_found)
10169   {
10170     for (i = 0; i < NUM_DIRECTIONS; i++)
10171     {
10172       for (j = 0; j < 3; j++)
10173       {
10174         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10175   
10176         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10177         {
10178           Feld[xx][yy] = EL_EMPTY;
10179           TEST_DrawLevelField(xx, yy);
10180         }
10181         else
10182           break;
10183       }
10184     }
10185   }
10186 }
10187
10188 static void InitBuggyBase(int x, int y)
10189 {
10190   int element = Feld[x][y];
10191   int activating_delay = FRAMES_PER_SECOND / 4;
10192
10193   ChangeDelay[x][y] =
10194     (element == EL_SP_BUGGY_BASE ?
10195      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10196      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10197      activating_delay :
10198      element == EL_SP_BUGGY_BASE_ACTIVE ?
10199      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10200 }
10201
10202 static void WarnBuggyBase(int x, int y)
10203 {
10204   int i;
10205   static int xy[4][2] =
10206   {
10207     { 0, -1 },
10208     { -1, 0 },
10209     { +1, 0 },
10210     { 0, +1 }
10211   };
10212
10213   for (i = 0; i < NUM_DIRECTIONS; i++)
10214   {
10215     int xx = x + xy[i][0];
10216     int yy = y + xy[i][1];
10217
10218     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10219     {
10220       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10221
10222       break;
10223     }
10224   }
10225 }
10226
10227 static void InitTrap(int x, int y)
10228 {
10229   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10230 }
10231
10232 static void ActivateTrap(int x, int y)
10233 {
10234   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10235 }
10236
10237 static void ChangeActiveTrap(int x, int y)
10238 {
10239   int graphic = IMG_TRAP_ACTIVE;
10240
10241   /* if new animation frame was drawn, correct crumbled sand border */
10242   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10243     TEST_DrawLevelFieldCrumbled(x, y);
10244 }
10245
10246 static int getSpecialActionElement(int element, int number, int base_element)
10247 {
10248   return (element != EL_EMPTY ? element :
10249           number != -1 ? base_element + number - 1 :
10250           EL_EMPTY);
10251 }
10252
10253 static int getModifiedActionNumber(int value_old, int operator, int operand,
10254                                    int value_min, int value_max)
10255 {
10256   int value_new = (operator == CA_MODE_SET      ? operand :
10257                    operator == CA_MODE_ADD      ? value_old + operand :
10258                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10259                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10260                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10261                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10262                    value_old);
10263
10264   return (value_new < value_min ? value_min :
10265           value_new > value_max ? value_max :
10266           value_new);
10267 }
10268
10269 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10270 {
10271   struct ElementInfo *ei = &element_info[element];
10272   struct ElementChangeInfo *change = &ei->change_page[page];
10273   int target_element = change->target_element;
10274   int action_type = change->action_type;
10275   int action_mode = change->action_mode;
10276   int action_arg = change->action_arg;
10277   int action_element = change->action_element;
10278   int i;
10279
10280   if (!change->has_action)
10281     return;
10282
10283   /* ---------- determine action paramater values -------------------------- */
10284
10285   int level_time_value =
10286     (level.time > 0 ? TimeLeft :
10287      TimePlayed);
10288
10289   int action_arg_element_raw =
10290     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10291      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10292      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10293      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10294      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10295      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10296      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10297      EL_EMPTY);
10298   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10299
10300 #if 0
10301   if (action_arg_element_raw == EL_GROUP_START)
10302     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10303 #endif
10304
10305   int action_arg_direction =
10306     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10307      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10308      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10309      change->actual_trigger_side :
10310      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10311      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10312      MV_NONE);
10313
10314   int action_arg_number_min =
10315     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10316      CA_ARG_MIN);
10317
10318   int action_arg_number_max =
10319     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10320      action_type == CA_SET_LEVEL_GEMS ? 999 :
10321      action_type == CA_SET_LEVEL_TIME ? 9999 :
10322      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10323      action_type == CA_SET_CE_VALUE ? 9999 :
10324      action_type == CA_SET_CE_SCORE ? 9999 :
10325      CA_ARG_MAX);
10326
10327   int action_arg_number_reset =
10328     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10329      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10330      action_type == CA_SET_LEVEL_TIME ? level.time :
10331      action_type == CA_SET_LEVEL_SCORE ? 0 :
10332 #if USE_NEW_CUSTOM_VALUE
10333      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10334 #else
10335      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10336 #endif
10337      action_type == CA_SET_CE_SCORE ? 0 :
10338      0);
10339
10340   int action_arg_number =
10341     (action_arg <= CA_ARG_MAX ? action_arg :
10342      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10343      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10344      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10345      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10346      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10347      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10348 #if USE_NEW_CUSTOM_VALUE
10349      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10350 #else
10351      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10352 #endif
10353      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10354      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10355      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10356      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10357      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10358      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10359      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10360      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10361      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10362      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10363      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10364      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10365      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10366      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10367      -1);
10368
10369   int action_arg_number_old =
10370     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10371      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10372      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10373      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10374      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10375      0);
10376
10377   int action_arg_number_new =
10378     getModifiedActionNumber(action_arg_number_old,
10379                             action_mode, action_arg_number,
10380                             action_arg_number_min, action_arg_number_max);
10381
10382 #if 1
10383   int trigger_player_bits =
10384     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10385      change->actual_trigger_player_bits : change->trigger_player);
10386 #else
10387   int trigger_player_bits =
10388     (change->actual_trigger_player >= EL_PLAYER_1 &&
10389      change->actual_trigger_player <= EL_PLAYER_4 ?
10390      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10391      PLAYER_BITS_ANY);
10392 #endif
10393
10394   int action_arg_player_bits =
10395     (action_arg >= CA_ARG_PLAYER_1 &&
10396      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10397      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10398      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10399      PLAYER_BITS_ANY);
10400
10401   /* ---------- execute action  -------------------------------------------- */
10402
10403   switch (action_type)
10404   {
10405     case CA_NO_ACTION:
10406     {
10407       return;
10408     }
10409
10410     /* ---------- level actions  ------------------------------------------- */
10411
10412     case CA_RESTART_LEVEL:
10413     {
10414       game.restart_level = TRUE;
10415
10416       break;
10417     }
10418
10419     case CA_SHOW_ENVELOPE:
10420     {
10421       int element = getSpecialActionElement(action_arg_element,
10422                                             action_arg_number, EL_ENVELOPE_1);
10423
10424       if (IS_ENVELOPE(element))
10425         local_player->show_envelope = element;
10426
10427       break;
10428     }
10429
10430     case CA_SET_LEVEL_TIME:
10431     {
10432       if (level.time > 0)       /* only modify limited time value */
10433       {
10434         TimeLeft = action_arg_number_new;
10435
10436 #if 1
10437         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10438
10439         DisplayGameControlValues();
10440 #else
10441         DrawGameValue_Time(TimeLeft);
10442 #endif
10443
10444         if (!TimeLeft && setup.time_limit)
10445           for (i = 0; i < MAX_PLAYERS; i++)
10446             KillPlayer(&stored_player[i]);
10447       }
10448
10449       break;
10450     }
10451
10452     case CA_SET_LEVEL_SCORE:
10453     {
10454       local_player->score = action_arg_number_new;
10455
10456 #if 1
10457       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10458
10459       DisplayGameControlValues();
10460 #else
10461       DrawGameValue_Score(local_player->score);
10462 #endif
10463
10464       break;
10465     }
10466
10467     case CA_SET_LEVEL_GEMS:
10468     {
10469       local_player->gems_still_needed = action_arg_number_new;
10470
10471 #if 1
10472       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10473
10474       DisplayGameControlValues();
10475 #else
10476       DrawGameValue_Emeralds(local_player->gems_still_needed);
10477 #endif
10478
10479       break;
10480     }
10481
10482 #if !USE_PLAYER_GRAVITY
10483     case CA_SET_LEVEL_GRAVITY:
10484     {
10485       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10486                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10487                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10488                       game.gravity);
10489       break;
10490     }
10491 #endif
10492
10493     case CA_SET_LEVEL_WIND:
10494     {
10495       game.wind_direction = action_arg_direction;
10496
10497       break;
10498     }
10499
10500     case CA_SET_LEVEL_RANDOM_SEED:
10501     {
10502 #if 1
10503       /* ensure that setting a new random seed while playing is predictable */
10504       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10505 #else
10506       InitRND(action_arg_number_new);
10507 #endif
10508
10509 #if 0
10510       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10511 #endif
10512
10513 #if 0
10514       {
10515         int i;
10516
10517         printf("::: ");
10518         for (i = 0; i < 9; i++)
10519           printf("%d, ", RND(2));
10520         printf("\n");
10521       }
10522 #endif
10523
10524       break;
10525     }
10526
10527     /* ---------- player actions  ------------------------------------------ */
10528
10529     case CA_MOVE_PLAYER:
10530     {
10531       /* automatically move to the next field in specified direction */
10532       for (i = 0; i < MAX_PLAYERS; i++)
10533         if (trigger_player_bits & (1 << i))
10534           stored_player[i].programmed_action = action_arg_direction;
10535
10536       break;
10537     }
10538
10539     case CA_EXIT_PLAYER:
10540     {
10541       for (i = 0; i < MAX_PLAYERS; i++)
10542         if (action_arg_player_bits & (1 << i))
10543           PlayerWins(&stored_player[i]);
10544
10545       break;
10546     }
10547
10548     case CA_KILL_PLAYER:
10549     {
10550       for (i = 0; i < MAX_PLAYERS; i++)
10551         if (action_arg_player_bits & (1 << i))
10552           KillPlayer(&stored_player[i]);
10553
10554       break;
10555     }
10556
10557     case CA_SET_PLAYER_KEYS:
10558     {
10559       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10560       int element = getSpecialActionElement(action_arg_element,
10561                                             action_arg_number, EL_KEY_1);
10562
10563       if (IS_KEY(element))
10564       {
10565         for (i = 0; i < MAX_PLAYERS; i++)
10566         {
10567           if (trigger_player_bits & (1 << i))
10568           {
10569             stored_player[i].key[KEY_NR(element)] = key_state;
10570
10571             DrawGameDoorValues();
10572           }
10573         }
10574       }
10575
10576       break;
10577     }
10578
10579     case CA_SET_PLAYER_SPEED:
10580     {
10581 #if 0
10582       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10583 #endif
10584
10585       for (i = 0; i < MAX_PLAYERS; i++)
10586       {
10587         if (trigger_player_bits & (1 << i))
10588         {
10589           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10590
10591           if (action_arg == CA_ARG_SPEED_FASTER &&
10592               stored_player[i].cannot_move)
10593           {
10594             action_arg_number = STEPSIZE_VERY_SLOW;
10595           }
10596           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10597                    action_arg == CA_ARG_SPEED_FASTER)
10598           {
10599             action_arg_number = 2;
10600             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10601                            CA_MODE_MULTIPLY);
10602           }
10603           else if (action_arg == CA_ARG_NUMBER_RESET)
10604           {
10605             action_arg_number = level.initial_player_stepsize[i];
10606           }
10607
10608           move_stepsize =
10609             getModifiedActionNumber(move_stepsize,
10610                                     action_mode,
10611                                     action_arg_number,
10612                                     action_arg_number_min,
10613                                     action_arg_number_max);
10614
10615           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10616         }
10617       }
10618
10619       break;
10620     }
10621
10622     case CA_SET_PLAYER_SHIELD:
10623     {
10624       for (i = 0; i < MAX_PLAYERS; i++)
10625       {
10626         if (trigger_player_bits & (1 << i))
10627         {
10628           if (action_arg == CA_ARG_SHIELD_OFF)
10629           {
10630             stored_player[i].shield_normal_time_left = 0;
10631             stored_player[i].shield_deadly_time_left = 0;
10632           }
10633           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10634           {
10635             stored_player[i].shield_normal_time_left = 999999;
10636           }
10637           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10638           {
10639             stored_player[i].shield_normal_time_left = 999999;
10640             stored_player[i].shield_deadly_time_left = 999999;
10641           }
10642         }
10643       }
10644
10645       break;
10646     }
10647
10648 #if USE_PLAYER_GRAVITY
10649     case CA_SET_PLAYER_GRAVITY:
10650     {
10651       for (i = 0; i < MAX_PLAYERS; i++)
10652       {
10653         if (trigger_player_bits & (1 << i))
10654         {
10655           stored_player[i].gravity =
10656             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10657              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10658              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10659              stored_player[i].gravity);
10660         }
10661       }
10662
10663       break;
10664     }
10665 #endif
10666
10667     case CA_SET_PLAYER_ARTWORK:
10668     {
10669       for (i = 0; i < MAX_PLAYERS; i++)
10670       {
10671         if (trigger_player_bits & (1 << i))
10672         {
10673           int artwork_element = action_arg_element;
10674
10675           if (action_arg == CA_ARG_ELEMENT_RESET)
10676             artwork_element =
10677               (level.use_artwork_element[i] ? level.artwork_element[i] :
10678                stored_player[i].element_nr);
10679
10680 #if USE_GFX_RESET_PLAYER_ARTWORK
10681           if (stored_player[i].artwork_element != artwork_element)
10682             stored_player[i].Frame = 0;
10683 #endif
10684
10685           stored_player[i].artwork_element = artwork_element;
10686
10687           SetPlayerWaiting(&stored_player[i], FALSE);
10688
10689           /* set number of special actions for bored and sleeping animation */
10690           stored_player[i].num_special_action_bored =
10691             get_num_special_action(artwork_element,
10692                                    ACTION_BORING_1, ACTION_BORING_LAST);
10693           stored_player[i].num_special_action_sleeping =
10694             get_num_special_action(artwork_element,
10695                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10696         }
10697       }
10698
10699       break;
10700     }
10701
10702     case CA_SET_PLAYER_INVENTORY:
10703     {
10704       for (i = 0; i < MAX_PLAYERS; i++)
10705       {
10706         struct PlayerInfo *player = &stored_player[i];
10707         int j, k;
10708
10709         if (trigger_player_bits & (1 << i))
10710         {
10711           int inventory_element = action_arg_element;
10712
10713           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10714               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10715               action_arg == CA_ARG_ELEMENT_ACTION)
10716           {
10717             int element = inventory_element;
10718             int collect_count = element_info[element].collect_count_initial;
10719
10720             if (!IS_CUSTOM_ELEMENT(element))
10721               collect_count = 1;
10722
10723             if (collect_count == 0)
10724               player->inventory_infinite_element = element;
10725             else
10726               for (k = 0; k < collect_count; k++)
10727                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10728                   player->inventory_element[player->inventory_size++] =
10729                     element;
10730           }
10731           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10732                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10733                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10734           {
10735             if (player->inventory_infinite_element != EL_UNDEFINED &&
10736                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10737                                      action_arg_element_raw))
10738               player->inventory_infinite_element = EL_UNDEFINED;
10739
10740             for (k = 0, j = 0; j < player->inventory_size; j++)
10741             {
10742               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10743                                         action_arg_element_raw))
10744                 player->inventory_element[k++] = player->inventory_element[j];
10745             }
10746
10747             player->inventory_size = k;
10748           }
10749           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10750           {
10751             if (player->inventory_size > 0)
10752             {
10753               for (j = 0; j < player->inventory_size - 1; j++)
10754                 player->inventory_element[j] = player->inventory_element[j + 1];
10755
10756               player->inventory_size--;
10757             }
10758           }
10759           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10760           {
10761             if (player->inventory_size > 0)
10762               player->inventory_size--;
10763           }
10764           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10765           {
10766             player->inventory_infinite_element = EL_UNDEFINED;
10767             player->inventory_size = 0;
10768           }
10769           else if (action_arg == CA_ARG_INVENTORY_RESET)
10770           {
10771             player->inventory_infinite_element = EL_UNDEFINED;
10772             player->inventory_size = 0;
10773
10774             if (level.use_initial_inventory[i])
10775             {
10776               for (j = 0; j < level.initial_inventory_size[i]; j++)
10777               {
10778                 int element = level.initial_inventory_content[i][j];
10779                 int collect_count = element_info[element].collect_count_initial;
10780
10781                 if (!IS_CUSTOM_ELEMENT(element))
10782                   collect_count = 1;
10783
10784                 if (collect_count == 0)
10785                   player->inventory_infinite_element = element;
10786                 else
10787                   for (k = 0; k < collect_count; k++)
10788                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10789                       player->inventory_element[player->inventory_size++] =
10790                         element;
10791               }
10792             }
10793           }
10794         }
10795       }
10796
10797       break;
10798     }
10799
10800     /* ---------- CE actions  ---------------------------------------------- */
10801
10802     case CA_SET_CE_VALUE:
10803     {
10804 #if USE_NEW_CUSTOM_VALUE
10805       int last_ce_value = CustomValue[x][y];
10806
10807       CustomValue[x][y] = action_arg_number_new;
10808
10809       if (CustomValue[x][y] != last_ce_value)
10810       {
10811         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10812         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10813
10814         if (CustomValue[x][y] == 0)
10815         {
10816           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10817           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10818         }
10819       }
10820 #endif
10821
10822       break;
10823     }
10824
10825     case CA_SET_CE_SCORE:
10826     {
10827 #if USE_NEW_CUSTOM_VALUE
10828       int last_ce_score = ei->collect_score;
10829
10830       ei->collect_score = action_arg_number_new;
10831
10832       if (ei->collect_score != last_ce_score)
10833       {
10834         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10835         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10836
10837         if (ei->collect_score == 0)
10838         {
10839           int xx, yy;
10840
10841           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10842           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10843
10844           /*
10845             This is a very special case that seems to be a mixture between
10846             CheckElementChange() and CheckTriggeredElementChange(): while
10847             the first one only affects single elements that are triggered
10848             directly, the second one affects multiple elements in the playfield
10849             that are triggered indirectly by another element. This is a third
10850             case: Changing the CE score always affects multiple identical CEs,
10851             so every affected CE must be checked, not only the single CE for
10852             which the CE score was changed in the first place (as every instance
10853             of that CE shares the same CE score, and therefore also can change)!
10854           */
10855           SCAN_PLAYFIELD(xx, yy)
10856           {
10857             if (Feld[xx][yy] == element)
10858               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10859                                  CE_SCORE_GETS_ZERO);
10860           }
10861         }
10862       }
10863 #endif
10864
10865       break;
10866     }
10867
10868     case CA_SET_CE_ARTWORK:
10869     {
10870       int artwork_element = action_arg_element;
10871       boolean reset_frame = FALSE;
10872       int xx, yy;
10873
10874       if (action_arg == CA_ARG_ELEMENT_RESET)
10875         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10876                            element);
10877
10878       if (ei->gfx_element != artwork_element)
10879         reset_frame = TRUE;
10880
10881       ei->gfx_element = artwork_element;
10882
10883       SCAN_PLAYFIELD(xx, yy)
10884       {
10885         if (Feld[xx][yy] == element)
10886         {
10887           if (reset_frame)
10888           {
10889             ResetGfxAnimation(xx, yy);
10890             ResetRandomAnimationValue(xx, yy);
10891           }
10892
10893           TEST_DrawLevelField(xx, yy);
10894         }
10895       }
10896
10897       break;
10898     }
10899
10900     /* ---------- engine actions  ------------------------------------------ */
10901
10902     case CA_SET_ENGINE_SCAN_MODE:
10903     {
10904       InitPlayfieldScanMode(action_arg);
10905
10906       break;
10907     }
10908
10909     default:
10910       break;
10911   }
10912 }
10913
10914 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10915 {
10916   int old_element = Feld[x][y];
10917   int new_element = GetElementFromGroupElement(element);
10918   int previous_move_direction = MovDir[x][y];
10919 #if USE_NEW_CUSTOM_VALUE
10920   int last_ce_value = CustomValue[x][y];
10921 #endif
10922   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10923   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10924   boolean add_player_onto_element = (new_element_is_player &&
10925 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10926                                      /* this breaks SnakeBite when a snake is
10927                                         halfway through a door that closes */
10928                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10929                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10930 #endif
10931                                      IS_WALKABLE(old_element));
10932
10933 #if 0
10934   /* check if element under the player changes from accessible to unaccessible
10935      (needed for special case of dropping element which then changes) */
10936   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10937       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10938   {
10939     Bang(x, y);
10940
10941     return;
10942   }
10943 #endif
10944
10945   if (!add_player_onto_element)
10946   {
10947     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10948       RemoveMovingField(x, y);
10949     else
10950       RemoveField(x, y);
10951
10952     Feld[x][y] = new_element;
10953
10954 #if !USE_GFX_RESET_GFX_ANIMATION
10955     ResetGfxAnimation(x, y);
10956     ResetRandomAnimationValue(x, y);
10957 #endif
10958
10959     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10960       MovDir[x][y] = previous_move_direction;
10961
10962 #if USE_NEW_CUSTOM_VALUE
10963     if (element_info[new_element].use_last_ce_value)
10964       CustomValue[x][y] = last_ce_value;
10965 #endif
10966
10967     InitField_WithBug1(x, y, FALSE);
10968
10969     new_element = Feld[x][y];   /* element may have changed */
10970
10971 #if USE_GFX_RESET_GFX_ANIMATION
10972     ResetGfxAnimation(x, y);
10973     ResetRandomAnimationValue(x, y);
10974 #endif
10975
10976     TEST_DrawLevelField(x, y);
10977
10978     if (GFX_CRUMBLED(new_element))
10979       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10980   }
10981
10982 #if 1
10983   /* check if element under the player changes from accessible to unaccessible
10984      (needed for special case of dropping element which then changes) */
10985   /* (must be checked after creating new element for walkable group elements) */
10986 #if USE_FIX_KILLED_BY_NON_WALKABLE
10987   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10988       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10989   {
10990     Bang(x, y);
10991
10992     return;
10993   }
10994 #else
10995   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10996       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10997   {
10998     Bang(x, y);
10999
11000     return;
11001   }
11002 #endif
11003 #endif
11004
11005   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11006   if (new_element_is_player)
11007     RelocatePlayer(x, y, new_element);
11008
11009   if (is_change)
11010     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11011
11012   TestIfBadThingTouchesPlayer(x, y);
11013   TestIfPlayerTouchesCustomElement(x, y);
11014   TestIfElementTouchesCustomElement(x, y);
11015 }
11016
11017 static void CreateField(int x, int y, int element)
11018 {
11019   CreateFieldExt(x, y, element, FALSE);
11020 }
11021
11022 static void CreateElementFromChange(int x, int y, int element)
11023 {
11024   element = GET_VALID_RUNTIME_ELEMENT(element);
11025
11026 #if USE_STOP_CHANGED_ELEMENTS
11027   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11028   {
11029     int old_element = Feld[x][y];
11030
11031     /* prevent changed element from moving in same engine frame
11032        unless both old and new element can either fall or move */
11033     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11034         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11035       Stop[x][y] = TRUE;
11036   }
11037 #endif
11038
11039   CreateFieldExt(x, y, element, TRUE);
11040 }
11041
11042 static boolean ChangeElement(int x, int y, int element, int page)
11043 {
11044   struct ElementInfo *ei = &element_info[element];
11045   struct ElementChangeInfo *change = &ei->change_page[page];
11046   int ce_value = CustomValue[x][y];
11047   int ce_score = ei->collect_score;
11048   int target_element;
11049   int old_element = Feld[x][y];
11050
11051   /* always use default change event to prevent running into a loop */
11052   if (ChangeEvent[x][y] == -1)
11053     ChangeEvent[x][y] = CE_DELAY;
11054
11055   if (ChangeEvent[x][y] == CE_DELAY)
11056   {
11057     /* reset actual trigger element, trigger player and action element */
11058     change->actual_trigger_element = EL_EMPTY;
11059     change->actual_trigger_player = EL_EMPTY;
11060     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11061     change->actual_trigger_side = CH_SIDE_NONE;
11062     change->actual_trigger_ce_value = 0;
11063     change->actual_trigger_ce_score = 0;
11064   }
11065
11066   /* do not change elements more than a specified maximum number of changes */
11067   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11068     return FALSE;
11069
11070   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11071
11072   if (change->explode)
11073   {
11074     Bang(x, y);
11075
11076     return TRUE;
11077   }
11078
11079   if (change->use_target_content)
11080   {
11081     boolean complete_replace = TRUE;
11082     boolean can_replace[3][3];
11083     int xx, yy;
11084
11085     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11086     {
11087       boolean is_empty;
11088       boolean is_walkable;
11089       boolean is_diggable;
11090       boolean is_collectible;
11091       boolean is_removable;
11092       boolean is_destructible;
11093       int ex = x + xx - 1;
11094       int ey = y + yy - 1;
11095       int content_element = change->target_content.e[xx][yy];
11096       int e;
11097
11098       can_replace[xx][yy] = TRUE;
11099
11100       if (ex == x && ey == y)   /* do not check changing element itself */
11101         continue;
11102
11103       if (content_element == EL_EMPTY_SPACE)
11104       {
11105         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11106
11107         continue;
11108       }
11109
11110       if (!IN_LEV_FIELD(ex, ey))
11111       {
11112         can_replace[xx][yy] = FALSE;
11113         complete_replace = FALSE;
11114
11115         continue;
11116       }
11117
11118       e = Feld[ex][ey];
11119
11120       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11121         e = MovingOrBlocked2Element(ex, ey);
11122
11123       is_empty = (IS_FREE(ex, ey) ||
11124                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11125
11126       is_walkable     = (is_empty || IS_WALKABLE(e));
11127       is_diggable     = (is_empty || IS_DIGGABLE(e));
11128       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11129       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11130       is_removable    = (is_diggable || is_collectible);
11131
11132       can_replace[xx][yy] =
11133         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11134           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11135           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11136           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11137           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11138           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11139          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11140
11141       if (!can_replace[xx][yy])
11142         complete_replace = FALSE;
11143     }
11144
11145     if (!change->only_if_complete || complete_replace)
11146     {
11147       boolean something_has_changed = FALSE;
11148
11149       if (change->only_if_complete && change->use_random_replace &&
11150           RND(100) < change->random_percentage)
11151         return FALSE;
11152
11153       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11154       {
11155         int ex = x + xx - 1;
11156         int ey = y + yy - 1;
11157         int content_element;
11158
11159         if (can_replace[xx][yy] && (!change->use_random_replace ||
11160                                     RND(100) < change->random_percentage))
11161         {
11162           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11163             RemoveMovingField(ex, ey);
11164
11165           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11166
11167           content_element = change->target_content.e[xx][yy];
11168           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11169                                               ce_value, ce_score);
11170
11171           CreateElementFromChange(ex, ey, target_element);
11172
11173           something_has_changed = TRUE;
11174
11175           /* for symmetry reasons, freeze newly created border elements */
11176           if (ex != x || ey != y)
11177             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11178         }
11179       }
11180
11181       if (something_has_changed)
11182       {
11183         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11184         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11185       }
11186     }
11187   }
11188   else
11189   {
11190     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11191                                         ce_value, ce_score);
11192
11193     if (element == EL_DIAGONAL_GROWING ||
11194         element == EL_DIAGONAL_SHRINKING)
11195     {
11196       target_element = Store[x][y];
11197
11198       Store[x][y] = EL_EMPTY;
11199     }
11200
11201     CreateElementFromChange(x, y, target_element);
11202
11203     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11204     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11205   }
11206
11207   /* this uses direct change before indirect change */
11208   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11209
11210   return TRUE;
11211 }
11212
11213 #if USE_NEW_DELAYED_ACTION
11214
11215 static void HandleElementChange(int x, int y, int page)
11216 {
11217   int element = MovingOrBlocked2Element(x, y);
11218   struct ElementInfo *ei = &element_info[element];
11219   struct ElementChangeInfo *change = &ei->change_page[page];
11220   boolean handle_action_before_change = FALSE;
11221
11222 #ifdef DEBUG
11223   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11224       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11225   {
11226     printf("\n\n");
11227     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11228            x, y, element, element_info[element].token_name);
11229     printf("HandleElementChange(): This should never happen!\n");
11230     printf("\n\n");
11231   }
11232 #endif
11233
11234   /* this can happen with classic bombs on walkable, changing elements */
11235   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11236   {
11237 #if 0
11238     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11239       ChangeDelay[x][y] = 0;
11240 #endif
11241
11242     return;
11243   }
11244
11245   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11246   {
11247     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11248
11249     if (change->can_change)
11250     {
11251 #if 1
11252       /* !!! not clear why graphic animation should be reset at all here !!! */
11253       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11254 #if USE_GFX_RESET_WHEN_NOT_MOVING
11255       /* when a custom element is about to change (for example by change delay),
11256          do not reset graphic animation when the custom element is moving */
11257       if (!IS_MOVING(x, y))
11258 #endif
11259       {
11260         ResetGfxAnimation(x, y);
11261         ResetRandomAnimationValue(x, y);
11262       }
11263 #endif
11264
11265       if (change->pre_change_function)
11266         change->pre_change_function(x, y);
11267     }
11268   }
11269
11270   ChangeDelay[x][y]--;
11271
11272   if (ChangeDelay[x][y] != 0)           /* continue element change */
11273   {
11274     if (change->can_change)
11275     {
11276       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11277
11278       if (IS_ANIMATED(graphic))
11279         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11280
11281       if (change->change_function)
11282         change->change_function(x, y);
11283     }
11284   }
11285   else                                  /* finish element change */
11286   {
11287     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11288     {
11289       page = ChangePage[x][y];
11290       ChangePage[x][y] = -1;
11291
11292       change = &ei->change_page[page];
11293     }
11294
11295     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11296     {
11297       ChangeDelay[x][y] = 1;            /* try change after next move step */
11298       ChangePage[x][y] = page;          /* remember page to use for change */
11299
11300       return;
11301     }
11302
11303 #if 1
11304     /* special case: set new level random seed before changing element */
11305     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11306       handle_action_before_change = TRUE;
11307
11308     if (change->has_action && handle_action_before_change)
11309       ExecuteCustomElementAction(x, y, element, page);
11310 #endif
11311
11312     if (change->can_change)
11313     {
11314       if (ChangeElement(x, y, element, page))
11315       {
11316         if (change->post_change_function)
11317           change->post_change_function(x, y);
11318       }
11319     }
11320
11321     if (change->has_action && !handle_action_before_change)
11322       ExecuteCustomElementAction(x, y, element, page);
11323   }
11324 }
11325
11326 #else
11327
11328 static void HandleElementChange(int x, int y, int page)
11329 {
11330   int element = MovingOrBlocked2Element(x, y);
11331   struct ElementInfo *ei = &element_info[element];
11332   struct ElementChangeInfo *change = &ei->change_page[page];
11333
11334 #ifdef DEBUG
11335   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11336   {
11337     printf("\n\n");
11338     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11339            x, y, element, element_info[element].token_name);
11340     printf("HandleElementChange(): This should never happen!\n");
11341     printf("\n\n");
11342   }
11343 #endif
11344
11345   /* this can happen with classic bombs on walkable, changing elements */
11346   if (!CAN_CHANGE(element))
11347   {
11348 #if 0
11349     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11350       ChangeDelay[x][y] = 0;
11351 #endif
11352
11353     return;
11354   }
11355
11356   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11357   {
11358     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11359
11360     ResetGfxAnimation(x, y);
11361     ResetRandomAnimationValue(x, y);
11362
11363     if (change->pre_change_function)
11364       change->pre_change_function(x, y);
11365   }
11366
11367   ChangeDelay[x][y]--;
11368
11369   if (ChangeDelay[x][y] != 0)           /* continue element change */
11370   {
11371     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11372
11373     if (IS_ANIMATED(graphic))
11374       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11375
11376     if (change->change_function)
11377       change->change_function(x, y);
11378   }
11379   else                                  /* finish element change */
11380   {
11381     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11382     {
11383       page = ChangePage[x][y];
11384       ChangePage[x][y] = -1;
11385
11386       change = &ei->change_page[page];
11387     }
11388
11389     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11390     {
11391       ChangeDelay[x][y] = 1;            /* try change after next move step */
11392       ChangePage[x][y] = page;          /* remember page to use for change */
11393
11394       return;
11395     }
11396
11397     if (ChangeElement(x, y, element, page))
11398     {
11399       if (change->post_change_function)
11400         change->post_change_function(x, y);
11401     }
11402   }
11403 }
11404
11405 #endif
11406
11407 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11408                                               int trigger_element,
11409                                               int trigger_event,
11410                                               int trigger_player,
11411                                               int trigger_side,
11412                                               int trigger_page)
11413 {
11414   boolean change_done_any = FALSE;
11415   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11416   int i;
11417
11418   if (!(trigger_events[trigger_element][trigger_event]))
11419     return FALSE;
11420
11421 #if 0
11422   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11423          trigger_event, recursion_loop_depth, recursion_loop_detected,
11424          recursion_loop_element, EL_NAME(recursion_loop_element));
11425 #endif
11426
11427   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11428
11429   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11430   {
11431     int element = EL_CUSTOM_START + i;
11432     boolean change_done = FALSE;
11433     int p;
11434
11435     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11436         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11437       continue;
11438
11439     for (p = 0; p < element_info[element].num_change_pages; p++)
11440     {
11441       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11442
11443       if (change->can_change_or_has_action &&
11444           change->has_event[trigger_event] &&
11445           change->trigger_side & trigger_side &&
11446           change->trigger_player & trigger_player &&
11447           change->trigger_page & trigger_page_bits &&
11448           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11449       {
11450         change->actual_trigger_element = trigger_element;
11451         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11452         change->actual_trigger_player_bits = trigger_player;
11453         change->actual_trigger_side = trigger_side;
11454         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11455         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11456
11457 #if 0
11458         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11459                element, EL_NAME(element), p);
11460 #endif
11461
11462         if ((change->can_change && !change_done) || change->has_action)
11463         {
11464           int x, y;
11465
11466           SCAN_PLAYFIELD(x, y)
11467           {
11468             if (Feld[x][y] == element)
11469             {
11470               if (change->can_change && !change_done)
11471               {
11472 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11473                 /* if element already changed in this frame, not only prevent
11474                    another element change (checked in ChangeElement()), but
11475                    also prevent additional element actions for this element */
11476
11477                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11478                     !level.use_action_after_change_bug)
11479                   continue;
11480 #endif
11481
11482 #if 0
11483                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11484                        element, EL_NAME(element), p);
11485 #endif
11486
11487                 ChangeDelay[x][y] = 1;
11488                 ChangeEvent[x][y] = trigger_event;
11489
11490                 HandleElementChange(x, y, p);
11491               }
11492 #if USE_NEW_DELAYED_ACTION
11493               else if (change->has_action)
11494               {
11495 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11496                 /* if element already changed in this frame, not only prevent
11497                    another element change (checked in ChangeElement()), but
11498                    also prevent additional element actions for this element */
11499
11500                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11501                     !level.use_action_after_change_bug)
11502                   continue;
11503 #endif
11504
11505
11506 #if 0
11507                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11508                        element, EL_NAME(element), p);
11509 #endif
11510
11511                 ExecuteCustomElementAction(x, y, element, p);
11512                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11513               }
11514 #else
11515               if (change->has_action)
11516               {
11517                 ExecuteCustomElementAction(x, y, element, p);
11518                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11519               }
11520 #endif
11521             }
11522           }
11523
11524           if (change->can_change)
11525           {
11526             change_done = TRUE;
11527             change_done_any = TRUE;
11528
11529 #if 0
11530             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11531                    element, EL_NAME(element), p);
11532 #endif
11533
11534           }
11535         }
11536       }
11537     }
11538   }
11539
11540   RECURSION_LOOP_DETECTION_END();
11541
11542   return change_done_any;
11543 }
11544
11545 static boolean CheckElementChangeExt(int x, int y,
11546                                      int element,
11547                                      int trigger_element,
11548                                      int trigger_event,
11549                                      int trigger_player,
11550                                      int trigger_side)
11551 {
11552   boolean change_done = FALSE;
11553   int p;
11554
11555   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11556       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11557     return FALSE;
11558
11559   if (Feld[x][y] == EL_BLOCKED)
11560   {
11561     Blocked2Moving(x, y, &x, &y);
11562     element = Feld[x][y];
11563   }
11564
11565 #if 0
11566   /* check if element has already changed */
11567   if (Feld[x][y] != element)
11568     return FALSE;
11569 #else
11570   /* check if element has already changed or is about to change after moving */
11571   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11572        Feld[x][y] != element) ||
11573
11574       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11575        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11576         ChangePage[x][y] != -1)))
11577     return FALSE;
11578 #endif
11579
11580 #if 0
11581   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11582          trigger_event, recursion_loop_depth, recursion_loop_detected,
11583          recursion_loop_element, EL_NAME(recursion_loop_element));
11584 #endif
11585
11586   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11587
11588 #if 0
11589   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11590 #endif
11591
11592   for (p = 0; p < element_info[element].num_change_pages; p++)
11593   {
11594     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11595
11596     /* check trigger element for all events where the element that is checked
11597        for changing interacts with a directly adjacent element -- this is
11598        different to element changes that affect other elements to change on the
11599        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11600     boolean check_trigger_element =
11601       (trigger_event == CE_TOUCHING_X ||
11602        trigger_event == CE_HITTING_X ||
11603        trigger_event == CE_HIT_BY_X ||
11604 #if 1
11605        /* this one was forgotten until 3.2.3 */
11606        trigger_event == CE_DIGGING_X);
11607 #endif
11608
11609     if (change->can_change_or_has_action &&
11610         change->has_event[trigger_event] &&
11611         change->trigger_side & trigger_side &&
11612         change->trigger_player & trigger_player &&
11613         (!check_trigger_element ||
11614          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11615     {
11616       change->actual_trigger_element = trigger_element;
11617       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11618       change->actual_trigger_player_bits = trigger_player;
11619       change->actual_trigger_side = trigger_side;
11620       change->actual_trigger_ce_value = CustomValue[x][y];
11621       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11622
11623       /* special case: trigger element not at (x,y) position for some events */
11624       if (check_trigger_element)
11625       {
11626         static struct
11627         {
11628           int dx, dy;
11629         } move_xy[] =
11630           {
11631             {  0,  0 },
11632             { -1,  0 },
11633             { +1,  0 },
11634             {  0,  0 },
11635             {  0, -1 },
11636             {  0,  0 }, { 0, 0 }, { 0, 0 },
11637             {  0, +1 }
11638           };
11639
11640         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11641         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11642
11643         change->actual_trigger_ce_value = CustomValue[xx][yy];
11644         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11645       }
11646
11647       if (change->can_change && !change_done)
11648       {
11649         ChangeDelay[x][y] = 1;
11650         ChangeEvent[x][y] = trigger_event;
11651
11652         HandleElementChange(x, y, p);
11653
11654         change_done = TRUE;
11655       }
11656 #if USE_NEW_DELAYED_ACTION
11657       else if (change->has_action)
11658       {
11659         ExecuteCustomElementAction(x, y, element, p);
11660         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11661       }
11662 #else
11663       if (change->has_action)
11664       {
11665         ExecuteCustomElementAction(x, y, element, p);
11666         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11667       }
11668 #endif
11669     }
11670   }
11671
11672   RECURSION_LOOP_DETECTION_END();
11673
11674   return change_done;
11675 }
11676
11677 static void PlayPlayerSound(struct PlayerInfo *player)
11678 {
11679   int jx = player->jx, jy = player->jy;
11680   int sound_element = player->artwork_element;
11681   int last_action = player->last_action_waiting;
11682   int action = player->action_waiting;
11683
11684   if (player->is_waiting)
11685   {
11686     if (action != last_action)
11687       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11688     else
11689       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11690   }
11691   else
11692   {
11693     if (action != last_action)
11694       StopSound(element_info[sound_element].sound[last_action]);
11695
11696     if (last_action == ACTION_SLEEPING)
11697       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11698   }
11699 }
11700
11701 static void PlayAllPlayersSound()
11702 {
11703   int i;
11704
11705   for (i = 0; i < MAX_PLAYERS; i++)
11706     if (stored_player[i].active)
11707       PlayPlayerSound(&stored_player[i]);
11708 }
11709
11710 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11711 {
11712   boolean last_waiting = player->is_waiting;
11713   int move_dir = player->MovDir;
11714
11715   player->dir_waiting = move_dir;
11716   player->last_action_waiting = player->action_waiting;
11717
11718   if (is_waiting)
11719   {
11720     if (!last_waiting)          /* not waiting -> waiting */
11721     {
11722       player->is_waiting = TRUE;
11723
11724       player->frame_counter_bored =
11725         FrameCounter +
11726         game.player_boring_delay_fixed +
11727         GetSimpleRandom(game.player_boring_delay_random);
11728       player->frame_counter_sleeping =
11729         FrameCounter +
11730         game.player_sleeping_delay_fixed +
11731         GetSimpleRandom(game.player_sleeping_delay_random);
11732
11733       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11734     }
11735
11736     if (game.player_sleeping_delay_fixed +
11737         game.player_sleeping_delay_random > 0 &&
11738         player->anim_delay_counter == 0 &&
11739         player->post_delay_counter == 0 &&
11740         FrameCounter >= player->frame_counter_sleeping)
11741       player->is_sleeping = TRUE;
11742     else if (game.player_boring_delay_fixed +
11743              game.player_boring_delay_random > 0 &&
11744              FrameCounter >= player->frame_counter_bored)
11745       player->is_bored = TRUE;
11746
11747     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11748                               player->is_bored ? ACTION_BORING :
11749                               ACTION_WAITING);
11750
11751     if (player->is_sleeping && player->use_murphy)
11752     {
11753       /* special case for sleeping Murphy when leaning against non-free tile */
11754
11755       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11756           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11757            !IS_MOVING(player->jx - 1, player->jy)))
11758         move_dir = MV_LEFT;
11759       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11760                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11761                 !IS_MOVING(player->jx + 1, player->jy)))
11762         move_dir = MV_RIGHT;
11763       else
11764         player->is_sleeping = FALSE;
11765
11766       player->dir_waiting = move_dir;
11767     }
11768
11769     if (player->is_sleeping)
11770     {
11771       if (player->num_special_action_sleeping > 0)
11772       {
11773         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11774         {
11775           int last_special_action = player->special_action_sleeping;
11776           int num_special_action = player->num_special_action_sleeping;
11777           int special_action =
11778             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11779              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11780              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11781              last_special_action + 1 : ACTION_SLEEPING);
11782           int special_graphic =
11783             el_act_dir2img(player->artwork_element, special_action, move_dir);
11784
11785           player->anim_delay_counter =
11786             graphic_info[special_graphic].anim_delay_fixed +
11787             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11788           player->post_delay_counter =
11789             graphic_info[special_graphic].post_delay_fixed +
11790             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11791
11792           player->special_action_sleeping = special_action;
11793         }
11794
11795         if (player->anim_delay_counter > 0)
11796         {
11797           player->action_waiting = player->special_action_sleeping;
11798           player->anim_delay_counter--;
11799         }
11800         else if (player->post_delay_counter > 0)
11801         {
11802           player->post_delay_counter--;
11803         }
11804       }
11805     }
11806     else if (player->is_bored)
11807     {
11808       if (player->num_special_action_bored > 0)
11809       {
11810         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11811         {
11812           int special_action =
11813             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11814           int special_graphic =
11815             el_act_dir2img(player->artwork_element, special_action, move_dir);
11816
11817           player->anim_delay_counter =
11818             graphic_info[special_graphic].anim_delay_fixed +
11819             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11820           player->post_delay_counter =
11821             graphic_info[special_graphic].post_delay_fixed +
11822             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11823
11824           player->special_action_bored = special_action;
11825         }
11826
11827         if (player->anim_delay_counter > 0)
11828         {
11829           player->action_waiting = player->special_action_bored;
11830           player->anim_delay_counter--;
11831         }
11832         else if (player->post_delay_counter > 0)
11833         {
11834           player->post_delay_counter--;
11835         }
11836       }
11837     }
11838   }
11839   else if (last_waiting)        /* waiting -> not waiting */
11840   {
11841     player->is_waiting = FALSE;
11842     player->is_bored = FALSE;
11843     player->is_sleeping = FALSE;
11844
11845     player->frame_counter_bored = -1;
11846     player->frame_counter_sleeping = -1;
11847
11848     player->anim_delay_counter = 0;
11849     player->post_delay_counter = 0;
11850
11851     player->dir_waiting = player->MovDir;
11852     player->action_waiting = ACTION_DEFAULT;
11853
11854     player->special_action_bored = ACTION_DEFAULT;
11855     player->special_action_sleeping = ACTION_DEFAULT;
11856   }
11857 }
11858
11859 static void CheckSingleStepMode(struct PlayerInfo *player)
11860 {
11861   if (tape.single_step && tape.recording && !tape.pausing)
11862   {
11863     /* as it is called "single step mode", just return to pause mode when the
11864        player stopped moving after one tile (or never starts moving at all) */
11865     if (!player->is_moving && !player->is_pushing)
11866     {
11867       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11868       SnapField(player, 0, 0);                  /* stop snapping */
11869     }
11870   }
11871 }
11872
11873 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11874 {
11875   int left      = player_action & JOY_LEFT;
11876   int right     = player_action & JOY_RIGHT;
11877   int up        = player_action & JOY_UP;
11878   int down      = player_action & JOY_DOWN;
11879   int button1   = player_action & JOY_BUTTON_1;
11880   int button2   = player_action & JOY_BUTTON_2;
11881   int dx        = (left ? -1 : right ? 1 : 0);
11882   int dy        = (up   ? -1 : down  ? 1 : 0);
11883
11884   if (!player->active || tape.pausing)
11885     return 0;
11886
11887   if (player_action)
11888   {
11889     if (button1)
11890       SnapField(player, dx, dy);
11891     else
11892     {
11893       if (button2)
11894         DropElement(player);
11895
11896       MovePlayer(player, dx, dy);
11897     }
11898
11899     CheckSingleStepMode(player);
11900
11901     SetPlayerWaiting(player, FALSE);
11902
11903     return player_action;
11904   }
11905   else
11906   {
11907     /* no actions for this player (no input at player's configured device) */
11908
11909     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11910     SnapField(player, 0, 0);
11911     CheckGravityMovementWhenNotMoving(player);
11912
11913     if (player->MovPos == 0)
11914       SetPlayerWaiting(player, TRUE);
11915
11916     if (player->MovPos == 0)    /* needed for tape.playing */
11917       player->is_moving = FALSE;
11918
11919     player->is_dropping = FALSE;
11920     player->is_dropping_pressed = FALSE;
11921     player->drop_pressed_delay = 0;
11922
11923     CheckSingleStepMode(player);
11924
11925     return 0;
11926   }
11927 }
11928
11929 static void CheckLevelTime()
11930 {
11931   int i;
11932
11933   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11934   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11935   {
11936     if (level.native_em_level->lev->home == 0)  /* all players at home */
11937     {
11938       PlayerWins(local_player);
11939
11940       AllPlayersGone = TRUE;
11941
11942       level.native_em_level->lev->home = -1;
11943     }
11944
11945     if (level.native_em_level->ply[0]->alive == 0 &&
11946         level.native_em_level->ply[1]->alive == 0 &&
11947         level.native_em_level->ply[2]->alive == 0 &&
11948         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11949       AllPlayersGone = TRUE;
11950   }
11951   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11952   {
11953     if (game_sp.LevelSolved &&
11954         !game_sp.GameOver)                              /* game won */
11955     {
11956       PlayerWins(local_player);
11957
11958       game_sp.GameOver = TRUE;
11959
11960       AllPlayersGone = TRUE;
11961     }
11962
11963     if (game_sp.GameOver)                               /* game lost */
11964       AllPlayersGone = TRUE;
11965   }
11966
11967   if (TimeFrames >= FRAMES_PER_SECOND)
11968   {
11969     TimeFrames = 0;
11970     TapeTime++;
11971
11972     for (i = 0; i < MAX_PLAYERS; i++)
11973     {
11974       struct PlayerInfo *player = &stored_player[i];
11975
11976       if (SHIELD_ON(player))
11977       {
11978         player->shield_normal_time_left--;
11979
11980         if (player->shield_deadly_time_left > 0)
11981           player->shield_deadly_time_left--;
11982       }
11983     }
11984
11985     if (!local_player->LevelSolved && !level.use_step_counter)
11986     {
11987       TimePlayed++;
11988
11989       if (TimeLeft > 0)
11990       {
11991         TimeLeft--;
11992
11993         if (TimeLeft <= 10 && setup.time_limit)
11994           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11995
11996 #if 1
11997         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11998            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11999
12000         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12001
12002         /* (already called by UpdateAndDisplayGameControlValues() below) */
12003         // DisplayGameControlValues();
12004 #else
12005         DrawGameValue_Time(TimeLeft);
12006 #endif
12007
12008         if (!TimeLeft && setup.time_limit)
12009         {
12010           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12011             level.native_em_level->lev->killed_out_of_time = TRUE;
12012           else
12013             for (i = 0; i < MAX_PLAYERS; i++)
12014               KillPlayer(&stored_player[i]);
12015         }
12016       }
12017 #if 1
12018       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12019       {
12020         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12021
12022         /* (already called by UpdateAndDisplayGameControlValues() below) */
12023         // DisplayGameControlValues();
12024       }
12025 #else
12026       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12027         DrawGameValue_Time(TimePlayed);
12028 #endif
12029
12030       level.native_em_level->lev->time =
12031         (game.no_time_limit ? TimePlayed : TimeLeft);
12032     }
12033
12034     if (tape.recording || tape.playing)
12035       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12036   }
12037
12038 #if 1
12039   UpdateAndDisplayGameControlValues();
12040 #else
12041   UpdateGameDoorValues();
12042   DrawGameDoorValues();
12043 #endif
12044 }
12045
12046 void AdvanceFrameAndPlayerCounters(int player_nr)
12047 {
12048   int i;
12049
12050   /* advance frame counters (global frame counter and time frame counter) */
12051   FrameCounter++;
12052   TimeFrames++;
12053
12054   /* advance player counters (counters for move delay, move animation etc.) */
12055   for (i = 0; i < MAX_PLAYERS; i++)
12056   {
12057     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12058     int move_delay_value = stored_player[i].move_delay_value;
12059     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12060
12061     if (!advance_player_counters)       /* not all players may be affected */
12062       continue;
12063
12064 #if USE_NEW_PLAYER_ANIM
12065     if (move_frames == 0)       /* less than one move per game frame */
12066     {
12067       int stepsize = TILEX / move_delay_value;
12068       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12069       int count = (stored_player[i].is_moving ?
12070                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12071
12072       if (count % delay == 0)
12073         move_frames = 1;
12074     }
12075 #endif
12076
12077     stored_player[i].Frame += move_frames;
12078
12079     if (stored_player[i].MovPos != 0)
12080       stored_player[i].StepFrame += move_frames;
12081
12082     if (stored_player[i].move_delay > 0)
12083       stored_player[i].move_delay--;
12084
12085     /* due to bugs in previous versions, counter must count up, not down */
12086     if (stored_player[i].push_delay != -1)
12087       stored_player[i].push_delay++;
12088
12089     if (stored_player[i].drop_delay > 0)
12090       stored_player[i].drop_delay--;
12091
12092     if (stored_player[i].is_dropping_pressed)
12093       stored_player[i].drop_pressed_delay++;
12094   }
12095 }
12096
12097 void StartGameActions(boolean init_network_game, boolean record_tape,
12098                       int random_seed)
12099 {
12100   unsigned int new_random_seed = InitRND(random_seed);
12101
12102   if (record_tape)
12103     TapeStartRecording(new_random_seed);
12104
12105 #if defined(NETWORK_AVALIABLE)
12106   if (init_network_game)
12107   {
12108     SendToServer_StartPlaying();
12109
12110     return;
12111   }
12112 #endif
12113
12114   InitGame();
12115 }
12116
12117 void GameActions()
12118 {
12119   static unsigned int game_frame_delay = 0;
12120   unsigned int game_frame_delay_value;
12121   byte *recorded_player_action;
12122   byte summarized_player_action = 0;
12123   byte tape_action[MAX_PLAYERS];
12124   int i;
12125
12126   /* detect endless loops, caused by custom element programming */
12127   if (recursion_loop_detected && recursion_loop_depth == 0)
12128   {
12129     char *message = getStringCat3("Internal Error ! Element ",
12130                                   EL_NAME(recursion_loop_element),
12131                                   " caused endless loop ! Quit the game ?");
12132
12133     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12134           EL_NAME(recursion_loop_element));
12135
12136     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12137
12138     recursion_loop_detected = FALSE;    /* if game should be continued */
12139
12140     free(message);
12141
12142     return;
12143   }
12144
12145   if (game.restart_level)
12146     StartGameActions(options.network, setup.autorecord, level.random_seed);
12147
12148   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12149   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12150   {
12151     if (level.native_em_level->lev->home == 0)  /* all players at home */
12152     {
12153       PlayerWins(local_player);
12154
12155       AllPlayersGone = TRUE;
12156
12157       level.native_em_level->lev->home = -1;
12158     }
12159
12160     if (level.native_em_level->ply[0]->alive == 0 &&
12161         level.native_em_level->ply[1]->alive == 0 &&
12162         level.native_em_level->ply[2]->alive == 0 &&
12163         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12164       AllPlayersGone = TRUE;
12165   }
12166   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12167   {
12168     if (game_sp.LevelSolved &&
12169         !game_sp.GameOver)                              /* game won */
12170     {
12171       PlayerWins(local_player);
12172
12173       game_sp.GameOver = TRUE;
12174
12175       AllPlayersGone = TRUE;
12176     }
12177
12178     if (game_sp.GameOver)                               /* game lost */
12179       AllPlayersGone = TRUE;
12180   }
12181
12182   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12183     GameWon();
12184
12185   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12186     TapeStop();
12187
12188   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12189     return;
12190
12191   game_frame_delay_value =
12192     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12193
12194   if (tape.playing && tape.warp_forward && !tape.pausing)
12195     game_frame_delay_value = 0;
12196
12197   /* ---------- main game synchronization point ---------- */
12198
12199   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12200
12201   if (network_playing && !network_player_action_received)
12202   {
12203     /* try to get network player actions in time */
12204
12205 #if defined(NETWORK_AVALIABLE)
12206     /* last chance to get network player actions without main loop delay */
12207     HandleNetworking();
12208 #endif
12209
12210     /* game was quit by network peer */
12211     if (game_status != GAME_MODE_PLAYING)
12212       return;
12213
12214     if (!network_player_action_received)
12215       return;           /* failed to get network player actions in time */
12216
12217     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12218   }
12219
12220   if (tape.pausing)
12221     return;
12222
12223   /* at this point we know that we really continue executing the game */
12224
12225   network_player_action_received = FALSE;
12226
12227   /* when playing tape, read previously recorded player input from tape data */
12228   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12229
12230 #if 1
12231   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12232   if (tape.pausing)
12233     return;
12234 #endif
12235
12236   if (tape.set_centered_player)
12237   {
12238     game.centered_player_nr_next = tape.centered_player_nr_next;
12239     game.set_centered_player = TRUE;
12240   }
12241
12242   for (i = 0; i < MAX_PLAYERS; i++)
12243   {
12244     summarized_player_action |= stored_player[i].action;
12245
12246     if (!network_playing)
12247       stored_player[i].effective_action = stored_player[i].action;
12248   }
12249
12250 #if defined(NETWORK_AVALIABLE)
12251   if (network_playing)
12252     SendToServer_MovePlayer(summarized_player_action);
12253 #endif
12254
12255   if (!options.network && !setup.team_mode)
12256     local_player->effective_action = summarized_player_action;
12257
12258   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12259   {
12260     for (i = 0; i < MAX_PLAYERS; i++)
12261       stored_player[i].effective_action =
12262         (i == game.centered_player_nr ? summarized_player_action : 0);
12263   }
12264
12265   if (recorded_player_action != NULL)
12266     for (i = 0; i < MAX_PLAYERS; i++)
12267       stored_player[i].effective_action = recorded_player_action[i];
12268
12269   for (i = 0; i < MAX_PLAYERS; i++)
12270   {
12271     tape_action[i] = stored_player[i].effective_action;
12272
12273     /* (this can only happen in the R'n'D game engine) */
12274     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12275       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12276   }
12277
12278   /* only record actions from input devices, but not programmed actions */
12279   if (tape.recording)
12280     TapeRecordAction(tape_action);
12281
12282 #if USE_NEW_PLAYER_ASSIGNMENTS
12283   {
12284     byte mapped_action[MAX_PLAYERS];
12285
12286     for (i = 0; i < MAX_PLAYERS; i++)
12287       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12288
12289     for (i = 0; i < MAX_PLAYERS; i++)
12290       stored_player[i].effective_action = mapped_action[i];
12291   }
12292 #endif
12293
12294   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12295   {
12296     GameActions_EM_Main();
12297   }
12298   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12299   {
12300     GameActions_SP_Main();
12301   }
12302   else
12303   {
12304     GameActions_RND();
12305   }
12306 }
12307
12308 void GameActions_EM_Main()
12309 {
12310   byte effective_action[MAX_PLAYERS];
12311   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12312   int i;
12313
12314   for (i = 0; i < MAX_PLAYERS; i++)
12315     effective_action[i] = stored_player[i].effective_action;
12316
12317   GameActions_EM(effective_action, warp_mode);
12318
12319   CheckLevelTime();
12320
12321   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12322 }
12323
12324 void GameActions_SP_Main()
12325 {
12326   byte effective_action[MAX_PLAYERS];
12327   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12328   int i;
12329
12330   for (i = 0; i < MAX_PLAYERS; i++)
12331     effective_action[i] = stored_player[i].effective_action;
12332
12333   GameActions_SP(effective_action, warp_mode);
12334
12335   CheckLevelTime();
12336
12337   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12338 }
12339
12340 void GameActions_RND()
12341 {
12342   int magic_wall_x = 0, magic_wall_y = 0;
12343   int i, x, y, element, graphic;
12344
12345   InitPlayfieldScanModeVars();
12346
12347 #if USE_ONE_MORE_CHANGE_PER_FRAME
12348   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12349   {
12350     SCAN_PLAYFIELD(x, y)
12351     {
12352       ChangeCount[x][y] = 0;
12353       ChangeEvent[x][y] = -1;
12354     }
12355   }
12356 #endif
12357
12358   if (game.set_centered_player)
12359   {
12360     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12361
12362     /* switching to "all players" only possible if all players fit to screen */
12363     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12364     {
12365       game.centered_player_nr_next = game.centered_player_nr;
12366       game.set_centered_player = FALSE;
12367     }
12368
12369     /* do not switch focus to non-existing (or non-active) player */
12370     if (game.centered_player_nr_next >= 0 &&
12371         !stored_player[game.centered_player_nr_next].active)
12372     {
12373       game.centered_player_nr_next = game.centered_player_nr;
12374       game.set_centered_player = FALSE;
12375     }
12376   }
12377
12378   if (game.set_centered_player &&
12379       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12380   {
12381     int sx, sy;
12382
12383     if (game.centered_player_nr_next == -1)
12384     {
12385       setScreenCenteredToAllPlayers(&sx, &sy);
12386     }
12387     else
12388     {
12389       sx = stored_player[game.centered_player_nr_next].jx;
12390       sy = stored_player[game.centered_player_nr_next].jy;
12391     }
12392
12393     game.centered_player_nr = game.centered_player_nr_next;
12394     game.set_centered_player = FALSE;
12395
12396     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12397     DrawGameDoorValues();
12398   }
12399
12400   for (i = 0; i < MAX_PLAYERS; i++)
12401   {
12402     int actual_player_action = stored_player[i].effective_action;
12403
12404 #if 1
12405     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12406        - rnd_equinox_tetrachloride 048
12407        - rnd_equinox_tetrachloride_ii 096
12408        - rnd_emanuel_schmieg 002
12409        - doctor_sloan_ww 001, 020
12410     */
12411     if (stored_player[i].MovPos == 0)
12412       CheckGravityMovement(&stored_player[i]);
12413 #endif
12414
12415     /* overwrite programmed action with tape action */
12416     if (stored_player[i].programmed_action)
12417       actual_player_action = stored_player[i].programmed_action;
12418
12419     PlayerActions(&stored_player[i], actual_player_action);
12420
12421     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12422   }
12423
12424   ScrollScreen(NULL, SCROLL_GO_ON);
12425
12426   /* for backwards compatibility, the following code emulates a fixed bug that
12427      occured when pushing elements (causing elements that just made their last
12428      pushing step to already (if possible) make their first falling step in the
12429      same game frame, which is bad); this code is also needed to use the famous
12430      "spring push bug" which is used in older levels and might be wanted to be
12431      used also in newer levels, but in this case the buggy pushing code is only
12432      affecting the "spring" element and no other elements */
12433
12434   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12435   {
12436     for (i = 0; i < MAX_PLAYERS; i++)
12437     {
12438       struct PlayerInfo *player = &stored_player[i];
12439       int x = player->jx;
12440       int y = player->jy;
12441
12442       if (player->active && player->is_pushing && player->is_moving &&
12443           IS_MOVING(x, y) &&
12444           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12445            Feld[x][y] == EL_SPRING))
12446       {
12447         ContinueMoving(x, y);
12448
12449         /* continue moving after pushing (this is actually a bug) */
12450         if (!IS_MOVING(x, y))
12451           Stop[x][y] = FALSE;
12452       }
12453     }
12454   }
12455
12456 #if 0
12457   debug_print_timestamp(0, "start main loop profiling");
12458 #endif
12459
12460   SCAN_PLAYFIELD(x, y)
12461   {
12462     ChangeCount[x][y] = 0;
12463     ChangeEvent[x][y] = -1;
12464
12465     /* this must be handled before main playfield loop */
12466     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12467     {
12468       MovDelay[x][y]--;
12469       if (MovDelay[x][y] <= 0)
12470         RemoveField(x, y);
12471     }
12472
12473 #if USE_NEW_SNAP_DELAY
12474     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12475     {
12476       MovDelay[x][y]--;
12477       if (MovDelay[x][y] <= 0)
12478       {
12479         RemoveField(x, y);
12480         TEST_DrawLevelField(x, y);
12481
12482         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12483       }
12484     }
12485 #endif
12486
12487 #if DEBUG
12488     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12489     {
12490       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12491       printf("GameActions(): This should never happen!\n");
12492
12493       ChangePage[x][y] = -1;
12494     }
12495 #endif
12496
12497     Stop[x][y] = FALSE;
12498     if (WasJustMoving[x][y] > 0)
12499       WasJustMoving[x][y]--;
12500     if (WasJustFalling[x][y] > 0)
12501       WasJustFalling[x][y]--;
12502     if (CheckCollision[x][y] > 0)
12503       CheckCollision[x][y]--;
12504     if (CheckImpact[x][y] > 0)
12505       CheckImpact[x][y]--;
12506
12507     GfxFrame[x][y]++;
12508
12509     /* reset finished pushing action (not done in ContinueMoving() to allow
12510        continuous pushing animation for elements with zero push delay) */
12511     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12512     {
12513       ResetGfxAnimation(x, y);
12514       TEST_DrawLevelField(x, y);
12515     }
12516
12517 #if DEBUG
12518     if (IS_BLOCKED(x, y))
12519     {
12520       int oldx, oldy;
12521
12522       Blocked2Moving(x, y, &oldx, &oldy);
12523       if (!IS_MOVING(oldx, oldy))
12524       {
12525         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12526         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12527         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12528         printf("GameActions(): This should never happen!\n");
12529       }
12530     }
12531 #endif
12532   }
12533
12534 #if 0
12535   debug_print_timestamp(0, "- time for pre-main loop:");
12536 #endif
12537
12538 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12539   SCAN_PLAYFIELD(x, y)
12540   {
12541     element = Feld[x][y];
12542     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12543
12544 #if 1
12545     {
12546 #if 1
12547       int element2 = element;
12548       int graphic2 = graphic;
12549 #else
12550       int element2 = Feld[x][y];
12551       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12552 #endif
12553       int last_gfx_frame = GfxFrame[x][y];
12554
12555       if (graphic_info[graphic2].anim_global_sync)
12556         GfxFrame[x][y] = FrameCounter;
12557       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12558         GfxFrame[x][y] = CustomValue[x][y];
12559       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12560         GfxFrame[x][y] = element_info[element2].collect_score;
12561       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12562         GfxFrame[x][y] = ChangeDelay[x][y];
12563
12564       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12565         DrawLevelGraphicAnimation(x, y, graphic2);
12566     }
12567 #else
12568     ResetGfxFrame(x, y, TRUE);
12569 #endif
12570
12571 #if 1
12572     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12573         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12574       ResetRandomAnimationValue(x, y);
12575 #endif
12576
12577 #if 1
12578     SetRandomAnimationValue(x, y);
12579 #endif
12580
12581 #if 1
12582     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12583 #endif
12584   }
12585 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12586
12587 #if 0
12588   debug_print_timestamp(0, "- time for TEST loop:     -->");
12589 #endif
12590
12591   SCAN_PLAYFIELD(x, y)
12592   {
12593     element = Feld[x][y];
12594     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12595
12596     ResetGfxFrame(x, y, TRUE);
12597
12598     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12599         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12600       ResetRandomAnimationValue(x, y);
12601
12602     SetRandomAnimationValue(x, y);
12603
12604     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12605
12606     if (IS_INACTIVE(element))
12607     {
12608       if (IS_ANIMATED(graphic))
12609         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12610
12611       continue;
12612     }
12613
12614     /* this may take place after moving, so 'element' may have changed */
12615     if (IS_CHANGING(x, y) &&
12616         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12617     {
12618       int page = element_info[element].event_page_nr[CE_DELAY];
12619
12620 #if 1
12621       HandleElementChange(x, y, page);
12622 #else
12623       if (CAN_CHANGE(element))
12624         HandleElementChange(x, y, page);
12625
12626       if (HAS_ACTION(element))
12627         ExecuteCustomElementAction(x, y, element, page);
12628 #endif
12629
12630       element = Feld[x][y];
12631       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12632     }
12633
12634 #if 0   // ---------------------------------------------------------------------
12635
12636     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12637     {
12638       StartMoving(x, y);
12639
12640       element = Feld[x][y];
12641       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12642
12643       if (IS_ANIMATED(graphic) &&
12644           !IS_MOVING(x, y) &&
12645           !Stop[x][y])
12646         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12647
12648       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12649         TEST_DrawTwinkleOnField(x, y);
12650     }
12651     else if (IS_MOVING(x, y))
12652       ContinueMoving(x, y);
12653     else
12654     {
12655       switch (element)
12656       {
12657         case EL_ACID:
12658         case EL_EXIT_OPEN:
12659         case EL_EM_EXIT_OPEN:
12660         case EL_SP_EXIT_OPEN:
12661         case EL_STEEL_EXIT_OPEN:
12662         case EL_EM_STEEL_EXIT_OPEN:
12663         case EL_SP_TERMINAL:
12664         case EL_SP_TERMINAL_ACTIVE:
12665         case EL_EXTRA_TIME:
12666         case EL_SHIELD_NORMAL:
12667         case EL_SHIELD_DEADLY:
12668           if (IS_ANIMATED(graphic))
12669             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12670           break;
12671
12672         case EL_DYNAMITE_ACTIVE:
12673         case EL_EM_DYNAMITE_ACTIVE:
12674         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12675         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12676         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12677         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12678         case EL_SP_DISK_RED_ACTIVE:
12679           CheckDynamite(x, y);
12680           break;
12681
12682         case EL_AMOEBA_GROWING:
12683           AmoebeWaechst(x, y);
12684           break;
12685
12686         case EL_AMOEBA_SHRINKING:
12687           AmoebaDisappearing(x, y);
12688           break;
12689
12690 #if !USE_NEW_AMOEBA_CODE
12691         case EL_AMOEBA_WET:
12692         case EL_AMOEBA_DRY:
12693         case EL_AMOEBA_FULL:
12694         case EL_BD_AMOEBA:
12695         case EL_EMC_DRIPPER:
12696           AmoebeAbleger(x, y);
12697           break;
12698 #endif
12699
12700         case EL_GAME_OF_LIFE:
12701         case EL_BIOMAZE:
12702           Life(x, y);
12703           break;
12704
12705         case EL_EXIT_CLOSED:
12706           CheckExit(x, y);
12707           break;
12708
12709         case EL_EM_EXIT_CLOSED:
12710           CheckExitEM(x, y);
12711           break;
12712
12713         case EL_STEEL_EXIT_CLOSED:
12714           CheckExitSteel(x, y);
12715           break;
12716
12717         case EL_EM_STEEL_EXIT_CLOSED:
12718           CheckExitSteelEM(x, y);
12719           break;
12720
12721         case EL_SP_EXIT_CLOSED:
12722           CheckExitSP(x, y);
12723           break;
12724
12725         case EL_EXPANDABLE_WALL_GROWING:
12726         case EL_EXPANDABLE_STEELWALL_GROWING:
12727           MauerWaechst(x, y);
12728           break;
12729
12730         case EL_EXPANDABLE_WALL:
12731         case EL_EXPANDABLE_WALL_HORIZONTAL:
12732         case EL_EXPANDABLE_WALL_VERTICAL:
12733         case EL_EXPANDABLE_WALL_ANY:
12734         case EL_BD_EXPANDABLE_WALL:
12735           MauerAbleger(x, y);
12736           break;
12737
12738         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12739         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12740         case EL_EXPANDABLE_STEELWALL_ANY:
12741           MauerAblegerStahl(x, y);
12742           break;
12743
12744         case EL_FLAMES:
12745           CheckForDragon(x, y);
12746           break;
12747
12748         case EL_EXPLOSION:
12749           break;
12750
12751         case EL_ELEMENT_SNAPPING:
12752         case EL_DIAGONAL_SHRINKING:
12753         case EL_DIAGONAL_GROWING:
12754         {
12755           graphic =
12756             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12757
12758           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12759           break;
12760         }
12761
12762         default:
12763           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12764             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12765           break;
12766       }
12767     }
12768
12769 #else   // ---------------------------------------------------------------------
12770
12771     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12772     {
12773       StartMoving(x, y);
12774
12775       element = Feld[x][y];
12776       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12777
12778       if (IS_ANIMATED(graphic) &&
12779           !IS_MOVING(x, y) &&
12780           !Stop[x][y])
12781         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12782
12783       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12784         TEST_DrawTwinkleOnField(x, y);
12785     }
12786     else if ((element == EL_ACID ||
12787               element == EL_EXIT_OPEN ||
12788               element == EL_EM_EXIT_OPEN ||
12789               element == EL_SP_EXIT_OPEN ||
12790               element == EL_STEEL_EXIT_OPEN ||
12791               element == EL_EM_STEEL_EXIT_OPEN ||
12792               element == EL_SP_TERMINAL ||
12793               element == EL_SP_TERMINAL_ACTIVE ||
12794               element == EL_EXTRA_TIME ||
12795               element == EL_SHIELD_NORMAL ||
12796               element == EL_SHIELD_DEADLY) &&
12797              IS_ANIMATED(graphic))
12798       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12799     else if (IS_MOVING(x, y))
12800       ContinueMoving(x, y);
12801     else if (IS_ACTIVE_BOMB(element))
12802       CheckDynamite(x, y);
12803     else if (element == EL_AMOEBA_GROWING)
12804       AmoebeWaechst(x, y);
12805     else if (element == EL_AMOEBA_SHRINKING)
12806       AmoebaDisappearing(x, y);
12807
12808 #if !USE_NEW_AMOEBA_CODE
12809     else if (IS_AMOEBALIVE(element))
12810       AmoebeAbleger(x, y);
12811 #endif
12812
12813     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12814       Life(x, y);
12815     else if (element == EL_EXIT_CLOSED)
12816       CheckExit(x, y);
12817     else if (element == EL_EM_EXIT_CLOSED)
12818       CheckExitEM(x, y);
12819     else if (element == EL_STEEL_EXIT_CLOSED)
12820       CheckExitSteel(x, y);
12821     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12822       CheckExitSteelEM(x, y);
12823     else if (element == EL_SP_EXIT_CLOSED)
12824       CheckExitSP(x, y);
12825     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12826              element == EL_EXPANDABLE_STEELWALL_GROWING)
12827       MauerWaechst(x, y);
12828     else if (element == EL_EXPANDABLE_WALL ||
12829              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12830              element == EL_EXPANDABLE_WALL_VERTICAL ||
12831              element == EL_EXPANDABLE_WALL_ANY ||
12832              element == EL_BD_EXPANDABLE_WALL)
12833       MauerAbleger(x, y);
12834     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12835              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12836              element == EL_EXPANDABLE_STEELWALL_ANY)
12837       MauerAblegerStahl(x, y);
12838     else if (element == EL_FLAMES)
12839       CheckForDragon(x, y);
12840     else if (element == EL_EXPLOSION)
12841       ; /* drawing of correct explosion animation is handled separately */
12842     else if (element == EL_ELEMENT_SNAPPING ||
12843              element == EL_DIAGONAL_SHRINKING ||
12844              element == EL_DIAGONAL_GROWING)
12845     {
12846       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12847
12848       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12849     }
12850     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12851       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12852
12853 #endif  // ---------------------------------------------------------------------
12854
12855     if (IS_BELT_ACTIVE(element))
12856       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12857
12858     if (game.magic_wall_active)
12859     {
12860       int jx = local_player->jx, jy = local_player->jy;
12861
12862       /* play the element sound at the position nearest to the player */
12863       if ((element == EL_MAGIC_WALL_FULL ||
12864            element == EL_MAGIC_WALL_ACTIVE ||
12865            element == EL_MAGIC_WALL_EMPTYING ||
12866            element == EL_BD_MAGIC_WALL_FULL ||
12867            element == EL_BD_MAGIC_WALL_ACTIVE ||
12868            element == EL_BD_MAGIC_WALL_EMPTYING ||
12869            element == EL_DC_MAGIC_WALL_FULL ||
12870            element == EL_DC_MAGIC_WALL_ACTIVE ||
12871            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12872           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12873       {
12874         magic_wall_x = x;
12875         magic_wall_y = y;
12876       }
12877     }
12878   }
12879
12880 #if 0
12881   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12882 #endif
12883
12884 #if USE_NEW_AMOEBA_CODE
12885   /* new experimental amoeba growth stuff */
12886   if (!(FrameCounter % 8))
12887   {
12888     static unsigned int random = 1684108901;
12889
12890     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12891     {
12892       x = RND(lev_fieldx);
12893       y = RND(lev_fieldy);
12894       element = Feld[x][y];
12895
12896       if (!IS_PLAYER(x,y) &&
12897           (element == EL_EMPTY ||
12898            CAN_GROW_INTO(element) ||
12899            element == EL_QUICKSAND_EMPTY ||
12900            element == EL_QUICKSAND_FAST_EMPTY ||
12901            element == EL_ACID_SPLASH_LEFT ||
12902            element == EL_ACID_SPLASH_RIGHT))
12903       {
12904         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12905             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12906             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12907             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12908           Feld[x][y] = EL_AMOEBA_DROP;
12909       }
12910
12911       random = random * 129 + 1;
12912     }
12913   }
12914 #endif
12915
12916 #if 0
12917   if (game.explosions_delayed)
12918 #endif
12919   {
12920     game.explosions_delayed = FALSE;
12921
12922     SCAN_PLAYFIELD(x, y)
12923     {
12924       element = Feld[x][y];
12925
12926       if (ExplodeField[x][y])
12927         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12928       else if (element == EL_EXPLOSION)
12929         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12930
12931       ExplodeField[x][y] = EX_TYPE_NONE;
12932     }
12933
12934     game.explosions_delayed = TRUE;
12935   }
12936
12937   if (game.magic_wall_active)
12938   {
12939     if (!(game.magic_wall_time_left % 4))
12940     {
12941       int element = Feld[magic_wall_x][magic_wall_y];
12942
12943       if (element == EL_BD_MAGIC_WALL_FULL ||
12944           element == EL_BD_MAGIC_WALL_ACTIVE ||
12945           element == EL_BD_MAGIC_WALL_EMPTYING)
12946         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12947       else if (element == EL_DC_MAGIC_WALL_FULL ||
12948                element == EL_DC_MAGIC_WALL_ACTIVE ||
12949                element == EL_DC_MAGIC_WALL_EMPTYING)
12950         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12951       else
12952         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12953     }
12954
12955     if (game.magic_wall_time_left > 0)
12956     {
12957       game.magic_wall_time_left--;
12958
12959       if (!game.magic_wall_time_left)
12960       {
12961         SCAN_PLAYFIELD(x, y)
12962         {
12963           element = Feld[x][y];
12964
12965           if (element == EL_MAGIC_WALL_ACTIVE ||
12966               element == EL_MAGIC_WALL_FULL)
12967           {
12968             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12969             TEST_DrawLevelField(x, y);
12970           }
12971           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12972                    element == EL_BD_MAGIC_WALL_FULL)
12973           {
12974             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12975             TEST_DrawLevelField(x, y);
12976           }
12977           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12978                    element == EL_DC_MAGIC_WALL_FULL)
12979           {
12980             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12981             TEST_DrawLevelField(x, y);
12982           }
12983         }
12984
12985         game.magic_wall_active = FALSE;
12986       }
12987     }
12988   }
12989
12990   if (game.light_time_left > 0)
12991   {
12992     game.light_time_left--;
12993
12994     if (game.light_time_left == 0)
12995       RedrawAllLightSwitchesAndInvisibleElements();
12996   }
12997
12998   if (game.timegate_time_left > 0)
12999   {
13000     game.timegate_time_left--;
13001
13002     if (game.timegate_time_left == 0)
13003       CloseAllOpenTimegates();
13004   }
13005
13006   if (game.lenses_time_left > 0)
13007   {
13008     game.lenses_time_left--;
13009
13010     if (game.lenses_time_left == 0)
13011       RedrawAllInvisibleElementsForLenses();
13012   }
13013
13014   if (game.magnify_time_left > 0)
13015   {
13016     game.magnify_time_left--;
13017
13018     if (game.magnify_time_left == 0)
13019       RedrawAllInvisibleElementsForMagnifier();
13020   }
13021
13022   for (i = 0; i < MAX_PLAYERS; i++)
13023   {
13024     struct PlayerInfo *player = &stored_player[i];
13025
13026     if (SHIELD_ON(player))
13027     {
13028       if (player->shield_deadly_time_left)
13029         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13030       else if (player->shield_normal_time_left)
13031         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13032     }
13033   }
13034
13035 #if USE_DELAYED_GFX_REDRAW
13036   SCAN_PLAYFIELD(x, y)
13037   {
13038 #if 1
13039     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13040 #else
13041     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13042         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13043 #endif
13044     {
13045       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13046          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13047
13048       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13049         DrawLevelField(x, y);
13050
13051       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13052         DrawLevelFieldCrumbled(x, y);
13053
13054       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13055         DrawLevelFieldCrumbledNeighbours(x, y);
13056
13057       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13058         DrawTwinkleOnField(x, y);
13059     }
13060
13061     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13062   }
13063 #endif
13064
13065   CheckLevelTime();
13066
13067   DrawAllPlayers();
13068   PlayAllPlayersSound();
13069
13070   if (options.debug)                    /* calculate frames per second */
13071   {
13072     static unsigned int fps_counter = 0;
13073     static int fps_frames = 0;
13074     unsigned int fps_delay_ms = Counter() - fps_counter;
13075
13076     fps_frames++;
13077
13078     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13079     {
13080       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13081
13082       fps_frames = 0;
13083       fps_counter = Counter();
13084     }
13085
13086     redraw_mask |= REDRAW_FPS;
13087   }
13088
13089   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13090
13091   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13092   {
13093     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13094
13095     local_player->show_envelope = 0;
13096   }
13097
13098 #if 0
13099   debug_print_timestamp(0, "stop main loop profiling ");
13100   printf("----------------------------------------------------------\n");
13101 #endif
13102
13103   /* use random number generator in every frame to make it less predictable */
13104   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13105     RND(1);
13106 }
13107
13108 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13109 {
13110   int min_x = x, min_y = y, max_x = x, max_y = y;
13111   int i;
13112
13113   for (i = 0; i < MAX_PLAYERS; i++)
13114   {
13115     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13116
13117     if (!stored_player[i].active || &stored_player[i] == player)
13118       continue;
13119
13120     min_x = MIN(min_x, jx);
13121     min_y = MIN(min_y, jy);
13122     max_x = MAX(max_x, jx);
13123     max_y = MAX(max_y, jy);
13124   }
13125
13126   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13127 }
13128
13129 static boolean AllPlayersInVisibleScreen()
13130 {
13131   int i;
13132
13133   for (i = 0; i < MAX_PLAYERS; i++)
13134   {
13135     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13136
13137     if (!stored_player[i].active)
13138       continue;
13139
13140     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13141       return FALSE;
13142   }
13143
13144   return TRUE;
13145 }
13146
13147 void ScrollLevel(int dx, int dy)
13148 {
13149 #if 0
13150   /* (directly solved in BlitBitmap() now) */
13151   static Bitmap *bitmap_db_field2 = NULL;
13152   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13153   int x, y;
13154 #else
13155   int x, y;
13156 #endif
13157
13158 #if 0
13159   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13160   /* only horizontal XOR vertical scroll direction allowed */
13161   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13162     return;
13163 #endif
13164
13165 #if 0
13166   /* (directly solved in BlitBitmap() now) */
13167   if (bitmap_db_field2 == NULL)
13168     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13169
13170   /* needed when blitting directly to same bitmap -- should not be needed with
13171      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13172   BlitBitmap(drawto_field, bitmap_db_field2,
13173              FX + TILEX * (dx == -1) - softscroll_offset,
13174              FY + TILEY * (dy == -1) - softscroll_offset,
13175              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13176              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13177              FX + TILEX * (dx == 1) - softscroll_offset,
13178              FY + TILEY * (dy == 1) - softscroll_offset);
13179   BlitBitmap(bitmap_db_field2, drawto_field,
13180              FX + TILEX * (dx == 1) - softscroll_offset,
13181              FY + TILEY * (dy == 1) - softscroll_offset,
13182              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13183              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13184              FX + TILEX * (dx == 1) - softscroll_offset,
13185              FY + TILEY * (dy == 1) - softscroll_offset);
13186
13187 #else
13188
13189 #if 0
13190   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13191   int xsize = (BX2 - BX1 + 1);
13192   int ysize = (BY2 - BY1 + 1);
13193   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13194   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13195   int step  = (start < end ? +1 : -1);
13196
13197   for (i = start; i != end; i += step)
13198   {
13199     BlitBitmap(drawto_field, drawto_field,
13200                FX + TILEX * (dx != 0 ? i + step : 0),
13201                FY + TILEY * (dy != 0 ? i + step : 0),
13202                TILEX * (dx != 0 ? 1 : xsize),
13203                TILEY * (dy != 0 ? 1 : ysize),
13204                FX + TILEX * (dx != 0 ? i : 0),
13205                FY + TILEY * (dy != 0 ? i : 0));
13206   }
13207
13208 #else
13209
13210 #if NEW_TILESIZE
13211 #if NEW_SCROLL
13212   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13213 #else
13214   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13215 #endif
13216 #else
13217 #if NEW_SCROLL
13218   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13219 #else
13220   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13221 #endif
13222 #endif
13223
13224 #if NEW_TILESIZE
13225   BlitBitmap(drawto_field, drawto_field,
13226              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13227              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13228              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13229              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13230              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13231              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13232 #else
13233   BlitBitmap(drawto_field, drawto_field,
13234              FX + TILEX * (dx == -1) - softscroll_offset,
13235              FY + TILEY * (dy == -1) - softscroll_offset,
13236              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13237              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13238              FX + TILEX * (dx == 1) - softscroll_offset,
13239              FY + TILEY * (dy == 1) - softscroll_offset);
13240 #endif
13241
13242 #endif
13243 #endif
13244
13245   if (dx != 0)
13246   {
13247     x = (dx == 1 ? BX1 : BX2);
13248     for (y = BY1; y <= BY2; y++)
13249       DrawScreenField(x, y);
13250   }
13251
13252   if (dy != 0)
13253   {
13254     y = (dy == 1 ? BY1 : BY2);
13255     for (x = BX1; x <= BX2; x++)
13256       DrawScreenField(x, y);
13257   }
13258
13259   redraw_mask |= REDRAW_FIELD;
13260 }
13261
13262 static boolean canFallDown(struct PlayerInfo *player)
13263 {
13264   int jx = player->jx, jy = player->jy;
13265
13266   return (IN_LEV_FIELD(jx, jy + 1) &&
13267           (IS_FREE(jx, jy + 1) ||
13268            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13269           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13270           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13271 }
13272
13273 static boolean canPassField(int x, int y, int move_dir)
13274 {
13275   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13276   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13277   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13278   int nextx = x + dx;
13279   int nexty = y + dy;
13280   int element = Feld[x][y];
13281
13282   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13283           !CAN_MOVE(element) &&
13284           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13285           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13286           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13287 }
13288
13289 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13290 {
13291   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13292   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13293   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13294   int newx = x + dx;
13295   int newy = y + dy;
13296
13297   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13298           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13299           (IS_DIGGABLE(Feld[newx][newy]) ||
13300            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13301            canPassField(newx, newy, move_dir)));
13302 }
13303
13304 static void CheckGravityMovement(struct PlayerInfo *player)
13305 {
13306 #if USE_PLAYER_GRAVITY
13307   if (player->gravity && !player->programmed_action)
13308 #else
13309   if (game.gravity && !player->programmed_action)
13310 #endif
13311   {
13312     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13313     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13314     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13315     int jx = player->jx, jy = player->jy;
13316     boolean player_is_moving_to_valid_field =
13317       (!player_is_snapping &&
13318        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13319         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13320     boolean player_can_fall_down = canFallDown(player);
13321
13322     if (player_can_fall_down &&
13323         !player_is_moving_to_valid_field)
13324       player->programmed_action = MV_DOWN;
13325   }
13326 }
13327
13328 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13329 {
13330   return CheckGravityMovement(player);
13331
13332 #if USE_PLAYER_GRAVITY
13333   if (player->gravity && !player->programmed_action)
13334 #else
13335   if (game.gravity && !player->programmed_action)
13336 #endif
13337   {
13338     int jx = player->jx, jy = player->jy;
13339     boolean field_under_player_is_free =
13340       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13341     boolean player_is_standing_on_valid_field =
13342       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13343        (IS_WALKABLE(Feld[jx][jy]) &&
13344         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13345
13346     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13347       player->programmed_action = MV_DOWN;
13348   }
13349 }
13350
13351 /*
13352   MovePlayerOneStep()
13353   -----------------------------------------------------------------------------
13354   dx, dy:               direction (non-diagonal) to try to move the player to
13355   real_dx, real_dy:     direction as read from input device (can be diagonal)
13356 */
13357
13358 boolean MovePlayerOneStep(struct PlayerInfo *player,
13359                           int dx, int dy, int real_dx, int real_dy)
13360 {
13361   int jx = player->jx, jy = player->jy;
13362   int new_jx = jx + dx, new_jy = jy + dy;
13363 #if !USE_FIXED_DONT_RUN_INTO
13364   int element;
13365 #endif
13366   int can_move;
13367   boolean player_can_move = !player->cannot_move;
13368
13369   if (!player->active || (!dx && !dy))
13370     return MP_NO_ACTION;
13371
13372   player->MovDir = (dx < 0 ? MV_LEFT :
13373                     dx > 0 ? MV_RIGHT :
13374                     dy < 0 ? MV_UP :
13375                     dy > 0 ? MV_DOWN :  MV_NONE);
13376
13377   if (!IN_LEV_FIELD(new_jx, new_jy))
13378     return MP_NO_ACTION;
13379
13380   if (!player_can_move)
13381   {
13382     if (player->MovPos == 0)
13383     {
13384       player->is_moving = FALSE;
13385       player->is_digging = FALSE;
13386       player->is_collecting = FALSE;
13387       player->is_snapping = FALSE;
13388       player->is_pushing = FALSE;
13389     }
13390   }
13391
13392 #if 1
13393   if (!options.network && game.centered_player_nr == -1 &&
13394       !AllPlayersInSight(player, new_jx, new_jy))
13395     return MP_NO_ACTION;
13396 #else
13397   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13398     return MP_NO_ACTION;
13399 #endif
13400
13401 #if !USE_FIXED_DONT_RUN_INTO
13402   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13403
13404   /* (moved to DigField()) */
13405   if (player_can_move && DONT_RUN_INTO(element))
13406   {
13407     if (element == EL_ACID && dx == 0 && dy == 1)
13408     {
13409       SplashAcid(new_jx, new_jy);
13410       Feld[jx][jy] = EL_PLAYER_1;
13411       InitMovingField(jx, jy, MV_DOWN);
13412       Store[jx][jy] = EL_ACID;
13413       ContinueMoving(jx, jy);
13414       BuryPlayer(player);
13415     }
13416     else
13417       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13418
13419     return MP_MOVING;
13420   }
13421 #endif
13422
13423   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13424   if (can_move != MP_MOVING)
13425     return can_move;
13426
13427   /* check if DigField() has caused relocation of the player */
13428   if (player->jx != jx || player->jy != jy)
13429     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13430
13431   StorePlayer[jx][jy] = 0;
13432   player->last_jx = jx;
13433   player->last_jy = jy;
13434   player->jx = new_jx;
13435   player->jy = new_jy;
13436   StorePlayer[new_jx][new_jy] = player->element_nr;
13437
13438   if (player->move_delay_value_next != -1)
13439   {
13440     player->move_delay_value = player->move_delay_value_next;
13441     player->move_delay_value_next = -1;
13442   }
13443
13444   player->MovPos =
13445     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13446
13447   player->step_counter++;
13448
13449   PlayerVisit[jx][jy] = FrameCounter;
13450
13451 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13452   player->is_moving = TRUE;
13453 #endif
13454
13455 #if 1
13456   /* should better be called in MovePlayer(), but this breaks some tapes */
13457   ScrollPlayer(player, SCROLL_INIT);
13458 #endif
13459
13460   return MP_MOVING;
13461 }
13462
13463 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13464 {
13465   int jx = player->jx, jy = player->jy;
13466   int old_jx = jx, old_jy = jy;
13467   int moved = MP_NO_ACTION;
13468
13469   if (!player->active)
13470     return FALSE;
13471
13472   if (!dx && !dy)
13473   {
13474     if (player->MovPos == 0)
13475     {
13476       player->is_moving = FALSE;
13477       player->is_digging = FALSE;
13478       player->is_collecting = FALSE;
13479       player->is_snapping = FALSE;
13480       player->is_pushing = FALSE;
13481     }
13482
13483     return FALSE;
13484   }
13485
13486   if (player->move_delay > 0)
13487     return FALSE;
13488
13489   player->move_delay = -1;              /* set to "uninitialized" value */
13490
13491   /* store if player is automatically moved to next field */
13492   player->is_auto_moving = (player->programmed_action != MV_NONE);
13493
13494   /* remove the last programmed player action */
13495   player->programmed_action = 0;
13496
13497   if (player->MovPos)
13498   {
13499     /* should only happen if pre-1.2 tape recordings are played */
13500     /* this is only for backward compatibility */
13501
13502     int original_move_delay_value = player->move_delay_value;
13503
13504 #if DEBUG
13505     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13506            tape.counter);
13507 #endif
13508
13509     /* scroll remaining steps with finest movement resolution */
13510     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13511
13512     while (player->MovPos)
13513     {
13514       ScrollPlayer(player, SCROLL_GO_ON);
13515       ScrollScreen(NULL, SCROLL_GO_ON);
13516
13517       AdvanceFrameAndPlayerCounters(player->index_nr);
13518
13519       DrawAllPlayers();
13520       BackToFront();
13521     }
13522
13523     player->move_delay_value = original_move_delay_value;
13524   }
13525
13526   player->is_active = FALSE;
13527
13528   if (player->last_move_dir & MV_HORIZONTAL)
13529   {
13530     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13531       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13532   }
13533   else
13534   {
13535     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13536       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13537   }
13538
13539 #if USE_FIXED_BORDER_RUNNING_GFX
13540   if (!moved && !player->is_active)
13541   {
13542     player->is_moving = FALSE;
13543     player->is_digging = FALSE;
13544     player->is_collecting = FALSE;
13545     player->is_snapping = FALSE;
13546     player->is_pushing = FALSE;
13547   }
13548 #endif
13549
13550   jx = player->jx;
13551   jy = player->jy;
13552
13553 #if 1
13554   if (moved & MP_MOVING && !ScreenMovPos &&
13555       (player->index_nr == game.centered_player_nr ||
13556        game.centered_player_nr == -1))
13557 #else
13558   if (moved & MP_MOVING && !ScreenMovPos &&
13559       (player == local_player || !options.network))
13560 #endif
13561   {
13562     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13563     int offset = game.scroll_delay_value;
13564
13565     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13566     {
13567       /* actual player has left the screen -- scroll in that direction */
13568       if (jx != old_jx)         /* player has moved horizontally */
13569         scroll_x += (jx - old_jx);
13570       else                      /* player has moved vertically */
13571         scroll_y += (jy - old_jy);
13572     }
13573     else
13574     {
13575       if (jx != old_jx)         /* player has moved horizontally */
13576       {
13577         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13578             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13579           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13580
13581         /* don't scroll over playfield boundaries */
13582         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13583           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13584
13585         /* don't scroll more than one field at a time */
13586         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13587
13588         /* don't scroll against the player's moving direction */
13589         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13590             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13591           scroll_x = old_scroll_x;
13592       }
13593       else                      /* player has moved vertically */
13594       {
13595         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13596             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13597           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13598
13599         /* don't scroll over playfield boundaries */
13600         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13601           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13602
13603         /* don't scroll more than one field at a time */
13604         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13605
13606         /* don't scroll against the player's moving direction */
13607         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13608             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13609           scroll_y = old_scroll_y;
13610       }
13611     }
13612
13613     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13614     {
13615 #if 1
13616       if (!options.network && game.centered_player_nr == -1 &&
13617           !AllPlayersInVisibleScreen())
13618       {
13619         scroll_x = old_scroll_x;
13620         scroll_y = old_scroll_y;
13621       }
13622       else
13623 #else
13624       if (!options.network && !AllPlayersInVisibleScreen())
13625       {
13626         scroll_x = old_scroll_x;
13627         scroll_y = old_scroll_y;
13628       }
13629       else
13630 #endif
13631       {
13632         ScrollScreen(player, SCROLL_INIT);
13633         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13634       }
13635     }
13636   }
13637
13638   player->StepFrame = 0;
13639
13640   if (moved & MP_MOVING)
13641   {
13642     if (old_jx != jx && old_jy == jy)
13643       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13644     else if (old_jx == jx && old_jy != jy)
13645       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13646
13647     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13648
13649     player->last_move_dir = player->MovDir;
13650     player->is_moving = TRUE;
13651     player->is_snapping = FALSE;
13652     player->is_switching = FALSE;
13653     player->is_dropping = FALSE;
13654     player->is_dropping_pressed = FALSE;
13655     player->drop_pressed_delay = 0;
13656
13657 #if 0
13658     /* should better be called here than above, but this breaks some tapes */
13659     ScrollPlayer(player, SCROLL_INIT);
13660 #endif
13661   }
13662   else
13663   {
13664     CheckGravityMovementWhenNotMoving(player);
13665
13666     player->is_moving = FALSE;
13667
13668     /* at this point, the player is allowed to move, but cannot move right now
13669        (e.g. because of something blocking the way) -- ensure that the player
13670        is also allowed to move in the next frame (in old versions before 3.1.1,
13671        the player was forced to wait again for eight frames before next try) */
13672
13673     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13674       player->move_delay = 0;   /* allow direct movement in the next frame */
13675   }
13676
13677   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13678     player->move_delay = player->move_delay_value;
13679
13680   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13681   {
13682     TestIfPlayerTouchesBadThing(jx, jy);
13683     TestIfPlayerTouchesCustomElement(jx, jy);
13684   }
13685
13686   if (!player->active)
13687     RemovePlayer(player);
13688
13689   return moved;
13690 }
13691
13692 void ScrollPlayer(struct PlayerInfo *player, int mode)
13693 {
13694   int jx = player->jx, jy = player->jy;
13695   int last_jx = player->last_jx, last_jy = player->last_jy;
13696   int move_stepsize = TILEX / player->move_delay_value;
13697
13698 #if USE_NEW_PLAYER_SPEED
13699   if (!player->active)
13700     return;
13701
13702   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13703     return;
13704 #else
13705   if (!player->active || player->MovPos == 0)
13706     return;
13707 #endif
13708
13709   if (mode == SCROLL_INIT)
13710   {
13711     player->actual_frame_counter = FrameCounter;
13712     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13713
13714     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13715         Feld[last_jx][last_jy] == EL_EMPTY)
13716     {
13717       int last_field_block_delay = 0;   /* start with no blocking at all */
13718       int block_delay_adjustment = player->block_delay_adjustment;
13719
13720       /* if player blocks last field, add delay for exactly one move */
13721       if (player->block_last_field)
13722       {
13723         last_field_block_delay += player->move_delay_value;
13724
13725         /* when blocking enabled, prevent moving up despite gravity */
13726 #if USE_PLAYER_GRAVITY
13727         if (player->gravity && player->MovDir == MV_UP)
13728           block_delay_adjustment = -1;
13729 #else
13730         if (game.gravity && player->MovDir == MV_UP)
13731           block_delay_adjustment = -1;
13732 #endif
13733       }
13734
13735       /* add block delay adjustment (also possible when not blocking) */
13736       last_field_block_delay += block_delay_adjustment;
13737
13738       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13739       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13740     }
13741
13742 #if USE_NEW_PLAYER_SPEED
13743     if (player->MovPos != 0)    /* player has not yet reached destination */
13744       return;
13745 #else
13746     return;
13747 #endif
13748   }
13749   else if (!FrameReached(&player->actual_frame_counter, 1))
13750     return;
13751
13752 #if USE_NEW_PLAYER_SPEED
13753   if (player->MovPos != 0)
13754   {
13755     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13756     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13757
13758     /* before DrawPlayer() to draw correct player graphic for this case */
13759     if (player->MovPos == 0)
13760       CheckGravityMovement(player);
13761   }
13762 #else
13763   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13764   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13765
13766   /* before DrawPlayer() to draw correct player graphic for this case */
13767   if (player->MovPos == 0)
13768     CheckGravityMovement(player);
13769 #endif
13770
13771   if (player->MovPos == 0)      /* player reached destination field */
13772   {
13773     if (player->move_delay_reset_counter > 0)
13774     {
13775       player->move_delay_reset_counter--;
13776
13777       if (player->move_delay_reset_counter == 0)
13778       {
13779         /* continue with normal speed after quickly moving through gate */
13780         HALVE_PLAYER_SPEED(player);
13781
13782         /* be able to make the next move without delay */
13783         player->move_delay = 0;
13784       }
13785     }
13786
13787     player->last_jx = jx;
13788     player->last_jy = jy;
13789
13790     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13791         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13792 #if 1
13793         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13794 #endif
13795         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13796         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13797 #if 1
13798         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13799 #endif
13800         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13801         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13802     {
13803       DrawPlayer(player);       /* needed here only to cleanup last field */
13804       RemovePlayer(player);
13805
13806       if (local_player->friends_still_needed == 0 ||
13807           IS_SP_ELEMENT(Feld[jx][jy]))
13808         PlayerWins(player);
13809     }
13810
13811     /* this breaks one level: "machine", level 000 */
13812     {
13813       int move_direction = player->MovDir;
13814       int enter_side = MV_DIR_OPPOSITE(move_direction);
13815       int leave_side = move_direction;
13816       int old_jx = last_jx;
13817       int old_jy = last_jy;
13818       int old_element = Feld[old_jx][old_jy];
13819       int new_element = Feld[jx][jy];
13820
13821       if (IS_CUSTOM_ELEMENT(old_element))
13822         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13823                                    CE_LEFT_BY_PLAYER,
13824                                    player->index_bit, leave_side);
13825
13826       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13827                                           CE_PLAYER_LEAVES_X,
13828                                           player->index_bit, leave_side);
13829
13830       if (IS_CUSTOM_ELEMENT(new_element))
13831         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13832                                    player->index_bit, enter_side);
13833
13834       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13835                                           CE_PLAYER_ENTERS_X,
13836                                           player->index_bit, enter_side);
13837
13838 #if USE_FIX_CE_ACTION_WITH_PLAYER
13839       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13840                                         CE_MOVE_OF_X, move_direction);
13841 #else
13842       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13843                                         CE_MOVE_OF_X, move_direction);
13844 #endif
13845     }
13846
13847     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13848     {
13849       TestIfPlayerTouchesBadThing(jx, jy);
13850       TestIfPlayerTouchesCustomElement(jx, jy);
13851
13852       /* needed because pushed element has not yet reached its destination,
13853          so it would trigger a change event at its previous field location */
13854       if (!player->is_pushing)
13855         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13856
13857       if (!player->active)
13858         RemovePlayer(player);
13859     }
13860
13861     if (!local_player->LevelSolved && level.use_step_counter)
13862     {
13863       int i;
13864
13865       TimePlayed++;
13866
13867       if (TimeLeft > 0)
13868       {
13869         TimeLeft--;
13870
13871         if (TimeLeft <= 10 && setup.time_limit)
13872           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13873
13874 #if 1
13875         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13876
13877         DisplayGameControlValues();
13878 #else
13879         DrawGameValue_Time(TimeLeft);
13880 #endif
13881
13882         if (!TimeLeft && setup.time_limit)
13883           for (i = 0; i < MAX_PLAYERS; i++)
13884             KillPlayer(&stored_player[i]);
13885       }
13886 #if 1
13887       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
13888       {
13889         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13890
13891         DisplayGameControlValues();
13892       }
13893 #else
13894       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
13895         DrawGameValue_Time(TimePlayed);
13896 #endif
13897     }
13898
13899     if (tape.single_step && tape.recording && !tape.pausing &&
13900         !player->programmed_action)
13901       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13902   }
13903 }
13904
13905 void ScrollScreen(struct PlayerInfo *player, int mode)
13906 {
13907   static unsigned int screen_frame_counter = 0;
13908
13909   if (mode == SCROLL_INIT)
13910   {
13911     /* set scrolling step size according to actual player's moving speed */
13912     ScrollStepSize = TILEX / player->move_delay_value;
13913
13914     screen_frame_counter = FrameCounter;
13915     ScreenMovDir = player->MovDir;
13916     ScreenMovPos = player->MovPos;
13917     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13918     return;
13919   }
13920   else if (!FrameReached(&screen_frame_counter, 1))
13921     return;
13922
13923   if (ScreenMovPos)
13924   {
13925     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13926     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13927     redraw_mask |= REDRAW_FIELD;
13928   }
13929   else
13930     ScreenMovDir = MV_NONE;
13931 }
13932
13933 void TestIfPlayerTouchesCustomElement(int x, int y)
13934 {
13935   static int xy[4][2] =
13936   {
13937     { 0, -1 },
13938     { -1, 0 },
13939     { +1, 0 },
13940     { 0, +1 }
13941   };
13942   static int trigger_sides[4][2] =
13943   {
13944     /* center side       border side */
13945     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13946     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13947     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13948     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13949   };
13950   static int touch_dir[4] =
13951   {
13952     MV_LEFT | MV_RIGHT,
13953     MV_UP   | MV_DOWN,
13954     MV_UP   | MV_DOWN,
13955     MV_LEFT | MV_RIGHT
13956   };
13957   int center_element = Feld[x][y];      /* should always be non-moving! */
13958   int i;
13959
13960   for (i = 0; i < NUM_DIRECTIONS; i++)
13961   {
13962     int xx = x + xy[i][0];
13963     int yy = y + xy[i][1];
13964     int center_side = trigger_sides[i][0];
13965     int border_side = trigger_sides[i][1];
13966     int border_element;
13967
13968     if (!IN_LEV_FIELD(xx, yy))
13969       continue;
13970
13971     if (IS_PLAYER(x, y))                /* player found at center element */
13972     {
13973       struct PlayerInfo *player = PLAYERINFO(x, y);
13974
13975       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13976         border_element = Feld[xx][yy];          /* may be moving! */
13977       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13978         border_element = Feld[xx][yy];
13979       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13980         border_element = MovingOrBlocked2Element(xx, yy);
13981       else
13982         continue;               /* center and border element do not touch */
13983
13984       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13985                                  player->index_bit, border_side);
13986       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13987                                           CE_PLAYER_TOUCHES_X,
13988                                           player->index_bit, border_side);
13989
13990 #if USE_FIX_CE_ACTION_WITH_PLAYER
13991       {
13992         /* use player element that is initially defined in the level playfield,
13993            not the player element that corresponds to the runtime player number
13994            (example: a level that contains EL_PLAYER_3 as the only player would
13995            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13996         int player_element = PLAYERINFO(x, y)->initial_element;
13997
13998         CheckElementChangeBySide(xx, yy, border_element, player_element,
13999                                  CE_TOUCHING_X, border_side);
14000       }
14001 #endif
14002     }
14003     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14004     {
14005       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14006
14007       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14008       {
14009         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14010           continue;             /* center and border element do not touch */
14011       }
14012
14013       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14014                                  player->index_bit, center_side);
14015       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14016                                           CE_PLAYER_TOUCHES_X,
14017                                           player->index_bit, center_side);
14018
14019 #if USE_FIX_CE_ACTION_WITH_PLAYER
14020       {
14021         /* use player element that is initially defined in the level playfield,
14022            not the player element that corresponds to the runtime player number
14023            (example: a level that contains EL_PLAYER_3 as the only player would
14024            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14025         int player_element = PLAYERINFO(xx, yy)->initial_element;
14026
14027         CheckElementChangeBySide(x, y, center_element, player_element,
14028                                  CE_TOUCHING_X, center_side);
14029       }
14030 #endif
14031
14032       break;
14033     }
14034   }
14035 }
14036
14037 #if USE_ELEMENT_TOUCHING_BUGFIX
14038
14039 void TestIfElementTouchesCustomElement(int x, int y)
14040 {
14041   static int xy[4][2] =
14042   {
14043     { 0, -1 },
14044     { -1, 0 },
14045     { +1, 0 },
14046     { 0, +1 }
14047   };
14048   static int trigger_sides[4][2] =
14049   {
14050     /* center side      border side */
14051     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14052     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14053     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14054     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14055   };
14056   static int touch_dir[4] =
14057   {
14058     MV_LEFT | MV_RIGHT,
14059     MV_UP   | MV_DOWN,
14060     MV_UP   | MV_DOWN,
14061     MV_LEFT | MV_RIGHT
14062   };
14063   boolean change_center_element = FALSE;
14064   int center_element = Feld[x][y];      /* should always be non-moving! */
14065   int border_element_old[NUM_DIRECTIONS];
14066   int i;
14067
14068   for (i = 0; i < NUM_DIRECTIONS; i++)
14069   {
14070     int xx = x + xy[i][0];
14071     int yy = y + xy[i][1];
14072     int border_element;
14073
14074     border_element_old[i] = -1;
14075
14076     if (!IN_LEV_FIELD(xx, yy))
14077       continue;
14078
14079     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14080       border_element = Feld[xx][yy];    /* may be moving! */
14081     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14082       border_element = Feld[xx][yy];
14083     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14084       border_element = MovingOrBlocked2Element(xx, yy);
14085     else
14086       continue;                 /* center and border element do not touch */
14087
14088     border_element_old[i] = border_element;
14089   }
14090
14091   for (i = 0; i < NUM_DIRECTIONS; i++)
14092   {
14093     int xx = x + xy[i][0];
14094     int yy = y + xy[i][1];
14095     int center_side = trigger_sides[i][0];
14096     int border_element = border_element_old[i];
14097
14098     if (border_element == -1)
14099       continue;
14100
14101     /* check for change of border element */
14102     CheckElementChangeBySide(xx, yy, border_element, center_element,
14103                              CE_TOUCHING_X, center_side);
14104
14105     /* (center element cannot be player, so we dont have to check this here) */
14106   }
14107
14108   for (i = 0; i < NUM_DIRECTIONS; i++)
14109   {
14110     int xx = x + xy[i][0];
14111     int yy = y + xy[i][1];
14112     int border_side = trigger_sides[i][1];
14113     int border_element = border_element_old[i];
14114
14115     if (border_element == -1)
14116       continue;
14117
14118     /* check for change of center element (but change it only once) */
14119     if (!change_center_element)
14120       change_center_element =
14121         CheckElementChangeBySide(x, y, center_element, border_element,
14122                                  CE_TOUCHING_X, border_side);
14123
14124 #if USE_FIX_CE_ACTION_WITH_PLAYER
14125     if (IS_PLAYER(xx, yy))
14126     {
14127       /* use player element that is initially defined in the level playfield,
14128          not the player element that corresponds to the runtime player number
14129          (example: a level that contains EL_PLAYER_3 as the only player would
14130          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14131       int player_element = PLAYERINFO(xx, yy)->initial_element;
14132
14133       CheckElementChangeBySide(x, y, center_element, player_element,
14134                                CE_TOUCHING_X, border_side);
14135     }
14136 #endif
14137   }
14138 }
14139
14140 #else
14141
14142 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14143 {
14144   static int xy[4][2] =
14145   {
14146     { 0, -1 },
14147     { -1, 0 },
14148     { +1, 0 },
14149     { 0, +1 }
14150   };
14151   static int trigger_sides[4][2] =
14152   {
14153     /* center side      border side */
14154     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14155     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14156     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14157     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14158   };
14159   static int touch_dir[4] =
14160   {
14161     MV_LEFT | MV_RIGHT,
14162     MV_UP   | MV_DOWN,
14163     MV_UP   | MV_DOWN,
14164     MV_LEFT | MV_RIGHT
14165   };
14166   boolean change_center_element = FALSE;
14167   int center_element = Feld[x][y];      /* should always be non-moving! */
14168   int i;
14169
14170   for (i = 0; i < NUM_DIRECTIONS; i++)
14171   {
14172     int xx = x + xy[i][0];
14173     int yy = y + xy[i][1];
14174     int center_side = trigger_sides[i][0];
14175     int border_side = trigger_sides[i][1];
14176     int border_element;
14177
14178     if (!IN_LEV_FIELD(xx, yy))
14179       continue;
14180
14181     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14182       border_element = Feld[xx][yy];    /* may be moving! */
14183     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14184       border_element = Feld[xx][yy];
14185     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14186       border_element = MovingOrBlocked2Element(xx, yy);
14187     else
14188       continue;                 /* center and border element do not touch */
14189
14190     /* check for change of center element (but change it only once) */
14191     if (!change_center_element)
14192       change_center_element =
14193         CheckElementChangeBySide(x, y, center_element, border_element,
14194                                  CE_TOUCHING_X, border_side);
14195
14196     /* check for change of border element */
14197     CheckElementChangeBySide(xx, yy, border_element, center_element,
14198                              CE_TOUCHING_X, center_side);
14199   }
14200 }
14201
14202 #endif
14203
14204 void TestIfElementHitsCustomElement(int x, int y, int direction)
14205 {
14206   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14207   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14208   int hitx = x + dx, hity = y + dy;
14209   int hitting_element = Feld[x][y];
14210   int touched_element;
14211
14212   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14213     return;
14214
14215   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14216                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14217
14218   if (IN_LEV_FIELD(hitx, hity))
14219   {
14220     int opposite_direction = MV_DIR_OPPOSITE(direction);
14221     int hitting_side = direction;
14222     int touched_side = opposite_direction;
14223     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14224                           MovDir[hitx][hity] != direction ||
14225                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14226
14227     object_hit = TRUE;
14228
14229     if (object_hit)
14230     {
14231       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14232                                CE_HITTING_X, touched_side);
14233
14234       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14235                                CE_HIT_BY_X, hitting_side);
14236
14237       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14238                                CE_HIT_BY_SOMETHING, opposite_direction);
14239
14240 #if USE_FIX_CE_ACTION_WITH_PLAYER
14241       if (IS_PLAYER(hitx, hity))
14242       {
14243         /* use player element that is initially defined in the level playfield,
14244            not the player element that corresponds to the runtime player number
14245            (example: a level that contains EL_PLAYER_3 as the only player would
14246            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14247         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14248
14249         CheckElementChangeBySide(x, y, hitting_element, player_element,
14250                                  CE_HITTING_X, touched_side);
14251       }
14252 #endif
14253     }
14254   }
14255
14256   /* "hitting something" is also true when hitting the playfield border */
14257   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14258                            CE_HITTING_SOMETHING, direction);
14259 }
14260
14261 #if 0
14262 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14263 {
14264   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14265   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14266   int hitx = x + dx, hity = y + dy;
14267   int hitting_element = Feld[x][y];
14268   int touched_element;
14269 #if 0
14270   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14271                         !IS_FREE(hitx, hity) &&
14272                         (!IS_MOVING(hitx, hity) ||
14273                          MovDir[hitx][hity] != direction ||
14274                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14275 #endif
14276
14277   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14278     return;
14279
14280 #if 0
14281   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14282     return;
14283 #endif
14284
14285   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14286                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14287
14288   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14289                            EP_CAN_SMASH_EVERYTHING, direction);
14290
14291   if (IN_LEV_FIELD(hitx, hity))
14292   {
14293     int opposite_direction = MV_DIR_OPPOSITE(direction);
14294     int hitting_side = direction;
14295     int touched_side = opposite_direction;
14296 #if 0
14297     int touched_element = MovingOrBlocked2Element(hitx, hity);
14298 #endif
14299 #if 1
14300     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14301                           MovDir[hitx][hity] != direction ||
14302                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14303
14304     object_hit = TRUE;
14305 #endif
14306
14307     if (object_hit)
14308     {
14309       int i;
14310
14311       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14312                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14313
14314       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14315                                CE_OTHER_IS_SMASHING, touched_side);
14316
14317       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14318                                CE_OTHER_GETS_SMASHED, hitting_side);
14319     }
14320   }
14321 }
14322 #endif
14323
14324 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14325 {
14326   int i, kill_x = -1, kill_y = -1;
14327
14328   int bad_element = -1;
14329   static int test_xy[4][2] =
14330   {
14331     { 0, -1 },
14332     { -1, 0 },
14333     { +1, 0 },
14334     { 0, +1 }
14335   };
14336   static int test_dir[4] =
14337   {
14338     MV_UP,
14339     MV_LEFT,
14340     MV_RIGHT,
14341     MV_DOWN
14342   };
14343
14344   for (i = 0; i < NUM_DIRECTIONS; i++)
14345   {
14346     int test_x, test_y, test_move_dir, test_element;
14347
14348     test_x = good_x + test_xy[i][0];
14349     test_y = good_y + test_xy[i][1];
14350
14351     if (!IN_LEV_FIELD(test_x, test_y))
14352       continue;
14353
14354     test_move_dir =
14355       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14356
14357     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14358
14359     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14360        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14361     */
14362     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14363         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14364     {
14365       kill_x = test_x;
14366       kill_y = test_y;
14367       bad_element = test_element;
14368
14369       break;
14370     }
14371   }
14372
14373   if (kill_x != -1 || kill_y != -1)
14374   {
14375     if (IS_PLAYER(good_x, good_y))
14376     {
14377       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14378
14379       if (player->shield_deadly_time_left > 0 &&
14380           !IS_INDESTRUCTIBLE(bad_element))
14381         Bang(kill_x, kill_y);
14382       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14383         KillPlayer(player);
14384     }
14385     else
14386       Bang(good_x, good_y);
14387   }
14388 }
14389
14390 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14391 {
14392   int i, kill_x = -1, kill_y = -1;
14393   int bad_element = Feld[bad_x][bad_y];
14394   static int test_xy[4][2] =
14395   {
14396     { 0, -1 },
14397     { -1, 0 },
14398     { +1, 0 },
14399     { 0, +1 }
14400   };
14401   static int touch_dir[4] =
14402   {
14403     MV_LEFT | MV_RIGHT,
14404     MV_UP   | MV_DOWN,
14405     MV_UP   | MV_DOWN,
14406     MV_LEFT | MV_RIGHT
14407   };
14408   static int test_dir[4] =
14409   {
14410     MV_UP,
14411     MV_LEFT,
14412     MV_RIGHT,
14413     MV_DOWN
14414   };
14415
14416   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14417     return;
14418
14419   for (i = 0; i < NUM_DIRECTIONS; i++)
14420   {
14421     int test_x, test_y, test_move_dir, test_element;
14422
14423     test_x = bad_x + test_xy[i][0];
14424     test_y = bad_y + test_xy[i][1];
14425
14426     if (!IN_LEV_FIELD(test_x, test_y))
14427       continue;
14428
14429     test_move_dir =
14430       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14431
14432     test_element = Feld[test_x][test_y];
14433
14434     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14435        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14436     */
14437     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14438         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14439     {
14440       /* good thing is player or penguin that does not move away */
14441       if (IS_PLAYER(test_x, test_y))
14442       {
14443         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14444
14445         if (bad_element == EL_ROBOT && player->is_moving)
14446           continue;     /* robot does not kill player if he is moving */
14447
14448         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14449         {
14450           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14451             continue;           /* center and border element do not touch */
14452         }
14453
14454         kill_x = test_x;
14455         kill_y = test_y;
14456
14457         break;
14458       }
14459       else if (test_element == EL_PENGUIN)
14460       {
14461         kill_x = test_x;
14462         kill_y = test_y;
14463
14464         break;
14465       }
14466     }
14467   }
14468
14469   if (kill_x != -1 || kill_y != -1)
14470   {
14471     if (IS_PLAYER(kill_x, kill_y))
14472     {
14473       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14474
14475       if (player->shield_deadly_time_left > 0 &&
14476           !IS_INDESTRUCTIBLE(bad_element))
14477         Bang(bad_x, bad_y);
14478       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14479         KillPlayer(player);
14480     }
14481     else
14482       Bang(kill_x, kill_y);
14483   }
14484 }
14485
14486 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14487 {
14488   int bad_element = Feld[bad_x][bad_y];
14489   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14490   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14491   int test_x = bad_x + dx, test_y = bad_y + dy;
14492   int test_move_dir, test_element;
14493   int kill_x = -1, kill_y = -1;
14494
14495   if (!IN_LEV_FIELD(test_x, test_y))
14496     return;
14497
14498   test_move_dir =
14499     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14500
14501   test_element = Feld[test_x][test_y];
14502
14503   if (test_move_dir != bad_move_dir)
14504   {
14505     /* good thing can be player or penguin that does not move away */
14506     if (IS_PLAYER(test_x, test_y))
14507     {
14508       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14509
14510       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14511          player as being hit when he is moving towards the bad thing, because
14512          the "get hit by" condition would be lost after the player stops) */
14513       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14514         return;         /* player moves away from bad thing */
14515
14516       kill_x = test_x;
14517       kill_y = test_y;
14518     }
14519     else if (test_element == EL_PENGUIN)
14520     {
14521       kill_x = test_x;
14522       kill_y = test_y;
14523     }
14524   }
14525
14526   if (kill_x != -1 || kill_y != -1)
14527   {
14528     if (IS_PLAYER(kill_x, kill_y))
14529     {
14530       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14531
14532       if (player->shield_deadly_time_left > 0 &&
14533           !IS_INDESTRUCTIBLE(bad_element))
14534         Bang(bad_x, bad_y);
14535       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14536         KillPlayer(player);
14537     }
14538     else
14539       Bang(kill_x, kill_y);
14540   }
14541 }
14542
14543 void TestIfPlayerTouchesBadThing(int x, int y)
14544 {
14545   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14546 }
14547
14548 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14549 {
14550   TestIfGoodThingHitsBadThing(x, y, move_dir);
14551 }
14552
14553 void TestIfBadThingTouchesPlayer(int x, int y)
14554 {
14555   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14556 }
14557
14558 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14559 {
14560   TestIfBadThingHitsGoodThing(x, y, move_dir);
14561 }
14562
14563 void TestIfFriendTouchesBadThing(int x, int y)
14564 {
14565   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14566 }
14567
14568 void TestIfBadThingTouchesFriend(int x, int y)
14569 {
14570   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14571 }
14572
14573 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14574 {
14575   int i, kill_x = bad_x, kill_y = bad_y;
14576   static int xy[4][2] =
14577   {
14578     { 0, -1 },
14579     { -1, 0 },
14580     { +1, 0 },
14581     { 0, +1 }
14582   };
14583
14584   for (i = 0; i < NUM_DIRECTIONS; i++)
14585   {
14586     int x, y, element;
14587
14588     x = bad_x + xy[i][0];
14589     y = bad_y + xy[i][1];
14590     if (!IN_LEV_FIELD(x, y))
14591       continue;
14592
14593     element = Feld[x][y];
14594     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14595         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14596     {
14597       kill_x = x;
14598       kill_y = y;
14599       break;
14600     }
14601   }
14602
14603   if (kill_x != bad_x || kill_y != bad_y)
14604     Bang(bad_x, bad_y);
14605 }
14606
14607 void KillPlayer(struct PlayerInfo *player)
14608 {
14609   int jx = player->jx, jy = player->jy;
14610
14611   if (!player->active)
14612     return;
14613
14614 #if 0
14615   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14616          player->killed, player->active, player->reanimated);
14617 #endif
14618
14619   /* the following code was introduced to prevent an infinite loop when calling
14620      -> Bang()
14621      -> CheckTriggeredElementChangeExt()
14622      -> ExecuteCustomElementAction()
14623      -> KillPlayer()
14624      -> (infinitely repeating the above sequence of function calls)
14625      which occurs when killing the player while having a CE with the setting
14626      "kill player X when explosion of <player X>"; the solution using a new
14627      field "player->killed" was chosen for backwards compatibility, although
14628      clever use of the fields "player->active" etc. would probably also work */
14629 #if 1
14630   if (player->killed)
14631     return;
14632 #endif
14633
14634   player->killed = TRUE;
14635
14636   /* remove accessible field at the player's position */
14637   Feld[jx][jy] = EL_EMPTY;
14638
14639   /* deactivate shield (else Bang()/Explode() would not work right) */
14640   player->shield_normal_time_left = 0;
14641   player->shield_deadly_time_left = 0;
14642
14643 #if 0
14644   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14645          player->killed, player->active, player->reanimated);
14646 #endif
14647
14648   Bang(jx, jy);
14649
14650 #if 0
14651   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14652          player->killed, player->active, player->reanimated);
14653 #endif
14654
14655 #if USE_PLAYER_REANIMATION
14656 #if 1
14657   if (player->reanimated)       /* killed player may have been reanimated */
14658     player->killed = player->reanimated = FALSE;
14659   else
14660     BuryPlayer(player);
14661 #else
14662   if (player->killed)           /* player may have been reanimated */
14663     BuryPlayer(player);
14664 #endif
14665 #else
14666   BuryPlayer(player);
14667 #endif
14668 }
14669
14670 static void KillPlayerUnlessEnemyProtected(int x, int y)
14671 {
14672   if (!PLAYER_ENEMY_PROTECTED(x, y))
14673     KillPlayer(PLAYERINFO(x, y));
14674 }
14675
14676 static void KillPlayerUnlessExplosionProtected(int x, int y)
14677 {
14678   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14679     KillPlayer(PLAYERINFO(x, y));
14680 }
14681
14682 void BuryPlayer(struct PlayerInfo *player)
14683 {
14684   int jx = player->jx, jy = player->jy;
14685
14686   if (!player->active)
14687     return;
14688
14689   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14690   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14691
14692   player->GameOver = TRUE;
14693   RemovePlayer(player);
14694 }
14695
14696 void RemovePlayer(struct PlayerInfo *player)
14697 {
14698   int jx = player->jx, jy = player->jy;
14699   int i, found = FALSE;
14700
14701   player->present = FALSE;
14702   player->active = FALSE;
14703
14704   if (!ExplodeField[jx][jy])
14705     StorePlayer[jx][jy] = 0;
14706
14707   if (player->is_moving)
14708     TEST_DrawLevelField(player->last_jx, player->last_jy);
14709
14710   for (i = 0; i < MAX_PLAYERS; i++)
14711     if (stored_player[i].active)
14712       found = TRUE;
14713
14714   if (!found)
14715     AllPlayersGone = TRUE;
14716
14717   ExitX = ZX = jx;
14718   ExitY = ZY = jy;
14719 }
14720
14721 #if USE_NEW_SNAP_DELAY
14722 static void setFieldForSnapping(int x, int y, int element, int direction)
14723 {
14724   struct ElementInfo *ei = &element_info[element];
14725   int direction_bit = MV_DIR_TO_BIT(direction);
14726   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14727   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14728                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14729
14730   Feld[x][y] = EL_ELEMENT_SNAPPING;
14731   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14732
14733   ResetGfxAnimation(x, y);
14734
14735   GfxElement[x][y] = element;
14736   GfxAction[x][y] = action;
14737   GfxDir[x][y] = direction;
14738   GfxFrame[x][y] = -1;
14739 }
14740 #endif
14741
14742 /*
14743   =============================================================================
14744   checkDiagonalPushing()
14745   -----------------------------------------------------------------------------
14746   check if diagonal input device direction results in pushing of object
14747   (by checking if the alternative direction is walkable, diggable, ...)
14748   =============================================================================
14749 */
14750
14751 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14752                                     int x, int y, int real_dx, int real_dy)
14753 {
14754   int jx, jy, dx, dy, xx, yy;
14755
14756   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14757     return TRUE;
14758
14759   /* diagonal direction: check alternative direction */
14760   jx = player->jx;
14761   jy = player->jy;
14762   dx = x - jx;
14763   dy = y - jy;
14764   xx = jx + (dx == 0 ? real_dx : 0);
14765   yy = jy + (dy == 0 ? real_dy : 0);
14766
14767   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14768 }
14769
14770 /*
14771   =============================================================================
14772   DigField()
14773   -----------------------------------------------------------------------------
14774   x, y:                 field next to player (non-diagonal) to try to dig to
14775   real_dx, real_dy:     direction as read from input device (can be diagonal)
14776   =============================================================================
14777 */
14778
14779 static int DigField(struct PlayerInfo *player,
14780                     int oldx, int oldy, int x, int y,
14781                     int real_dx, int real_dy, int mode)
14782 {
14783   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14784   boolean player_was_pushing = player->is_pushing;
14785   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14786   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14787   int jx = oldx, jy = oldy;
14788   int dx = x - jx, dy = y - jy;
14789   int nextx = x + dx, nexty = y + dy;
14790   int move_direction = (dx == -1 ? MV_LEFT  :
14791                         dx == +1 ? MV_RIGHT :
14792                         dy == -1 ? MV_UP    :
14793                         dy == +1 ? MV_DOWN  : MV_NONE);
14794   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14795   int dig_side = MV_DIR_OPPOSITE(move_direction);
14796   int old_element = Feld[jx][jy];
14797 #if USE_FIXED_DONT_RUN_INTO
14798   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14799 #else
14800   int element;
14801 #endif
14802   int collect_count;
14803
14804   if (is_player)                /* function can also be called by EL_PENGUIN */
14805   {
14806     if (player->MovPos == 0)
14807     {
14808       player->is_digging = FALSE;
14809       player->is_collecting = FALSE;
14810     }
14811
14812     if (player->MovPos == 0)    /* last pushing move finished */
14813       player->is_pushing = FALSE;
14814
14815     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14816     {
14817       player->is_switching = FALSE;
14818       player->push_delay = -1;
14819
14820       return MP_NO_ACTION;
14821     }
14822   }
14823
14824 #if !USE_FIXED_DONT_RUN_INTO
14825   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14826     return MP_NO_ACTION;
14827 #endif
14828
14829   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14830     old_element = Back[jx][jy];
14831
14832   /* in case of element dropped at player position, check background */
14833   else if (Back[jx][jy] != EL_EMPTY &&
14834            game.engine_version >= VERSION_IDENT(2,2,0,0))
14835     old_element = Back[jx][jy];
14836
14837   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14838     return MP_NO_ACTION;        /* field has no opening in this direction */
14839
14840   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14841     return MP_NO_ACTION;        /* field has no opening in this direction */
14842
14843 #if USE_FIXED_DONT_RUN_INTO
14844   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14845   {
14846     SplashAcid(x, y);
14847
14848     Feld[jx][jy] = player->artwork_element;
14849     InitMovingField(jx, jy, MV_DOWN);
14850     Store[jx][jy] = EL_ACID;
14851     ContinueMoving(jx, jy);
14852     BuryPlayer(player);
14853
14854     return MP_DONT_RUN_INTO;
14855   }
14856 #endif
14857
14858 #if USE_FIXED_DONT_RUN_INTO
14859   if (player_can_move && DONT_RUN_INTO(element))
14860   {
14861     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14862
14863     return MP_DONT_RUN_INTO;
14864   }
14865 #endif
14866
14867 #if USE_FIXED_DONT_RUN_INTO
14868   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14869     return MP_NO_ACTION;
14870 #endif
14871
14872 #if !USE_FIXED_DONT_RUN_INTO
14873   element = Feld[x][y];
14874 #endif
14875
14876   collect_count = element_info[element].collect_count_initial;
14877
14878   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14879     return MP_NO_ACTION;
14880
14881   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14882     player_can_move = player_can_move_or_snap;
14883
14884   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14885       game.engine_version >= VERSION_IDENT(2,2,0,0))
14886   {
14887     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14888                                player->index_bit, dig_side);
14889     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14890                                         player->index_bit, dig_side);
14891
14892     if (element == EL_DC_LANDMINE)
14893       Bang(x, y);
14894
14895     if (Feld[x][y] != element)          /* field changed by snapping */
14896       return MP_ACTION;
14897
14898     return MP_NO_ACTION;
14899   }
14900
14901 #if USE_PLAYER_GRAVITY
14902   if (player->gravity && is_player && !player->is_auto_moving &&
14903       canFallDown(player) && move_direction != MV_DOWN &&
14904       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14905     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14906 #else
14907   if (game.gravity && is_player && !player->is_auto_moving &&
14908       canFallDown(player) && move_direction != MV_DOWN &&
14909       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14910     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14911 #endif
14912
14913   if (player_can_move &&
14914       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14915   {
14916     int sound_element = SND_ELEMENT(element);
14917     int sound_action = ACTION_WALKING;
14918
14919     if (IS_RND_GATE(element))
14920     {
14921       if (!player->key[RND_GATE_NR(element)])
14922         return MP_NO_ACTION;
14923     }
14924     else if (IS_RND_GATE_GRAY(element))
14925     {
14926       if (!player->key[RND_GATE_GRAY_NR(element)])
14927         return MP_NO_ACTION;
14928     }
14929     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14930     {
14931       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14932         return MP_NO_ACTION;
14933     }
14934     else if (element == EL_EXIT_OPEN ||
14935              element == EL_EM_EXIT_OPEN ||
14936 #if 1
14937              element == EL_EM_EXIT_OPENING ||
14938 #endif
14939              element == EL_STEEL_EXIT_OPEN ||
14940              element == EL_EM_STEEL_EXIT_OPEN ||
14941 #if 1
14942              element == EL_EM_STEEL_EXIT_OPENING ||
14943 #endif
14944              element == EL_SP_EXIT_OPEN ||
14945              element == EL_SP_EXIT_OPENING)
14946     {
14947       sound_action = ACTION_PASSING;    /* player is passing exit */
14948     }
14949     else if (element == EL_EMPTY)
14950     {
14951       sound_action = ACTION_MOVING;             /* nothing to walk on */
14952     }
14953
14954     /* play sound from background or player, whatever is available */
14955     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14956       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14957     else
14958       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14959   }
14960   else if (player_can_move &&
14961            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14962   {
14963     if (!ACCESS_FROM(element, opposite_direction))
14964       return MP_NO_ACTION;      /* field not accessible from this direction */
14965
14966     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14967       return MP_NO_ACTION;
14968
14969     if (IS_EM_GATE(element))
14970     {
14971       if (!player->key[EM_GATE_NR(element)])
14972         return MP_NO_ACTION;
14973     }
14974     else if (IS_EM_GATE_GRAY(element))
14975     {
14976       if (!player->key[EM_GATE_GRAY_NR(element)])
14977         return MP_NO_ACTION;
14978     }
14979     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14980     {
14981       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14982         return MP_NO_ACTION;
14983     }
14984     else if (IS_EMC_GATE(element))
14985     {
14986       if (!player->key[EMC_GATE_NR(element)])
14987         return MP_NO_ACTION;
14988     }
14989     else if (IS_EMC_GATE_GRAY(element))
14990     {
14991       if (!player->key[EMC_GATE_GRAY_NR(element)])
14992         return MP_NO_ACTION;
14993     }
14994     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14995     {
14996       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14997         return MP_NO_ACTION;
14998     }
14999     else if (element == EL_DC_GATE_WHITE ||
15000              element == EL_DC_GATE_WHITE_GRAY ||
15001              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15002     {
15003       if (player->num_white_keys == 0)
15004         return MP_NO_ACTION;
15005
15006       player->num_white_keys--;
15007     }
15008     else if (IS_SP_PORT(element))
15009     {
15010       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15011           element == EL_SP_GRAVITY_PORT_RIGHT ||
15012           element == EL_SP_GRAVITY_PORT_UP ||
15013           element == EL_SP_GRAVITY_PORT_DOWN)
15014 #if USE_PLAYER_GRAVITY
15015         player->gravity = !player->gravity;
15016 #else
15017         game.gravity = !game.gravity;
15018 #endif
15019       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15020                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15021                element == EL_SP_GRAVITY_ON_PORT_UP ||
15022                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15023 #if USE_PLAYER_GRAVITY
15024         player->gravity = TRUE;
15025 #else
15026         game.gravity = TRUE;
15027 #endif
15028       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15029                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15030                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15031                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15032 #if USE_PLAYER_GRAVITY
15033         player->gravity = FALSE;
15034 #else
15035         game.gravity = FALSE;
15036 #endif
15037     }
15038
15039     /* automatically move to the next field with double speed */
15040     player->programmed_action = move_direction;
15041
15042     if (player->move_delay_reset_counter == 0)
15043     {
15044       player->move_delay_reset_counter = 2;     /* two double speed steps */
15045
15046       DOUBLE_PLAYER_SPEED(player);
15047     }
15048
15049     PlayLevelSoundAction(x, y, ACTION_PASSING);
15050   }
15051   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15052   {
15053     RemoveField(x, y);
15054
15055     if (mode != DF_SNAP)
15056     {
15057       GfxElement[x][y] = GFX_ELEMENT(element);
15058       player->is_digging = TRUE;
15059     }
15060
15061     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15062
15063     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15064                                         player->index_bit, dig_side);
15065
15066     if (mode == DF_SNAP)
15067     {
15068 #if USE_NEW_SNAP_DELAY
15069       if (level.block_snap_field)
15070         setFieldForSnapping(x, y, element, move_direction);
15071       else
15072         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15073 #else
15074       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15075 #endif
15076
15077       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15078                                           player->index_bit, dig_side);
15079     }
15080   }
15081   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15082   {
15083     RemoveField(x, y);
15084
15085     if (is_player && mode != DF_SNAP)
15086     {
15087       GfxElement[x][y] = element;
15088       player->is_collecting = TRUE;
15089     }
15090
15091     if (element == EL_SPEED_PILL)
15092     {
15093       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15094     }
15095     else if (element == EL_EXTRA_TIME && level.time > 0)
15096     {
15097       TimeLeft += level.extra_time;
15098
15099 #if 1
15100       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15101
15102       DisplayGameControlValues();
15103 #else
15104       DrawGameValue_Time(TimeLeft);
15105 #endif
15106     }
15107     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15108     {
15109       player->shield_normal_time_left += level.shield_normal_time;
15110       if (element == EL_SHIELD_DEADLY)
15111         player->shield_deadly_time_left += level.shield_deadly_time;
15112     }
15113     else if (element == EL_DYNAMITE ||
15114              element == EL_EM_DYNAMITE ||
15115              element == EL_SP_DISK_RED)
15116     {
15117       if (player->inventory_size < MAX_INVENTORY_SIZE)
15118         player->inventory_element[player->inventory_size++] = element;
15119
15120       DrawGameDoorValues();
15121     }
15122     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15123     {
15124       player->dynabomb_count++;
15125       player->dynabombs_left++;
15126     }
15127     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15128     {
15129       player->dynabomb_size++;
15130     }
15131     else if (element == EL_DYNABOMB_INCREASE_POWER)
15132     {
15133       player->dynabomb_xl = TRUE;
15134     }
15135     else if (IS_KEY(element))
15136     {
15137       player->key[KEY_NR(element)] = TRUE;
15138
15139       DrawGameDoorValues();
15140     }
15141     else if (element == EL_DC_KEY_WHITE)
15142     {
15143       player->num_white_keys++;
15144
15145       /* display white keys? */
15146       /* DrawGameDoorValues(); */
15147     }
15148     else if (IS_ENVELOPE(element))
15149     {
15150       player->show_envelope = element;
15151     }
15152     else if (element == EL_EMC_LENSES)
15153     {
15154       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15155
15156       RedrawAllInvisibleElementsForLenses();
15157     }
15158     else if (element == EL_EMC_MAGNIFIER)
15159     {
15160       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15161
15162       RedrawAllInvisibleElementsForMagnifier();
15163     }
15164     else if (IS_DROPPABLE(element) ||
15165              IS_THROWABLE(element))     /* can be collected and dropped */
15166     {
15167       int i;
15168
15169       if (collect_count == 0)
15170         player->inventory_infinite_element = element;
15171       else
15172         for (i = 0; i < collect_count; i++)
15173           if (player->inventory_size < MAX_INVENTORY_SIZE)
15174             player->inventory_element[player->inventory_size++] = element;
15175
15176       DrawGameDoorValues();
15177     }
15178     else if (collect_count > 0)
15179     {
15180       local_player->gems_still_needed -= collect_count;
15181       if (local_player->gems_still_needed < 0)
15182         local_player->gems_still_needed = 0;
15183
15184 #if 1
15185       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15186
15187       DisplayGameControlValues();
15188 #else
15189       DrawGameValue_Emeralds(local_player->gems_still_needed);
15190 #endif
15191     }
15192
15193     RaiseScoreElement(element);
15194     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15195
15196     if (is_player)
15197       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15198                                           player->index_bit, dig_side);
15199
15200     if (mode == DF_SNAP)
15201     {
15202 #if USE_NEW_SNAP_DELAY
15203       if (level.block_snap_field)
15204         setFieldForSnapping(x, y, element, move_direction);
15205       else
15206         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15207 #else
15208       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15209 #endif
15210
15211       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15212                                           player->index_bit, dig_side);
15213     }
15214   }
15215   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15216   {
15217     if (mode == DF_SNAP && element != EL_BD_ROCK)
15218       return MP_NO_ACTION;
15219
15220     if (CAN_FALL(element) && dy)
15221       return MP_NO_ACTION;
15222
15223     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15224         !(element == EL_SPRING && level.use_spring_bug))
15225       return MP_NO_ACTION;
15226
15227     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15228         ((move_direction & MV_VERTICAL &&
15229           ((element_info[element].move_pattern & MV_LEFT &&
15230             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15231            (element_info[element].move_pattern & MV_RIGHT &&
15232             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15233          (move_direction & MV_HORIZONTAL &&
15234           ((element_info[element].move_pattern & MV_UP &&
15235             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15236            (element_info[element].move_pattern & MV_DOWN &&
15237             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15238       return MP_NO_ACTION;
15239
15240     /* do not push elements already moving away faster than player */
15241     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15242         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15243       return MP_NO_ACTION;
15244
15245     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15246     {
15247       if (player->push_delay_value == -1 || !player_was_pushing)
15248         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15249     }
15250     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15251     {
15252       if (player->push_delay_value == -1)
15253         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15254     }
15255     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15256     {
15257       if (!player->is_pushing)
15258         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15259     }
15260
15261     player->is_pushing = TRUE;
15262     player->is_active = TRUE;
15263
15264     if (!(IN_LEV_FIELD(nextx, nexty) &&
15265           (IS_FREE(nextx, nexty) ||
15266            (IS_SB_ELEMENT(element) &&
15267             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15268            (IS_CUSTOM_ELEMENT(element) &&
15269             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15270       return MP_NO_ACTION;
15271
15272     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15273       return MP_NO_ACTION;
15274
15275     if (player->push_delay == -1)       /* new pushing; restart delay */
15276       player->push_delay = 0;
15277
15278     if (player->push_delay < player->push_delay_value &&
15279         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15280         element != EL_SPRING && element != EL_BALLOON)
15281     {
15282       /* make sure that there is no move delay before next try to push */
15283       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15284         player->move_delay = 0;
15285
15286       return MP_NO_ACTION;
15287     }
15288
15289     if (IS_CUSTOM_ELEMENT(element) &&
15290         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15291     {
15292       if (!DigFieldByCE(nextx, nexty, element))
15293         return MP_NO_ACTION;
15294     }
15295
15296     if (IS_SB_ELEMENT(element))
15297     {
15298       if (element == EL_SOKOBAN_FIELD_FULL)
15299       {
15300         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15301         local_player->sokobanfields_still_needed++;
15302       }
15303
15304       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15305       {
15306         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15307         local_player->sokobanfields_still_needed--;
15308       }
15309
15310       Feld[x][y] = EL_SOKOBAN_OBJECT;
15311
15312       if (Back[x][y] == Back[nextx][nexty])
15313         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15314       else if (Back[x][y] != 0)
15315         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15316                                     ACTION_EMPTYING);
15317       else
15318         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15319                                     ACTION_FILLING);
15320
15321 #if 1
15322       if (local_player->sokobanfields_still_needed == 0 &&
15323           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15324 #else
15325       if (local_player->sokobanfields_still_needed == 0 &&
15326           game.emulation == EMU_SOKOBAN)
15327 #endif
15328       {
15329         PlayerWins(player);
15330
15331         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15332       }
15333     }
15334     else
15335       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15336
15337     InitMovingField(x, y, move_direction);
15338     GfxAction[x][y] = ACTION_PUSHING;
15339
15340     if (mode == DF_SNAP)
15341       ContinueMoving(x, y);
15342     else
15343       MovPos[x][y] = (dx != 0 ? dx : dy);
15344
15345     Pushed[x][y] = TRUE;
15346     Pushed[nextx][nexty] = TRUE;
15347
15348     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15349       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15350     else
15351       player->push_delay_value = -1;    /* get new value later */
15352
15353     /* check for element change _after_ element has been pushed */
15354     if (game.use_change_when_pushing_bug)
15355     {
15356       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15357                                  player->index_bit, dig_side);
15358       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15359                                           player->index_bit, dig_side);
15360     }
15361   }
15362   else if (IS_SWITCHABLE(element))
15363   {
15364     if (PLAYER_SWITCHING(player, x, y))
15365     {
15366       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15367                                           player->index_bit, dig_side);
15368
15369       return MP_ACTION;
15370     }
15371
15372     player->is_switching = TRUE;
15373     player->switch_x = x;
15374     player->switch_y = y;
15375
15376     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15377
15378     if (element == EL_ROBOT_WHEEL)
15379     {
15380       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15381       ZX = x;
15382       ZY = y;
15383
15384       game.robot_wheel_active = TRUE;
15385
15386       TEST_DrawLevelField(x, y);
15387     }
15388     else if (element == EL_SP_TERMINAL)
15389     {
15390       int xx, yy;
15391
15392       SCAN_PLAYFIELD(xx, yy)
15393       {
15394         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15395           Bang(xx, yy);
15396         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15397           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15398       }
15399     }
15400     else if (IS_BELT_SWITCH(element))
15401     {
15402       ToggleBeltSwitch(x, y);
15403     }
15404     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15405              element == EL_SWITCHGATE_SWITCH_DOWN ||
15406              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15407              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15408     {
15409       ToggleSwitchgateSwitch(x, y);
15410     }
15411     else if (element == EL_LIGHT_SWITCH ||
15412              element == EL_LIGHT_SWITCH_ACTIVE)
15413     {
15414       ToggleLightSwitch(x, y);
15415     }
15416     else if (element == EL_TIMEGATE_SWITCH ||
15417              element == EL_DC_TIMEGATE_SWITCH)
15418     {
15419       ActivateTimegateSwitch(x, y);
15420     }
15421     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15422              element == EL_BALLOON_SWITCH_RIGHT ||
15423              element == EL_BALLOON_SWITCH_UP    ||
15424              element == EL_BALLOON_SWITCH_DOWN  ||
15425              element == EL_BALLOON_SWITCH_NONE  ||
15426              element == EL_BALLOON_SWITCH_ANY)
15427     {
15428       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15429                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15430                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15431                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15432                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15433                              move_direction);
15434     }
15435     else if (element == EL_LAMP)
15436     {
15437       Feld[x][y] = EL_LAMP_ACTIVE;
15438       local_player->lights_still_needed--;
15439
15440       ResetGfxAnimation(x, y);
15441       TEST_DrawLevelField(x, y);
15442     }
15443     else if (element == EL_TIME_ORB_FULL)
15444     {
15445       Feld[x][y] = EL_TIME_ORB_EMPTY;
15446
15447       if (level.time > 0 || level.use_time_orb_bug)
15448       {
15449         TimeLeft += level.time_orb_time;
15450         game.no_time_limit = FALSE;
15451
15452 #if 1
15453         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15454
15455         DisplayGameControlValues();
15456 #else
15457         DrawGameValue_Time(TimeLeft);
15458 #endif
15459       }
15460
15461       ResetGfxAnimation(x, y);
15462       TEST_DrawLevelField(x, y);
15463     }
15464     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15465              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15466     {
15467       int xx, yy;
15468
15469       game.ball_state = !game.ball_state;
15470
15471       SCAN_PLAYFIELD(xx, yy)
15472       {
15473         int e = Feld[xx][yy];
15474
15475         if (game.ball_state)
15476         {
15477           if (e == EL_EMC_MAGIC_BALL)
15478             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15479           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15480             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15481         }
15482         else
15483         {
15484           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15485             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15486           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15487             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15488         }
15489       }
15490     }
15491
15492     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15493                                         player->index_bit, dig_side);
15494
15495     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15496                                         player->index_bit, dig_side);
15497
15498     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15499                                         player->index_bit, dig_side);
15500
15501     return MP_ACTION;
15502   }
15503   else
15504   {
15505     if (!PLAYER_SWITCHING(player, x, y))
15506     {
15507       player->is_switching = TRUE;
15508       player->switch_x = x;
15509       player->switch_y = y;
15510
15511       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15512                                  player->index_bit, dig_side);
15513       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15514                                           player->index_bit, dig_side);
15515
15516       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15517                                  player->index_bit, dig_side);
15518       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15519                                           player->index_bit, dig_side);
15520     }
15521
15522     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15523                                player->index_bit, dig_side);
15524     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15525                                         player->index_bit, dig_side);
15526
15527     return MP_NO_ACTION;
15528   }
15529
15530   player->push_delay = -1;
15531
15532   if (is_player)                /* function can also be called by EL_PENGUIN */
15533   {
15534     if (Feld[x][y] != element)          /* really digged/collected something */
15535     {
15536       player->is_collecting = !player->is_digging;
15537       player->is_active = TRUE;
15538     }
15539   }
15540
15541   return MP_MOVING;
15542 }
15543
15544 static boolean DigFieldByCE(int x, int y, int digging_element)
15545 {
15546   int element = Feld[x][y];
15547
15548   if (!IS_FREE(x, y))
15549   {
15550     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15551                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15552                   ACTION_BREAKING);
15553
15554     /* no element can dig solid indestructible elements */
15555     if (IS_INDESTRUCTIBLE(element) &&
15556         !IS_DIGGABLE(element) &&
15557         !IS_COLLECTIBLE(element))
15558       return FALSE;
15559
15560     if (AmoebaNr[x][y] &&
15561         (element == EL_AMOEBA_FULL ||
15562          element == EL_BD_AMOEBA ||
15563          element == EL_AMOEBA_GROWING))
15564     {
15565       AmoebaCnt[AmoebaNr[x][y]]--;
15566       AmoebaCnt2[AmoebaNr[x][y]]--;
15567     }
15568
15569     if (IS_MOVING(x, y))
15570       RemoveMovingField(x, y);
15571     else
15572     {
15573       RemoveField(x, y);
15574       TEST_DrawLevelField(x, y);
15575     }
15576
15577     /* if digged element was about to explode, prevent the explosion */
15578     ExplodeField[x][y] = EX_TYPE_NONE;
15579
15580     PlayLevelSoundAction(x, y, action);
15581   }
15582
15583   Store[x][y] = EL_EMPTY;
15584
15585 #if 1
15586   /* this makes it possible to leave the removed element again */
15587   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15588     Store[x][y] = element;
15589 #else
15590   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15591   {
15592     int move_leave_element = element_info[digging_element].move_leave_element;
15593
15594     /* this makes it possible to leave the removed element again */
15595     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15596                    element : move_leave_element);
15597   }
15598 #endif
15599
15600   return TRUE;
15601 }
15602
15603 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15604 {
15605   int jx = player->jx, jy = player->jy;
15606   int x = jx + dx, y = jy + dy;
15607   int snap_direction = (dx == -1 ? MV_LEFT  :
15608                         dx == +1 ? MV_RIGHT :
15609                         dy == -1 ? MV_UP    :
15610                         dy == +1 ? MV_DOWN  : MV_NONE);
15611   boolean can_continue_snapping = (level.continuous_snapping &&
15612                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15613
15614   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15615     return FALSE;
15616
15617   if (!player->active || !IN_LEV_FIELD(x, y))
15618     return FALSE;
15619
15620   if (dx && dy)
15621     return FALSE;
15622
15623   if (!dx && !dy)
15624   {
15625     if (player->MovPos == 0)
15626       player->is_pushing = FALSE;
15627
15628     player->is_snapping = FALSE;
15629
15630     if (player->MovPos == 0)
15631     {
15632       player->is_moving = FALSE;
15633       player->is_digging = FALSE;
15634       player->is_collecting = FALSE;
15635     }
15636
15637     return FALSE;
15638   }
15639
15640 #if USE_NEW_CONTINUOUS_SNAPPING
15641   /* prevent snapping with already pressed snap key when not allowed */
15642   if (player->is_snapping && !can_continue_snapping)
15643     return FALSE;
15644 #else
15645   if (player->is_snapping)
15646     return FALSE;
15647 #endif
15648
15649   player->MovDir = snap_direction;
15650
15651   if (player->MovPos == 0)
15652   {
15653     player->is_moving = FALSE;
15654     player->is_digging = FALSE;
15655     player->is_collecting = FALSE;
15656   }
15657
15658   player->is_dropping = FALSE;
15659   player->is_dropping_pressed = FALSE;
15660   player->drop_pressed_delay = 0;
15661
15662   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15663     return FALSE;
15664
15665   player->is_snapping = TRUE;
15666   player->is_active = TRUE;
15667
15668   if (player->MovPos == 0)
15669   {
15670     player->is_moving = FALSE;
15671     player->is_digging = FALSE;
15672     player->is_collecting = FALSE;
15673   }
15674
15675   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15676     TEST_DrawLevelField(player->last_jx, player->last_jy);
15677
15678   TEST_DrawLevelField(x, y);
15679
15680   return TRUE;
15681 }
15682
15683 static boolean DropElement(struct PlayerInfo *player)
15684 {
15685   int old_element, new_element;
15686   int dropx = player->jx, dropy = player->jy;
15687   int drop_direction = player->MovDir;
15688   int drop_side = drop_direction;
15689 #if 1
15690   int drop_element = get_next_dropped_element(player);
15691 #else
15692   int drop_element = (player->inventory_size > 0 ?
15693                       player->inventory_element[player->inventory_size - 1] :
15694                       player->inventory_infinite_element != EL_UNDEFINED ?
15695                       player->inventory_infinite_element :
15696                       player->dynabombs_left > 0 ?
15697                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15698                       EL_UNDEFINED);
15699 #endif
15700
15701   player->is_dropping_pressed = TRUE;
15702
15703   /* do not drop an element on top of another element; when holding drop key
15704      pressed without moving, dropped element must move away before the next
15705      element can be dropped (this is especially important if the next element
15706      is dynamite, which can be placed on background for historical reasons) */
15707   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15708     return MP_ACTION;
15709
15710   if (IS_THROWABLE(drop_element))
15711   {
15712     dropx += GET_DX_FROM_DIR(drop_direction);
15713     dropy += GET_DY_FROM_DIR(drop_direction);
15714
15715     if (!IN_LEV_FIELD(dropx, dropy))
15716       return FALSE;
15717   }
15718
15719   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15720   new_element = drop_element;           /* default: no change when dropping */
15721
15722   /* check if player is active, not moving and ready to drop */
15723   if (!player->active || player->MovPos || player->drop_delay > 0)
15724     return FALSE;
15725
15726   /* check if player has anything that can be dropped */
15727   if (new_element == EL_UNDEFINED)
15728     return FALSE;
15729
15730   /* check if drop key was pressed long enough for EM style dynamite */
15731   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15732     return FALSE;
15733
15734   /* check if anything can be dropped at the current position */
15735   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15736     return FALSE;
15737
15738   /* collected custom elements can only be dropped on empty fields */
15739   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15740     return FALSE;
15741
15742   if (old_element != EL_EMPTY)
15743     Back[dropx][dropy] = old_element;   /* store old element on this field */
15744
15745   ResetGfxAnimation(dropx, dropy);
15746   ResetRandomAnimationValue(dropx, dropy);
15747
15748   if (player->inventory_size > 0 ||
15749       player->inventory_infinite_element != EL_UNDEFINED)
15750   {
15751     if (player->inventory_size > 0)
15752     {
15753       player->inventory_size--;
15754
15755       DrawGameDoorValues();
15756
15757       if (new_element == EL_DYNAMITE)
15758         new_element = EL_DYNAMITE_ACTIVE;
15759       else if (new_element == EL_EM_DYNAMITE)
15760         new_element = EL_EM_DYNAMITE_ACTIVE;
15761       else if (new_element == EL_SP_DISK_RED)
15762         new_element = EL_SP_DISK_RED_ACTIVE;
15763     }
15764
15765     Feld[dropx][dropy] = new_element;
15766
15767     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15768       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15769                           el2img(Feld[dropx][dropy]), 0);
15770
15771     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15772
15773     /* needed if previous element just changed to "empty" in the last frame */
15774     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15775
15776     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15777                                player->index_bit, drop_side);
15778     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15779                                         CE_PLAYER_DROPS_X,
15780                                         player->index_bit, drop_side);
15781
15782     TestIfElementTouchesCustomElement(dropx, dropy);
15783   }
15784   else          /* player is dropping a dyna bomb */
15785   {
15786     player->dynabombs_left--;
15787
15788     Feld[dropx][dropy] = new_element;
15789
15790     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15791       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15792                           el2img(Feld[dropx][dropy]), 0);
15793
15794     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15795   }
15796
15797   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15798     InitField_WithBug1(dropx, dropy, FALSE);
15799
15800   new_element = Feld[dropx][dropy];     /* element might have changed */
15801
15802   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15803       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15804   {
15805 #if 0
15806     int move_direction;
15807     int nextx, nexty;
15808 #endif
15809
15810     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15811       MovDir[dropx][dropy] = drop_direction;
15812
15813 #if 0
15814     move_direction = MovDir[dropx][dropy];
15815     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15816     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15817 #endif
15818
15819     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15820
15821 #if USE_FIX_IMPACT_COLLISION
15822     /* do not cause impact style collision by dropping elements that can fall */
15823     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15824 #else
15825     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15826 #endif
15827   }
15828
15829   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15830   player->is_dropping = TRUE;
15831
15832   player->drop_pressed_delay = 0;
15833   player->is_dropping_pressed = FALSE;
15834
15835   player->drop_x = dropx;
15836   player->drop_y = dropy;
15837
15838   return TRUE;
15839 }
15840
15841 /* ------------------------------------------------------------------------- */
15842 /* game sound playing functions                                              */
15843 /* ------------------------------------------------------------------------- */
15844
15845 static int *loop_sound_frame = NULL;
15846 static int *loop_sound_volume = NULL;
15847
15848 void InitPlayLevelSound()
15849 {
15850   int num_sounds = getSoundListSize();
15851
15852   checked_free(loop_sound_frame);
15853   checked_free(loop_sound_volume);
15854
15855   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15856   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15857 }
15858
15859 static void PlayLevelSound(int x, int y, int nr)
15860 {
15861   int sx = SCREENX(x), sy = SCREENY(y);
15862   int volume, stereo_position;
15863   int max_distance = 8;
15864   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15865
15866   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15867       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15868     return;
15869
15870   if (!IN_LEV_FIELD(x, y) ||
15871       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15872       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15873     return;
15874
15875   volume = SOUND_MAX_VOLUME;
15876
15877   if (!IN_SCR_FIELD(sx, sy))
15878   {
15879     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15880     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15881
15882     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15883   }
15884
15885   stereo_position = (SOUND_MAX_LEFT +
15886                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15887                      (SCR_FIELDX + 2 * max_distance));
15888
15889   if (IS_LOOP_SOUND(nr))
15890   {
15891     /* This assures that quieter loop sounds do not overwrite louder ones,
15892        while restarting sound volume comparison with each new game frame. */
15893
15894     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15895       return;
15896
15897     loop_sound_volume[nr] = volume;
15898     loop_sound_frame[nr] = FrameCounter;
15899   }
15900
15901   PlaySoundExt(nr, volume, stereo_position, type);
15902 }
15903
15904 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15905 {
15906   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15907                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15908                  y < LEVELY(BY1) ? LEVELY(BY1) :
15909                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15910                  sound_action);
15911 }
15912
15913 static void PlayLevelSoundAction(int x, int y, int action)
15914 {
15915   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15916 }
15917
15918 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15919 {
15920   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15921
15922   if (sound_effect != SND_UNDEFINED)
15923     PlayLevelSound(x, y, sound_effect);
15924 }
15925
15926 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15927                                               int action)
15928 {
15929   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15930
15931   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15932     PlayLevelSound(x, y, sound_effect);
15933 }
15934
15935 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15936 {
15937   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15938
15939   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15940     PlayLevelSound(x, y, sound_effect);
15941 }
15942
15943 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15944 {
15945   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15946
15947   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15948     StopSound(sound_effect);
15949 }
15950
15951 static void PlayLevelMusic()
15952 {
15953   if (levelset.music[level_nr] != MUS_UNDEFINED)
15954     PlayMusic(levelset.music[level_nr]);        /* from config file */
15955   else
15956     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15957 }
15958
15959 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15960 {
15961   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15962   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15963   int x = xx - 1 - offset;
15964   int y = yy - 1 - offset;
15965
15966   switch (sample)
15967   {
15968     case SAMPLE_blank:
15969       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15970       break;
15971
15972     case SAMPLE_roll:
15973       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15974       break;
15975
15976     case SAMPLE_stone:
15977       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15978       break;
15979
15980     case SAMPLE_nut:
15981       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15982       break;
15983
15984     case SAMPLE_crack:
15985       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15986       break;
15987
15988     case SAMPLE_bug:
15989       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15990       break;
15991
15992     case SAMPLE_tank:
15993       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15994       break;
15995
15996     case SAMPLE_android_clone:
15997       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15998       break;
15999
16000     case SAMPLE_android_move:
16001       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16002       break;
16003
16004     case SAMPLE_spring:
16005       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16006       break;
16007
16008     case SAMPLE_slurp:
16009       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16010       break;
16011
16012     case SAMPLE_eater:
16013       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16014       break;
16015
16016     case SAMPLE_eater_eat:
16017       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16018       break;
16019
16020     case SAMPLE_alien:
16021       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16022       break;
16023
16024     case SAMPLE_collect:
16025       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16026       break;
16027
16028     case SAMPLE_diamond:
16029       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16030       break;
16031
16032     case SAMPLE_squash:
16033       /* !!! CHECK THIS !!! */
16034 #if 1
16035       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16036 #else
16037       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16038 #endif
16039       break;
16040
16041     case SAMPLE_wonderfall:
16042       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16043       break;
16044
16045     case SAMPLE_drip:
16046       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16047       break;
16048
16049     case SAMPLE_push:
16050       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16051       break;
16052
16053     case SAMPLE_dirt:
16054       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16055       break;
16056
16057     case SAMPLE_acid:
16058       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16059       break;
16060
16061     case SAMPLE_ball:
16062       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16063       break;
16064
16065     case SAMPLE_grow:
16066       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16067       break;
16068
16069     case SAMPLE_wonder:
16070       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16071       break;
16072
16073     case SAMPLE_door:
16074       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16075       break;
16076
16077     case SAMPLE_exit_open:
16078       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16079       break;
16080
16081     case SAMPLE_exit_leave:
16082       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16083       break;
16084
16085     case SAMPLE_dynamite:
16086       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16087       break;
16088
16089     case SAMPLE_tick:
16090       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16091       break;
16092
16093     case SAMPLE_press:
16094       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16095       break;
16096
16097     case SAMPLE_wheel:
16098       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16099       break;
16100
16101     case SAMPLE_boom:
16102       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16103       break;
16104
16105     case SAMPLE_die:
16106       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16107       break;
16108
16109     case SAMPLE_time:
16110       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16111       break;
16112
16113     default:
16114       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16115       break;
16116   }
16117 }
16118
16119 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16120 {
16121   int element = map_element_SP_to_RND(element_sp);
16122   int action = map_action_SP_to_RND(action_sp);
16123   int offset = (setup.sp_show_border_elements ? 0 : 1);
16124   int x = xx - offset;
16125   int y = yy - offset;
16126
16127 #if 0
16128   printf("::: %d -> %d\n", element_sp, action_sp);
16129 #endif
16130
16131   PlayLevelSoundElementAction(x, y, element, action);
16132 }
16133
16134 void RaiseScore(int value)
16135 {
16136   local_player->score += value;
16137
16138 #if 1
16139   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16140
16141   DisplayGameControlValues();
16142 #else
16143   DrawGameValue_Score(local_player->score);
16144 #endif
16145 }
16146
16147 void RaiseScoreElement(int element)
16148 {
16149   switch (element)
16150   {
16151     case EL_EMERALD:
16152     case EL_BD_DIAMOND:
16153     case EL_EMERALD_YELLOW:
16154     case EL_EMERALD_RED:
16155     case EL_EMERALD_PURPLE:
16156     case EL_SP_INFOTRON:
16157       RaiseScore(level.score[SC_EMERALD]);
16158       break;
16159     case EL_DIAMOND:
16160       RaiseScore(level.score[SC_DIAMOND]);
16161       break;
16162     case EL_CRYSTAL:
16163       RaiseScore(level.score[SC_CRYSTAL]);
16164       break;
16165     case EL_PEARL:
16166       RaiseScore(level.score[SC_PEARL]);
16167       break;
16168     case EL_BUG:
16169     case EL_BD_BUTTERFLY:
16170     case EL_SP_ELECTRON:
16171       RaiseScore(level.score[SC_BUG]);
16172       break;
16173     case EL_SPACESHIP:
16174     case EL_BD_FIREFLY:
16175     case EL_SP_SNIKSNAK:
16176       RaiseScore(level.score[SC_SPACESHIP]);
16177       break;
16178     case EL_YAMYAM:
16179     case EL_DARK_YAMYAM:
16180       RaiseScore(level.score[SC_YAMYAM]);
16181       break;
16182     case EL_ROBOT:
16183       RaiseScore(level.score[SC_ROBOT]);
16184       break;
16185     case EL_PACMAN:
16186       RaiseScore(level.score[SC_PACMAN]);
16187       break;
16188     case EL_NUT:
16189       RaiseScore(level.score[SC_NUT]);
16190       break;
16191     case EL_DYNAMITE:
16192     case EL_EM_DYNAMITE:
16193     case EL_SP_DISK_RED:
16194     case EL_DYNABOMB_INCREASE_NUMBER:
16195     case EL_DYNABOMB_INCREASE_SIZE:
16196     case EL_DYNABOMB_INCREASE_POWER:
16197       RaiseScore(level.score[SC_DYNAMITE]);
16198       break;
16199     case EL_SHIELD_NORMAL:
16200     case EL_SHIELD_DEADLY:
16201       RaiseScore(level.score[SC_SHIELD]);
16202       break;
16203     case EL_EXTRA_TIME:
16204       RaiseScore(level.extra_time_score);
16205       break;
16206     case EL_KEY_1:
16207     case EL_KEY_2:
16208     case EL_KEY_3:
16209     case EL_KEY_4:
16210     case EL_EM_KEY_1:
16211     case EL_EM_KEY_2:
16212     case EL_EM_KEY_3:
16213     case EL_EM_KEY_4:
16214     case EL_EMC_KEY_5:
16215     case EL_EMC_KEY_6:
16216     case EL_EMC_KEY_7:
16217     case EL_EMC_KEY_8:
16218     case EL_DC_KEY_WHITE:
16219       RaiseScore(level.score[SC_KEY]);
16220       break;
16221     default:
16222       RaiseScore(element_info[element].collect_score);
16223       break;
16224   }
16225 }
16226
16227 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16228 {
16229   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16230   {
16231 #if defined(NETWORK_AVALIABLE)
16232     if (options.network)
16233       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16234     else
16235 #endif
16236     {
16237       if (quick_quit)
16238       {
16239 #if 1
16240
16241 #if 1
16242         FadeSkipNextFadeIn();
16243 #else
16244         fading = fading_none;
16245 #endif
16246
16247 #else
16248         OpenDoor(DOOR_CLOSE_1);
16249 #endif
16250
16251         game_status = GAME_MODE_MAIN;
16252
16253 #if 1
16254         DrawAndFadeInMainMenu(REDRAW_FIELD);
16255 #else
16256         DrawMainMenu();
16257 #endif
16258       }
16259       else
16260       {
16261 #if 0
16262         FadeOut(REDRAW_FIELD);
16263 #endif
16264
16265         game_status = GAME_MODE_MAIN;
16266
16267         DrawAndFadeInMainMenu(REDRAW_FIELD);
16268       }
16269     }
16270   }
16271   else          /* continue playing the game */
16272   {
16273     if (tape.playing && tape.deactivate_display)
16274       TapeDeactivateDisplayOff(TRUE);
16275
16276     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16277
16278     if (tape.playing && tape.deactivate_display)
16279       TapeDeactivateDisplayOn();
16280   }
16281 }
16282
16283 void RequestQuitGame(boolean ask_if_really_quit)
16284 {
16285   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16286   boolean skip_request = AllPlayersGone || quick_quit;
16287
16288   RequestQuitGameExt(skip_request, quick_quit,
16289                      "Do you really want to quit the game ?");
16290 }
16291
16292
16293 /* ------------------------------------------------------------------------- */
16294 /* random generator functions                                                */
16295 /* ------------------------------------------------------------------------- */
16296
16297 unsigned int InitEngineRandom_RND(int seed)
16298 {
16299   game.num_random_calls = 0;
16300
16301 #if 0
16302   unsigned int rnd_seed = InitEngineRandom(seed);
16303
16304   printf("::: START RND: %d\n", rnd_seed);
16305
16306   return rnd_seed;
16307 #else
16308
16309   return InitEngineRandom(seed);
16310
16311 #endif
16312
16313 }
16314
16315 unsigned int RND(int max)
16316 {
16317   if (max > 0)
16318   {
16319     game.num_random_calls++;
16320
16321     return GetEngineRandom(max);
16322   }
16323
16324   return 0;
16325 }
16326
16327
16328 /* ------------------------------------------------------------------------- */
16329 /* game engine snapshot handling functions                                   */
16330 /* ------------------------------------------------------------------------- */
16331
16332 struct EngineSnapshotInfo
16333 {
16334   /* runtime values for custom element collect score */
16335   int collect_score[NUM_CUSTOM_ELEMENTS];
16336
16337   /* runtime values for group element choice position */
16338   int choice_pos[NUM_GROUP_ELEMENTS];
16339
16340   /* runtime values for belt position animations */
16341   int belt_graphic[4][NUM_BELT_PARTS];
16342   int belt_anim_mode[4][NUM_BELT_PARTS];
16343 };
16344
16345 static struct EngineSnapshotInfo engine_snapshot_rnd;
16346 static char *snapshot_level_identifier = NULL;
16347 static int snapshot_level_nr = -1;
16348
16349 static void SaveEngineSnapshotValues_RND()
16350 {
16351   static int belt_base_active_element[4] =
16352   {
16353     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16354     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16355     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16356     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16357   };
16358   int i, j;
16359
16360   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16361   {
16362     int element = EL_CUSTOM_START + i;
16363
16364     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16365   }
16366
16367   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16368   {
16369     int element = EL_GROUP_START + i;
16370
16371     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16372   }
16373
16374   for (i = 0; i < 4; i++)
16375   {
16376     for (j = 0; j < NUM_BELT_PARTS; j++)
16377     {
16378       int element = belt_base_active_element[i] + j;
16379       int graphic = el2img(element);
16380       int anim_mode = graphic_info[graphic].anim_mode;
16381
16382       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16383       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16384     }
16385   }
16386 }
16387
16388 static void LoadEngineSnapshotValues_RND()
16389 {
16390   unsigned int num_random_calls = game.num_random_calls;
16391   int i, j;
16392
16393   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16394   {
16395     int element = EL_CUSTOM_START + i;
16396
16397     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16398   }
16399
16400   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16401   {
16402     int element = EL_GROUP_START + i;
16403
16404     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16405   }
16406
16407   for (i = 0; i < 4; i++)
16408   {
16409     for (j = 0; j < NUM_BELT_PARTS; j++)
16410     {
16411       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16412       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16413
16414       graphic_info[graphic].anim_mode = anim_mode;
16415     }
16416   }
16417
16418   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16419   {
16420     InitRND(tape.random_seed);
16421     for (i = 0; i < num_random_calls; i++)
16422       RND(1);
16423   }
16424
16425   if (game.num_random_calls != num_random_calls)
16426   {
16427     Error(ERR_INFO, "number of random calls out of sync");
16428     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16429     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16430     Error(ERR_EXIT, "this should not happen -- please debug");
16431   }
16432 }
16433
16434 void SaveEngineSnapshot()
16435 {
16436   /* do not save snapshots from editor */
16437   if (level_editor_test_game)
16438     return;
16439
16440   /* free previous snapshot buffers, if needed */
16441   FreeEngineSnapshotBuffers();
16442
16443   /* copy some special values to a structure better suited for the snapshot */
16444
16445   SaveEngineSnapshotValues_RND();
16446   SaveEngineSnapshotValues_EM();
16447   SaveEngineSnapshotValues_SP();
16448
16449   /* save values stored in special snapshot structure */
16450
16451   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16452   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16453   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16454
16455   /* save further RND engine values */
16456
16457   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16458   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16459   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16460
16461   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16462   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16463   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16464   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16465
16466   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16467   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16468   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16469   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16470   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16471
16472   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16473   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16474   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16475
16476   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16477
16478   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16479
16480   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16481   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16482
16483   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16484   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16485   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16486   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16487   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16488   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16489   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16490   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16491   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16492   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16493   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16494   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16495   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16496   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16497   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16498   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16499   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16500   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16501
16502   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16503   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16504
16505   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16506   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16507   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16508
16509   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16510   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16511
16512   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16513   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16514   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16515   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16516   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16517
16518   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16519   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16520
16521   /* save level identification information */
16522
16523   setString(&snapshot_level_identifier, leveldir_current->identifier);
16524   snapshot_level_nr = level_nr;
16525
16526 #if 0
16527   ListNode *node = engine_snapshot_list_rnd;
16528   int num_bytes = 0;
16529
16530   while (node != NULL)
16531   {
16532     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16533
16534     node = node->next;
16535   }
16536
16537   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16538 #endif
16539 }
16540
16541 void LoadEngineSnapshot()
16542 {
16543   /* restore generically stored snapshot buffers */
16544
16545   LoadEngineSnapshotBuffers();
16546
16547   /* restore special values from snapshot structure */
16548
16549   LoadEngineSnapshotValues_RND();
16550   LoadEngineSnapshotValues_EM();
16551   LoadEngineSnapshotValues_SP();
16552 }
16553
16554 boolean CheckEngineSnapshot()
16555 {
16556   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16557           snapshot_level_nr == level_nr);
16558 }
16559
16560
16561 /* ---------- new game button stuff ---------------------------------------- */
16562
16563 static struct
16564 {
16565   int graphic;
16566   struct Rect *pos;
16567   int gadget_id;
16568   char *infotext;
16569 } gamebutton_info[NUM_GAME_BUTTONS] =
16570 {
16571   {
16572     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16573     GAME_CTRL_ID_STOP,                  "stop game"
16574   },
16575   {
16576     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16577     GAME_CTRL_ID_PAUSE,                 "pause game"
16578   },
16579   {
16580     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16581     GAME_CTRL_ID_PLAY,                  "play game"
16582   },
16583   {
16584     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16585     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16586   },
16587   {
16588     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16589     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16590   },
16591   {
16592     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16593     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16594   }
16595 };
16596
16597 void CreateGameButtons()
16598 {
16599   int i;
16600
16601   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16602   {
16603     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16604     struct Rect *pos = gamebutton_info[i].pos;
16605     struct GadgetInfo *gi;
16606     int button_type;
16607     boolean checked;
16608     unsigned int event_mask;
16609     int gd_x   = gfx->src_x;
16610     int gd_y   = gfx->src_y;
16611     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16612     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16613     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16614     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16615     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16616     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16617     int id = i;
16618
16619     if (id == GAME_CTRL_ID_STOP ||
16620         id == GAME_CTRL_ID_PAUSE ||
16621         id == GAME_CTRL_ID_PLAY)
16622     {
16623       button_type = GD_TYPE_NORMAL_BUTTON;
16624       checked = FALSE;
16625       event_mask = GD_EVENT_RELEASED;
16626     }
16627     else
16628     {
16629       button_type = GD_TYPE_CHECK_BUTTON;
16630       checked =
16631         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16632          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16633          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16634       event_mask = GD_EVENT_PRESSED;
16635     }
16636
16637     gi = CreateGadget(GDI_CUSTOM_ID, id,
16638                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16639                       GDI_X, DX + pos->x,
16640                       GDI_Y, DY + pos->y,
16641                       GDI_WIDTH, gfx->width,
16642                       GDI_HEIGHT, gfx->height,
16643                       GDI_TYPE, button_type,
16644                       GDI_STATE, GD_BUTTON_UNPRESSED,
16645                       GDI_CHECKED, checked,
16646                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16647                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16648                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16649                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16650                       GDI_DIRECT_DRAW, FALSE,
16651                       GDI_EVENT_MASK, event_mask,
16652                       GDI_CALLBACK_ACTION, HandleGameButtons,
16653                       GDI_END);
16654
16655     if (gi == NULL)
16656       Error(ERR_EXIT, "cannot create gadget");
16657
16658     game_gadget[id] = gi;
16659   }
16660 }
16661
16662 void FreeGameButtons()
16663 {
16664   int i;
16665
16666   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16667     FreeGadget(game_gadget[i]);
16668 }
16669
16670 static void MapGameButtons()
16671 {
16672   int i;
16673
16674   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16675     MapGadget(game_gadget[i]);
16676 }
16677
16678 void UnmapGameButtons()
16679 {
16680   int i;
16681
16682   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16683     UnmapGadget(game_gadget[i]);
16684 }
16685
16686 void RedrawGameButtons()
16687 {
16688   int i;
16689
16690   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16691     RedrawGadget(game_gadget[i]);
16692 }
16693
16694 static void HandleGameButtonsExt(int id)
16695 {
16696   if (game_status != GAME_MODE_PLAYING)
16697     return;
16698
16699   switch (id)
16700   {
16701     case GAME_CTRL_ID_STOP:
16702       if (tape.playing)
16703         TapeStop();
16704       else
16705         RequestQuitGame(TRUE);
16706       break;
16707
16708     case GAME_CTRL_ID_PAUSE:
16709       if (options.network)
16710       {
16711 #if defined(NETWORK_AVALIABLE)
16712         if (tape.pausing)
16713           SendToServer_ContinuePlaying();
16714         else
16715           SendToServer_PausePlaying();
16716 #endif
16717       }
16718       else
16719         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16720       break;
16721
16722     case GAME_CTRL_ID_PLAY:
16723       if (tape.pausing)
16724       {
16725 #if defined(NETWORK_AVALIABLE)
16726         if (options.network)
16727           SendToServer_ContinuePlaying();
16728         else
16729 #endif
16730         {
16731           tape.pausing = FALSE;
16732           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16733         }
16734       }
16735       break;
16736
16737     case SOUND_CTRL_ID_MUSIC:
16738       if (setup.sound_music)
16739       { 
16740         setup.sound_music = FALSE;
16741
16742         FadeMusic();
16743       }
16744       else if (audio.music_available)
16745       { 
16746         setup.sound = setup.sound_music = TRUE;
16747
16748         SetAudioMode(setup.sound);
16749
16750         PlayLevelMusic();
16751       }
16752       break;
16753
16754     case SOUND_CTRL_ID_LOOPS:
16755       if (setup.sound_loops)
16756         setup.sound_loops = FALSE;
16757       else if (audio.loops_available)
16758       {
16759         setup.sound = setup.sound_loops = TRUE;
16760
16761         SetAudioMode(setup.sound);
16762       }
16763       break;
16764
16765     case SOUND_CTRL_ID_SIMPLE:
16766       if (setup.sound_simple)
16767         setup.sound_simple = FALSE;
16768       else if (audio.sound_available)
16769       {
16770         setup.sound = setup.sound_simple = TRUE;
16771
16772         SetAudioMode(setup.sound);
16773       }
16774       break;
16775
16776     default:
16777       break;
16778   }
16779 }
16780
16781 static void HandleGameButtons(struct GadgetInfo *gi)
16782 {
16783   HandleGameButtonsExt(gi->custom_id);
16784 }
16785
16786 void HandleSoundButtonKeys(Key key)
16787 {
16788 #if 1
16789   if (key == setup.shortcut.sound_simple)
16790     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16791   else if (key == setup.shortcut.sound_loops)
16792     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16793   else if (key == setup.shortcut.sound_music)
16794     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16795 #else
16796   if (key == setup.shortcut.sound_simple)
16797     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16798   else if (key == setup.shortcut.sound_loops)
16799     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16800   else if (key == setup.shortcut.sound_music)
16801     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
16802 #endif
16803 }