rnd-20140307-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24
25 /* DEBUG SETTINGS */
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 /* EXPERIMENTAL STUFF */
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 /* EXPERIMENTAL STUFF */
33 #define USE_NEW_STUFF                   (                         1)
34
35 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
36 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
37 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
38 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
39 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
40 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
41 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
42 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
43 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
44 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
45 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
46 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
47 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
48 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
49 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
50 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
51 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
52 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
53 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
54
55 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
56
57 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
58
59 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
60
61 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
62 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
63
64 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
65 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
66 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
67 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
68
69 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
70
71 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
72
73 #define USE_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
74
75 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
76
77 #if USE_DELAYED_GFX_REDRAW
78 #define TEST_DrawLevelField(x, y)                               \
79         GfxRedraw[x][y] |= GFX_REDRAW_TILE
80 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
81         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
82 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
83         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
84 #define TEST_DrawTwinkleOnField(x, y)                           \
85         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
86 #else
87 #define TEST_DrawLevelField(x, y)                               \
88              DrawLevelField(x, y)
89 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
90              DrawLevelFieldCrumbled(x, y)
91 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
92              DrawLevelFieldCrumbledNeighbours(x, y)
93 #define TEST_DrawTwinkleOnField(x, y)                           \
94              DrawTwinkleOnField(x, y)
95 #endif
96
97
98 /* for DigField() */
99 #define DF_NO_PUSH              0
100 #define DF_DIG                  1
101 #define DF_SNAP                 2
102
103 /* for MovePlayer() */
104 #define MP_NO_ACTION            0
105 #define MP_MOVING               1
106 #define MP_ACTION               2
107 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
108
109 /* for ScrollPlayer() */
110 #define SCROLL_INIT             0
111 #define SCROLL_GO_ON            1
112
113 /* for Bang()/Explode() */
114 #define EX_PHASE_START          0
115 #define EX_TYPE_NONE            0
116 #define EX_TYPE_NORMAL          (1 << 0)
117 #define EX_TYPE_CENTER          (1 << 1)
118 #define EX_TYPE_BORDER          (1 << 2)
119 #define EX_TYPE_CROSS           (1 << 3)
120 #define EX_TYPE_DYNA            (1 << 4)
121 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
122
123 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
124 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
125 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
126 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
127
128 /* game panel display and control definitions */
129 #define GAME_PANEL_LEVEL_NUMBER                 0
130 #define GAME_PANEL_GEMS                         1
131 #define GAME_PANEL_INVENTORY_COUNT              2
132 #define GAME_PANEL_INVENTORY_FIRST_1            3
133 #define GAME_PANEL_INVENTORY_FIRST_2            4
134 #define GAME_PANEL_INVENTORY_FIRST_3            5
135 #define GAME_PANEL_INVENTORY_FIRST_4            6
136 #define GAME_PANEL_INVENTORY_FIRST_5            7
137 #define GAME_PANEL_INVENTORY_FIRST_6            8
138 #define GAME_PANEL_INVENTORY_FIRST_7            9
139 #define GAME_PANEL_INVENTORY_FIRST_8            10
140 #define GAME_PANEL_INVENTORY_LAST_1             11
141 #define GAME_PANEL_INVENTORY_LAST_2             12
142 #define GAME_PANEL_INVENTORY_LAST_3             13
143 #define GAME_PANEL_INVENTORY_LAST_4             14
144 #define GAME_PANEL_INVENTORY_LAST_5             15
145 #define GAME_PANEL_INVENTORY_LAST_6             16
146 #define GAME_PANEL_INVENTORY_LAST_7             17
147 #define GAME_PANEL_INVENTORY_LAST_8             18
148 #define GAME_PANEL_KEY_1                        19
149 #define GAME_PANEL_KEY_2                        20
150 #define GAME_PANEL_KEY_3                        21
151 #define GAME_PANEL_KEY_4                        22
152 #define GAME_PANEL_KEY_5                        23
153 #define GAME_PANEL_KEY_6                        24
154 #define GAME_PANEL_KEY_7                        25
155 #define GAME_PANEL_KEY_8                        26
156 #define GAME_PANEL_KEY_WHITE                    27
157 #define GAME_PANEL_KEY_WHITE_COUNT              28
158 #define GAME_PANEL_SCORE                        29
159 #define GAME_PANEL_HIGHSCORE                    30
160 #define GAME_PANEL_TIME                         31
161 #define GAME_PANEL_TIME_HH                      32
162 #define GAME_PANEL_TIME_MM                      33
163 #define GAME_PANEL_TIME_SS                      34
164 #define GAME_PANEL_FRAME                        35
165 #define GAME_PANEL_SHIELD_NORMAL                36
166 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
167 #define GAME_PANEL_SHIELD_DEADLY                38
168 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
169 #define GAME_PANEL_EXIT                         40
170 #define GAME_PANEL_EMC_MAGIC_BALL               41
171 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
172 #define GAME_PANEL_LIGHT_SWITCH                 43
173 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
174 #define GAME_PANEL_TIMEGATE_SWITCH              45
175 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
176 #define GAME_PANEL_SWITCHGATE_SWITCH            47
177 #define GAME_PANEL_EMC_LENSES                   48
178 #define GAME_PANEL_EMC_LENSES_TIME              49
179 #define GAME_PANEL_EMC_MAGNIFIER                50
180 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
181 #define GAME_PANEL_BALLOON_SWITCH               52
182 #define GAME_PANEL_DYNABOMB_NUMBER              53
183 #define GAME_PANEL_DYNABOMB_SIZE                54
184 #define GAME_PANEL_DYNABOMB_POWER               55
185 #define GAME_PANEL_PENGUINS                     56
186 #define GAME_PANEL_SOKOBAN_OBJECTS              57
187 #define GAME_PANEL_SOKOBAN_FIELDS               58
188 #define GAME_PANEL_ROBOT_WHEEL                  59
189 #define GAME_PANEL_CONVEYOR_BELT_1              60
190 #define GAME_PANEL_CONVEYOR_BELT_2              61
191 #define GAME_PANEL_CONVEYOR_BELT_3              62
192 #define GAME_PANEL_CONVEYOR_BELT_4              63
193 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
194 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
195 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
196 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
197 #define GAME_PANEL_MAGIC_WALL                   68
198 #define GAME_PANEL_MAGIC_WALL_TIME              69
199 #define GAME_PANEL_GRAVITY_STATE                70
200 #define GAME_PANEL_GRAPHIC_1                    71
201 #define GAME_PANEL_GRAPHIC_2                    72
202 #define GAME_PANEL_GRAPHIC_3                    73
203 #define GAME_PANEL_GRAPHIC_4                    74
204 #define GAME_PANEL_GRAPHIC_5                    75
205 #define GAME_PANEL_GRAPHIC_6                    76
206 #define GAME_PANEL_GRAPHIC_7                    77
207 #define GAME_PANEL_GRAPHIC_8                    78
208 #define GAME_PANEL_ELEMENT_1                    79
209 #define GAME_PANEL_ELEMENT_2                    80
210 #define GAME_PANEL_ELEMENT_3                    81
211 #define GAME_PANEL_ELEMENT_4                    82
212 #define GAME_PANEL_ELEMENT_5                    83
213 #define GAME_PANEL_ELEMENT_6                    84
214 #define GAME_PANEL_ELEMENT_7                    85
215 #define GAME_PANEL_ELEMENT_8                    86
216 #define GAME_PANEL_ELEMENT_COUNT_1              87
217 #define GAME_PANEL_ELEMENT_COUNT_2              88
218 #define GAME_PANEL_ELEMENT_COUNT_3              89
219 #define GAME_PANEL_ELEMENT_COUNT_4              90
220 #define GAME_PANEL_ELEMENT_COUNT_5              91
221 #define GAME_PANEL_ELEMENT_COUNT_6              92
222 #define GAME_PANEL_ELEMENT_COUNT_7              93
223 #define GAME_PANEL_ELEMENT_COUNT_8              94
224 #define GAME_PANEL_CE_SCORE_1                   95
225 #define GAME_PANEL_CE_SCORE_2                   96
226 #define GAME_PANEL_CE_SCORE_3                   97
227 #define GAME_PANEL_CE_SCORE_4                   98
228 #define GAME_PANEL_CE_SCORE_5                   99
229 #define GAME_PANEL_CE_SCORE_6                   100
230 #define GAME_PANEL_CE_SCORE_7                   101
231 #define GAME_PANEL_CE_SCORE_8                   102
232 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
233 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
234 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
235 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
236 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
237 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
238 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
239 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
240 #define GAME_PANEL_PLAYER_NAME                  111
241 #define GAME_PANEL_LEVEL_NAME                   112
242 #define GAME_PANEL_LEVEL_AUTHOR                 113
243
244 #define NUM_GAME_PANEL_CONTROLS                 114
245
246 struct GamePanelOrderInfo
247 {
248   int nr;
249   int sort_priority;
250 };
251
252 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
253
254 struct GamePanelControlInfo
255 {
256   int nr;
257
258   struct TextPosInfo *pos;
259   int type;
260
261   int value, last_value;
262   int frame, last_frame;
263   int gfx_frame;
264   int gfx_random;
265 };
266
267 static struct GamePanelControlInfo game_panel_controls[] =
268 {
269   {
270     GAME_PANEL_LEVEL_NUMBER,
271     &game.panel.level_number,
272     TYPE_INTEGER,
273   },
274   {
275     GAME_PANEL_GEMS,
276     &game.panel.gems,
277     TYPE_INTEGER,
278   },
279   {
280     GAME_PANEL_INVENTORY_COUNT,
281     &game.panel.inventory_count,
282     TYPE_INTEGER,
283   },
284   {
285     GAME_PANEL_INVENTORY_FIRST_1,
286     &game.panel.inventory_first[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_FIRST_2,
291     &game.panel.inventory_first[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_FIRST_3,
296     &game.panel.inventory_first[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_FIRST_4,
301     &game.panel.inventory_first[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_FIRST_5,
306     &game.panel.inventory_first[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_FIRST_6,
311     &game.panel.inventory_first[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_FIRST_7,
316     &game.panel.inventory_first[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_FIRST_8,
321     &game.panel.inventory_first[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_INVENTORY_LAST_1,
326     &game.panel.inventory_last[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_INVENTORY_LAST_2,
331     &game.panel.inventory_last[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_INVENTORY_LAST_3,
336     &game.panel.inventory_last[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_INVENTORY_LAST_4,
341     &game.panel.inventory_last[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_INVENTORY_LAST_5,
346     &game.panel.inventory_last[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_INVENTORY_LAST_6,
351     &game.panel.inventory_last[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_INVENTORY_LAST_7,
356     &game.panel.inventory_last[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_INVENTORY_LAST_8,
361     &game.panel.inventory_last[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_1,
366     &game.panel.key[0],
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_2,
371     &game.panel.key[1],
372     TYPE_ELEMENT,
373   },
374   {
375     GAME_PANEL_KEY_3,
376     &game.panel.key[2],
377     TYPE_ELEMENT,
378   },
379   {
380     GAME_PANEL_KEY_4,
381     &game.panel.key[3],
382     TYPE_ELEMENT,
383   },
384   {
385     GAME_PANEL_KEY_5,
386     &game.panel.key[4],
387     TYPE_ELEMENT,
388   },
389   {
390     GAME_PANEL_KEY_6,
391     &game.panel.key[5],
392     TYPE_ELEMENT,
393   },
394   {
395     GAME_PANEL_KEY_7,
396     &game.panel.key[6],
397     TYPE_ELEMENT,
398   },
399   {
400     GAME_PANEL_KEY_8,
401     &game.panel.key[7],
402     TYPE_ELEMENT,
403   },
404   {
405     GAME_PANEL_KEY_WHITE,
406     &game.panel.key_white,
407     TYPE_ELEMENT,
408   },
409   {
410     GAME_PANEL_KEY_WHITE_COUNT,
411     &game.panel.key_white_count,
412     TYPE_INTEGER,
413   },
414   {
415     GAME_PANEL_SCORE,
416     &game.panel.score,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_HIGHSCORE,
421     &game.panel.highscore,
422     TYPE_INTEGER,
423   },
424   {
425     GAME_PANEL_TIME,
426     &game.panel.time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_TIME_HH,
431     &game.panel.time_hh,
432     TYPE_INTEGER,
433   },
434   {
435     GAME_PANEL_TIME_MM,
436     &game.panel.time_mm,
437     TYPE_INTEGER,
438   },
439   {
440     GAME_PANEL_TIME_SS,
441     &game.panel.time_ss,
442     TYPE_INTEGER,
443   },
444   {
445     GAME_PANEL_FRAME,
446     &game.panel.frame,
447     TYPE_INTEGER,
448   },
449   {
450     GAME_PANEL_SHIELD_NORMAL,
451     &game.panel.shield_normal,
452     TYPE_ELEMENT,
453   },
454   {
455     GAME_PANEL_SHIELD_NORMAL_TIME,
456     &game.panel.shield_normal_time,
457     TYPE_INTEGER,
458   },
459   {
460     GAME_PANEL_SHIELD_DEADLY,
461     &game.panel.shield_deadly,
462     TYPE_ELEMENT,
463   },
464   {
465     GAME_PANEL_SHIELD_DEADLY_TIME,
466     &game.panel.shield_deadly_time,
467     TYPE_INTEGER,
468   },
469   {
470     GAME_PANEL_EXIT,
471     &game.panel.exit,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_MAGIC_BALL,
476     &game.panel.emc_magic_ball,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
481     &game.panel.emc_magic_ball_switch,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_LIGHT_SWITCH,
486     &game.panel.light_switch,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_PANEL_LIGHT_SWITCH_TIME,
491     &game.panel.light_switch_time,
492     TYPE_INTEGER,
493   },
494   {
495     GAME_PANEL_TIMEGATE_SWITCH,
496     &game.panel.timegate_switch,
497     TYPE_ELEMENT,
498   },
499   {
500     GAME_PANEL_TIMEGATE_SWITCH_TIME,
501     &game.panel.timegate_switch_time,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_SWITCHGATE_SWITCH,
506     &game.panel.switchgate_switch,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_EMC_LENSES,
511     &game.panel.emc_lenses,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_PANEL_EMC_LENSES_TIME,
516     &game.panel.emc_lenses_time,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_EMC_MAGNIFIER,
521     &game.panel.emc_magnifier,
522     TYPE_ELEMENT,
523   },
524   {
525     GAME_PANEL_EMC_MAGNIFIER_TIME,
526     &game.panel.emc_magnifier_time,
527     TYPE_INTEGER,
528   },
529   {
530     GAME_PANEL_BALLOON_SWITCH,
531     &game.panel.balloon_switch,
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_DYNABOMB_NUMBER,
536     &game.panel.dynabomb_number,
537     TYPE_INTEGER,
538   },
539   {
540     GAME_PANEL_DYNABOMB_SIZE,
541     &game.panel.dynabomb_size,
542     TYPE_INTEGER,
543   },
544   {
545     GAME_PANEL_DYNABOMB_POWER,
546     &game.panel.dynabomb_power,
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_PENGUINS,
551     &game.panel.penguins,
552     TYPE_INTEGER,
553   },
554   {
555     GAME_PANEL_SOKOBAN_OBJECTS,
556     &game.panel.sokoban_objects,
557     TYPE_INTEGER,
558   },
559   {
560     GAME_PANEL_SOKOBAN_FIELDS,
561     &game.panel.sokoban_fields,
562     TYPE_INTEGER,
563   },
564   {
565     GAME_PANEL_ROBOT_WHEEL,
566     &game.panel.robot_wheel,
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_CONVEYOR_BELT_1,
571     &game.panel.conveyor_belt[0],
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_CONVEYOR_BELT_2,
576     &game.panel.conveyor_belt[1],
577     TYPE_ELEMENT,
578   },
579   {
580     GAME_PANEL_CONVEYOR_BELT_3,
581     &game.panel.conveyor_belt[2],
582     TYPE_ELEMENT,
583   },
584   {
585     GAME_PANEL_CONVEYOR_BELT_4,
586     &game.panel.conveyor_belt[3],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
591     &game.panel.conveyor_belt_switch[0],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
596     &game.panel.conveyor_belt_switch[1],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
601     &game.panel.conveyor_belt_switch[2],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
606     &game.panel.conveyor_belt_switch[3],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_MAGIC_WALL,
611     &game.panel.magic_wall,
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_MAGIC_WALL_TIME,
616     &game.panel.magic_wall_time,
617     TYPE_INTEGER,
618   },
619   {
620     GAME_PANEL_GRAVITY_STATE,
621     &game.panel.gravity_state,
622     TYPE_STRING,
623   },
624   {
625     GAME_PANEL_GRAPHIC_1,
626     &game.panel.graphic[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_GRAPHIC_2,
631     &game.panel.graphic[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_GRAPHIC_3,
636     &game.panel.graphic[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_GRAPHIC_4,
641     &game.panel.graphic[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_GRAPHIC_5,
646     &game.panel.graphic[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_GRAPHIC_6,
651     &game.panel.graphic[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_7,
656     &game.panel.graphic[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_GRAPHIC_8,
661     &game.panel.graphic[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_1,
666     &game.panel.element[0],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_ELEMENT_2,
671     &game.panel.element[1],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_ELEMENT_3,
676     &game.panel.element[2],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_ELEMENT_4,
681     &game.panel.element[3],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_ELEMENT_5,
686     &game.panel.element[4],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_6,
691     &game.panel.element[5],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_7,
696     &game.panel.element[6],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_8,
701     &game.panel.element[7],
702     TYPE_ELEMENT,
703   },
704   {
705     GAME_PANEL_ELEMENT_COUNT_1,
706     &game.panel.element_count[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_ELEMENT_COUNT_2,
711     &game.panel.element_count[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_ELEMENT_COUNT_3,
716     &game.panel.element_count[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_ELEMENT_COUNT_4,
721     &game.panel.element_count[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_ELEMENT_COUNT_5,
726     &game.panel.element_count[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_6,
731     &game.panel.element_count[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_7,
736     &game.panel.element_count[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_ELEMENT_COUNT_8,
741     &game.panel.element_count[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1,
746     &game.panel.ce_score[0],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2,
751     &game.panel.ce_score[1],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3,
756     &game.panel.ce_score[2],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4,
761     &game.panel.ce_score[3],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5,
766     &game.panel.ce_score[4],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6,
771     &game.panel.ce_score[5],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7,
776     &game.panel.ce_score[6],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8,
781     &game.panel.ce_score[7],
782     TYPE_INTEGER,
783   },
784   {
785     GAME_PANEL_CE_SCORE_1_ELEMENT,
786     &game.panel.ce_score_element[0],
787     TYPE_ELEMENT,
788   },
789   {
790     GAME_PANEL_CE_SCORE_2_ELEMENT,
791     &game.panel.ce_score_element[1],
792     TYPE_ELEMENT,
793   },
794   {
795     GAME_PANEL_CE_SCORE_3_ELEMENT,
796     &game.panel.ce_score_element[2],
797     TYPE_ELEMENT,
798   },
799   {
800     GAME_PANEL_CE_SCORE_4_ELEMENT,
801     &game.panel.ce_score_element[3],
802     TYPE_ELEMENT,
803   },
804   {
805     GAME_PANEL_CE_SCORE_5_ELEMENT,
806     &game.panel.ce_score_element[4],
807     TYPE_ELEMENT,
808   },
809   {
810     GAME_PANEL_CE_SCORE_6_ELEMENT,
811     &game.panel.ce_score_element[5],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_7_ELEMENT,
816     &game.panel.ce_score_element[6],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_CE_SCORE_8_ELEMENT,
821     &game.panel.ce_score_element[7],
822     TYPE_ELEMENT,
823   },
824   {
825     GAME_PANEL_PLAYER_NAME,
826     &game.panel.player_name,
827     TYPE_STRING,
828   },
829   {
830     GAME_PANEL_LEVEL_NAME,
831     &game.panel.level_name,
832     TYPE_STRING,
833   },
834   {
835     GAME_PANEL_LEVEL_AUTHOR,
836     &game.panel.level_author,
837     TYPE_STRING,
838   },
839
840   {
841     -1,
842     NULL,
843     -1,
844   }
845 };
846
847 /* values for delayed check of falling and moving elements and for collision */
848 #define CHECK_DELAY_MOVING      3
849 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
850 #define CHECK_DELAY_COLLISION   2
851 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
852
853 /* values for initial player move delay (initial delay counter value) */
854 #define INITIAL_MOVE_DELAY_OFF  -1
855 #define INITIAL_MOVE_DELAY_ON   0
856
857 /* values for player movement speed (which is in fact a delay value) */
858 #define MOVE_DELAY_MIN_SPEED    32
859 #define MOVE_DELAY_NORMAL_SPEED 8
860 #define MOVE_DELAY_HIGH_SPEED   4
861 #define MOVE_DELAY_MAX_SPEED    1
862
863 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
864 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
865
866 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
867 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
868
869 /* values for other actions */
870 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
871 #define MOVE_STEPSIZE_MIN       (1)
872 #define MOVE_STEPSIZE_MAX       (TILEX)
873
874 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
875 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
876
877 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
878
879 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
880                                  RND(element_info[e].push_delay_random))
881 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
882                                  RND(element_info[e].drop_delay_random))
883 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
884                                  RND(element_info[e].move_delay_random))
885 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
886                                     (element_info[e].move_delay_random))
887 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
888                                  RND(element_info[e].ce_value_random_initial))
889 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
890 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
891                                  RND((c)->delay_random * (c)->delay_frames))
892 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
893                                  RND((c)->delay_random))
894
895
896 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
897          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
898
899 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
900         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
901          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
902          (be) + (e) - EL_SELF)
903
904 #define GET_PLAYER_FROM_BITS(p)                                         \
905         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
906
907 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
908         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
909          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
910          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
911          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
912          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
913          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
914          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
915          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
916          (e))
917
918 #define CAN_GROW_INTO(e)                                                \
919         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
920
921 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
922                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
923                                         (condition)))
924
925 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
926                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
927                                         (CAN_MOVE_INTO_ACID(e) &&       \
928                                          Feld[x][y] == EL_ACID) ||      \
929                                         (condition)))
930
931 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
932                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
933                                         (CAN_MOVE_INTO_ACID(e) &&       \
934                                          Feld[x][y] == EL_ACID) ||      \
935                                         (condition)))
936
937 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
938                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
939                                         (condition) ||                  \
940                                         (CAN_MOVE_INTO_ACID(e) &&       \
941                                          Feld[x][y] == EL_ACID) ||      \
942                                         (DONT_COLLIDE_WITH(e) &&        \
943                                          IS_PLAYER(x, y) &&             \
944                                          !PLAYER_ENEMY_PROTECTED(x, y))))
945
946 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
947         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
948
949 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
950         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
951
952 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
953         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
954
955 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
956         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
957                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
958
959 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
960         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
961
962 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
963         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
964
965 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
966         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
967
968 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
969         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
970
971 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
972         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
973
974 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
975         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
976                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
977                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
978                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
979                                                  IS_FOOD_PENGUIN(Feld[x][y])))
980 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
981         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
982
983 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
984         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
985
986 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
987         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
988
989 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
990         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
991                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
992
993 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
994
995 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
996                 (!IS_PLAYER(x, y) &&                                    \
997                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
998
999 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1000         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1001
1002 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1003 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1004
1005 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1006 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1008 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1009
1010 /* game button identifiers */
1011 #define GAME_CTRL_ID_STOP               0
1012 #define GAME_CTRL_ID_PAUSE              1
1013 #define GAME_CTRL_ID_PLAY               2
1014 #define SOUND_CTRL_ID_MUSIC             3
1015 #define SOUND_CTRL_ID_LOOPS             4
1016 #define SOUND_CTRL_ID_SIMPLE            5
1017 #define GAME_CTRL_ID_SAVE               6
1018 #define GAME_CTRL_ID_LOAD               7
1019
1020 #define NUM_GAME_BUTTONS                8
1021
1022
1023 /* forward declaration for internal use */
1024
1025 static void CreateField(int, int, int);
1026
1027 static void ResetGfxAnimation(int, int);
1028
1029 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1030 static void AdvanceFrameAndPlayerCounters(int);
1031
1032 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1033 static boolean MovePlayer(struct PlayerInfo *, int, int);
1034 static void ScrollPlayer(struct PlayerInfo *, int);
1035 static void ScrollScreen(struct PlayerInfo *, int);
1036
1037 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1038 static boolean DigFieldByCE(int, int, int);
1039 static boolean SnapField(struct PlayerInfo *, int, int);
1040 static boolean DropElement(struct PlayerInfo *);
1041
1042 static void InitBeltMovement(void);
1043 static void CloseAllOpenTimegates(void);
1044 static void CheckGravityMovement(struct PlayerInfo *);
1045 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1046 static void KillPlayerUnlessEnemyProtected(int, int);
1047 static void KillPlayerUnlessExplosionProtected(int, int);
1048
1049 static void TestIfPlayerTouchesCustomElement(int, int);
1050 static void TestIfElementTouchesCustomElement(int, int);
1051 static void TestIfElementHitsCustomElement(int, int, int);
1052 #if 0
1053 static void TestIfElementSmashesCustomElement(int, int, int);
1054 #endif
1055
1056 static void HandleElementChange(int, int, int);
1057 static void ExecuteCustomElementAction(int, int, int, int);
1058 static boolean ChangeElement(int, int, int, int);
1059
1060 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1061 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1062         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1063 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1064         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1065 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1066         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1067 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1068         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1069
1070 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1071 #define CheckElementChange(x, y, e, te, ev)                             \
1072         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1073 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1074         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1075 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1076         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1077
1078 static void PlayLevelSound(int, int, int);
1079 static void PlayLevelSoundNearest(int, int, int);
1080 static void PlayLevelSoundAction(int, int, int);
1081 static void PlayLevelSoundElementAction(int, int, int, int);
1082 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1083 static void PlayLevelSoundActionIfLoop(int, int, int);
1084 static void StopLevelSoundActionIfLoop(int, int, int);
1085 static void PlayLevelMusic();
1086
1087 static void HandleGameButtons(struct GadgetInfo *);
1088
1089 int AmoebeNachbarNr(int, int);
1090 void AmoebeUmwandeln(int, int);
1091 void ContinueMoving(int, int);
1092 void Bang(int, int);
1093 void InitMovDir(int, int);
1094 void InitAmoebaNr(int, int);
1095 int NewHiScore(void);
1096
1097 void TestIfGoodThingHitsBadThing(int, int, int);
1098 void TestIfBadThingHitsGoodThing(int, int, int);
1099 void TestIfPlayerTouchesBadThing(int, int);
1100 void TestIfPlayerRunsIntoBadThing(int, int, int);
1101 void TestIfBadThingTouchesPlayer(int, int);
1102 void TestIfBadThingRunsIntoPlayer(int, int, int);
1103 void TestIfFriendTouchesBadThing(int, int);
1104 void TestIfBadThingTouchesFriend(int, int);
1105 void TestIfBadThingTouchesOtherBadThing(int, int);
1106 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1107
1108 void KillPlayer(struct PlayerInfo *);
1109 void BuryPlayer(struct PlayerInfo *);
1110 void RemovePlayer(struct PlayerInfo *);
1111
1112 static int getInvisibleActiveFromInvisibleElement(int);
1113 static int getInvisibleFromInvisibleActiveElement(int);
1114
1115 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1116
1117 /* for detection of endless loops, caused by custom element programming */
1118 /* (using maximal playfield width x 10 is just a rough approximation) */
1119 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1120
1121 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1122 {                                                                       \
1123   if (recursion_loop_detected)                                          \
1124     return (rc);                                                        \
1125                                                                         \
1126   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1127   {                                                                     \
1128     recursion_loop_detected = TRUE;                                     \
1129     recursion_loop_element = (e);                                       \
1130   }                                                                     \
1131                                                                         \
1132   recursion_loop_depth++;                                               \
1133 }
1134
1135 #define RECURSION_LOOP_DETECTION_END()                                  \
1136 {                                                                       \
1137   recursion_loop_depth--;                                               \
1138 }
1139
1140 static int recursion_loop_depth;
1141 static boolean recursion_loop_detected;
1142 static boolean recursion_loop_element;
1143
1144 static int map_player_action[MAX_PLAYERS];
1145
1146
1147 /* ------------------------------------------------------------------------- */
1148 /* definition of elements that automatically change to other elements after  */
1149 /* a specified time, eventually calling a function when changing             */
1150 /* ------------------------------------------------------------------------- */
1151
1152 /* forward declaration for changer functions */
1153 static void InitBuggyBase(int, int);
1154 static void WarnBuggyBase(int, int);
1155
1156 static void InitTrap(int, int);
1157 static void ActivateTrap(int, int);
1158 static void ChangeActiveTrap(int, int);
1159
1160 static void InitRobotWheel(int, int);
1161 static void RunRobotWheel(int, int);
1162 static void StopRobotWheel(int, int);
1163
1164 static void InitTimegateWheel(int, int);
1165 static void RunTimegateWheel(int, int);
1166
1167 static void InitMagicBallDelay(int, int);
1168 static void ActivateMagicBall(int, int);
1169
1170 struct ChangingElementInfo
1171 {
1172   int element;
1173   int target_element;
1174   int change_delay;
1175   void (*pre_change_function)(int x, int y);
1176   void (*change_function)(int x, int y);
1177   void (*post_change_function)(int x, int y);
1178 };
1179
1180 static struct ChangingElementInfo change_delay_list[] =
1181 {
1182   {
1183     EL_NUT_BREAKING,
1184     EL_EMERALD,
1185     6,
1186     NULL,
1187     NULL,
1188     NULL
1189   },
1190   {
1191     EL_PEARL_BREAKING,
1192     EL_EMPTY,
1193     8,
1194     NULL,
1195     NULL,
1196     NULL
1197   },
1198   {
1199     EL_EXIT_OPENING,
1200     EL_EXIT_OPEN,
1201     29,
1202     NULL,
1203     NULL,
1204     NULL
1205   },
1206   {
1207     EL_EXIT_CLOSING,
1208     EL_EXIT_CLOSED,
1209     29,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_STEEL_EXIT_OPENING,
1216     EL_STEEL_EXIT_OPEN,
1217     29,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_STEEL_EXIT_CLOSING,
1224     EL_STEEL_EXIT_CLOSED,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_EM_EXIT_OPENING,
1232     EL_EM_EXIT_OPEN,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_EM_EXIT_CLOSING,
1240 #if 1
1241     EL_EMPTY,
1242 #else
1243     EL_EM_EXIT_CLOSED,
1244 #endif
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_OPENING,
1252     EL_EM_STEEL_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_STEEL_EXIT_CLOSING,
1260 #if 1
1261     EL_STEELWALL,
1262 #else
1263     EL_EM_STEEL_EXIT_CLOSED,
1264 #endif
1265     29,
1266     NULL,
1267     NULL,
1268     NULL
1269   },
1270   {
1271     EL_SP_EXIT_OPENING,
1272     EL_SP_EXIT_OPEN,
1273     29,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_SP_EXIT_CLOSING,
1280     EL_SP_EXIT_CLOSED,
1281     29,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_SWITCHGATE_OPENING,
1288     EL_SWITCHGATE_OPEN,
1289     29,
1290     NULL,
1291     NULL,
1292     NULL
1293   },
1294   {
1295     EL_SWITCHGATE_CLOSING,
1296     EL_SWITCHGATE_CLOSED,
1297     29,
1298     NULL,
1299     NULL,
1300     NULL
1301   },
1302   {
1303     EL_TIMEGATE_OPENING,
1304     EL_TIMEGATE_OPEN,
1305     29,
1306     NULL,
1307     NULL,
1308     NULL
1309   },
1310   {
1311     EL_TIMEGATE_CLOSING,
1312     EL_TIMEGATE_CLOSED,
1313     29,
1314     NULL,
1315     NULL,
1316     NULL
1317   },
1318
1319   {
1320     EL_ACID_SPLASH_LEFT,
1321     EL_EMPTY,
1322     8,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327   {
1328     EL_ACID_SPLASH_RIGHT,
1329     EL_EMPTY,
1330     8,
1331     NULL,
1332     NULL,
1333     NULL
1334   },
1335   {
1336     EL_SP_BUGGY_BASE,
1337     EL_SP_BUGGY_BASE_ACTIVATING,
1338     0,
1339     InitBuggyBase,
1340     NULL,
1341     NULL
1342   },
1343   {
1344     EL_SP_BUGGY_BASE_ACTIVATING,
1345     EL_SP_BUGGY_BASE_ACTIVE,
1346     0,
1347     InitBuggyBase,
1348     NULL,
1349     NULL
1350   },
1351   {
1352     EL_SP_BUGGY_BASE_ACTIVE,
1353     EL_SP_BUGGY_BASE,
1354     0,
1355     InitBuggyBase,
1356     WarnBuggyBase,
1357     NULL
1358   },
1359   {
1360     EL_TRAP,
1361     EL_TRAP_ACTIVE,
1362     0,
1363     InitTrap,
1364     NULL,
1365     ActivateTrap
1366   },
1367   {
1368     EL_TRAP_ACTIVE,
1369     EL_TRAP,
1370     31,
1371     NULL,
1372     ChangeActiveTrap,
1373     NULL
1374   },
1375   {
1376     EL_ROBOT_WHEEL_ACTIVE,
1377     EL_ROBOT_WHEEL,
1378     0,
1379     InitRobotWheel,
1380     RunRobotWheel,
1381     StopRobotWheel
1382   },
1383   {
1384     EL_TIMEGATE_SWITCH_ACTIVE,
1385     EL_TIMEGATE_SWITCH,
1386     0,
1387     InitTimegateWheel,
1388     RunTimegateWheel,
1389     NULL
1390   },
1391   {
1392     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1393     EL_DC_TIMEGATE_SWITCH,
1394     0,
1395     InitTimegateWheel,
1396     RunTimegateWheel,
1397     NULL
1398   },
1399   {
1400     EL_EMC_MAGIC_BALL_ACTIVE,
1401     EL_EMC_MAGIC_BALL_ACTIVE,
1402     0,
1403     InitMagicBallDelay,
1404     NULL,
1405     ActivateMagicBall
1406   },
1407   {
1408     EL_EMC_SPRING_BUMPER_ACTIVE,
1409     EL_EMC_SPRING_BUMPER,
1410     8,
1411     NULL,
1412     NULL,
1413     NULL
1414   },
1415   {
1416     EL_DIAGONAL_SHRINKING,
1417     EL_UNDEFINED,
1418     0,
1419     NULL,
1420     NULL,
1421     NULL
1422   },
1423   {
1424     EL_DIAGONAL_GROWING,
1425     EL_UNDEFINED,
1426     0,
1427     NULL,
1428     NULL,
1429     NULL,
1430   },
1431
1432   {
1433     EL_UNDEFINED,
1434     EL_UNDEFINED,
1435     -1,
1436     NULL,
1437     NULL,
1438     NULL
1439   }
1440 };
1441
1442 struct
1443 {
1444   int element;
1445   int push_delay_fixed, push_delay_random;
1446 }
1447 push_delay_list[] =
1448 {
1449   { EL_SPRING,                  0, 0 },
1450   { EL_BALLOON,                 0, 0 },
1451
1452   { EL_SOKOBAN_OBJECT,          2, 0 },
1453   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1454   { EL_SATELLITE,               2, 0 },
1455   { EL_SP_DISK_YELLOW,          2, 0 },
1456
1457   { EL_UNDEFINED,               0, 0 },
1458 };
1459
1460 struct
1461 {
1462   int element;
1463   int move_stepsize;
1464 }
1465 move_stepsize_list[] =
1466 {
1467   { EL_AMOEBA_DROP,             2 },
1468   { EL_AMOEBA_DROPPING,         2 },
1469   { EL_QUICKSAND_FILLING,       1 },
1470   { EL_QUICKSAND_EMPTYING,      1 },
1471   { EL_QUICKSAND_FAST_FILLING,  2 },
1472   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1473   { EL_MAGIC_WALL_FILLING,      2 },
1474   { EL_MAGIC_WALL_EMPTYING,     2 },
1475   { EL_BD_MAGIC_WALL_FILLING,   2 },
1476   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1477   { EL_DC_MAGIC_WALL_FILLING,   2 },
1478   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1479
1480   { EL_UNDEFINED,               0 },
1481 };
1482
1483 struct
1484 {
1485   int element;
1486   int count;
1487 }
1488 collect_count_list[] =
1489 {
1490   { EL_EMERALD,                 1 },
1491   { EL_BD_DIAMOND,              1 },
1492   { EL_EMERALD_YELLOW,          1 },
1493   { EL_EMERALD_RED,             1 },
1494   { EL_EMERALD_PURPLE,          1 },
1495   { EL_DIAMOND,                 3 },
1496   { EL_SP_INFOTRON,             1 },
1497   { EL_PEARL,                   5 },
1498   { EL_CRYSTAL,                 8 },
1499
1500   { EL_UNDEFINED,               0 },
1501 };
1502
1503 struct
1504 {
1505   int element;
1506   int direction;
1507 }
1508 access_direction_list[] =
1509 {
1510   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1513   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1514   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1515   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1516   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1517   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1518   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1519   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1520   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1521
1522   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1523   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1524   { EL_SP_PORT_UP,                                                   MV_DOWN },
1525   { EL_SP_PORT_DOWN,                                         MV_UP           },
1526   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1527   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1528   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1530   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1531   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1532   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1533   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1534   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1535   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1536   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1537   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1538   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1539   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1540   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1541
1542   { EL_UNDEFINED,                       MV_NONE                              }
1543 };
1544
1545 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1546
1547 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1548 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1549 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1550                                  IS_JUST_CHANGING(x, y))
1551
1552 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1553
1554 /* static variables for playfield scan mode (scanning forward or backward) */
1555 static int playfield_scan_start_x = 0;
1556 static int playfield_scan_start_y = 0;
1557 static int playfield_scan_delta_x = 1;
1558 static int playfield_scan_delta_y = 1;
1559
1560 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1561                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1562                                      (y) += playfield_scan_delta_y)     \
1563                                 for ((x) = playfield_scan_start_x;      \
1564                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1565                                      (x) += playfield_scan_delta_x)
1566
1567 #ifdef DEBUG
1568 void DEBUG_SetMaximumDynamite()
1569 {
1570   int i;
1571
1572   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1573     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1574       local_player->inventory_element[local_player->inventory_size++] =
1575         EL_DYNAMITE;
1576 }
1577 #endif
1578
1579 static void InitPlayfieldScanModeVars()
1580 {
1581   if (game.use_reverse_scan_direction)
1582   {
1583     playfield_scan_start_x = lev_fieldx - 1;
1584     playfield_scan_start_y = lev_fieldy - 1;
1585
1586     playfield_scan_delta_x = -1;
1587     playfield_scan_delta_y = -1;
1588   }
1589   else
1590   {
1591     playfield_scan_start_x = 0;
1592     playfield_scan_start_y = 0;
1593
1594     playfield_scan_delta_x = 1;
1595     playfield_scan_delta_y = 1;
1596   }
1597 }
1598
1599 static void InitPlayfieldScanMode(int mode)
1600 {
1601   game.use_reverse_scan_direction =
1602     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1603
1604   InitPlayfieldScanModeVars();
1605 }
1606
1607 static int get_move_delay_from_stepsize(int move_stepsize)
1608 {
1609   move_stepsize =
1610     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1611
1612   /* make sure that stepsize value is always a power of 2 */
1613   move_stepsize = (1 << log_2(move_stepsize));
1614
1615   return TILEX / move_stepsize;
1616 }
1617
1618 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1619                                boolean init_game)
1620 {
1621   int player_nr = player->index_nr;
1622   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1623   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1624
1625   /* do no immediately change move delay -- the player might just be moving */
1626   player->move_delay_value_next = move_delay;
1627
1628   /* information if player can move must be set separately */
1629   player->cannot_move = cannot_move;
1630
1631   if (init_game)
1632   {
1633     player->move_delay       = game.initial_move_delay[player_nr];
1634     player->move_delay_value = game.initial_move_delay_value[player_nr];
1635
1636     player->move_delay_value_next = -1;
1637
1638     player->move_delay_reset_counter = 0;
1639   }
1640 }
1641
1642 void GetPlayerConfig()
1643 {
1644   GameFrameDelay = setup.game_frame_delay;
1645
1646   if (!audio.sound_available)
1647     setup.sound_simple = FALSE;
1648
1649   if (!audio.loops_available)
1650     setup.sound_loops = FALSE;
1651
1652   if (!audio.music_available)
1653     setup.sound_music = FALSE;
1654
1655   if (!video.fullscreen_available)
1656     setup.fullscreen = FALSE;
1657
1658   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1659
1660   SetAudioMode(setup.sound);
1661   InitJoysticks();
1662 }
1663
1664 int GetElementFromGroupElement(int element)
1665 {
1666   if (IS_GROUP_ELEMENT(element))
1667   {
1668     struct ElementGroupInfo *group = element_info[element].group;
1669     int last_anim_random_frame = gfx.anim_random_frame;
1670     int element_pos;
1671
1672     if (group->choice_mode == ANIM_RANDOM)
1673       gfx.anim_random_frame = RND(group->num_elements_resolved);
1674
1675     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1676                                     group->choice_mode, 0,
1677                                     group->choice_pos);
1678
1679     if (group->choice_mode == ANIM_RANDOM)
1680       gfx.anim_random_frame = last_anim_random_frame;
1681
1682     group->choice_pos++;
1683
1684     element = group->element_resolved[element_pos];
1685   }
1686
1687   return element;
1688 }
1689
1690 static void InitPlayerField(int x, int y, int element, boolean init_game)
1691 {
1692   if (element == EL_SP_MURPHY)
1693   {
1694     if (init_game)
1695     {
1696       if (stored_player[0].present)
1697       {
1698         Feld[x][y] = EL_SP_MURPHY_CLONE;
1699
1700         return;
1701       }
1702       else
1703       {
1704         stored_player[0].initial_element = element;
1705         stored_player[0].use_murphy = TRUE;
1706
1707         if (!level.use_artwork_element[0])
1708           stored_player[0].artwork_element = EL_SP_MURPHY;
1709       }
1710
1711       Feld[x][y] = EL_PLAYER_1;
1712     }
1713   }
1714
1715   if (init_game)
1716   {
1717     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1718     int jx = player->jx, jy = player->jy;
1719
1720     player->present = TRUE;
1721
1722     player->block_last_field = (element == EL_SP_MURPHY ?
1723                                 level.sp_block_last_field :
1724                                 level.block_last_field);
1725
1726     /* ---------- initialize player's last field block delay --------------- */
1727
1728     /* always start with reliable default value (no adjustment needed) */
1729     player->block_delay_adjustment = 0;
1730
1731     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1732     if (player->block_last_field && element == EL_SP_MURPHY)
1733       player->block_delay_adjustment = 1;
1734
1735     /* special case 2: in game engines before 3.1.1, blocking was different */
1736     if (game.use_block_last_field_bug)
1737       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1738
1739     if (!options.network || player->connected)
1740     {
1741       player->active = TRUE;
1742
1743       /* remove potentially duplicate players */
1744       if (StorePlayer[jx][jy] == Feld[x][y])
1745         StorePlayer[jx][jy] = 0;
1746
1747       StorePlayer[x][y] = Feld[x][y];
1748
1749 #if DEBUG_INIT_PLAYER
1750       if (options.debug)
1751       {
1752         printf("- player element %d activated", player->element_nr);
1753         printf(" (local player is %d and currently %s)\n",
1754                local_player->element_nr,
1755                local_player->active ? "active" : "not active");
1756       }
1757     }
1758 #endif
1759
1760     Feld[x][y] = EL_EMPTY;
1761
1762     player->jx = player->last_jx = x;
1763     player->jy = player->last_jy = y;
1764   }
1765
1766 #if USE_PLAYER_REANIMATION
1767   if (!init_game)
1768   {
1769     int player_nr = GET_PLAYER_NR(element);
1770     struct PlayerInfo *player = &stored_player[player_nr];
1771
1772     if (player->active && player->killed)
1773       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1774   }
1775 #endif
1776 }
1777
1778 static void InitField(int x, int y, boolean init_game)
1779 {
1780   int element = Feld[x][y];
1781
1782   switch (element)
1783   {
1784     case EL_SP_MURPHY:
1785     case EL_PLAYER_1:
1786     case EL_PLAYER_2:
1787     case EL_PLAYER_3:
1788     case EL_PLAYER_4:
1789       InitPlayerField(x, y, element, init_game);
1790       break;
1791
1792     case EL_SOKOBAN_FIELD_PLAYER:
1793       element = Feld[x][y] = EL_PLAYER_1;
1794       InitField(x, y, init_game);
1795
1796       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1797       InitField(x, y, init_game);
1798       break;
1799
1800     case EL_SOKOBAN_FIELD_EMPTY:
1801       local_player->sokobanfields_still_needed++;
1802       break;
1803
1804     case EL_STONEBLOCK:
1805       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1807       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1808         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1809       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1810         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1811       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1812         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1813       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1814         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1815       break;
1816
1817     case EL_BUG:
1818     case EL_BUG_RIGHT:
1819     case EL_BUG_UP:
1820     case EL_BUG_LEFT:
1821     case EL_BUG_DOWN:
1822     case EL_SPACESHIP:
1823     case EL_SPACESHIP_RIGHT:
1824     case EL_SPACESHIP_UP:
1825     case EL_SPACESHIP_LEFT:
1826     case EL_SPACESHIP_DOWN:
1827     case EL_BD_BUTTERFLY:
1828     case EL_BD_BUTTERFLY_RIGHT:
1829     case EL_BD_BUTTERFLY_UP:
1830     case EL_BD_BUTTERFLY_LEFT:
1831     case EL_BD_BUTTERFLY_DOWN:
1832     case EL_BD_FIREFLY:
1833     case EL_BD_FIREFLY_RIGHT:
1834     case EL_BD_FIREFLY_UP:
1835     case EL_BD_FIREFLY_LEFT:
1836     case EL_BD_FIREFLY_DOWN:
1837     case EL_PACMAN_RIGHT:
1838     case EL_PACMAN_UP:
1839     case EL_PACMAN_LEFT:
1840     case EL_PACMAN_DOWN:
1841     case EL_YAMYAM:
1842     case EL_YAMYAM_LEFT:
1843     case EL_YAMYAM_RIGHT:
1844     case EL_YAMYAM_UP:
1845     case EL_YAMYAM_DOWN:
1846     case EL_DARK_YAMYAM:
1847     case EL_ROBOT:
1848     case EL_PACMAN:
1849     case EL_SP_SNIKSNAK:
1850     case EL_SP_ELECTRON:
1851     case EL_MOLE:
1852     case EL_MOLE_LEFT:
1853     case EL_MOLE_RIGHT:
1854     case EL_MOLE_UP:
1855     case EL_MOLE_DOWN:
1856       InitMovDir(x, y);
1857       break;
1858
1859     case EL_AMOEBA_FULL:
1860     case EL_BD_AMOEBA:
1861       InitAmoebaNr(x, y);
1862       break;
1863
1864     case EL_AMOEBA_DROP:
1865       if (y == lev_fieldy - 1)
1866       {
1867         Feld[x][y] = EL_AMOEBA_GROWING;
1868         Store[x][y] = EL_AMOEBA_WET;
1869       }
1870       break;
1871
1872     case EL_DYNAMITE_ACTIVE:
1873     case EL_SP_DISK_RED_ACTIVE:
1874     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1875     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1876     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1877     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1878       MovDelay[x][y] = 96;
1879       break;
1880
1881     case EL_EM_DYNAMITE_ACTIVE:
1882       MovDelay[x][y] = 32;
1883       break;
1884
1885     case EL_LAMP:
1886       local_player->lights_still_needed++;
1887       break;
1888
1889     case EL_PENGUIN:
1890       local_player->friends_still_needed++;
1891       break;
1892
1893     case EL_PIG:
1894     case EL_DRAGON:
1895       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1896       break;
1897
1898     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1904     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1905     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1906     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1907     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1908     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1909     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1910       if (init_game)
1911       {
1912         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1913         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1914         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1915
1916         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1917         {
1918           game.belt_dir[belt_nr] = belt_dir;
1919           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1920         }
1921         else    /* more than one switch -- set it like the first switch */
1922         {
1923           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1924         }
1925       }
1926       break;
1927
1928 #if !USE_BOTH_SWITCHGATE_SWITCHES
1929     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1930       if (init_game)
1931         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1932       break;
1933
1934     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1935       if (init_game)
1936         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1937       break;
1938 #endif
1939
1940     case EL_LIGHT_SWITCH_ACTIVE:
1941       if (init_game)
1942         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1943       break;
1944
1945     case EL_INVISIBLE_STEELWALL:
1946     case EL_INVISIBLE_WALL:
1947     case EL_INVISIBLE_SAND:
1948       if (game.light_time_left > 0 ||
1949           game.lenses_time_left > 0)
1950         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1951       break;
1952
1953     case EL_EMC_MAGIC_BALL:
1954       if (game.ball_state)
1955         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1956       break;
1957
1958     case EL_EMC_MAGIC_BALL_SWITCH:
1959       if (game.ball_state)
1960         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1961       break;
1962
1963     case EL_TRIGGER_PLAYER:
1964     case EL_TRIGGER_ELEMENT:
1965     case EL_TRIGGER_CE_VALUE:
1966     case EL_TRIGGER_CE_SCORE:
1967     case EL_SELF:
1968     case EL_ANY_ELEMENT:
1969     case EL_CURRENT_CE_VALUE:
1970     case EL_CURRENT_CE_SCORE:
1971     case EL_PREV_CE_1:
1972     case EL_PREV_CE_2:
1973     case EL_PREV_CE_3:
1974     case EL_PREV_CE_4:
1975     case EL_PREV_CE_5:
1976     case EL_PREV_CE_6:
1977     case EL_PREV_CE_7:
1978     case EL_PREV_CE_8:
1979     case EL_NEXT_CE_1:
1980     case EL_NEXT_CE_2:
1981     case EL_NEXT_CE_3:
1982     case EL_NEXT_CE_4:
1983     case EL_NEXT_CE_5:
1984     case EL_NEXT_CE_6:
1985     case EL_NEXT_CE_7:
1986     case EL_NEXT_CE_8:
1987       /* reference elements should not be used on the playfield */
1988       Feld[x][y] = EL_EMPTY;
1989       break;
1990
1991     default:
1992       if (IS_CUSTOM_ELEMENT(element))
1993       {
1994         if (CAN_MOVE(element))
1995           InitMovDir(x, y);
1996
1997 #if USE_NEW_CUSTOM_VALUE
1998         if (!element_info[element].use_last_ce_value || init_game)
1999           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2000 #endif
2001       }
2002       else if (IS_GROUP_ELEMENT(element))
2003       {
2004         Feld[x][y] = GetElementFromGroupElement(element);
2005
2006         InitField(x, y, init_game);
2007       }
2008
2009       break;
2010   }
2011
2012   if (!init_game)
2013     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2014 }
2015
2016 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2017 {
2018   InitField(x, y, init_game);
2019
2020   /* not needed to call InitMovDir() -- already done by InitField()! */
2021   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2022       CAN_MOVE(Feld[x][y]))
2023     InitMovDir(x, y);
2024 }
2025
2026 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2027 {
2028   int old_element = Feld[x][y];
2029
2030   InitField(x, y, init_game);
2031
2032   /* not needed to call InitMovDir() -- already done by InitField()! */
2033   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2034       CAN_MOVE(old_element) &&
2035       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2036     InitMovDir(x, y);
2037
2038   /* this case is in fact a combination of not less than three bugs:
2039      first, it calls InitMovDir() for elements that can move, although this is
2040      already done by InitField(); then, it checks the element that was at this
2041      field _before_ the call to InitField() (which can change it); lastly, it
2042      was not called for "mole with direction" elements, which were treated as
2043      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2044   */
2045 }
2046
2047 static int get_key_element_from_nr(int key_nr)
2048 {
2049   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2050                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2051                           EL_EM_KEY_1 : EL_KEY_1);
2052
2053   return key_base_element + key_nr;
2054 }
2055
2056 static int get_next_dropped_element(struct PlayerInfo *player)
2057 {
2058   return (player->inventory_size > 0 ?
2059           player->inventory_element[player->inventory_size - 1] :
2060           player->inventory_infinite_element != EL_UNDEFINED ?
2061           player->inventory_infinite_element :
2062           player->dynabombs_left > 0 ?
2063           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2064           EL_UNDEFINED);
2065 }
2066
2067 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2068 {
2069   /* pos >= 0: get element from bottom of the stack;
2070      pos <  0: get element from top of the stack */
2071
2072   if (pos < 0)
2073   {
2074     int min_inventory_size = -pos;
2075     int inventory_pos = player->inventory_size - min_inventory_size;
2076     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2077
2078     return (player->inventory_size >= min_inventory_size ?
2079             player->inventory_element[inventory_pos] :
2080             player->inventory_infinite_element != EL_UNDEFINED ?
2081             player->inventory_infinite_element :
2082             player->dynabombs_left >= min_dynabombs_left ?
2083             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2084             EL_UNDEFINED);
2085   }
2086   else
2087   {
2088     int min_dynabombs_left = pos + 1;
2089     int min_inventory_size = pos + 1 - player->dynabombs_left;
2090     int inventory_pos = pos - player->dynabombs_left;
2091
2092     return (player->inventory_infinite_element != EL_UNDEFINED ?
2093             player->inventory_infinite_element :
2094             player->dynabombs_left >= min_dynabombs_left ?
2095             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2096             player->inventory_size >= min_inventory_size ?
2097             player->inventory_element[inventory_pos] :
2098             EL_UNDEFINED);
2099   }
2100 }
2101
2102 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2103 {
2104   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2105   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2106   int compare_result;
2107
2108   if (gpo1->sort_priority != gpo2->sort_priority)
2109     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2110   else
2111     compare_result = gpo1->nr - gpo2->nr;
2112
2113   return compare_result;
2114 }
2115
2116 void InitGameControlValues()
2117 {
2118   int i;
2119
2120   for (i = 0; game_panel_controls[i].nr != -1; i++)
2121   {
2122     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2123     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2124     struct TextPosInfo *pos = gpc->pos;
2125     int nr = gpc->nr;
2126     int type = gpc->type;
2127
2128     if (nr != i)
2129     {
2130       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2131       Error(ERR_EXIT, "this should not happen -- please debug");
2132     }
2133
2134     /* force update of game controls after initialization */
2135     gpc->value = gpc->last_value = -1;
2136     gpc->frame = gpc->last_frame = -1;
2137     gpc->gfx_frame = -1;
2138
2139     /* determine panel value width for later calculation of alignment */
2140     if (type == TYPE_INTEGER || type == TYPE_STRING)
2141     {
2142       pos->width = pos->size * getFontWidth(pos->font);
2143       pos->height = getFontHeight(pos->font);
2144     }
2145     else if (type == TYPE_ELEMENT)
2146     {
2147       pos->width = pos->size;
2148       pos->height = pos->size;
2149     }
2150
2151     /* fill structure for game panel draw order */
2152     gpo->nr = gpc->nr;
2153     gpo->sort_priority = pos->sort_priority;
2154   }
2155
2156   /* sort game panel controls according to sort_priority and control number */
2157   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2158         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2159 }
2160
2161 void UpdatePlayfieldElementCount()
2162 {
2163   boolean use_element_count = FALSE;
2164   int i, j, x, y;
2165
2166   /* first check if it is needed at all to calculate playfield element count */
2167   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2168     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2169       use_element_count = TRUE;
2170
2171   if (!use_element_count)
2172     return;
2173
2174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2175     element_info[i].element_count = 0;
2176
2177   SCAN_PLAYFIELD(x, y)
2178   {
2179     element_info[Feld[x][y]].element_count++;
2180   }
2181
2182   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2183     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2184       if (IS_IN_GROUP(j, i))
2185         element_info[EL_GROUP_START + i].element_count +=
2186           element_info[j].element_count;
2187 }
2188
2189 void UpdateGameControlValues()
2190 {
2191   int i, k;
2192   int time = (local_player->LevelSolved ?
2193               local_player->LevelSolved_CountingTime :
2194               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2195               level.native_em_level->lev->time :
2196               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2197               level.native_sp_level->game_sp->time_played :
2198               game.no_time_limit ? TimePlayed : TimeLeft);
2199   int score = (local_player->LevelSolved ?
2200                local_player->LevelSolved_CountingScore :
2201                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2202                level.native_em_level->lev->score :
2203                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2204                level.native_sp_level->game_sp->score :
2205                local_player->score);
2206   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207               level.native_em_level->lev->required :
2208               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209               level.native_sp_level->game_sp->infotrons_still_needed :
2210               local_player->gems_still_needed);
2211   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2212                      level.native_em_level->lev->required > 0 :
2213                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2214                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2215                      local_player->gems_still_needed > 0 ||
2216                      local_player->sokobanfields_still_needed > 0 ||
2217                      local_player->lights_still_needed > 0);
2218
2219   UpdatePlayfieldElementCount();
2220
2221   /* update game panel control values */
2222
2223   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2224   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2225
2226   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2227   for (i = 0; i < MAX_NUM_KEYS; i++)
2228     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2229   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2230   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2231
2232   if (game.centered_player_nr == -1)
2233   {
2234     for (i = 0; i < MAX_PLAYERS; i++)
2235     {
2236       /* only one player in Supaplex game engine */
2237       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2238         break;
2239
2240       for (k = 0; k < MAX_NUM_KEYS; k++)
2241       {
2242         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2243         {
2244           if (level.native_em_level->ply[i]->keys & (1 << k))
2245             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2246               get_key_element_from_nr(k);
2247         }
2248         else if (stored_player[i].key[k])
2249           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2250             get_key_element_from_nr(k);
2251       }
2252
2253       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2254         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2255           level.native_em_level->ply[i]->dynamite;
2256       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2257         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2258           level.native_sp_level->game_sp->red_disk_count;
2259       else
2260         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2261           stored_player[i].inventory_size;
2262
2263       if (stored_player[i].num_white_keys > 0)
2264         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2265           EL_DC_KEY_WHITE;
2266
2267       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2268         stored_player[i].num_white_keys;
2269     }
2270   }
2271   else
2272   {
2273     int player_nr = game.centered_player_nr;
2274
2275     for (k = 0; k < MAX_NUM_KEYS; k++)
2276     {
2277       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2278       {
2279         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2280           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281             get_key_element_from_nr(k);
2282       }
2283       else if (stored_player[player_nr].key[k])
2284         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2285           get_key_element_from_nr(k);
2286     }
2287
2288     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2289       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2290         level.native_em_level->ply[player_nr]->dynamite;
2291     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2292       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2293         level.native_sp_level->game_sp->red_disk_count;
2294     else
2295       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2296         stored_player[player_nr].inventory_size;
2297
2298     if (stored_player[player_nr].num_white_keys > 0)
2299       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2300
2301     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302       stored_player[player_nr].num_white_keys;
2303   }
2304
2305   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2306   {
2307     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2308       get_inventory_element_from_pos(local_player, i);
2309     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2310       get_inventory_element_from_pos(local_player, -i - 1);
2311   }
2312
2313   game_panel_controls[GAME_PANEL_SCORE].value = score;
2314   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2315
2316   game_panel_controls[GAME_PANEL_TIME].value = time;
2317
2318   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2319   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2320   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2321
2322   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2323
2324   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2325     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2326      EL_EMPTY);
2327   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2328     local_player->shield_normal_time_left;
2329   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2330     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2331      EL_EMPTY);
2332   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2333     local_player->shield_deadly_time_left;
2334
2335   game_panel_controls[GAME_PANEL_EXIT].value =
2336     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2337
2338   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2339     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2340   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2341     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2342      EL_EMC_MAGIC_BALL_SWITCH);
2343
2344   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2345     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2346   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2347     game.light_time_left;
2348
2349   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2350     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2351   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2352     game.timegate_time_left;
2353
2354   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2355     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2356
2357   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2358     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2359   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2360     game.lenses_time_left;
2361
2362   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2363     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2364   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2365     game.magnify_time_left;
2366
2367   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2368     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2369      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2370      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2371      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2372      EL_BALLOON_SWITCH_NONE);
2373
2374   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2375     local_player->dynabomb_count;
2376   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2377     local_player->dynabomb_size;
2378   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2379     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2380
2381   game_panel_controls[GAME_PANEL_PENGUINS].value =
2382     local_player->friends_still_needed;
2383
2384   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2385     local_player->sokobanfields_still_needed;
2386   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2387     local_player->sokobanfields_still_needed;
2388
2389   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2390     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2391
2392   for (i = 0; i < NUM_BELTS; i++)
2393   {
2394     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2395       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2396        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2397     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2398       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2399   }
2400
2401   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2402     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2403   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2404     game.magic_wall_time_left;
2405
2406 #if USE_PLAYER_GRAVITY
2407   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2408     local_player->gravity;
2409 #else
2410   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2411 #endif
2412
2413   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2414     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2415
2416   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2418       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2419        game.panel.element[i].id : EL_UNDEFINED);
2420
2421   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2422     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2423       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2424        element_info[game.panel.element_count[i].id].element_count : 0);
2425
2426   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2428       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2429        element_info[game.panel.ce_score[i].id].collect_score : 0);
2430
2431   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2432     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2433       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2434        element_info[game.panel.ce_score_element[i].id].collect_score :
2435        EL_UNDEFINED);
2436
2437   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2438   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2439   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2440
2441   /* update game panel control frames */
2442
2443   for (i = 0; game_panel_controls[i].nr != -1; i++)
2444   {
2445     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2446
2447     if (gpc->type == TYPE_ELEMENT)
2448     {
2449       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2450       {
2451         int last_anim_random_frame = gfx.anim_random_frame;
2452         int element = gpc->value;
2453         int graphic = el2panelimg(element);
2454
2455         if (gpc->value != gpc->last_value)
2456         {
2457           gpc->gfx_frame = 0;
2458           gpc->gfx_random = INIT_GFX_RANDOM();
2459         }
2460         else
2461         {
2462           gpc->gfx_frame++;
2463
2464           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2465               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2466             gpc->gfx_random = INIT_GFX_RANDOM();
2467         }
2468
2469         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2470           gfx.anim_random_frame = gpc->gfx_random;
2471
2472         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2473           gpc->gfx_frame = element_info[element].collect_score;
2474
2475         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2476                                               gpc->gfx_frame);
2477
2478         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2479           gfx.anim_random_frame = last_anim_random_frame;
2480       }
2481     }
2482   }
2483 }
2484
2485 void DisplayGameControlValues()
2486 {
2487   boolean redraw_panel = FALSE;
2488   int i;
2489
2490   for (i = 0; game_panel_controls[i].nr != -1; i++)
2491   {
2492     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2493
2494     if (PANEL_DEACTIVATED(gpc->pos))
2495       continue;
2496
2497     if (gpc->value == gpc->last_value &&
2498         gpc->frame == gpc->last_frame)
2499       continue;
2500
2501     redraw_panel = TRUE;
2502   }
2503
2504   if (!redraw_panel)
2505     return;
2506
2507   /* copy default game door content to main double buffer */
2508 #if 1
2509   /* !!! CHECK AGAIN !!! */
2510   SetPanelBackground();
2511   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2512   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2513 #else
2514   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2515              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2516 #endif
2517
2518   /* redraw game control buttons */
2519 #if 1
2520   RedrawGameButtons();
2521 #else
2522   UnmapGameButtons();
2523   MapGameButtons();
2524 #endif
2525
2526   game_status = GAME_MODE_PSEUDO_PANEL;
2527
2528 #if 1
2529   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2530 #else
2531   for (i = 0; game_panel_controls[i].nr != -1; i++)
2532 #endif
2533   {
2534 #if 1
2535     int nr = game_panel_order[i].nr;
2536     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2537 #else
2538     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2539     int nr = gpc->nr;
2540 #endif
2541     struct TextPosInfo *pos = gpc->pos;
2542     int type = gpc->type;
2543     int value = gpc->value;
2544     int frame = gpc->frame;
2545 #if 0
2546     int last_value = gpc->last_value;
2547     int last_frame = gpc->last_frame;
2548 #endif
2549     int size = pos->size;
2550     int font = pos->font;
2551     boolean draw_masked = pos->draw_masked;
2552     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2553
2554     if (PANEL_DEACTIVATED(pos))
2555       continue;
2556
2557 #if 0
2558     if (value == last_value && frame == last_frame)
2559       continue;
2560 #endif
2561
2562     gpc->last_value = value;
2563     gpc->last_frame = frame;
2564
2565 #if 0
2566     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2567 #endif
2568
2569     if (type == TYPE_INTEGER)
2570     {
2571       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2572           nr == GAME_PANEL_TIME)
2573       {
2574         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2575
2576         if (use_dynamic_size)           /* use dynamic number of digits */
2577         {
2578           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2579           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2580           int size2 = size1 + 1;
2581           int font1 = pos->font;
2582           int font2 = pos->font_alt;
2583
2584           size = (value < value_change ? size1 : size2);
2585           font = (value < value_change ? font1 : font2);
2586
2587 #if 0
2588           /* clear background if value just changed its size (dynamic digits) */
2589           if ((last_value < value_change) != (value < value_change))
2590           {
2591             int width1 = size1 * getFontWidth(font1);
2592             int width2 = size2 * getFontWidth(font2);
2593             int max_width = MAX(width1, width2);
2594             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2595
2596             pos->width = max_width;
2597
2598             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2599                                        max_width, max_height);
2600           }
2601 #endif
2602         }
2603       }
2604
2605 #if 1
2606       /* correct text size if "digits" is zero or less */
2607       if (size <= 0)
2608         size = strlen(int2str(value, size));
2609
2610       /* dynamically correct text alignment */
2611       pos->width = size * getFontWidth(font);
2612 #endif
2613
2614       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2615                   int2str(value, size), font, mask_mode);
2616     }
2617     else if (type == TYPE_ELEMENT)
2618     {
2619       int element, graphic;
2620       Bitmap *src_bitmap;
2621       int src_x, src_y;
2622       int width, height;
2623       int dst_x = PANEL_XPOS(pos);
2624       int dst_y = PANEL_YPOS(pos);
2625
2626 #if 1
2627       if (value != EL_UNDEFINED && value != EL_EMPTY)
2628       {
2629         element = value;
2630         graphic = el2panelimg(value);
2631
2632         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2633
2634 #if 1
2635         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2636           size = TILESIZE;
2637 #endif
2638
2639         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2640                               &src_x, &src_y);
2641
2642         width  = graphic_info[graphic].width  * size / TILESIZE;
2643         height = graphic_info[graphic].height * size / TILESIZE;
2644
2645         if (draw_masked)
2646         {
2647           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2648                         dst_x - src_x, dst_y - src_y);
2649           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2650                            dst_x, dst_y);
2651         }
2652         else
2653         {
2654           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2655                      dst_x, dst_y);
2656         }
2657       }
2658 #else
2659       if (value == EL_UNDEFINED || value == EL_EMPTY)
2660       {
2661         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2662         graphic = el2panelimg(element);
2663
2664         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2665         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2666         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2667       }
2668       else
2669       {
2670         element = value;
2671         graphic = el2panelimg(value);
2672
2673         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2674       }
2675
2676       width  = graphic_info[graphic].width  * size / TILESIZE;
2677       height = graphic_info[graphic].height * size / TILESIZE;
2678
2679       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2680 #endif
2681     }
2682     else if (type == TYPE_STRING)
2683     {
2684       boolean active = (value != 0);
2685       char *state_normal = "off";
2686       char *state_active = "on";
2687       char *state = (active ? state_active : state_normal);
2688       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2689                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2690                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2691                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2692
2693       if (nr == GAME_PANEL_GRAVITY_STATE)
2694       {
2695         int font1 = pos->font;          /* (used for normal state) */
2696         int font2 = pos->font_alt;      /* (used for active state) */
2697 #if 0
2698         int size1 = strlen(state_normal);
2699         int size2 = strlen(state_active);
2700         int width1 = size1 * getFontWidth(font1);
2701         int width2 = size2 * getFontWidth(font2);
2702         int max_width = MAX(width1, width2);
2703         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2704
2705         pos->width = max_width;
2706
2707         /* clear background for values that may have changed its size */
2708         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2709                                    max_width, max_height);
2710 #endif
2711
2712         font = (active ? font2 : font1);
2713       }
2714
2715       if (s != NULL)
2716       {
2717         char *s_cut;
2718
2719 #if 1
2720         if (size <= 0)
2721         {
2722           /* don't truncate output if "chars" is zero or less */
2723           size = strlen(s);
2724
2725           /* dynamically correct text alignment */
2726           pos->width = size * getFontWidth(font);
2727         }
2728 #endif
2729
2730         s_cut = getStringCopyN(s, size);
2731
2732         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2733                     s_cut, font, mask_mode);
2734
2735         free(s_cut);
2736       }
2737     }
2738
2739     redraw_mask |= REDRAW_DOOR_1;
2740   }
2741
2742   game_status = GAME_MODE_PLAYING;
2743 }
2744
2745 void UpdateAndDisplayGameControlValues()
2746 {
2747   if (tape.warp_forward)
2748     return;
2749
2750   UpdateGameControlValues();
2751   DisplayGameControlValues();
2752 }
2753
2754 void DrawGameValue_Emeralds(int value)
2755 {
2756   struct TextPosInfo *pos = &game.panel.gems;
2757   int font_nr = pos->font;
2758   int font_width = getFontWidth(font_nr);
2759   int chars = pos->size;
2760
2761 #if 1
2762   return;       /* !!! USE NEW STUFF !!! */
2763 #endif
2764
2765   if (PANEL_DEACTIVATED(pos))
2766     return;
2767
2768   pos->width = chars * font_width;
2769
2770   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2771 }
2772
2773 void DrawGameValue_Dynamite(int value)
2774 {
2775   struct TextPosInfo *pos = &game.panel.inventory_count;
2776   int font_nr = pos->font;
2777   int font_width = getFontWidth(font_nr);
2778   int chars = pos->size;
2779
2780 #if 1
2781   return;       /* !!! USE NEW STUFF !!! */
2782 #endif
2783
2784   if (PANEL_DEACTIVATED(pos))
2785     return;
2786
2787   pos->width = chars * font_width;
2788
2789   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2790 }
2791
2792 void DrawGameValue_Score(int value)
2793 {
2794   struct TextPosInfo *pos = &game.panel.score;
2795   int font_nr = pos->font;
2796   int font_width = getFontWidth(font_nr);
2797   int chars = pos->size;
2798
2799 #if 1
2800   return;       /* !!! USE NEW STUFF !!! */
2801 #endif
2802
2803   if (PANEL_DEACTIVATED(pos))
2804     return;
2805
2806   pos->width = chars * font_width;
2807
2808   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2809 }
2810
2811 void DrawGameValue_Time(int value)
2812 {
2813   struct TextPosInfo *pos = &game.panel.time;
2814   static int last_value = -1;
2815   int chars1 = 3;
2816   int chars2 = 4;
2817   int chars = pos->size;
2818   int font1_nr = pos->font;
2819   int font2_nr = pos->font_alt;
2820   int font_nr = font1_nr;
2821   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2822
2823 #if 1
2824   return;       /* !!! USE NEW STUFF !!! */
2825 #endif
2826
2827   if (PANEL_DEACTIVATED(pos))
2828     return;
2829
2830   if (use_dynamic_chars)                /* use dynamic number of chars */
2831   {
2832     chars   = (value < 1000 ? chars1   : chars2);
2833     font_nr = (value < 1000 ? font1_nr : font2_nr);
2834   }
2835
2836   /* clear background if value just changed its size (dynamic chars only) */
2837   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2838   {
2839     int width1 = chars1 * getFontWidth(font1_nr);
2840     int width2 = chars2 * getFontWidth(font2_nr);
2841     int max_width = MAX(width1, width2);
2842     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2843
2844     pos->width = max_width;
2845
2846     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2847                                max_width, max_height);
2848   }
2849
2850   pos->width = chars * getFontWidth(font_nr);
2851
2852   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2853
2854   last_value = value;
2855 }
2856
2857 void DrawGameValue_Level(int value)
2858 {
2859   struct TextPosInfo *pos = &game.panel.level_number;
2860   int chars1 = 2;
2861   int chars2 = 3;
2862   int chars = pos->size;
2863   int font1_nr = pos->font;
2864   int font2_nr = pos->font_alt;
2865   int font_nr = font1_nr;
2866   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2867
2868 #if 1
2869   return;       /* !!! USE NEW STUFF !!! */
2870 #endif
2871
2872   if (PANEL_DEACTIVATED(pos))
2873     return;
2874
2875   if (use_dynamic_chars)                /* use dynamic number of chars */
2876   {
2877     chars   = (level_nr < 100 ? chars1   : chars2);
2878     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2879   }
2880
2881   pos->width = chars * getFontWidth(font_nr);
2882
2883   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2884 }
2885
2886 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2887 {
2888   int i;
2889
2890 #if 1
2891   return;       /* !!! USE NEW STUFF !!! */
2892 #endif
2893
2894   for (i = 0; i < MAX_NUM_KEYS; i++)
2895   {
2896     struct TextPosInfo *pos = &game.panel.key[i];
2897     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2898     int src_y = DOOR_GFX_PAGEY1 + 123;
2899     int dst_x = PANEL_XPOS(pos);
2900     int dst_y = PANEL_YPOS(pos);
2901
2902     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2903                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2904                    EL_KEY_1) + i;
2905     int graphic = el2edimg(element);
2906
2907     if (PANEL_DEACTIVATED(pos))
2908       continue;
2909
2910 #if 0
2911     /* masked blit with tiles from half-size scaled bitmap does not work yet
2912        (no mask bitmap created for these sizes after loading and scaling) --
2913        solution: load without creating mask, scale, then create final mask */
2914
2915     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2916                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2917
2918     if (key[i])
2919     {
2920       Bitmap *src_bitmap;
2921       int src_x, src_y;
2922
2923       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2924
2925       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2926                     dst_x - src_x, dst_y - src_y);
2927       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2928                        dst_x, dst_y);
2929     }
2930 #else
2931     if (key[i])
2932       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2933     else
2934       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2935                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2936 #endif
2937   }
2938 }
2939
2940 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2941                        int key_bits)
2942 {
2943   int key[MAX_NUM_KEYS];
2944   int i;
2945
2946   /* prevent EM engine from updating time/score values parallel to GameWon() */
2947   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2948       local_player->LevelSolved)
2949     return;
2950
2951   for (i = 0; i < MAX_NUM_KEYS; i++)
2952     key[i] = key_bits & (1 << i);
2953
2954   DrawGameValue_Level(level_nr);
2955
2956   DrawGameValue_Emeralds(emeralds);
2957   DrawGameValue_Dynamite(dynamite);
2958   DrawGameValue_Score(score);
2959   DrawGameValue_Time(time);
2960
2961   DrawGameValue_Keys(key);
2962 }
2963
2964 void UpdateGameDoorValues()
2965 {
2966   UpdateGameControlValues();
2967 }
2968
2969 void DrawGameDoorValues()
2970 {
2971   DisplayGameControlValues();
2972 }
2973
2974 void DrawGameDoorValues_OLD()
2975 {
2976   int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2977   int dynamite_value = 0;
2978   int score_value = (local_player->LevelSolved ? local_player->score_final :
2979                      local_player->score);
2980   int gems_value = local_player->gems_still_needed;
2981   int key_bits = 0;
2982   int i, j;
2983
2984   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2985   {
2986     DrawGameDoorValues_EM();
2987
2988     return;
2989   }
2990
2991   if (game.centered_player_nr == -1)
2992   {
2993     for (i = 0; i < MAX_PLAYERS; i++)
2994     {
2995       for (j = 0; j < MAX_NUM_KEYS; j++)
2996         if (stored_player[i].key[j])
2997           key_bits |= (1 << j);
2998
2999       dynamite_value += stored_player[i].inventory_size;
3000     }
3001   }
3002   else
3003   {
3004     int player_nr = game.centered_player_nr;
3005
3006     for (i = 0; i < MAX_NUM_KEYS; i++)
3007       if (stored_player[player_nr].key[i])
3008         key_bits |= (1 << i);
3009
3010     dynamite_value = stored_player[player_nr].inventory_size;
3011   }
3012
3013   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3014                     key_bits);
3015 }
3016
3017
3018 /*
3019   =============================================================================
3020   InitGameEngine()
3021   -----------------------------------------------------------------------------
3022   initialize game engine due to level / tape version number
3023   =============================================================================
3024 */
3025
3026 static void InitGameEngine()
3027 {
3028   int i, j, k, l, x, y;
3029
3030   /* set game engine from tape file when re-playing, else from level file */
3031   game.engine_version = (tape.playing ? tape.engine_version :
3032                          level.game_version);
3033
3034   /* set single or multi-player game mode (needed for re-playing tapes) */
3035   game.team_mode = setup.team_mode;
3036
3037   if (tape.playing)
3038   {
3039     int num_players = 0;
3040
3041     for (i = 0; i < MAX_PLAYERS; i++)
3042       if (tape.player_participates[i])
3043         num_players++;
3044
3045     /* multi-player tapes contain input data for more than one player */
3046     game.team_mode = (num_players > 1);
3047   }
3048
3049   /* ---------------------------------------------------------------------- */
3050   /* set flags for bugs and changes according to active game engine version */
3051   /* ---------------------------------------------------------------------- */
3052
3053   /*
3054     Summary of bugfix/change:
3055     Fixed handling for custom elements that change when pushed by the player.
3056
3057     Fixed/changed in version:
3058     3.1.0
3059
3060     Description:
3061     Before 3.1.0, custom elements that "change when pushing" changed directly
3062     after the player started pushing them (until then handled in "DigField()").
3063     Since 3.1.0, these custom elements are not changed until the "pushing"
3064     move of the element is finished (now handled in "ContinueMoving()").
3065
3066     Affected levels/tapes:
3067     The first condition is generally needed for all levels/tapes before version
3068     3.1.0, which might use the old behaviour before it was changed; known tapes
3069     that are affected are some tapes from the level set "Walpurgis Gardens" by
3070     Jamie Cullen.
3071     The second condition is an exception from the above case and is needed for
3072     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3073     above (including some development versions of 3.1.0), but before it was
3074     known that this change would break tapes like the above and was fixed in
3075     3.1.1, so that the changed behaviour was active although the engine version
3076     while recording maybe was before 3.1.0. There is at least one tape that is
3077     affected by this exception, which is the tape for the one-level set "Bug
3078     Machine" by Juergen Bonhagen.
3079   */
3080
3081   game.use_change_when_pushing_bug =
3082     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3083      !(tape.playing &&
3084        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3085        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3086
3087   /*
3088     Summary of bugfix/change:
3089     Fixed handling for blocking the field the player leaves when moving.
3090
3091     Fixed/changed in version:
3092     3.1.1
3093
3094     Description:
3095     Before 3.1.1, when "block last field when moving" was enabled, the field
3096     the player is leaving when moving was blocked for the time of the move,
3097     and was directly unblocked afterwards. This resulted in the last field
3098     being blocked for exactly one less than the number of frames of one player
3099     move. Additionally, even when blocking was disabled, the last field was
3100     blocked for exactly one frame.
3101     Since 3.1.1, due to changes in player movement handling, the last field
3102     is not blocked at all when blocking is disabled. When blocking is enabled,
3103     the last field is blocked for exactly the number of frames of one player
3104     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3105     last field is blocked for exactly one more than the number of frames of
3106     one player move.
3107
3108     Affected levels/tapes:
3109     (!!! yet to be determined -- probably many !!!)
3110   */
3111
3112   game.use_block_last_field_bug =
3113     (game.engine_version < VERSION_IDENT(3,1,1,0));
3114
3115   /*
3116     Summary of bugfix/change:
3117     Changed behaviour of CE changes with multiple changes per single frame.
3118
3119     Fixed/changed in version:
3120     3.2.0-6
3121
3122     Description:
3123     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3124     This resulted in race conditions where CEs seem to behave strange in some
3125     situations (where triggered CE changes were just skipped because there was
3126     already a CE change on that tile in the playfield in that engine frame).
3127     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3128     (The number of changes per frame must be limited in any case, because else
3129     it is easily possible to define CE changes that would result in an infinite
3130     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3131     should be set large enough so that it would only be reached in cases where
3132     the corresponding CE change conditions run into a loop. Therefore, it seems
3133     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3134     maximal number of change pages for custom elements.)
3135
3136     Affected levels/tapes:
3137     Probably many.
3138   */
3139
3140 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3141   game.max_num_changes_per_frame = 1;
3142 #else
3143   game.max_num_changes_per_frame =
3144     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3145 #endif
3146
3147   /* ---------------------------------------------------------------------- */
3148
3149   /* default scan direction: scan playfield from top/left to bottom/right */
3150   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3151
3152   /* dynamically adjust element properties according to game engine version */
3153   InitElementPropertiesEngine(game.engine_version);
3154
3155 #if 0
3156   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3157   printf("          tape version == %06d [%s] [file: %06d]\n",
3158          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3159          tape.file_version);
3160   printf("       => game.engine_version == %06d\n", game.engine_version);
3161 #endif
3162
3163   /* ---------- initialize player's initial move delay --------------------- */
3164
3165   /* dynamically adjust player properties according to level information */
3166   for (i = 0; i < MAX_PLAYERS; i++)
3167     game.initial_move_delay_value[i] =
3168       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3169
3170   /* dynamically adjust player properties according to game engine version */
3171   for (i = 0; i < MAX_PLAYERS; i++)
3172     game.initial_move_delay[i] =
3173       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3174        game.initial_move_delay_value[i] : 0);
3175
3176   /* ---------- initialize player's initial push delay --------------------- */
3177
3178   /* dynamically adjust player properties according to game engine version */
3179   game.initial_push_delay_value =
3180     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3181
3182   /* ---------- initialize changing elements ------------------------------- */
3183
3184   /* initialize changing elements information */
3185   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3186   {
3187     struct ElementInfo *ei = &element_info[i];
3188
3189     /* this pointer might have been changed in the level editor */
3190     ei->change = &ei->change_page[0];
3191
3192     if (!IS_CUSTOM_ELEMENT(i))
3193     {
3194       ei->change->target_element = EL_EMPTY_SPACE;
3195       ei->change->delay_fixed = 0;
3196       ei->change->delay_random = 0;
3197       ei->change->delay_frames = 1;
3198     }
3199
3200     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3201     {
3202       ei->has_change_event[j] = FALSE;
3203
3204       ei->event_page_nr[j] = 0;
3205       ei->event_page[j] = &ei->change_page[0];
3206     }
3207   }
3208
3209   /* add changing elements from pre-defined list */
3210   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3211   {
3212     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3213     struct ElementInfo *ei = &element_info[ch_delay->element];
3214
3215     ei->change->target_element       = ch_delay->target_element;
3216     ei->change->delay_fixed          = ch_delay->change_delay;
3217
3218     ei->change->pre_change_function  = ch_delay->pre_change_function;
3219     ei->change->change_function      = ch_delay->change_function;
3220     ei->change->post_change_function = ch_delay->post_change_function;
3221
3222     ei->change->can_change = TRUE;
3223     ei->change->can_change_or_has_action = TRUE;
3224
3225     ei->has_change_event[CE_DELAY] = TRUE;
3226
3227     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3228     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3229   }
3230
3231   /* ---------- initialize internal run-time variables --------------------- */
3232
3233   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3234   {
3235     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3236
3237     for (j = 0; j < ei->num_change_pages; j++)
3238     {
3239       ei->change_page[j].can_change_or_has_action =
3240         (ei->change_page[j].can_change |
3241          ei->change_page[j].has_action);
3242     }
3243   }
3244
3245   /* add change events from custom element configuration */
3246   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3247   {
3248     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3249
3250     for (j = 0; j < ei->num_change_pages; j++)
3251     {
3252       if (!ei->change_page[j].can_change_or_has_action)
3253         continue;
3254
3255       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3256       {
3257         /* only add event page for the first page found with this event */
3258         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3259         {
3260           ei->has_change_event[k] = TRUE;
3261
3262           ei->event_page_nr[k] = j;
3263           ei->event_page[k] = &ei->change_page[j];
3264         }
3265       }
3266     }
3267   }
3268
3269 #if 1
3270   /* ---------- initialize reference elements in change conditions --------- */
3271
3272   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3273   {
3274     int element = EL_CUSTOM_START + i;
3275     struct ElementInfo *ei = &element_info[element];
3276
3277     for (j = 0; j < ei->num_change_pages; j++)
3278     {
3279       int trigger_element = ei->change_page[j].initial_trigger_element;
3280
3281       if (trigger_element >= EL_PREV_CE_8 &&
3282           trigger_element <= EL_NEXT_CE_8)
3283         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3284
3285       ei->change_page[j].trigger_element = trigger_element;
3286     }
3287   }
3288 #endif
3289
3290   /* ---------- initialize run-time trigger player and element ------------- */
3291
3292   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3293   {
3294     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3295
3296     for (j = 0; j < ei->num_change_pages; j++)
3297     {
3298       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3299       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3300       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3301       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3302       ei->change_page[j].actual_trigger_ce_value = 0;
3303       ei->change_page[j].actual_trigger_ce_score = 0;
3304     }
3305   }
3306
3307   /* ---------- initialize trigger events ---------------------------------- */
3308
3309   /* initialize trigger events information */
3310   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3311     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3312       trigger_events[i][j] = FALSE;
3313
3314   /* add trigger events from element change event properties */
3315   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3316   {
3317     struct ElementInfo *ei = &element_info[i];
3318
3319     for (j = 0; j < ei->num_change_pages; j++)
3320     {
3321       if (!ei->change_page[j].can_change_or_has_action)
3322         continue;
3323
3324       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3325       {
3326         int trigger_element = ei->change_page[j].trigger_element;
3327
3328         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3329         {
3330           if (ei->change_page[j].has_event[k])
3331           {
3332             if (IS_GROUP_ELEMENT(trigger_element))
3333             {
3334               struct ElementGroupInfo *group =
3335                 element_info[trigger_element].group;
3336
3337               for (l = 0; l < group->num_elements_resolved; l++)
3338                 trigger_events[group->element_resolved[l]][k] = TRUE;
3339             }
3340             else if (trigger_element == EL_ANY_ELEMENT)
3341               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3342                 trigger_events[l][k] = TRUE;
3343             else
3344               trigger_events[trigger_element][k] = TRUE;
3345           }
3346         }
3347       }
3348     }
3349   }
3350
3351   /* ---------- initialize push delay -------------------------------------- */
3352
3353   /* initialize push delay values to default */
3354   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3355   {
3356     if (!IS_CUSTOM_ELEMENT(i))
3357     {
3358       /* set default push delay values (corrected since version 3.0.7-1) */
3359       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3360       {
3361         element_info[i].push_delay_fixed = 2;
3362         element_info[i].push_delay_random = 8;
3363       }
3364       else
3365       {
3366         element_info[i].push_delay_fixed = 8;
3367         element_info[i].push_delay_random = 8;
3368       }
3369     }
3370   }
3371
3372   /* set push delay value for certain elements from pre-defined list */
3373   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3374   {
3375     int e = push_delay_list[i].element;
3376
3377     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3378     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3379   }
3380
3381   /* set push delay value for Supaplex elements for newer engine versions */
3382   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3383   {
3384     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3385     {
3386       if (IS_SP_ELEMENT(i))
3387       {
3388         /* set SP push delay to just enough to push under a falling zonk */
3389         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3390
3391         element_info[i].push_delay_fixed  = delay;
3392         element_info[i].push_delay_random = 0;
3393       }
3394     }
3395   }
3396
3397   /* ---------- initialize move stepsize ----------------------------------- */
3398
3399   /* initialize move stepsize values to default */
3400   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3401     if (!IS_CUSTOM_ELEMENT(i))
3402       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3403
3404   /* set move stepsize value for certain elements from pre-defined list */
3405   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3406   {
3407     int e = move_stepsize_list[i].element;
3408
3409     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3410   }
3411
3412   /* ---------- initialize collect score ----------------------------------- */
3413
3414   /* initialize collect score values for custom elements from initial value */
3415   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3416     if (IS_CUSTOM_ELEMENT(i))
3417       element_info[i].collect_score = element_info[i].collect_score_initial;
3418
3419   /* ---------- initialize collect count ----------------------------------- */
3420
3421   /* initialize collect count values for non-custom elements */
3422   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3423     if (!IS_CUSTOM_ELEMENT(i))
3424       element_info[i].collect_count_initial = 0;
3425
3426   /* add collect count values for all elements from pre-defined list */
3427   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3428     element_info[collect_count_list[i].element].collect_count_initial =
3429       collect_count_list[i].count;
3430
3431   /* ---------- initialize access direction -------------------------------- */
3432
3433   /* initialize access direction values to default (access from every side) */
3434   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3435     if (!IS_CUSTOM_ELEMENT(i))
3436       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3437
3438   /* set access direction value for certain elements from pre-defined list */
3439   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3440     element_info[access_direction_list[i].element].access_direction =
3441       access_direction_list[i].direction;
3442
3443   /* ---------- initialize explosion content ------------------------------- */
3444   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3445   {
3446     if (IS_CUSTOM_ELEMENT(i))
3447       continue;
3448
3449     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3450     {
3451       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3452
3453       element_info[i].content.e[x][y] =
3454         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3455          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3456          i == EL_PLAYER_3 ? EL_EMERALD :
3457          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3458          i == EL_MOLE ? EL_EMERALD_RED :
3459          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3460          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3461          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3462          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3463          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3464          i == EL_WALL_EMERALD ? EL_EMERALD :
3465          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3466          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3467          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3468          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3469          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3470          i == EL_WALL_PEARL ? EL_PEARL :
3471          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3472          EL_EMPTY);
3473     }
3474   }
3475
3476   /* ---------- initialize recursion detection ------------------------------ */
3477   recursion_loop_depth = 0;
3478   recursion_loop_detected = FALSE;
3479   recursion_loop_element = EL_UNDEFINED;
3480
3481   /* ---------- initialize graphics engine ---------------------------------- */
3482   game.scroll_delay_value =
3483     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3484      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3485   game.scroll_delay_value =
3486     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3487 }
3488
3489 int get_num_special_action(int element, int action_first, int action_last)
3490 {
3491   int num_special_action = 0;
3492   int i, j;
3493
3494   for (i = action_first; i <= action_last; i++)
3495   {
3496     boolean found = FALSE;
3497
3498     for (j = 0; j < NUM_DIRECTIONS; j++)
3499       if (el_act_dir2img(element, i, j) !=
3500           el_act_dir2img(element, ACTION_DEFAULT, j))
3501         found = TRUE;
3502
3503     if (found)
3504       num_special_action++;
3505     else
3506       break;
3507   }
3508
3509   return num_special_action;
3510 }
3511
3512
3513 /*
3514   =============================================================================
3515   InitGame()
3516   -----------------------------------------------------------------------------
3517   initialize and start new game
3518   =============================================================================
3519 */
3520
3521 void InitGame()
3522 {
3523   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3524   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3525   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3526 #if 0
3527   boolean do_fading = (game_status == GAME_MODE_MAIN);
3528 #endif
3529 #if 1
3530   int initial_move_dir = MV_DOWN;
3531 #else
3532   int initial_move_dir = MV_NONE;
3533 #endif
3534   int i, j, x, y;
3535
3536   game_status = GAME_MODE_PLAYING;
3537
3538 #if 1
3539   /* needed if different viewport properties defined for playing */
3540   ChangeViewportPropertiesIfNeeded();
3541 #endif
3542
3543 #if 1
3544   DrawCompleteVideoDisplay();
3545 #endif
3546
3547   InitGameEngine();
3548   InitGameControlValues();
3549
3550   /* don't play tapes over network */
3551   network_playing = (options.network && !tape.playing);
3552
3553   for (i = 0; i < MAX_PLAYERS; i++)
3554   {
3555     struct PlayerInfo *player = &stored_player[i];
3556
3557     player->index_nr = i;
3558     player->index_bit = (1 << i);
3559     player->element_nr = EL_PLAYER_1 + i;
3560
3561     player->present = FALSE;
3562     player->active = FALSE;
3563     player->mapped = FALSE;
3564
3565     player->killed = FALSE;
3566     player->reanimated = FALSE;
3567
3568     player->action = 0;
3569     player->effective_action = 0;
3570     player->programmed_action = 0;
3571
3572     player->score = 0;
3573     player->score_final = 0;
3574
3575     player->gems_still_needed = level.gems_needed;
3576     player->sokobanfields_still_needed = 0;
3577     player->lights_still_needed = 0;
3578     player->friends_still_needed = 0;
3579
3580     for (j = 0; j < MAX_NUM_KEYS; j++)
3581       player->key[j] = FALSE;
3582
3583     player->num_white_keys = 0;
3584
3585     player->dynabomb_count = 0;
3586     player->dynabomb_size = 1;
3587     player->dynabombs_left = 0;
3588     player->dynabomb_xl = FALSE;
3589
3590     player->MovDir = initial_move_dir;
3591     player->MovPos = 0;
3592     player->GfxPos = 0;
3593     player->GfxDir = initial_move_dir;
3594     player->GfxAction = ACTION_DEFAULT;
3595     player->Frame = 0;
3596     player->StepFrame = 0;
3597
3598     player->initial_element = player->element_nr;
3599     player->artwork_element =
3600       (level.use_artwork_element[i] ? level.artwork_element[i] :
3601        player->element_nr);
3602     player->use_murphy = FALSE;
3603
3604     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3605     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3606
3607     player->gravity = level.initial_player_gravity[i];
3608
3609     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3610
3611     player->actual_frame_counter = 0;
3612
3613     player->step_counter = 0;
3614
3615     player->last_move_dir = initial_move_dir;
3616
3617     player->is_active = FALSE;
3618
3619     player->is_waiting = FALSE;
3620     player->is_moving = FALSE;
3621     player->is_auto_moving = FALSE;
3622     player->is_digging = FALSE;
3623     player->is_snapping = FALSE;
3624     player->is_collecting = FALSE;
3625     player->is_pushing = FALSE;
3626     player->is_switching = FALSE;
3627     player->is_dropping = FALSE;
3628     player->is_dropping_pressed = FALSE;
3629
3630     player->is_bored = FALSE;
3631     player->is_sleeping = FALSE;
3632
3633     player->frame_counter_bored = -1;
3634     player->frame_counter_sleeping = -1;
3635
3636     player->anim_delay_counter = 0;
3637     player->post_delay_counter = 0;
3638
3639     player->dir_waiting = initial_move_dir;
3640     player->action_waiting = ACTION_DEFAULT;
3641     player->last_action_waiting = ACTION_DEFAULT;
3642     player->special_action_bored = ACTION_DEFAULT;
3643     player->special_action_sleeping = ACTION_DEFAULT;
3644
3645     player->switch_x = -1;
3646     player->switch_y = -1;
3647
3648     player->drop_x = -1;
3649     player->drop_y = -1;
3650
3651     player->show_envelope = 0;
3652
3653     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3654
3655     player->push_delay       = -1;      /* initialized when pushing starts */
3656     player->push_delay_value = game.initial_push_delay_value;
3657
3658     player->drop_delay = 0;
3659     player->drop_pressed_delay = 0;
3660
3661     player->last_jx = -1;
3662     player->last_jy = -1;
3663     player->jx = -1;
3664     player->jy = -1;
3665
3666     player->shield_normal_time_left = 0;
3667     player->shield_deadly_time_left = 0;
3668
3669     player->inventory_infinite_element = EL_UNDEFINED;
3670     player->inventory_size = 0;
3671
3672     if (level.use_initial_inventory[i])
3673     {
3674       for (j = 0; j < level.initial_inventory_size[i]; j++)
3675       {
3676         int element = level.initial_inventory_content[i][j];
3677         int collect_count = element_info[element].collect_count_initial;
3678         int k;
3679
3680         if (!IS_CUSTOM_ELEMENT(element))
3681           collect_count = 1;
3682
3683         if (collect_count == 0)
3684           player->inventory_infinite_element = element;
3685         else
3686           for (k = 0; k < collect_count; k++)
3687             if (player->inventory_size < MAX_INVENTORY_SIZE)
3688               player->inventory_element[player->inventory_size++] = element;
3689       }
3690     }
3691
3692     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3693     SnapField(player, 0, 0);
3694
3695     player->LevelSolved = FALSE;
3696     player->GameOver = FALSE;
3697
3698     player->LevelSolved_GameWon = FALSE;
3699     player->LevelSolved_GameEnd = FALSE;
3700     player->LevelSolved_PanelOff = FALSE;
3701     player->LevelSolved_SaveTape = FALSE;
3702     player->LevelSolved_SaveScore = FALSE;
3703     player->LevelSolved_CountingTime = 0;
3704     player->LevelSolved_CountingScore = 0;
3705
3706     map_player_action[i] = i;
3707   }
3708
3709   network_player_action_received = FALSE;
3710
3711 #if defined(NETWORK_AVALIABLE)
3712   /* initial null action */
3713   if (network_playing)
3714     SendToServer_MovePlayer(MV_NONE);
3715 #endif
3716
3717   ZX = ZY = -1;
3718   ExitX = ExitY = -1;
3719
3720   FrameCounter = 0;
3721   TimeFrames = 0;
3722   TimePlayed = 0;
3723   TimeLeft = level.time;
3724   TapeTime = 0;
3725
3726   ScreenMovDir = MV_NONE;
3727   ScreenMovPos = 0;
3728   ScreenGfxPos = 0;
3729
3730   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3731
3732   AllPlayersGone = FALSE;
3733
3734   game.no_time_limit = (level.time == 0);
3735
3736   game.yamyam_content_nr = 0;
3737   game.robot_wheel_active = FALSE;
3738   game.magic_wall_active = FALSE;
3739   game.magic_wall_time_left = 0;
3740   game.light_time_left = 0;
3741   game.timegate_time_left = 0;
3742   game.switchgate_pos = 0;
3743   game.wind_direction = level.wind_direction_initial;
3744
3745 #if !USE_PLAYER_GRAVITY
3746   game.gravity = FALSE;
3747   game.explosions_delayed = TRUE;
3748 #endif
3749
3750   game.lenses_time_left = 0;
3751   game.magnify_time_left = 0;
3752
3753   game.ball_state = level.ball_state_initial;
3754   game.ball_content_nr = 0;
3755
3756   game.envelope_active = FALSE;
3757
3758   /* set focus to local player for network games, else to all players */
3759   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3760   game.centered_player_nr_next = game.centered_player_nr;
3761   game.set_centered_player = FALSE;
3762
3763   if (network_playing && tape.recording)
3764   {
3765     /* store client dependent player focus when recording network games */
3766     tape.centered_player_nr_next = game.centered_player_nr_next;
3767     tape.set_centered_player = TRUE;
3768   }
3769
3770   for (i = 0; i < NUM_BELTS; i++)
3771   {
3772     game.belt_dir[i] = MV_NONE;
3773     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3774   }
3775
3776   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3777     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3778
3779 #if DEBUG_INIT_PLAYER
3780   if (options.debug)
3781   {
3782     printf("Player status at level initialization:\n");
3783   }
3784 #endif
3785
3786   SCAN_PLAYFIELD(x, y)
3787   {
3788     Feld[x][y] = level.field[x][y];
3789     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3790     ChangeDelay[x][y] = 0;
3791     ChangePage[x][y] = -1;
3792 #if USE_NEW_CUSTOM_VALUE
3793     CustomValue[x][y] = 0;              /* initialized in InitField() */
3794 #endif
3795     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3796     AmoebaNr[x][y] = 0;
3797     WasJustMoving[x][y] = 0;
3798     WasJustFalling[x][y] = 0;
3799     CheckCollision[x][y] = 0;
3800     CheckImpact[x][y] = 0;
3801     Stop[x][y] = FALSE;
3802     Pushed[x][y] = FALSE;
3803
3804     ChangeCount[x][y] = 0;
3805     ChangeEvent[x][y] = -1;
3806
3807     ExplodePhase[x][y] = 0;
3808     ExplodeDelay[x][y] = 0;
3809     ExplodeField[x][y] = EX_TYPE_NONE;
3810
3811     RunnerVisit[x][y] = 0;
3812     PlayerVisit[x][y] = 0;
3813
3814     GfxFrame[x][y] = 0;
3815     GfxRandom[x][y] = INIT_GFX_RANDOM();
3816     GfxElement[x][y] = EL_UNDEFINED;
3817     GfxAction[x][y] = ACTION_DEFAULT;
3818     GfxDir[x][y] = MV_NONE;
3819     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3820   }
3821
3822   SCAN_PLAYFIELD(x, y)
3823   {
3824     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3825       emulate_bd = FALSE;
3826     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3827       emulate_sb = FALSE;
3828     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3829       emulate_sp = FALSE;
3830
3831     InitField(x, y, TRUE);
3832
3833     ResetGfxAnimation(x, y);
3834   }
3835
3836   InitBeltMovement();
3837
3838   for (i = 0; i < MAX_PLAYERS; i++)
3839   {
3840     struct PlayerInfo *player = &stored_player[i];
3841
3842     /* set number of special actions for bored and sleeping animation */
3843     player->num_special_action_bored =
3844       get_num_special_action(player->artwork_element,
3845                              ACTION_BORING_1, ACTION_BORING_LAST);
3846     player->num_special_action_sleeping =
3847       get_num_special_action(player->artwork_element,
3848                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3849   }
3850
3851   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3852                     emulate_sb ? EMU_SOKOBAN :
3853                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3854
3855 #if USE_NEW_ALL_SLIPPERY
3856   /* initialize type of slippery elements */
3857   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3858   {
3859     if (!IS_CUSTOM_ELEMENT(i))
3860     {
3861       /* default: elements slip down either to the left or right randomly */
3862       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3863
3864       /* SP style elements prefer to slip down on the left side */
3865       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3866         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3867
3868       /* BD style elements prefer to slip down on the left side */
3869       if (game.emulation == EMU_BOULDERDASH)
3870         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3871     }
3872   }
3873 #endif
3874
3875   /* initialize explosion and ignition delay */
3876   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3877   {
3878     if (!IS_CUSTOM_ELEMENT(i))
3879     {
3880       int num_phase = 8;
3881       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3882                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3883                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3884       int last_phase = (num_phase + 1) * delay;
3885       int half_phase = (num_phase / 2) * delay;
3886
3887       element_info[i].explosion_delay = last_phase - 1;
3888       element_info[i].ignition_delay = half_phase;
3889
3890       if (i == EL_BLACK_ORB)
3891         element_info[i].ignition_delay = 1;
3892     }
3893
3894 #if 0
3895     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3896       element_info[i].explosion_delay = 1;
3897
3898     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3899       element_info[i].ignition_delay = 1;
3900 #endif
3901   }
3902
3903   /* correct non-moving belts to start moving left */
3904   for (i = 0; i < NUM_BELTS; i++)
3905     if (game.belt_dir[i] == MV_NONE)
3906       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3907
3908 #if USE_NEW_PLAYER_ASSIGNMENTS
3909   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3910   /* choose default local player */
3911   local_player = &stored_player[0];
3912
3913   for (i = 0; i < MAX_PLAYERS; i++)
3914     stored_player[i].connected = FALSE;
3915
3916   local_player->connected = TRUE;
3917   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3918
3919 #if 0
3920   printf("::: TEAM MODE: %d\n", game.team_mode);
3921 #endif
3922
3923   if (tape.playing)
3924   {
3925 #if 1
3926     for (i = 0; i < MAX_PLAYERS; i++)
3927       stored_player[i].connected = tape.player_participates[i];
3928 #else
3929     /* try to guess locally connected team mode players (needed for correct
3930        assignment of player figures from level to locally playing players) */
3931
3932     for (i = 0; i < MAX_PLAYERS; i++)
3933       if (tape.player_participates[i])
3934         stored_player[i].connected = TRUE;
3935 #endif
3936   }
3937   else if (game.team_mode && !options.network)
3938   {
3939     /* try to guess locally connected team mode players (needed for correct
3940        assignment of player figures from level to locally playing players) */
3941
3942     for (i = 0; i < MAX_PLAYERS; i++)
3943       if (setup.input[i].use_joystick ||
3944           setup.input[i].key.left != KSYM_UNDEFINED)
3945         stored_player[i].connected = TRUE;
3946   }
3947
3948 #if DEBUG_INIT_PLAYER
3949   if (options.debug)
3950   {
3951     printf("Player status after level initialization:\n");
3952
3953     for (i = 0; i < MAX_PLAYERS; i++)
3954     {
3955       struct PlayerInfo *player = &stored_player[i];
3956
3957       printf("- player %d: present == %d, connected == %d, active == %d",
3958              i + 1,
3959              player->present,
3960              player->connected,
3961              player->active);
3962
3963       if (local_player == player)
3964         printf(" (local player)");
3965
3966       printf("\n");
3967     }
3968   }
3969 #endif
3970
3971 #if DEBUG_INIT_PLAYER
3972   if (options.debug)
3973     printf("Reassigning players ...\n");
3974 #endif
3975
3976   /* check if any connected player was not found in playfield */
3977   for (i = 0; i < MAX_PLAYERS; i++)
3978   {
3979     struct PlayerInfo *player = &stored_player[i];
3980
3981     if (player->connected && !player->present)
3982     {
3983       struct PlayerInfo *field_player = NULL;
3984
3985 #if DEBUG_INIT_PLAYER
3986       if (options.debug)
3987         printf("- looking for field player for player %d ...\n", i + 1);
3988 #endif
3989
3990       /* assign first free player found that is present in the playfield */
3991
3992 #if 1
3993       /* first try: look for unmapped playfield player that is not connected */
3994       for (j = 0; j < MAX_PLAYERS; j++)
3995         if (field_player == NULL &&
3996             stored_player[j].present &&
3997             !stored_player[j].mapped &&
3998             !stored_player[j].connected)
3999           field_player = &stored_player[j];
4000
4001       /* second try: look for *any* unmapped playfield player */
4002       for (j = 0; j < MAX_PLAYERS; j++)
4003         if (field_player == NULL &&
4004             stored_player[j].present &&
4005             !stored_player[j].mapped)
4006           field_player = &stored_player[j];
4007 #else
4008       /* first try: look for unmapped playfield player that is not connected */
4009       if (field_player == NULL)
4010         for (j = 0; j < MAX_PLAYERS; j++)
4011           if (stored_player[j].present &&
4012               !stored_player[j].mapped &&
4013               !stored_player[j].connected)
4014             field_player = &stored_player[j];
4015
4016       /* second try: look for *any* unmapped playfield player */
4017       if (field_player == NULL)
4018         for (j = 0; j < MAX_PLAYERS; j++)
4019           if (stored_player[j].present &&
4020               !stored_player[j].mapped)
4021             field_player = &stored_player[j];
4022 #endif
4023
4024       if (field_player != NULL)
4025       {
4026         int jx = field_player->jx, jy = field_player->jy;
4027
4028 #if DEBUG_INIT_PLAYER
4029         if (options.debug)
4030           printf("- found player %d\n", field_player->index_nr + 1);
4031 #endif
4032
4033         player->present = FALSE;
4034         player->active = FALSE;
4035
4036         field_player->present = TRUE;
4037         field_player->active = TRUE;
4038
4039         /*
4040         player->initial_element = field_player->initial_element;
4041         player->artwork_element = field_player->artwork_element;
4042
4043         player->block_last_field       = field_player->block_last_field;
4044         player->block_delay_adjustment = field_player->block_delay_adjustment;
4045         */
4046
4047         StorePlayer[jx][jy] = field_player->element_nr;
4048
4049         field_player->jx = field_player->last_jx = jx;
4050         field_player->jy = field_player->last_jy = jy;
4051
4052         if (local_player == player)
4053           local_player = field_player;
4054
4055         map_player_action[field_player->index_nr] = i;
4056
4057         field_player->mapped = TRUE;
4058
4059 #if DEBUG_INIT_PLAYER
4060         if (options.debug)
4061           printf("- map_player_action[%d] == %d\n",
4062                  field_player->index_nr + 1, i + 1);
4063 #endif
4064       }
4065     }
4066
4067     if (player->connected && player->present)
4068       player->mapped = TRUE;
4069   }
4070
4071 #if DEBUG_INIT_PLAYER
4072   if (options.debug)
4073   {
4074     printf("Player status after player assignment (first stage):\n");
4075
4076     for (i = 0; i < MAX_PLAYERS; i++)
4077     {
4078       struct PlayerInfo *player = &stored_player[i];
4079
4080       printf("- player %d: present == %d, connected == %d, active == %d",
4081              i + 1,
4082              player->present,
4083              player->connected,
4084              player->active);
4085
4086       if (local_player == player)
4087         printf(" (local player)");
4088
4089       printf("\n");
4090     }
4091   }
4092 #endif
4093
4094 #else
4095
4096   /* check if any connected player was not found in playfield */
4097   for (i = 0; i < MAX_PLAYERS; i++)
4098   {
4099     struct PlayerInfo *player = &stored_player[i];
4100
4101     if (player->connected && !player->present)
4102     {
4103       for (j = 0; j < MAX_PLAYERS; j++)
4104       {
4105         struct PlayerInfo *field_player = &stored_player[j];
4106         int jx = field_player->jx, jy = field_player->jy;
4107
4108         /* assign first free player found that is present in the playfield */
4109         if (field_player->present && !field_player->connected)
4110         {
4111           player->present = TRUE;
4112           player->active = TRUE;
4113
4114           field_player->present = FALSE;
4115           field_player->active = FALSE;
4116
4117           player->initial_element = field_player->initial_element;
4118           player->artwork_element = field_player->artwork_element;
4119
4120           player->block_last_field       = field_player->block_last_field;
4121           player->block_delay_adjustment = field_player->block_delay_adjustment;
4122
4123           StorePlayer[jx][jy] = player->element_nr;
4124
4125           player->jx = player->last_jx = jx;
4126           player->jy = player->last_jy = jy;
4127
4128           break;
4129         }
4130       }
4131     }
4132   }
4133 #endif
4134
4135 #if 0
4136   printf("::: local_player->present == %d\n", local_player->present);
4137 #endif
4138
4139   if (tape.playing)
4140   {
4141     /* when playing a tape, eliminate all players who do not participate */
4142
4143 #if USE_NEW_PLAYER_ASSIGNMENTS
4144
4145 #if 1
4146     if (!game.team_mode)
4147 #endif
4148
4149     for (i = 0; i < MAX_PLAYERS; i++)
4150     {
4151       if (stored_player[i].active &&
4152           !tape.player_participates[map_player_action[i]])
4153       {
4154         struct PlayerInfo *player = &stored_player[i];
4155         int jx = player->jx, jy = player->jy;
4156
4157 #if DEBUG_INIT_PLAYER
4158         if (options.debug)
4159           printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
4160 #endif
4161
4162         player->active = FALSE;
4163         StorePlayer[jx][jy] = 0;
4164         Feld[jx][jy] = EL_EMPTY;
4165       }
4166     }
4167
4168 #else
4169
4170     for (i = 0; i < MAX_PLAYERS; i++)
4171     {
4172       if (stored_player[i].active &&
4173           !tape.player_participates[i])
4174       {
4175         struct PlayerInfo *player = &stored_player[i];
4176         int jx = player->jx, jy = player->jy;
4177
4178         player->active = FALSE;
4179         StorePlayer[jx][jy] = 0;
4180         Feld[jx][jy] = EL_EMPTY;
4181       }
4182     }
4183 #endif
4184   }
4185   else if (!options.network && !game.team_mode)         /* && !tape.playing */
4186   {
4187     /* when in single player mode, eliminate all but the first active player */
4188
4189     for (i = 0; i < MAX_PLAYERS; i++)
4190     {
4191       if (stored_player[i].active)
4192       {
4193         for (j = i + 1; j < MAX_PLAYERS; j++)
4194         {
4195           if (stored_player[j].active)
4196           {
4197             struct PlayerInfo *player = &stored_player[j];
4198             int jx = player->jx, jy = player->jy;
4199
4200             player->active = FALSE;
4201             player->present = FALSE;
4202
4203             StorePlayer[jx][jy] = 0;
4204             Feld[jx][jy] = EL_EMPTY;
4205           }
4206         }
4207       }
4208     }
4209   }
4210
4211   /* when recording the game, store which players take part in the game */
4212   if (tape.recording)
4213   {
4214 #if USE_NEW_PLAYER_ASSIGNMENTS
4215     for (i = 0; i < MAX_PLAYERS; i++)
4216       if (stored_player[i].connected)
4217         tape.player_participates[i] = TRUE;
4218 #else
4219     for (i = 0; i < MAX_PLAYERS; i++)
4220       if (stored_player[i].active)
4221         tape.player_participates[i] = TRUE;
4222 #endif
4223   }
4224
4225 #if DEBUG_INIT_PLAYER
4226   if (options.debug)
4227   {
4228     printf("Player status after player assignment (final stage):\n");
4229
4230     for (i = 0; i < MAX_PLAYERS; i++)
4231     {
4232       struct PlayerInfo *player = &stored_player[i];
4233
4234       printf("- player %d: present == %d, connected == %d, active == %d",
4235              i + 1,
4236              player->present,
4237              player->connected,
4238              player->active);
4239
4240       if (local_player == player)
4241         printf(" (local player)");
4242
4243       printf("\n");
4244     }
4245   }
4246 #endif
4247
4248   if (BorderElement == EL_EMPTY)
4249   {
4250     SBX_Left = 0;
4251     SBX_Right = lev_fieldx - SCR_FIELDX;
4252     SBY_Upper = 0;
4253     SBY_Lower = lev_fieldy - SCR_FIELDY;
4254   }
4255   else
4256   {
4257     SBX_Left = -1;
4258     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4259     SBY_Upper = -1;
4260     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4261   }
4262
4263 #if NEW_TILESIZE
4264
4265   if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4266     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4267
4268   if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4269     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4270
4271   if (EVEN(SCR_FIELDX))
4272     SBX_Left--;
4273   if (EVEN(SCR_FIELDY))
4274     SBY_Upper--;
4275
4276 #else
4277
4278   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4279     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4280
4281   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4282     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4283 #endif
4284
4285   /* if local player not found, look for custom element that might create
4286      the player (make some assumptions about the right custom element) */
4287   if (!local_player->present)
4288   {
4289     int start_x = 0, start_y = 0;
4290     int found_rating = 0;
4291     int found_element = EL_UNDEFINED;
4292     int player_nr = local_player->index_nr;
4293
4294     SCAN_PLAYFIELD(x, y)
4295     {
4296       int element = Feld[x][y];
4297       int content;
4298       int xx, yy;
4299       boolean is_player;
4300
4301       if (level.use_start_element[player_nr] &&
4302           level.start_element[player_nr] == element &&
4303           found_rating < 4)
4304       {
4305         start_x = x;
4306         start_y = y;
4307
4308         found_rating = 4;
4309         found_element = element;
4310       }
4311
4312       if (!IS_CUSTOM_ELEMENT(element))
4313         continue;
4314
4315       if (CAN_CHANGE(element))
4316       {
4317         for (i = 0; i < element_info[element].num_change_pages; i++)
4318         {
4319           /* check for player created from custom element as single target */
4320           content = element_info[element].change_page[i].target_element;
4321           is_player = ELEM_IS_PLAYER(content);
4322
4323           if (is_player && (found_rating < 3 ||
4324                             (found_rating == 3 && element < found_element)))
4325           {
4326             start_x = x;
4327             start_y = y;
4328
4329             found_rating = 3;
4330             found_element = element;
4331           }
4332         }
4333       }
4334
4335       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4336       {
4337         /* check for player created from custom element as explosion content */
4338         content = element_info[element].content.e[xx][yy];
4339         is_player = ELEM_IS_PLAYER(content);
4340
4341         if (is_player && (found_rating < 2 ||
4342                           (found_rating == 2 && element < found_element)))
4343         {
4344           start_x = x + xx - 1;
4345           start_y = y + yy - 1;
4346
4347           found_rating = 2;
4348           found_element = element;
4349         }
4350
4351         if (!CAN_CHANGE(element))
4352           continue;
4353
4354         for (i = 0; i < element_info[element].num_change_pages; i++)
4355         {
4356           /* check for player created from custom element as extended target */
4357           content =
4358             element_info[element].change_page[i].target_content.e[xx][yy];
4359
4360           is_player = ELEM_IS_PLAYER(content);
4361
4362           if (is_player && (found_rating < 1 ||
4363                             (found_rating == 1 && element < found_element)))
4364           {
4365             start_x = x + xx - 1;
4366             start_y = y + yy - 1;
4367
4368             found_rating = 1;
4369             found_element = element;
4370           }
4371         }
4372       }
4373     }
4374
4375     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4376                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4377                 start_x - MIDPOSX);
4378
4379     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4380                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4381                 start_y - MIDPOSY);
4382   }
4383   else
4384   {
4385     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4386                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4387                 local_player->jx - MIDPOSX);
4388
4389     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4390                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4391                 local_player->jy - MIDPOSY);
4392   }
4393
4394 #if 0
4395   printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4396 #endif
4397
4398 #if 0
4399   /* do not use PLAYING mask for fading out from main screen */
4400   game_status = GAME_MODE_MAIN;
4401 #endif
4402
4403   StopAnimation();
4404
4405   if (!game.restart_level)
4406     CloseDoor(DOOR_CLOSE_1);
4407
4408 #if 1
4409   if (level_editor_test_game)
4410     FadeSkipNextFadeIn();
4411   else
4412     FadeSetEnterScreen();
4413 #else
4414   if (level_editor_test_game)
4415     fading = fading_none;
4416   else
4417     fading = menu.destination;
4418 #endif
4419
4420 #if 1
4421   FadeOut(REDRAW_FIELD);
4422 #else
4423   if (do_fading)
4424     FadeOut(REDRAW_FIELD);
4425 #endif
4426
4427 #if 0
4428   game_status = GAME_MODE_PLAYING;
4429 #endif
4430
4431   /* !!! FIX THIS (START) !!! */
4432   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4433   {
4434     InitGameEngine_EM();
4435
4436     /* blit playfield from scroll buffer to normal back buffer for fading in */
4437     BlitScreenToBitmap_EM(backbuffer);
4438   }
4439   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4440   {
4441     InitGameEngine_SP();
4442
4443     /* blit playfield from scroll buffer to normal back buffer for fading in */
4444     BlitScreenToBitmap_SP(backbuffer);
4445   }
4446   else
4447   {
4448     DrawLevel();
4449     DrawAllPlayers();
4450
4451     /* after drawing the level, correct some elements */
4452     if (game.timegate_time_left == 0)
4453       CloseAllOpenTimegates();
4454
4455 #if NEW_TILESIZE
4456     BlitScreenToBitmap(backbuffer);
4457 #else
4458     /* blit playfield from scroll buffer to normal back buffer for fading in */
4459     if (setup.soft_scrolling)
4460       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4461 #endif
4462
4463     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4464   }
4465   /* !!! FIX THIS (END) !!! */
4466
4467 #if 1
4468   FadeIn(REDRAW_FIELD);
4469 #else
4470   if (do_fading)
4471     FadeIn(REDRAW_FIELD);
4472
4473   BackToFront();
4474 #endif
4475
4476   if (!game.restart_level)
4477   {
4478     /* copy default game door content to main double buffer */
4479 #if 1
4480 #if 1
4481     /* !!! CHECK AGAIN !!! */
4482     SetPanelBackground();
4483     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4484     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4485 #else
4486     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4487
4488     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4489     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4490     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4491                MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4492 #endif
4493 #else
4494     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4495                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4496 #endif
4497   }
4498
4499   SetPanelBackground();
4500   SetDrawBackgroundMask(REDRAW_DOOR_1);
4501
4502 #if 1
4503   UpdateAndDisplayGameControlValues();
4504 #else
4505   UpdateGameDoorValues();
4506   DrawGameDoorValues();
4507 #endif
4508
4509   if (!game.restart_level)
4510   {
4511     UnmapGameButtons();
4512     UnmapTapeButtons();
4513     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4514     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4515     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4516     MapGameButtons();
4517     MapTapeButtons();
4518
4519     /* copy actual game door content to door double buffer for OpenDoor() */
4520 #if 1
4521     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4522 #else
4523     BlitBitmap(drawto, bitmap_db_door,
4524                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4525 #endif
4526
4527     OpenDoor(DOOR_OPEN_ALL);
4528
4529     PlaySound(SND_GAME_STARTING);
4530
4531     if (setup.sound_music)
4532       PlayLevelMusic();
4533
4534     KeyboardAutoRepeatOffUnlessAutoplay();
4535
4536 #if DEBUG_INIT_PLAYER
4537     if (options.debug)
4538     {
4539       printf("Player status (final):\n");
4540
4541       for (i = 0; i < MAX_PLAYERS; i++)
4542       {
4543         struct PlayerInfo *player = &stored_player[i];
4544
4545         printf("- player %d: present == %d, connected == %d, active == %d",
4546                i + 1,
4547                player->present,
4548                player->connected,
4549                player->active);
4550
4551         if (local_player == player)
4552           printf(" (local player)");
4553
4554         printf("\n");
4555       }
4556     }
4557 #endif
4558   }
4559
4560 #if 1
4561   UnmapAllGadgets();
4562
4563   MapGameButtons();
4564   MapTapeButtons();
4565 #endif
4566
4567   if (!game.restart_level && !tape.playing)
4568   {
4569     LevelStats_incPlayed(level_nr);
4570
4571     SaveLevelSetup_SeriesInfo();
4572
4573 #if 0
4574     printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4575 #endif
4576   }
4577
4578   game.restart_level = FALSE;
4579 }
4580
4581 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4582 {
4583   /* this is used for non-R'n'D game engines to update certain engine values */
4584
4585   /* needed to determine if sounds are played within the visible screen area */
4586   scroll_x = actual_scroll_x;
4587   scroll_y = actual_scroll_y;
4588 }
4589
4590 void InitMovDir(int x, int y)
4591 {
4592   int i, element = Feld[x][y];
4593   static int xy[4][2] =
4594   {
4595     {  0, +1 },
4596     { +1,  0 },
4597     {  0, -1 },
4598     { -1,  0 }
4599   };
4600   static int direction[3][4] =
4601   {
4602     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4603     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4604     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4605   };
4606
4607   switch (element)
4608   {
4609     case EL_BUG_RIGHT:
4610     case EL_BUG_UP:
4611     case EL_BUG_LEFT:
4612     case EL_BUG_DOWN:
4613       Feld[x][y] = EL_BUG;
4614       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4615       break;
4616
4617     case EL_SPACESHIP_RIGHT:
4618     case EL_SPACESHIP_UP:
4619     case EL_SPACESHIP_LEFT:
4620     case EL_SPACESHIP_DOWN:
4621       Feld[x][y] = EL_SPACESHIP;
4622       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4623       break;
4624
4625     case EL_BD_BUTTERFLY_RIGHT:
4626     case EL_BD_BUTTERFLY_UP:
4627     case EL_BD_BUTTERFLY_LEFT:
4628     case EL_BD_BUTTERFLY_DOWN:
4629       Feld[x][y] = EL_BD_BUTTERFLY;
4630       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4631       break;
4632
4633     case EL_BD_FIREFLY_RIGHT:
4634     case EL_BD_FIREFLY_UP:
4635     case EL_BD_FIREFLY_LEFT:
4636     case EL_BD_FIREFLY_DOWN:
4637       Feld[x][y] = EL_BD_FIREFLY;
4638       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4639       break;
4640
4641     case EL_PACMAN_RIGHT:
4642     case EL_PACMAN_UP:
4643     case EL_PACMAN_LEFT:
4644     case EL_PACMAN_DOWN:
4645       Feld[x][y] = EL_PACMAN;
4646       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4647       break;
4648
4649     case EL_YAMYAM_LEFT:
4650     case EL_YAMYAM_RIGHT:
4651     case EL_YAMYAM_UP:
4652     case EL_YAMYAM_DOWN:
4653       Feld[x][y] = EL_YAMYAM;
4654       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4655       break;
4656
4657     case EL_SP_SNIKSNAK:
4658       MovDir[x][y] = MV_UP;
4659       break;
4660
4661     case EL_SP_ELECTRON:
4662       MovDir[x][y] = MV_LEFT;
4663       break;
4664
4665     case EL_MOLE_LEFT:
4666     case EL_MOLE_RIGHT:
4667     case EL_MOLE_UP:
4668     case EL_MOLE_DOWN:
4669       Feld[x][y] = EL_MOLE;
4670       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4671       break;
4672
4673     default:
4674       if (IS_CUSTOM_ELEMENT(element))
4675       {
4676         struct ElementInfo *ei = &element_info[element];
4677         int move_direction_initial = ei->move_direction_initial;
4678         int move_pattern = ei->move_pattern;
4679
4680         if (move_direction_initial == MV_START_PREVIOUS)
4681         {
4682           if (MovDir[x][y] != MV_NONE)
4683             return;
4684
4685           move_direction_initial = MV_START_AUTOMATIC;
4686         }
4687
4688         if (move_direction_initial == MV_START_RANDOM)
4689           MovDir[x][y] = 1 << RND(4);
4690         else if (move_direction_initial & MV_ANY_DIRECTION)
4691           MovDir[x][y] = move_direction_initial;
4692         else if (move_pattern == MV_ALL_DIRECTIONS ||
4693                  move_pattern == MV_TURNING_LEFT ||
4694                  move_pattern == MV_TURNING_RIGHT ||
4695                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4696                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4697                  move_pattern == MV_TURNING_RANDOM)
4698           MovDir[x][y] = 1 << RND(4);
4699         else if (move_pattern == MV_HORIZONTAL)
4700           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4701         else if (move_pattern == MV_VERTICAL)
4702           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4703         else if (move_pattern & MV_ANY_DIRECTION)
4704           MovDir[x][y] = element_info[element].move_pattern;
4705         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4706                  move_pattern == MV_ALONG_RIGHT_SIDE)
4707         {
4708           /* use random direction as default start direction */
4709           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4710             MovDir[x][y] = 1 << RND(4);
4711
4712           for (i = 0; i < NUM_DIRECTIONS; i++)
4713           {
4714             int x1 = x + xy[i][0];
4715             int y1 = y + xy[i][1];
4716
4717             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4718             {
4719               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4720                 MovDir[x][y] = direction[0][i];
4721               else
4722                 MovDir[x][y] = direction[1][i];
4723
4724               break;
4725             }
4726           }
4727         }                
4728       }
4729       else
4730       {
4731         MovDir[x][y] = 1 << RND(4);
4732
4733         if (element != EL_BUG &&
4734             element != EL_SPACESHIP &&
4735             element != EL_BD_BUTTERFLY &&
4736             element != EL_BD_FIREFLY)
4737           break;
4738
4739         for (i = 0; i < NUM_DIRECTIONS; i++)
4740         {
4741           int x1 = x + xy[i][0];
4742           int y1 = y + xy[i][1];
4743
4744           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4745           {
4746             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4747             {
4748               MovDir[x][y] = direction[0][i];
4749               break;
4750             }
4751             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4752                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4753             {
4754               MovDir[x][y] = direction[1][i];
4755               break;
4756             }
4757           }
4758         }
4759       }
4760       break;
4761   }
4762
4763   GfxDir[x][y] = MovDir[x][y];
4764 }
4765
4766 void InitAmoebaNr(int x, int y)
4767 {
4768   int i;
4769   int group_nr = AmoebeNachbarNr(x, y);
4770
4771   if (group_nr == 0)
4772   {
4773     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4774     {
4775       if (AmoebaCnt[i] == 0)
4776       {
4777         group_nr = i;
4778         break;
4779       }
4780     }
4781   }
4782
4783   AmoebaNr[x][y] = group_nr;
4784   AmoebaCnt[group_nr]++;
4785   AmoebaCnt2[group_nr]++;
4786 }
4787
4788 static void PlayerWins(struct PlayerInfo *player)
4789 {
4790   player->LevelSolved = TRUE;
4791   player->GameOver = TRUE;
4792
4793   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4794                          level.native_em_level->lev->score : player->score);
4795
4796   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4797                                       TimeLeft);
4798   player->LevelSolved_CountingScore = player->score_final;
4799 }
4800
4801 void GameWon()
4802 {
4803   static int time, time_final;
4804   static int score, score_final;
4805   static int game_over_delay_1 = 0;
4806   static int game_over_delay_2 = 0;
4807   int game_over_delay_value_1 = 50;
4808   int game_over_delay_value_2 = 50;
4809
4810   if (!local_player->LevelSolved_GameWon)
4811   {
4812     int i;
4813
4814     /* do not start end game actions before the player stops moving (to exit) */
4815     if (local_player->MovPos)
4816       return;
4817
4818     local_player->LevelSolved_GameWon = TRUE;
4819     local_player->LevelSolved_SaveTape = tape.recording;
4820     local_player->LevelSolved_SaveScore = !tape.playing;
4821
4822     if (!tape.playing)
4823     {
4824       LevelStats_incSolved(level_nr);
4825
4826       SaveLevelSetup_SeriesInfo();
4827
4828 #if 0
4829       printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4830 #endif
4831     }
4832
4833     if (tape.auto_play)         /* tape might already be stopped here */
4834       tape.auto_play_level_solved = TRUE;
4835
4836 #if 1
4837     TapeStop();
4838 #endif
4839
4840     game_over_delay_1 = game_over_delay_value_1;
4841     game_over_delay_2 = game_over_delay_value_2;
4842
4843     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4844     score = score_final = local_player->score_final;
4845
4846     if (TimeLeft > 0)
4847     {
4848       time_final = 0;
4849       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4850     }
4851     else if (game.no_time_limit && TimePlayed < 999)
4852     {
4853       time_final = 999;
4854       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4855     }
4856
4857     local_player->score_final = score_final;
4858
4859     if (level_editor_test_game)
4860     {
4861       time = time_final;
4862       score = score_final;
4863
4864 #if 1
4865       local_player->LevelSolved_CountingTime = time;
4866       local_player->LevelSolved_CountingScore = score;
4867
4868       game_panel_controls[GAME_PANEL_TIME].value = time;
4869       game_panel_controls[GAME_PANEL_SCORE].value = score;
4870
4871       DisplayGameControlValues();
4872 #else
4873       DrawGameValue_Time(time);
4874       DrawGameValue_Score(score);
4875 #endif
4876     }
4877
4878     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4879     {
4880       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4881       {
4882         /* close exit door after last player */
4883         if ((AllPlayersGone &&
4884              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4885               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4886               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4887             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4888             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4889         {
4890           int element = Feld[ExitX][ExitY];
4891
4892 #if 0
4893           if (element == EL_EM_EXIT_OPEN ||
4894               element == EL_EM_STEEL_EXIT_OPEN)
4895           {
4896             Bang(ExitX, ExitY);
4897           }
4898           else
4899 #endif
4900           {
4901             Feld[ExitX][ExitY] =
4902               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4903                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4904                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4905                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4906                EL_EM_STEEL_EXIT_CLOSING);
4907
4908             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4909           }
4910         }
4911
4912         /* player disappears */
4913         DrawLevelField(ExitX, ExitY);
4914       }
4915
4916       for (i = 0; i < MAX_PLAYERS; i++)
4917       {
4918         struct PlayerInfo *player = &stored_player[i];
4919
4920         if (player->present)
4921         {
4922           RemovePlayer(player);
4923
4924           /* player disappears */
4925           DrawLevelField(player->jx, player->jy);
4926         }
4927       }
4928     }
4929
4930     PlaySound(SND_GAME_WINNING);
4931   }
4932
4933   if (game_over_delay_1 > 0)
4934   {
4935     game_over_delay_1--;
4936
4937     return;
4938   }
4939
4940   if (time != time_final)
4941   {
4942     int time_to_go = ABS(time_final - time);
4943     int time_count_dir = (time < time_final ? +1 : -1);
4944     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4945
4946     time  += time_count_steps * time_count_dir;
4947     score += time_count_steps * level.score[SC_TIME_BONUS];
4948
4949 #if 1
4950     local_player->LevelSolved_CountingTime = time;
4951     local_player->LevelSolved_CountingScore = score;
4952
4953     game_panel_controls[GAME_PANEL_TIME].value = time;
4954     game_panel_controls[GAME_PANEL_SCORE].value = score;
4955
4956     DisplayGameControlValues();
4957 #else
4958     DrawGameValue_Time(time);
4959     DrawGameValue_Score(score);
4960 #endif
4961
4962     if (time == time_final)
4963       StopSound(SND_GAME_LEVELTIME_BONUS);
4964     else if (setup.sound_loops)
4965       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4966     else
4967       PlaySound(SND_GAME_LEVELTIME_BONUS);
4968
4969     return;
4970   }
4971
4972   local_player->LevelSolved_PanelOff = TRUE;
4973
4974   if (game_over_delay_2 > 0)
4975   {
4976     game_over_delay_2--;
4977
4978     return;
4979   }
4980
4981 #if 1
4982   GameEnd();
4983 #endif
4984 }
4985
4986 void GameEnd()
4987 {
4988   int hi_pos;
4989   boolean raise_level = FALSE;
4990
4991   local_player->LevelSolved_GameEnd = TRUE;
4992
4993   CloseDoor(DOOR_CLOSE_1);
4994
4995   if (local_player->LevelSolved_SaveTape)
4996   {
4997 #if 0
4998     TapeStop();
4999 #endif
5000
5001 #if 1
5002     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
5003 #else
5004     SaveTape(tape.level_nr);            /* ask to save tape */
5005 #endif
5006   }
5007
5008   if (level_editor_test_game)
5009   {
5010     game_status = GAME_MODE_MAIN;
5011
5012 #if 1
5013     DrawAndFadeInMainMenu(REDRAW_FIELD);
5014 #else
5015     DrawMainMenu();
5016 #endif
5017
5018     return;
5019   }
5020
5021   if (!local_player->LevelSolved_SaveScore)
5022   {
5023 #if 1
5024     FadeOut(REDRAW_FIELD);
5025 #endif
5026
5027     game_status = GAME_MODE_MAIN;
5028
5029     DrawAndFadeInMainMenu(REDRAW_FIELD);
5030
5031     return;
5032   }
5033
5034   if (level_nr == leveldir_current->handicap_level)
5035   {
5036     leveldir_current->handicap_level++;
5037
5038     SaveLevelSetup_SeriesInfo();
5039   }
5040
5041   if (level_nr < leveldir_current->last_level)
5042     raise_level = TRUE;                 /* advance to next level */
5043
5044   if ((hi_pos = NewHiScore()) >= 0) 
5045   {
5046     game_status = GAME_MODE_SCORES;
5047
5048     DrawHallOfFame(hi_pos);
5049
5050     if (raise_level)
5051     {
5052       level_nr++;
5053       TapeErase();
5054     }
5055   }
5056   else
5057   {
5058 #if 1
5059     FadeOut(REDRAW_FIELD);
5060 #endif
5061
5062     game_status = GAME_MODE_MAIN;
5063
5064     if (raise_level)
5065     {
5066       level_nr++;
5067       TapeErase();
5068     }
5069
5070     DrawAndFadeInMainMenu(REDRAW_FIELD);
5071   }
5072 }
5073
5074 int NewHiScore()
5075 {
5076   int k, l;
5077   int position = -1;
5078
5079   LoadScore(level_nr);
5080
5081   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5082       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5083     return -1;
5084
5085   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5086   {
5087     if (local_player->score_final > highscore[k].Score)
5088     {
5089       /* player has made it to the hall of fame */
5090
5091       if (k < MAX_SCORE_ENTRIES - 1)
5092       {
5093         int m = MAX_SCORE_ENTRIES - 1;
5094
5095 #ifdef ONE_PER_NAME
5096         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5097           if (strEqual(setup.player_name, highscore[l].Name))
5098             m = l;
5099         if (m == k)     /* player's new highscore overwrites his old one */
5100           goto put_into_list;
5101 #endif
5102
5103         for (l = m; l > k; l--)
5104         {
5105           strcpy(highscore[l].Name, highscore[l - 1].Name);
5106           highscore[l].Score = highscore[l - 1].Score;
5107         }
5108       }
5109
5110 #ifdef ONE_PER_NAME
5111       put_into_list:
5112 #endif
5113       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5114       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5115       highscore[k].Score = local_player->score_final; 
5116       position = k;
5117       break;
5118     }
5119
5120 #ifdef ONE_PER_NAME
5121     else if (!strncmp(setup.player_name, highscore[k].Name,
5122                       MAX_PLAYER_NAME_LEN))
5123       break;    /* player already there with a higher score */
5124 #endif
5125
5126   }
5127
5128   if (position >= 0) 
5129     SaveScore(level_nr);
5130
5131   return position;
5132 }
5133
5134 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5135 {
5136   int element = Feld[x][y];
5137   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5138   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5139   int horiz_move = (dx != 0);
5140   int sign = (horiz_move ? dx : dy);
5141   int step = sign * element_info[element].move_stepsize;
5142
5143   /* special values for move stepsize for spring and things on conveyor belt */
5144   if (horiz_move)
5145   {
5146     if (CAN_FALL(element) &&
5147         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5148       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5149     else if (element == EL_SPRING)
5150       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5151   }
5152
5153   return step;
5154 }
5155
5156 inline static int getElementMoveStepsize(int x, int y)
5157 {
5158   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5159 }
5160
5161 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5162 {
5163   if (player->GfxAction != action || player->GfxDir != dir)
5164   {
5165 #if 0
5166     printf("Player frame reset! (%d => %d, %d => %d)\n",
5167            player->GfxAction, action, player->GfxDir, dir);
5168 #endif
5169
5170     player->GfxAction = action;
5171     player->GfxDir = dir;
5172     player->Frame = 0;
5173     player->StepFrame = 0;
5174   }
5175 }
5176
5177 #if USE_GFX_RESET_GFX_ANIMATION
5178 static void ResetGfxFrame(int x, int y, boolean redraw)
5179 {
5180   int element = Feld[x][y];
5181   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5182   int last_gfx_frame = GfxFrame[x][y];
5183
5184   if (graphic_info[graphic].anim_global_sync)
5185     GfxFrame[x][y] = FrameCounter;
5186   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5187     GfxFrame[x][y] = CustomValue[x][y];
5188   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5189     GfxFrame[x][y] = element_info[element].collect_score;
5190   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5191     GfxFrame[x][y] = ChangeDelay[x][y];
5192
5193   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5194     DrawLevelGraphicAnimation(x, y, graphic);
5195 }
5196 #endif
5197
5198 static void ResetGfxAnimation(int x, int y)
5199 {
5200   GfxAction[x][y] = ACTION_DEFAULT;
5201   GfxDir[x][y] = MovDir[x][y];
5202   GfxFrame[x][y] = 0;
5203
5204 #if USE_GFX_RESET_GFX_ANIMATION
5205   ResetGfxFrame(x, y, FALSE);
5206 #endif
5207 }
5208
5209 static void ResetRandomAnimationValue(int x, int y)
5210 {
5211   GfxRandom[x][y] = INIT_GFX_RANDOM();
5212 }
5213
5214 void InitMovingField(int x, int y, int direction)
5215 {
5216   int element = Feld[x][y];
5217   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5218   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5219   int newx = x + dx;
5220   int newy = y + dy;
5221   boolean is_moving_before, is_moving_after;
5222 #if 0
5223   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5224 #endif
5225
5226   /* check if element was/is moving or being moved before/after mode change */
5227 #if 1
5228 #if 1
5229   is_moving_before = (WasJustMoving[x][y] != 0);
5230 #else
5231   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5232   is_moving_before = WasJustMoving[x][y];
5233 #endif
5234 #else
5235   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5236 #endif
5237   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5238
5239   /* reset animation only for moving elements which change direction of moving
5240      or which just started or stopped moving
5241      (else CEs with property "can move" / "not moving" are reset each frame) */
5242 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5243 #if 1
5244   if (is_moving_before != is_moving_after ||
5245       direction != MovDir[x][y])
5246     ResetGfxAnimation(x, y);
5247 #else
5248   if ((is_moving_before || is_moving_after) && !continues_moving)
5249     ResetGfxAnimation(x, y);
5250 #endif
5251 #else
5252   if (!continues_moving)
5253     ResetGfxAnimation(x, y);
5254 #endif
5255
5256   MovDir[x][y] = direction;
5257   GfxDir[x][y] = direction;
5258
5259 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5260   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5261                      direction == MV_DOWN && CAN_FALL(element) ?
5262                      ACTION_FALLING : ACTION_MOVING);
5263 #else
5264   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5265                      ACTION_FALLING : ACTION_MOVING);
5266 #endif
5267
5268   /* this is needed for CEs with property "can move" / "not moving" */
5269
5270   if (is_moving_after)
5271   {
5272     if (Feld[newx][newy] == EL_EMPTY)
5273       Feld[newx][newy] = EL_BLOCKED;
5274
5275     MovDir[newx][newy] = MovDir[x][y];
5276
5277 #if USE_NEW_CUSTOM_VALUE
5278     CustomValue[newx][newy] = CustomValue[x][y];
5279 #endif
5280
5281     GfxFrame[newx][newy] = GfxFrame[x][y];
5282     GfxRandom[newx][newy] = GfxRandom[x][y];
5283     GfxAction[newx][newy] = GfxAction[x][y];
5284     GfxDir[newx][newy] = GfxDir[x][y];
5285   }
5286 }
5287
5288 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5289 {
5290   int direction = MovDir[x][y];
5291   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5292   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5293
5294   *goes_to_x = newx;
5295   *goes_to_y = newy;
5296 }
5297
5298 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5299 {
5300   int oldx = x, oldy = y;
5301   int direction = MovDir[x][y];
5302
5303   if (direction == MV_LEFT)
5304     oldx++;
5305   else if (direction == MV_RIGHT)
5306     oldx--;
5307   else if (direction == MV_UP)
5308     oldy++;
5309   else if (direction == MV_DOWN)
5310     oldy--;
5311
5312   *comes_from_x = oldx;
5313   *comes_from_y = oldy;
5314 }
5315
5316 int MovingOrBlocked2Element(int x, int y)
5317 {
5318   int element = Feld[x][y];
5319
5320   if (element == EL_BLOCKED)
5321   {
5322     int oldx, oldy;
5323
5324     Blocked2Moving(x, y, &oldx, &oldy);
5325     return Feld[oldx][oldy];
5326   }
5327   else
5328     return element;
5329 }
5330
5331 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5332 {
5333   /* like MovingOrBlocked2Element(), but if element is moving
5334      and (x,y) is the field the moving element is just leaving,
5335      return EL_BLOCKED instead of the element value */
5336   int element = Feld[x][y];
5337
5338   if (IS_MOVING(x, y))
5339   {
5340     if (element == EL_BLOCKED)
5341     {
5342       int oldx, oldy;
5343
5344       Blocked2Moving(x, y, &oldx, &oldy);
5345       return Feld[oldx][oldy];
5346     }
5347     else
5348       return EL_BLOCKED;
5349   }
5350   else
5351     return element;
5352 }
5353
5354 static void RemoveField(int x, int y)
5355 {
5356   Feld[x][y] = EL_EMPTY;
5357
5358   MovPos[x][y] = 0;
5359   MovDir[x][y] = 0;
5360   MovDelay[x][y] = 0;
5361
5362 #if USE_NEW_CUSTOM_VALUE
5363   CustomValue[x][y] = 0;
5364 #endif
5365
5366   AmoebaNr[x][y] = 0;
5367   ChangeDelay[x][y] = 0;
5368   ChangePage[x][y] = -1;
5369   Pushed[x][y] = FALSE;
5370
5371 #if 0
5372   ExplodeField[x][y] = EX_TYPE_NONE;
5373 #endif
5374
5375   GfxElement[x][y] = EL_UNDEFINED;
5376   GfxAction[x][y] = ACTION_DEFAULT;
5377   GfxDir[x][y] = MV_NONE;
5378 #if 0
5379   /* !!! this would prevent the removed tile from being redrawn !!! */
5380   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5381 #endif
5382 }
5383
5384 void RemoveMovingField(int x, int y)
5385 {
5386   int oldx = x, oldy = y, newx = x, newy = y;
5387   int element = Feld[x][y];
5388   int next_element = EL_UNDEFINED;
5389
5390   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5391     return;
5392
5393   if (IS_MOVING(x, y))
5394   {
5395     Moving2Blocked(x, y, &newx, &newy);
5396
5397     if (Feld[newx][newy] != EL_BLOCKED)
5398     {
5399       /* element is moving, but target field is not free (blocked), but
5400          already occupied by something different (example: acid pool);
5401          in this case, only remove the moving field, but not the target */
5402
5403       RemoveField(oldx, oldy);
5404
5405       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5406
5407       TEST_DrawLevelField(oldx, oldy);
5408
5409       return;
5410     }
5411   }
5412   else if (element == EL_BLOCKED)
5413   {
5414     Blocked2Moving(x, y, &oldx, &oldy);
5415     if (!IS_MOVING(oldx, oldy))
5416       return;
5417   }
5418
5419   if (element == EL_BLOCKED &&
5420       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5421        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5422        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5423        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5424        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5425        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5426     next_element = get_next_element(Feld[oldx][oldy]);
5427
5428   RemoveField(oldx, oldy);
5429   RemoveField(newx, newy);
5430
5431   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5432
5433   if (next_element != EL_UNDEFINED)
5434     Feld[oldx][oldy] = next_element;
5435
5436   TEST_DrawLevelField(oldx, oldy);
5437   TEST_DrawLevelField(newx, newy);
5438 }
5439
5440 void DrawDynamite(int x, int y)
5441 {
5442   int sx = SCREENX(x), sy = SCREENY(y);
5443   int graphic = el2img(Feld[x][y]);
5444   int frame;
5445
5446   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5447     return;
5448
5449   if (IS_WALKABLE_INSIDE(Back[x][y]))
5450     return;
5451
5452   if (Back[x][y])
5453     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5454   else if (Store[x][y])
5455     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5456
5457   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5458
5459   if (Back[x][y] || Store[x][y])
5460     DrawGraphicThruMask(sx, sy, graphic, frame);
5461   else
5462     DrawGraphic(sx, sy, graphic, frame);
5463 }
5464
5465 void CheckDynamite(int x, int y)
5466 {
5467   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5468   {
5469     MovDelay[x][y]--;
5470
5471     if (MovDelay[x][y] != 0)
5472     {
5473       DrawDynamite(x, y);
5474       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5475
5476       return;
5477     }
5478   }
5479
5480   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5481
5482   Bang(x, y);
5483 }
5484
5485 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5486 {
5487   boolean num_checked_players = 0;
5488   int i;
5489
5490   for (i = 0; i < MAX_PLAYERS; i++)
5491   {
5492     if (stored_player[i].active)
5493     {
5494       int sx = stored_player[i].jx;
5495       int sy = stored_player[i].jy;
5496
5497       if (num_checked_players == 0)
5498       {
5499         *sx1 = *sx2 = sx;
5500         *sy1 = *sy2 = sy;
5501       }
5502       else
5503       {
5504         *sx1 = MIN(*sx1, sx);
5505         *sy1 = MIN(*sy1, sy);
5506         *sx2 = MAX(*sx2, sx);
5507         *sy2 = MAX(*sy2, sy);
5508       }
5509
5510       num_checked_players++;
5511     }
5512   }
5513 }
5514
5515 static boolean checkIfAllPlayersFitToScreen_RND()
5516 {
5517   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5518
5519   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5520
5521   return (sx2 - sx1 < SCR_FIELDX &&
5522           sy2 - sy1 < SCR_FIELDY);
5523 }
5524
5525 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5526 {
5527   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5528
5529   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5530
5531   *sx = (sx1 + sx2) / 2;
5532   *sy = (sy1 + sy2) / 2;
5533 }
5534
5535 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5536                         boolean center_screen, boolean quick_relocation)
5537 {
5538   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5539   boolean no_delay = (tape.warp_forward);
5540   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5541   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5542
5543   if (quick_relocation)
5544   {
5545     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5546     {
5547       if (!level.shifted_relocation || center_screen)
5548       {
5549         /* quick relocation (without scrolling), with centering of screen */
5550
5551         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5552                     x > SBX_Right + MIDPOSX ? SBX_Right :
5553                     x - MIDPOSX);
5554
5555         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5556                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5557                     y - MIDPOSY);
5558       }
5559       else
5560       {
5561         /* quick relocation (without scrolling), but do not center screen */
5562
5563         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5564                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5565                                old_x - MIDPOSX);
5566
5567         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5568                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5569                                old_y - MIDPOSY);
5570
5571         int offset_x = x + (scroll_x - center_scroll_x);
5572         int offset_y = y + (scroll_y - center_scroll_y);
5573
5574         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5575                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5576                     offset_x - MIDPOSX);
5577
5578         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5579                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5580                     offset_y - MIDPOSY);
5581       }
5582     }
5583     else
5584     {
5585 #if 1
5586       if (!level.shifted_relocation || center_screen)
5587       {
5588         /* quick relocation (without scrolling), with centering of screen */
5589
5590         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5591                     x > SBX_Right + MIDPOSX ? SBX_Right :
5592                     x - MIDPOSX);
5593
5594         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5595                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5596                     y - MIDPOSY);
5597       }
5598       else
5599       {
5600         /* quick relocation (without scrolling), but do not center screen */
5601
5602         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5603                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5604                                old_x - MIDPOSX);
5605
5606         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5607                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5608                                old_y - MIDPOSY);
5609
5610         int offset_x = x + (scroll_x - center_scroll_x);
5611         int offset_y = y + (scroll_y - center_scroll_y);
5612
5613         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5614                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5615                     offset_x - MIDPOSX);
5616
5617         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5618                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5619                     offset_y - MIDPOSY);
5620       }
5621 #else
5622       /* quick relocation (without scrolling), inside visible screen area */
5623
5624       int offset = game.scroll_delay_value;
5625
5626       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5627           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5628         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5629
5630       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5631           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5632         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5633
5634       /* don't scroll over playfield boundaries */
5635       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5636         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5637
5638       /* don't scroll over playfield boundaries */
5639       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5640         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5641 #endif
5642     }
5643
5644     RedrawPlayfield(TRUE, 0,0,0,0);
5645   }
5646   else
5647   {
5648 #if 1
5649     int scroll_xx, scroll_yy;
5650
5651     if (!level.shifted_relocation || center_screen)
5652     {
5653       /* visible relocation (with scrolling), with centering of screen */
5654
5655       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5656                    x > SBX_Right + MIDPOSX ? SBX_Right :
5657                    x - MIDPOSX);
5658
5659       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5660                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5661                    y - MIDPOSY);
5662     }
5663     else
5664     {
5665       /* visible relocation (with scrolling), but do not center screen */
5666
5667       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5668                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5669                              old_x - MIDPOSX);
5670
5671       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5672                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5673                              old_y - MIDPOSY);
5674
5675       int offset_x = x + (scroll_x - center_scroll_x);
5676       int offset_y = y + (scroll_y - center_scroll_y);
5677
5678       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5679                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5680                    offset_x - MIDPOSX);
5681
5682       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5683                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5684                    offset_y - MIDPOSY);
5685     }
5686
5687 #else
5688
5689     /* visible relocation (with scrolling), with centering of screen */
5690
5691     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5692                      x > SBX_Right + MIDPOSX ? SBX_Right :
5693                      x - MIDPOSX);
5694
5695     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5696                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5697                      y - MIDPOSY);
5698 #endif
5699
5700     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5701
5702     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5703     {
5704       int dx = 0, dy = 0;
5705       int fx = FX, fy = FY;
5706
5707       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5708       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5709
5710       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5711         break;
5712
5713       scroll_x -= dx;
5714       scroll_y -= dy;
5715
5716       fx += dx * TILEX / 2;
5717       fy += dy * TILEY / 2;
5718
5719       ScrollLevel(dx, dy);
5720       DrawAllPlayers();
5721
5722       /* scroll in two steps of half tile size to make things smoother */
5723       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5724       FlushDisplay();
5725       Delay(wait_delay_value);
5726
5727       /* scroll second step to align at full tile size */
5728       BackToFront();
5729       Delay(wait_delay_value);
5730     }
5731
5732     DrawAllPlayers();
5733     BackToFront();
5734     Delay(wait_delay_value);
5735   }
5736 }
5737
5738 void RelocatePlayer(int jx, int jy, int el_player_raw)
5739 {
5740   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5741   int player_nr = GET_PLAYER_NR(el_player);
5742   struct PlayerInfo *player = &stored_player[player_nr];
5743   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5744   boolean no_delay = (tape.warp_forward);
5745   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5746   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5747   int old_jx = player->jx;
5748   int old_jy = player->jy;
5749   int old_element = Feld[old_jx][old_jy];
5750   int element = Feld[jx][jy];
5751   boolean player_relocated = (old_jx != jx || old_jy != jy);
5752
5753   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5754   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5755   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5756   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5757   int leave_side_horiz = move_dir_horiz;
5758   int leave_side_vert  = move_dir_vert;
5759   int enter_side = enter_side_horiz | enter_side_vert;
5760   int leave_side = leave_side_horiz | leave_side_vert;
5761
5762   if (player->GameOver)         /* do not reanimate dead player */
5763     return;
5764
5765   if (!player_relocated)        /* no need to relocate the player */
5766     return;
5767
5768   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5769   {
5770     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5771     DrawLevelField(jx, jy);
5772   }
5773
5774   if (player->present)
5775   {
5776     while (player->MovPos)
5777     {
5778       ScrollPlayer(player, SCROLL_GO_ON);
5779       ScrollScreen(NULL, SCROLL_GO_ON);
5780
5781       AdvanceFrameAndPlayerCounters(player->index_nr);
5782
5783       DrawPlayer(player);
5784
5785       BackToFront();
5786       Delay(wait_delay_value);
5787     }
5788
5789     DrawPlayer(player);         /* needed here only to cleanup last field */
5790     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5791
5792     player->is_moving = FALSE;
5793   }
5794
5795   if (IS_CUSTOM_ELEMENT(old_element))
5796     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5797                                CE_LEFT_BY_PLAYER,
5798                                player->index_bit, leave_side);
5799
5800   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5801                                       CE_PLAYER_LEAVES_X,
5802                                       player->index_bit, leave_side);
5803
5804   Feld[jx][jy] = el_player;
5805   InitPlayerField(jx, jy, el_player, TRUE);
5806
5807   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5808      possible that the relocation target field did not contain a player element,
5809      but a walkable element, to which the new player was relocated -- in this
5810      case, restore that (already initialized!) element on the player field */
5811   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5812   {
5813     Feld[jx][jy] = element;     /* restore previously existing element */
5814 #if 0
5815     /* !!! do not initialize already initialized element a second time !!! */
5816     /* (this causes at least problems with "element creation" CE trigger for
5817        already existing elements, and existing Sokoban fields counted twice) */
5818     InitField(jx, jy, FALSE);
5819 #endif
5820   }
5821
5822   /* only visually relocate centered player */
5823   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5824                      FALSE, level.instant_relocation);
5825
5826   TestIfPlayerTouchesBadThing(jx, jy);
5827   TestIfPlayerTouchesCustomElement(jx, jy);
5828
5829   if (IS_CUSTOM_ELEMENT(element))
5830     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5831                                player->index_bit, enter_side);
5832
5833   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5834                                       player->index_bit, enter_side);
5835
5836 #if 1
5837   if (player->is_switching)
5838   {
5839     /* ensure that relocation while still switching an element does not cause
5840        a new element to be treated as also switched directly after relocation
5841        (this is important for teleporter switches that teleport the player to
5842        a place where another teleporter switch is in the same direction, which
5843        would then incorrectly be treated as immediately switched before the
5844        direction key that caused the switch was released) */
5845
5846     player->switch_x += jx - old_jx;
5847     player->switch_y += jy - old_jy;
5848   }
5849 #endif
5850 }
5851
5852 void Explode(int ex, int ey, int phase, int mode)
5853 {
5854   int x, y;
5855   int last_phase;
5856   int border_element;
5857
5858   /* !!! eliminate this variable !!! */
5859   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5860
5861   if (game.explosions_delayed)
5862   {
5863     ExplodeField[ex][ey] = mode;
5864     return;
5865   }
5866
5867   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5868   {
5869     int center_element = Feld[ex][ey];
5870     int artwork_element, explosion_element;     /* set these values later */
5871
5872 #if 0
5873     /* --- This is only really needed (and now handled) in "Impact()". --- */
5874     /* do not explode moving elements that left the explode field in time */
5875     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5876         center_element == EL_EMPTY &&
5877         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5878       return;
5879 #endif
5880
5881 #if 0
5882     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5883     if (mode == EX_TYPE_NORMAL ||
5884         mode == EX_TYPE_CENTER ||
5885         mode == EX_TYPE_CROSS)
5886       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5887 #endif
5888
5889     /* remove things displayed in background while burning dynamite */
5890     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5891       Back[ex][ey] = 0;
5892
5893     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5894     {
5895       /* put moving element to center field (and let it explode there) */
5896       center_element = MovingOrBlocked2Element(ex, ey);
5897       RemoveMovingField(ex, ey);
5898       Feld[ex][ey] = center_element;
5899     }
5900
5901     /* now "center_element" is finally determined -- set related values now */
5902     artwork_element = center_element;           /* for custom player artwork */
5903     explosion_element = center_element;         /* for custom player artwork */
5904
5905     if (IS_PLAYER(ex, ey))
5906     {
5907       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5908
5909       artwork_element = stored_player[player_nr].artwork_element;
5910
5911       if (level.use_explosion_element[player_nr])
5912       {
5913         explosion_element = level.explosion_element[player_nr];
5914         artwork_element = explosion_element;
5915       }
5916     }
5917
5918 #if 1
5919     if (mode == EX_TYPE_NORMAL ||
5920         mode == EX_TYPE_CENTER ||
5921         mode == EX_TYPE_CROSS)
5922       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5923 #endif
5924
5925     last_phase = element_info[explosion_element].explosion_delay + 1;
5926
5927     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5928     {
5929       int xx = x - ex + 1;
5930       int yy = y - ey + 1;
5931       int element;
5932
5933       if (!IN_LEV_FIELD(x, y) ||
5934           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5935           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5936         continue;
5937
5938       element = Feld[x][y];
5939
5940       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5941       {
5942         element = MovingOrBlocked2Element(x, y);
5943
5944         if (!IS_EXPLOSION_PROOF(element))
5945           RemoveMovingField(x, y);
5946       }
5947
5948       /* indestructible elements can only explode in center (but not flames) */
5949       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5950                                            mode == EX_TYPE_BORDER)) ||
5951           element == EL_FLAMES)
5952         continue;
5953
5954       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5955          behaviour, for example when touching a yamyam that explodes to rocks
5956          with active deadly shield, a rock is created under the player !!! */
5957       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5958 #if 0
5959       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5960           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5961            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5962 #else
5963       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5964 #endif
5965       {
5966         if (IS_ACTIVE_BOMB(element))
5967         {
5968           /* re-activate things under the bomb like gate or penguin */
5969           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5970           Back[x][y] = 0;
5971         }
5972
5973         continue;
5974       }
5975
5976       /* save walkable background elements while explosion on same tile */
5977       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5978           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5979         Back[x][y] = element;
5980
5981       /* ignite explodable elements reached by other explosion */
5982       if (element == EL_EXPLOSION)
5983         element = Store2[x][y];
5984
5985       if (AmoebaNr[x][y] &&
5986           (element == EL_AMOEBA_FULL ||
5987            element == EL_BD_AMOEBA ||
5988            element == EL_AMOEBA_GROWING))
5989       {
5990         AmoebaCnt[AmoebaNr[x][y]]--;
5991         AmoebaCnt2[AmoebaNr[x][y]]--;
5992       }
5993
5994       RemoveField(x, y);
5995
5996       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5997       {
5998         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5999
6000         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6001
6002         if (PLAYERINFO(ex, ey)->use_murphy)
6003           Store[x][y] = EL_EMPTY;
6004       }
6005
6006       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6007          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6008       else if (ELEM_IS_PLAYER(center_element))
6009         Store[x][y] = EL_EMPTY;
6010       else if (center_element == EL_YAMYAM)
6011         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6012       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6013         Store[x][y] = element_info[center_element].content.e[xx][yy];
6014 #if 1
6015       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6016          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6017          otherwise) -- FIX THIS !!! */
6018       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6019         Store[x][y] = element_info[element].content.e[1][1];
6020 #else
6021       else if (!CAN_EXPLODE(element))
6022         Store[x][y] = element_info[element].content.e[1][1];
6023 #endif
6024       else
6025         Store[x][y] = EL_EMPTY;
6026
6027       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6028           center_element == EL_AMOEBA_TO_DIAMOND)
6029         Store2[x][y] = element;
6030
6031       Feld[x][y] = EL_EXPLOSION;
6032       GfxElement[x][y] = artwork_element;
6033
6034       ExplodePhase[x][y] = 1;
6035       ExplodeDelay[x][y] = last_phase;
6036
6037       Stop[x][y] = TRUE;
6038     }
6039
6040     if (center_element == EL_YAMYAM)
6041       game.yamyam_content_nr =
6042         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6043
6044     return;
6045   }
6046
6047   if (Stop[ex][ey])
6048     return;
6049
6050   x = ex;
6051   y = ey;
6052
6053   if (phase == 1)
6054     GfxFrame[x][y] = 0;         /* restart explosion animation */
6055
6056   last_phase = ExplodeDelay[x][y];
6057
6058   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6059
6060 #ifdef DEBUG
6061
6062   /* activate this even in non-DEBUG version until cause for crash in
6063      getGraphicAnimationFrame() (see below) is found and eliminated */
6064
6065 #endif
6066 #if 1
6067
6068 #if 1
6069   /* this can happen if the player leaves an explosion just in time */
6070   if (GfxElement[x][y] == EL_UNDEFINED)
6071     GfxElement[x][y] = EL_EMPTY;
6072 #else
6073   if (GfxElement[x][y] == EL_UNDEFINED)
6074   {
6075     printf("\n\n");
6076     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6077     printf("Explode(): This should never happen!\n");
6078     printf("\n\n");
6079
6080     GfxElement[x][y] = EL_EMPTY;
6081   }
6082 #endif
6083
6084 #endif
6085
6086   border_element = Store2[x][y];
6087   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6088     border_element = StorePlayer[x][y];
6089
6090   if (phase == element_info[border_element].ignition_delay ||
6091       phase == last_phase)
6092   {
6093     boolean border_explosion = FALSE;
6094
6095     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6096         !PLAYER_EXPLOSION_PROTECTED(x, y))
6097     {
6098       KillPlayerUnlessExplosionProtected(x, y);
6099       border_explosion = TRUE;
6100     }
6101     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6102     {
6103       Feld[x][y] = Store2[x][y];
6104       Store2[x][y] = 0;
6105       Bang(x, y);
6106       border_explosion = TRUE;
6107     }
6108     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6109     {
6110       AmoebeUmwandeln(x, y);
6111       Store2[x][y] = 0;
6112       border_explosion = TRUE;
6113     }
6114
6115     /* if an element just explodes due to another explosion (chain-reaction),
6116        do not immediately end the new explosion when it was the last frame of
6117        the explosion (as it would be done in the following "if"-statement!) */
6118     if (border_explosion && phase == last_phase)
6119       return;
6120   }
6121
6122   if (phase == last_phase)
6123   {
6124     int element;
6125
6126     element = Feld[x][y] = Store[x][y];
6127     Store[x][y] = Store2[x][y] = 0;
6128     GfxElement[x][y] = EL_UNDEFINED;
6129
6130     /* player can escape from explosions and might therefore be still alive */
6131     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6132         element <= EL_PLAYER_IS_EXPLODING_4)
6133     {
6134       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6135       int explosion_element = EL_PLAYER_1 + player_nr;
6136       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6137       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6138
6139       if (level.use_explosion_element[player_nr])
6140         explosion_element = level.explosion_element[player_nr];
6141
6142       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6143                     element_info[explosion_element].content.e[xx][yy]);
6144     }
6145
6146     /* restore probably existing indestructible background element */
6147     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6148       element = Feld[x][y] = Back[x][y];
6149     Back[x][y] = 0;
6150
6151     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6152     GfxDir[x][y] = MV_NONE;
6153     ChangeDelay[x][y] = 0;
6154     ChangePage[x][y] = -1;
6155
6156 #if USE_NEW_CUSTOM_VALUE
6157     CustomValue[x][y] = 0;
6158 #endif
6159
6160     InitField_WithBug2(x, y, FALSE);
6161
6162     TEST_DrawLevelField(x, y);
6163
6164     TestIfElementTouchesCustomElement(x, y);
6165
6166     if (GFX_CRUMBLED(element))
6167       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6168
6169     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6170       StorePlayer[x][y] = 0;
6171
6172     if (ELEM_IS_PLAYER(element))
6173       RelocatePlayer(x, y, element);
6174   }
6175   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6176   {
6177     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6178     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6179
6180     if (phase == delay)
6181       TEST_DrawLevelFieldCrumbled(x, y);
6182
6183     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6184     {
6185       DrawLevelElement(x, y, Back[x][y]);
6186       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6187     }
6188     else if (IS_WALKABLE_UNDER(Back[x][y]))
6189     {
6190       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6191       DrawLevelElementThruMask(x, y, Back[x][y]);
6192     }
6193     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6194       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6195   }
6196 }
6197
6198 void DynaExplode(int ex, int ey)
6199 {
6200   int i, j;
6201   int dynabomb_element = Feld[ex][ey];
6202   int dynabomb_size = 1;
6203   boolean dynabomb_xl = FALSE;
6204   struct PlayerInfo *player;
6205   static int xy[4][2] =
6206   {
6207     { 0, -1 },
6208     { -1, 0 },
6209     { +1, 0 },
6210     { 0, +1 }
6211   };
6212
6213   if (IS_ACTIVE_BOMB(dynabomb_element))
6214   {
6215     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6216     dynabomb_size = player->dynabomb_size;
6217     dynabomb_xl = player->dynabomb_xl;
6218     player->dynabombs_left++;
6219   }
6220
6221   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6222
6223   for (i = 0; i < NUM_DIRECTIONS; i++)
6224   {
6225     for (j = 1; j <= dynabomb_size; j++)
6226     {
6227       int x = ex + j * xy[i][0];
6228       int y = ey + j * xy[i][1];
6229       int element;
6230
6231       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6232         break;
6233
6234       element = Feld[x][y];
6235
6236       /* do not restart explosions of fields with active bombs */
6237       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6238         continue;
6239
6240       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6241
6242       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6243           !IS_DIGGABLE(element) && !dynabomb_xl)
6244         break;
6245     }
6246   }
6247 }
6248
6249 void Bang(int x, int y)
6250 {
6251   int element = MovingOrBlocked2Element(x, y);
6252   int explosion_type = EX_TYPE_NORMAL;
6253
6254   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6255   {
6256     struct PlayerInfo *player = PLAYERINFO(x, y);
6257
6258 #if USE_FIX_CE_ACTION_WITH_PLAYER
6259     element = Feld[x][y] = player->initial_element;
6260 #else
6261     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6262                             player->element_nr);
6263 #endif
6264
6265     if (level.use_explosion_element[player->index_nr])
6266     {
6267       int explosion_element = level.explosion_element[player->index_nr];
6268
6269       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6270         explosion_type = EX_TYPE_CROSS;
6271       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6272         explosion_type = EX_TYPE_CENTER;
6273     }
6274   }
6275
6276   switch (element)
6277   {
6278     case EL_BUG:
6279     case EL_SPACESHIP:
6280     case EL_BD_BUTTERFLY:
6281     case EL_BD_FIREFLY:
6282     case EL_YAMYAM:
6283     case EL_DARK_YAMYAM:
6284     case EL_ROBOT:
6285     case EL_PACMAN:
6286     case EL_MOLE:
6287       RaiseScoreElement(element);
6288       break;
6289
6290     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6291     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6292     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6293     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6294     case EL_DYNABOMB_INCREASE_NUMBER:
6295     case EL_DYNABOMB_INCREASE_SIZE:
6296     case EL_DYNABOMB_INCREASE_POWER:
6297       explosion_type = EX_TYPE_DYNA;
6298       break;
6299
6300     case EL_DC_LANDMINE:
6301 #if 0
6302     case EL_EM_EXIT_OPEN:
6303     case EL_EM_STEEL_EXIT_OPEN:
6304 #endif
6305       explosion_type = EX_TYPE_CENTER;
6306       break;
6307
6308     case EL_PENGUIN:
6309     case EL_LAMP:
6310     case EL_LAMP_ACTIVE:
6311     case EL_AMOEBA_TO_DIAMOND:
6312       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6313         explosion_type = EX_TYPE_CENTER;
6314       break;
6315
6316     default:
6317       if (element_info[element].explosion_type == EXPLODES_CROSS)
6318         explosion_type = EX_TYPE_CROSS;
6319       else if (element_info[element].explosion_type == EXPLODES_1X1)
6320         explosion_type = EX_TYPE_CENTER;
6321       break;
6322   }
6323
6324   if (explosion_type == EX_TYPE_DYNA)
6325     DynaExplode(x, y);
6326   else
6327     Explode(x, y, EX_PHASE_START, explosion_type);
6328
6329   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6330 }
6331
6332 void SplashAcid(int x, int y)
6333 {
6334   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6335       (!IN_LEV_FIELD(x - 1, y - 2) ||
6336        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6337     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6338
6339   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6340       (!IN_LEV_FIELD(x + 1, y - 2) ||
6341        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6342     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6343
6344   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6345 }
6346
6347 static void InitBeltMovement()
6348 {
6349   static int belt_base_element[4] =
6350   {
6351     EL_CONVEYOR_BELT_1_LEFT,
6352     EL_CONVEYOR_BELT_2_LEFT,
6353     EL_CONVEYOR_BELT_3_LEFT,
6354     EL_CONVEYOR_BELT_4_LEFT
6355   };
6356   static int belt_base_active_element[4] =
6357   {
6358     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6359     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6360     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6361     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6362   };
6363
6364   int x, y, i, j;
6365
6366   /* set frame order for belt animation graphic according to belt direction */
6367   for (i = 0; i < NUM_BELTS; i++)
6368   {
6369     int belt_nr = i;
6370
6371     for (j = 0; j < NUM_BELT_PARTS; j++)
6372     {
6373       int element = belt_base_active_element[belt_nr] + j;
6374       int graphic_1 = el2img(element);
6375       int graphic_2 = el2panelimg(element);
6376
6377       if (game.belt_dir[i] == MV_LEFT)
6378       {
6379         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6380         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6381       }
6382       else
6383       {
6384         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6385         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6386       }
6387     }
6388   }
6389
6390   SCAN_PLAYFIELD(x, y)
6391   {
6392     int element = Feld[x][y];
6393
6394     for (i = 0; i < NUM_BELTS; i++)
6395     {
6396       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6397       {
6398         int e_belt_nr = getBeltNrFromBeltElement(element);
6399         int belt_nr = i;
6400
6401         if (e_belt_nr == belt_nr)
6402         {
6403           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6404
6405           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6406         }
6407       }
6408     }
6409   }
6410 }
6411
6412 static void ToggleBeltSwitch(int x, int y)
6413 {
6414   static int belt_base_element[4] =
6415   {
6416     EL_CONVEYOR_BELT_1_LEFT,
6417     EL_CONVEYOR_BELT_2_LEFT,
6418     EL_CONVEYOR_BELT_3_LEFT,
6419     EL_CONVEYOR_BELT_4_LEFT
6420   };
6421   static int belt_base_active_element[4] =
6422   {
6423     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6424     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6425     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6426     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6427   };
6428   static int belt_base_switch_element[4] =
6429   {
6430     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6431     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6432     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6433     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6434   };
6435   static int belt_move_dir[4] =
6436   {
6437     MV_LEFT,
6438     MV_NONE,
6439     MV_RIGHT,
6440     MV_NONE,
6441   };
6442
6443   int element = Feld[x][y];
6444   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6445   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6446   int belt_dir = belt_move_dir[belt_dir_nr];
6447   int xx, yy, i;
6448
6449   if (!IS_BELT_SWITCH(element))
6450     return;
6451
6452   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6453   game.belt_dir[belt_nr] = belt_dir;
6454
6455   if (belt_dir_nr == 3)
6456     belt_dir_nr = 1;
6457
6458   /* set frame order for belt animation graphic according to belt direction */
6459   for (i = 0; i < NUM_BELT_PARTS; i++)
6460   {
6461     int element = belt_base_active_element[belt_nr] + i;
6462     int graphic_1 = el2img(element);
6463     int graphic_2 = el2panelimg(element);
6464
6465     if (belt_dir == MV_LEFT)
6466     {
6467       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6468       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6469     }
6470     else
6471     {
6472       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6473       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6474     }
6475   }
6476
6477   SCAN_PLAYFIELD(xx, yy)
6478   {
6479     int element = Feld[xx][yy];
6480
6481     if (IS_BELT_SWITCH(element))
6482     {
6483       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6484
6485       if (e_belt_nr == belt_nr)
6486       {
6487         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6488         TEST_DrawLevelField(xx, yy);
6489       }
6490     }
6491     else if (IS_BELT(element) && belt_dir != MV_NONE)
6492     {
6493       int e_belt_nr = getBeltNrFromBeltElement(element);
6494
6495       if (e_belt_nr == belt_nr)
6496       {
6497         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6498
6499         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6500         TEST_DrawLevelField(xx, yy);
6501       }
6502     }
6503     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6504     {
6505       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6506
6507       if (e_belt_nr == belt_nr)
6508       {
6509         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6510
6511         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6512         TEST_DrawLevelField(xx, yy);
6513       }
6514     }
6515   }
6516 }
6517
6518 static void ToggleSwitchgateSwitch(int x, int y)
6519 {
6520   int xx, yy;
6521
6522   game.switchgate_pos = !game.switchgate_pos;
6523
6524   SCAN_PLAYFIELD(xx, yy)
6525   {
6526     int element = Feld[xx][yy];
6527
6528 #if !USE_BOTH_SWITCHGATE_SWITCHES
6529     if (element == EL_SWITCHGATE_SWITCH_UP ||
6530         element == EL_SWITCHGATE_SWITCH_DOWN)
6531     {
6532       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6533       TEST_DrawLevelField(xx, yy);
6534     }
6535     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6536              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6537     {
6538       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6539       TEST_DrawLevelField(xx, yy);
6540     }
6541 #else
6542     if (element == EL_SWITCHGATE_SWITCH_UP)
6543     {
6544       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6545       TEST_DrawLevelField(xx, yy);
6546     }
6547     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6548     {
6549       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6550       TEST_DrawLevelField(xx, yy);
6551     }
6552     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6553     {
6554       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6555       TEST_DrawLevelField(xx, yy);
6556     }
6557     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6558     {
6559       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6560       TEST_DrawLevelField(xx, yy);
6561     }
6562 #endif
6563     else if (element == EL_SWITCHGATE_OPEN ||
6564              element == EL_SWITCHGATE_OPENING)
6565     {
6566       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6567
6568       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6569     }
6570     else if (element == EL_SWITCHGATE_CLOSED ||
6571              element == EL_SWITCHGATE_CLOSING)
6572     {
6573       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6574
6575       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6576     }
6577   }
6578 }
6579
6580 static int getInvisibleActiveFromInvisibleElement(int element)
6581 {
6582   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6583           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6584           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6585           element);
6586 }
6587
6588 static int getInvisibleFromInvisibleActiveElement(int element)
6589 {
6590   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6591           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6592           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6593           element);
6594 }
6595
6596 static void RedrawAllLightSwitchesAndInvisibleElements()
6597 {
6598   int x, y;
6599
6600   SCAN_PLAYFIELD(x, y)
6601   {
6602     int element = Feld[x][y];
6603
6604     if (element == EL_LIGHT_SWITCH &&
6605         game.light_time_left > 0)
6606     {
6607       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6608       TEST_DrawLevelField(x, y);
6609     }
6610     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6611              game.light_time_left == 0)
6612     {
6613       Feld[x][y] = EL_LIGHT_SWITCH;
6614       TEST_DrawLevelField(x, y);
6615     }
6616     else if (element == EL_EMC_DRIPPER &&
6617              game.light_time_left > 0)
6618     {
6619       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6620       TEST_DrawLevelField(x, y);
6621     }
6622     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6623              game.light_time_left == 0)
6624     {
6625       Feld[x][y] = EL_EMC_DRIPPER;
6626       TEST_DrawLevelField(x, y);
6627     }
6628     else if (element == EL_INVISIBLE_STEELWALL ||
6629              element == EL_INVISIBLE_WALL ||
6630              element == EL_INVISIBLE_SAND)
6631     {
6632       if (game.light_time_left > 0)
6633         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6634
6635       TEST_DrawLevelField(x, y);
6636
6637       /* uncrumble neighbour fields, if needed */
6638       if (element == EL_INVISIBLE_SAND)
6639         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6640     }
6641     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6642              element == EL_INVISIBLE_WALL_ACTIVE ||
6643              element == EL_INVISIBLE_SAND_ACTIVE)
6644     {
6645       if (game.light_time_left == 0)
6646         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6647
6648       TEST_DrawLevelField(x, y);
6649
6650       /* re-crumble neighbour fields, if needed */
6651       if (element == EL_INVISIBLE_SAND)
6652         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6653     }
6654   }
6655 }
6656
6657 static void RedrawAllInvisibleElementsForLenses()
6658 {
6659   int x, y;
6660
6661   SCAN_PLAYFIELD(x, y)
6662   {
6663     int element = Feld[x][y];
6664
6665     if (element == EL_EMC_DRIPPER &&
6666         game.lenses_time_left > 0)
6667     {
6668       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6669       TEST_DrawLevelField(x, y);
6670     }
6671     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6672              game.lenses_time_left == 0)
6673     {
6674       Feld[x][y] = EL_EMC_DRIPPER;
6675       TEST_DrawLevelField(x, y);
6676     }
6677     else if (element == EL_INVISIBLE_STEELWALL ||
6678              element == EL_INVISIBLE_WALL ||
6679              element == EL_INVISIBLE_SAND)
6680     {
6681       if (game.lenses_time_left > 0)
6682         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6683
6684       TEST_DrawLevelField(x, y);
6685
6686       /* uncrumble neighbour fields, if needed */
6687       if (element == EL_INVISIBLE_SAND)
6688         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6689     }
6690     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6691              element == EL_INVISIBLE_WALL_ACTIVE ||
6692              element == EL_INVISIBLE_SAND_ACTIVE)
6693     {
6694       if (game.lenses_time_left == 0)
6695         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6696
6697       TEST_DrawLevelField(x, y);
6698
6699       /* re-crumble neighbour fields, if needed */
6700       if (element == EL_INVISIBLE_SAND)
6701         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6702     }
6703   }
6704 }
6705
6706 static void RedrawAllInvisibleElementsForMagnifier()
6707 {
6708   int x, y;
6709
6710   SCAN_PLAYFIELD(x, y)
6711   {
6712     int element = Feld[x][y];
6713
6714     if (element == EL_EMC_FAKE_GRASS &&
6715         game.magnify_time_left > 0)
6716     {
6717       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6718       TEST_DrawLevelField(x, y);
6719     }
6720     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6721              game.magnify_time_left == 0)
6722     {
6723       Feld[x][y] = EL_EMC_FAKE_GRASS;
6724       TEST_DrawLevelField(x, y);
6725     }
6726     else if (IS_GATE_GRAY(element) &&
6727              game.magnify_time_left > 0)
6728     {
6729       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6730                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6731                     IS_EM_GATE_GRAY(element) ?
6732                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6733                     IS_EMC_GATE_GRAY(element) ?
6734                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6735                     IS_DC_GATE_GRAY(element) ?
6736                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6737                     element);
6738       TEST_DrawLevelField(x, y);
6739     }
6740     else if (IS_GATE_GRAY_ACTIVE(element) &&
6741              game.magnify_time_left == 0)
6742     {
6743       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6744                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6745                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6746                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6747                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6748                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6749                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6750                     EL_DC_GATE_WHITE_GRAY :
6751                     element);
6752       TEST_DrawLevelField(x, y);
6753     }
6754   }
6755 }
6756
6757 static void ToggleLightSwitch(int x, int y)
6758 {
6759   int element = Feld[x][y];
6760
6761   game.light_time_left =
6762     (element == EL_LIGHT_SWITCH ?
6763      level.time_light * FRAMES_PER_SECOND : 0);
6764
6765   RedrawAllLightSwitchesAndInvisibleElements();
6766 }
6767
6768 static void ActivateTimegateSwitch(int x, int y)
6769 {
6770   int xx, yy;
6771
6772   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6773
6774   SCAN_PLAYFIELD(xx, yy)
6775   {
6776     int element = Feld[xx][yy];
6777
6778     if (element == EL_TIMEGATE_CLOSED ||
6779         element == EL_TIMEGATE_CLOSING)
6780     {
6781       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6782       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6783     }
6784
6785     /*
6786     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6787     {
6788       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6789       TEST_DrawLevelField(xx, yy);
6790     }
6791     */
6792
6793   }
6794
6795 #if 1
6796   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6797                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6798 #else
6799   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6800 #endif
6801 }
6802
6803 void Impact(int x, int y)
6804 {
6805   boolean last_line = (y == lev_fieldy - 1);
6806   boolean object_hit = FALSE;
6807   boolean impact = (last_line || object_hit);
6808   int element = Feld[x][y];
6809   int smashed = EL_STEELWALL;
6810
6811   if (!last_line)       /* check if element below was hit */
6812   {
6813     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6814       return;
6815
6816     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6817                                          MovDir[x][y + 1] != MV_DOWN ||
6818                                          MovPos[x][y + 1] <= TILEY / 2));
6819
6820     /* do not smash moving elements that left the smashed field in time */
6821     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6822         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6823       object_hit = FALSE;
6824
6825 #if USE_QUICKSAND_IMPACT_BUGFIX
6826     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6827     {
6828       RemoveMovingField(x, y + 1);
6829       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6830       Feld[x][y + 2] = EL_ROCK;
6831       TEST_DrawLevelField(x, y + 2);
6832
6833       object_hit = TRUE;
6834     }
6835
6836     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6837     {
6838       RemoveMovingField(x, y + 1);
6839       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6840       Feld[x][y + 2] = EL_ROCK;
6841       TEST_DrawLevelField(x, y + 2);
6842
6843       object_hit = TRUE;
6844     }
6845 #endif
6846
6847     if (object_hit)
6848       smashed = MovingOrBlocked2Element(x, y + 1);
6849
6850     impact = (last_line || object_hit);
6851   }
6852
6853   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6854   {
6855     SplashAcid(x, y + 1);
6856     return;
6857   }
6858
6859   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6860   /* only reset graphic animation if graphic really changes after impact */
6861   if (impact &&
6862       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6863   {
6864     ResetGfxAnimation(x, y);
6865     TEST_DrawLevelField(x, y);
6866   }
6867
6868   if (impact && CAN_EXPLODE_IMPACT(element))
6869   {
6870     Bang(x, y);
6871     return;
6872   }
6873   else if (impact && element == EL_PEARL &&
6874            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6875   {
6876     ResetGfxAnimation(x, y);
6877
6878     Feld[x][y] = EL_PEARL_BREAKING;
6879     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6880     return;
6881   }
6882   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6883   {
6884     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6885
6886     return;
6887   }
6888
6889   if (impact && element == EL_AMOEBA_DROP)
6890   {
6891     if (object_hit && IS_PLAYER(x, y + 1))
6892       KillPlayerUnlessEnemyProtected(x, y + 1);
6893     else if (object_hit && smashed == EL_PENGUIN)
6894       Bang(x, y + 1);
6895     else
6896     {
6897       Feld[x][y] = EL_AMOEBA_GROWING;
6898       Store[x][y] = EL_AMOEBA_WET;
6899
6900       ResetRandomAnimationValue(x, y);
6901     }
6902     return;
6903   }
6904
6905   if (object_hit)               /* check which object was hit */
6906   {
6907     if ((CAN_PASS_MAGIC_WALL(element) && 
6908          (smashed == EL_MAGIC_WALL ||
6909           smashed == EL_BD_MAGIC_WALL)) ||
6910         (CAN_PASS_DC_MAGIC_WALL(element) &&
6911          smashed == EL_DC_MAGIC_WALL))
6912     {
6913       int xx, yy;
6914       int activated_magic_wall =
6915         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6916          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6917          EL_DC_MAGIC_WALL_ACTIVE);
6918
6919       /* activate magic wall / mill */
6920       SCAN_PLAYFIELD(xx, yy)
6921       {
6922         if (Feld[xx][yy] == smashed)
6923           Feld[xx][yy] = activated_magic_wall;
6924       }
6925
6926       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6927       game.magic_wall_active = TRUE;
6928
6929       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6930                             SND_MAGIC_WALL_ACTIVATING :
6931                             smashed == EL_BD_MAGIC_WALL ?
6932                             SND_BD_MAGIC_WALL_ACTIVATING :
6933                             SND_DC_MAGIC_WALL_ACTIVATING));
6934     }
6935
6936     if (IS_PLAYER(x, y + 1))
6937     {
6938       if (CAN_SMASH_PLAYER(element))
6939       {
6940         KillPlayerUnlessEnemyProtected(x, y + 1);
6941         return;
6942       }
6943     }
6944     else if (smashed == EL_PENGUIN)
6945     {
6946       if (CAN_SMASH_PLAYER(element))
6947       {
6948         Bang(x, y + 1);
6949         return;
6950       }
6951     }
6952     else if (element == EL_BD_DIAMOND)
6953     {
6954       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6955       {
6956         Bang(x, y + 1);
6957         return;
6958       }
6959     }
6960     else if (((element == EL_SP_INFOTRON ||
6961                element == EL_SP_ZONK) &&
6962               (smashed == EL_SP_SNIKSNAK ||
6963                smashed == EL_SP_ELECTRON ||
6964                smashed == EL_SP_DISK_ORANGE)) ||
6965              (element == EL_SP_INFOTRON &&
6966               smashed == EL_SP_DISK_YELLOW))
6967     {
6968       Bang(x, y + 1);
6969       return;
6970     }
6971     else if (CAN_SMASH_EVERYTHING(element))
6972     {
6973       if (IS_CLASSIC_ENEMY(smashed) ||
6974           CAN_EXPLODE_SMASHED(smashed))
6975       {
6976         Bang(x, y + 1);
6977         return;
6978       }
6979       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6980       {
6981         if (smashed == EL_LAMP ||
6982             smashed == EL_LAMP_ACTIVE)
6983         {
6984           Bang(x, y + 1);
6985           return;
6986         }
6987         else if (smashed == EL_NUT)
6988         {
6989           Feld[x][y + 1] = EL_NUT_BREAKING;
6990           PlayLevelSound(x, y, SND_NUT_BREAKING);
6991           RaiseScoreElement(EL_NUT);
6992           return;
6993         }
6994         else if (smashed == EL_PEARL)
6995         {
6996           ResetGfxAnimation(x, y);
6997
6998           Feld[x][y + 1] = EL_PEARL_BREAKING;
6999           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7000           return;
7001         }
7002         else if (smashed == EL_DIAMOND)
7003         {
7004           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7005           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7006           return;
7007         }
7008         else if (IS_BELT_SWITCH(smashed))
7009         {
7010           ToggleBeltSwitch(x, y + 1);
7011         }
7012         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7013                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7014                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7015                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7016         {
7017           ToggleSwitchgateSwitch(x, y + 1);
7018         }
7019         else if (smashed == EL_LIGHT_SWITCH ||
7020                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7021         {
7022           ToggleLightSwitch(x, y + 1);
7023         }
7024         else
7025         {
7026 #if 0
7027           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7028 #endif
7029
7030           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7031
7032           CheckElementChangeBySide(x, y + 1, smashed, element,
7033                                    CE_SWITCHED, CH_SIDE_TOP);
7034           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7035                                             CH_SIDE_TOP);
7036         }
7037       }
7038       else
7039       {
7040         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7041       }
7042     }
7043   }
7044
7045   /* play sound of magic wall / mill */
7046   if (!last_line &&
7047       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7048        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7049        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7050   {
7051     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7052       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7053     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7054       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7055     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7056       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7057
7058     return;
7059   }
7060
7061   /* play sound of object that hits the ground */
7062   if (last_line || object_hit)
7063     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7064 }
7065
7066 inline static void TurnRoundExt(int x, int y)
7067 {
7068   static struct
7069   {
7070     int dx, dy;
7071   } move_xy[] =
7072   {
7073     {  0,  0 },
7074     { -1,  0 },
7075     { +1,  0 },
7076     {  0,  0 },
7077     {  0, -1 },
7078     {  0,  0 }, { 0, 0 }, { 0, 0 },
7079     {  0, +1 }
7080   };
7081   static struct
7082   {
7083     int left, right, back;
7084   } turn[] =
7085   {
7086     { 0,        0,              0        },
7087     { MV_DOWN,  MV_UP,          MV_RIGHT },
7088     { MV_UP,    MV_DOWN,        MV_LEFT  },
7089     { 0,        0,              0        },
7090     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7091     { 0,        0,              0        },
7092     { 0,        0,              0        },
7093     { 0,        0,              0        },
7094     { MV_RIGHT, MV_LEFT,        MV_UP    }
7095   };
7096
7097   int element = Feld[x][y];
7098   int move_pattern = element_info[element].move_pattern;
7099
7100   int old_move_dir = MovDir[x][y];
7101   int left_dir  = turn[old_move_dir].left;
7102   int right_dir = turn[old_move_dir].right;
7103   int back_dir  = turn[old_move_dir].back;
7104
7105   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7106   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7107   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7108   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7109
7110   int left_x  = x + left_dx,  left_y  = y + left_dy;
7111   int right_x = x + right_dx, right_y = y + right_dy;
7112   int move_x  = x + move_dx,  move_y  = y + move_dy;
7113
7114   int xx, yy;
7115
7116   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7117   {
7118     TestIfBadThingTouchesOtherBadThing(x, y);
7119
7120     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7121       MovDir[x][y] = right_dir;
7122     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7123       MovDir[x][y] = left_dir;
7124
7125     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7126       MovDelay[x][y] = 9;
7127     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7128       MovDelay[x][y] = 1;
7129   }
7130   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7131   {
7132     TestIfBadThingTouchesOtherBadThing(x, y);
7133
7134     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7135       MovDir[x][y] = left_dir;
7136     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7137       MovDir[x][y] = right_dir;
7138
7139     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7140       MovDelay[x][y] = 9;
7141     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7142       MovDelay[x][y] = 1;
7143   }
7144   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7145   {
7146     TestIfBadThingTouchesOtherBadThing(x, y);
7147
7148     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7149       MovDir[x][y] = left_dir;
7150     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7151       MovDir[x][y] = right_dir;
7152
7153     if (MovDir[x][y] != old_move_dir)
7154       MovDelay[x][y] = 9;
7155   }
7156   else if (element == EL_YAMYAM)
7157   {
7158     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7159     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7160
7161     if (can_turn_left && can_turn_right)
7162       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7163     else if (can_turn_left)
7164       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7165     else if (can_turn_right)
7166       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7167     else
7168       MovDir[x][y] = back_dir;
7169
7170     MovDelay[x][y] = 16 + 16 * RND(3);
7171   }
7172   else if (element == EL_DARK_YAMYAM)
7173   {
7174     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7175                                                          left_x, left_y);
7176     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7177                                                          right_x, right_y);
7178
7179     if (can_turn_left && can_turn_right)
7180       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7181     else if (can_turn_left)
7182       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7183     else if (can_turn_right)
7184       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7185     else
7186       MovDir[x][y] = back_dir;
7187
7188     MovDelay[x][y] = 16 + 16 * RND(3);
7189   }
7190   else if (element == EL_PACMAN)
7191   {
7192     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7193     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7194
7195     if (can_turn_left && can_turn_right)
7196       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7197     else if (can_turn_left)
7198       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7199     else if (can_turn_right)
7200       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7201     else
7202       MovDir[x][y] = back_dir;
7203
7204     MovDelay[x][y] = 6 + RND(40);
7205   }
7206   else if (element == EL_PIG)
7207   {
7208     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7209     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7210     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7211     boolean should_turn_left, should_turn_right, should_move_on;
7212     int rnd_value = 24;
7213     int rnd = RND(rnd_value);
7214
7215     should_turn_left = (can_turn_left &&
7216                         (!can_move_on ||
7217                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7218                                                    y + back_dy + left_dy)));
7219     should_turn_right = (can_turn_right &&
7220                          (!can_move_on ||
7221                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7222                                                     y + back_dy + right_dy)));
7223     should_move_on = (can_move_on &&
7224                       (!can_turn_left ||
7225                        !can_turn_right ||
7226                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7227                                                  y + move_dy + left_dy) ||
7228                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7229                                                  y + move_dy + right_dy)));
7230
7231     if (should_turn_left || should_turn_right || should_move_on)
7232     {
7233       if (should_turn_left && should_turn_right && should_move_on)
7234         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7235                         rnd < 2 * rnd_value / 3 ? right_dir :
7236                         old_move_dir);
7237       else if (should_turn_left && should_turn_right)
7238         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7239       else if (should_turn_left && should_move_on)
7240         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7241       else if (should_turn_right && should_move_on)
7242         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7243       else if (should_turn_left)
7244         MovDir[x][y] = left_dir;
7245       else if (should_turn_right)
7246         MovDir[x][y] = right_dir;
7247       else if (should_move_on)
7248         MovDir[x][y] = old_move_dir;
7249     }
7250     else if (can_move_on && rnd > rnd_value / 8)
7251       MovDir[x][y] = old_move_dir;
7252     else if (can_turn_left && can_turn_right)
7253       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7254     else if (can_turn_left && rnd > rnd_value / 8)
7255       MovDir[x][y] = left_dir;
7256     else if (can_turn_right && rnd > rnd_value/8)
7257       MovDir[x][y] = right_dir;
7258     else
7259       MovDir[x][y] = back_dir;
7260
7261     xx = x + move_xy[MovDir[x][y]].dx;
7262     yy = y + move_xy[MovDir[x][y]].dy;
7263
7264     if (!IN_LEV_FIELD(xx, yy) ||
7265         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7266       MovDir[x][y] = old_move_dir;
7267
7268     MovDelay[x][y] = 0;
7269   }
7270   else if (element == EL_DRAGON)
7271   {
7272     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7273     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7274     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7275     int rnd_value = 24;
7276     int rnd = RND(rnd_value);
7277
7278     if (can_move_on && rnd > rnd_value / 8)
7279       MovDir[x][y] = old_move_dir;
7280     else if (can_turn_left && can_turn_right)
7281       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7282     else if (can_turn_left && rnd > rnd_value / 8)
7283       MovDir[x][y] = left_dir;
7284     else if (can_turn_right && rnd > rnd_value / 8)
7285       MovDir[x][y] = right_dir;
7286     else
7287       MovDir[x][y] = back_dir;
7288
7289     xx = x + move_xy[MovDir[x][y]].dx;
7290     yy = y + move_xy[MovDir[x][y]].dy;
7291
7292     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7293       MovDir[x][y] = old_move_dir;
7294
7295     MovDelay[x][y] = 0;
7296   }
7297   else if (element == EL_MOLE)
7298   {
7299     boolean can_move_on =
7300       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7301                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7302                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7303     if (!can_move_on)
7304     {
7305       boolean can_turn_left =
7306         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7307                               IS_AMOEBOID(Feld[left_x][left_y])));
7308
7309       boolean can_turn_right =
7310         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7311                               IS_AMOEBOID(Feld[right_x][right_y])));
7312
7313       if (can_turn_left && can_turn_right)
7314         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7315       else if (can_turn_left)
7316         MovDir[x][y] = left_dir;
7317       else
7318         MovDir[x][y] = right_dir;
7319     }
7320
7321     if (MovDir[x][y] != old_move_dir)
7322       MovDelay[x][y] = 9;
7323   }
7324   else if (element == EL_BALLOON)
7325   {
7326     MovDir[x][y] = game.wind_direction;
7327     MovDelay[x][y] = 0;
7328   }
7329   else if (element == EL_SPRING)
7330   {
7331 #if USE_NEW_SPRING_BUMPER
7332     if (MovDir[x][y] & MV_HORIZONTAL)
7333     {
7334       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7335           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7336       {
7337         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7338         ResetGfxAnimation(move_x, move_y);
7339         TEST_DrawLevelField(move_x, move_y);
7340
7341         MovDir[x][y] = back_dir;
7342       }
7343       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7344                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7345         MovDir[x][y] = MV_NONE;
7346     }
7347 #else
7348     if (MovDir[x][y] & MV_HORIZONTAL &&
7349         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7350          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7351       MovDir[x][y] = MV_NONE;
7352 #endif
7353
7354     MovDelay[x][y] = 0;
7355   }
7356   else if (element == EL_ROBOT ||
7357            element == EL_SATELLITE ||
7358            element == EL_PENGUIN ||
7359            element == EL_EMC_ANDROID)
7360   {
7361     int attr_x = -1, attr_y = -1;
7362
7363     if (AllPlayersGone)
7364     {
7365       attr_x = ExitX;
7366       attr_y = ExitY;
7367     }
7368     else
7369     {
7370       int i;
7371
7372       for (i = 0; i < MAX_PLAYERS; i++)
7373       {
7374         struct PlayerInfo *player = &stored_player[i];
7375         int jx = player->jx, jy = player->jy;
7376
7377         if (!player->active)
7378           continue;
7379
7380         if (attr_x == -1 ||
7381             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7382         {
7383           attr_x = jx;
7384           attr_y = jy;
7385         }
7386       }
7387     }
7388
7389     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7390         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7391          game.engine_version < VERSION_IDENT(3,1,0,0)))
7392     {
7393       attr_x = ZX;
7394       attr_y = ZY;
7395     }
7396
7397     if (element == EL_PENGUIN)
7398     {
7399       int i;
7400       static int xy[4][2] =
7401       {
7402         { 0, -1 },
7403         { -1, 0 },
7404         { +1, 0 },
7405         { 0, +1 }
7406       };
7407
7408       for (i = 0; i < NUM_DIRECTIONS; i++)
7409       {
7410         int ex = x + xy[i][0];
7411         int ey = y + xy[i][1];
7412
7413         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7414                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7415                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7416                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7417         {
7418           attr_x = ex;
7419           attr_y = ey;
7420           break;
7421         }
7422       }
7423     }
7424
7425     MovDir[x][y] = MV_NONE;
7426     if (attr_x < x)
7427       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7428     else if (attr_x > x)
7429       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7430     if (attr_y < y)
7431       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7432     else if (attr_y > y)
7433       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7434
7435     if (element == EL_ROBOT)
7436     {
7437       int newx, newy;
7438
7439       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7440         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7441       Moving2Blocked(x, y, &newx, &newy);
7442
7443       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7444         MovDelay[x][y] = 8 + 8 * !RND(3);
7445       else
7446         MovDelay[x][y] = 16;
7447     }
7448     else if (element == EL_PENGUIN)
7449     {
7450       int newx, newy;
7451
7452       MovDelay[x][y] = 1;
7453
7454       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7455       {
7456         boolean first_horiz = RND(2);
7457         int new_move_dir = MovDir[x][y];
7458
7459         MovDir[x][y] =
7460           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7461         Moving2Blocked(x, y, &newx, &newy);
7462
7463         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7464           return;
7465
7466         MovDir[x][y] =
7467           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7468         Moving2Blocked(x, y, &newx, &newy);
7469
7470         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7471           return;
7472
7473         MovDir[x][y] = old_move_dir;
7474         return;
7475       }
7476     }
7477     else if (element == EL_SATELLITE)
7478     {
7479       int newx, newy;
7480
7481       MovDelay[x][y] = 1;
7482
7483       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7484       {
7485         boolean first_horiz = RND(2);
7486         int new_move_dir = MovDir[x][y];
7487
7488         MovDir[x][y] =
7489           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7490         Moving2Blocked(x, y, &newx, &newy);
7491
7492         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7493           return;
7494
7495         MovDir[x][y] =
7496           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7497         Moving2Blocked(x, y, &newx, &newy);
7498
7499         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7500           return;
7501
7502         MovDir[x][y] = old_move_dir;
7503         return;
7504       }
7505     }
7506     else if (element == EL_EMC_ANDROID)
7507     {
7508       static int check_pos[16] =
7509       {
7510         -1,             /*  0 => (invalid)          */
7511         7,              /*  1 => MV_LEFT            */
7512         3,              /*  2 => MV_RIGHT           */
7513         -1,             /*  3 => (invalid)          */
7514         1,              /*  4 =>            MV_UP   */
7515         0,              /*  5 => MV_LEFT  | MV_UP   */
7516         2,              /*  6 => MV_RIGHT | MV_UP   */
7517         -1,             /*  7 => (invalid)          */
7518         5,              /*  8 =>            MV_DOWN */
7519         6,              /*  9 => MV_LEFT  | MV_DOWN */
7520         4,              /* 10 => MV_RIGHT | MV_DOWN */
7521         -1,             /* 11 => (invalid)          */
7522         -1,             /* 12 => (invalid)          */
7523         -1,             /* 13 => (invalid)          */
7524         -1,             /* 14 => (invalid)          */
7525         -1,             /* 15 => (invalid)          */
7526       };
7527       static struct
7528       {
7529         int dx, dy;
7530         int dir;
7531       } check_xy[8] =
7532       {
7533         { -1, -1,       MV_LEFT  | MV_UP   },
7534         {  0, -1,                  MV_UP   },
7535         { +1, -1,       MV_RIGHT | MV_UP   },
7536         { +1,  0,       MV_RIGHT           },
7537         { +1, +1,       MV_RIGHT | MV_DOWN },
7538         {  0, +1,                  MV_DOWN },
7539         { -1, +1,       MV_LEFT  | MV_DOWN },
7540         { -1,  0,       MV_LEFT            },
7541       };
7542       int start_pos, check_order;
7543       boolean can_clone = FALSE;
7544       int i;
7545
7546       /* check if there is any free field around current position */
7547       for (i = 0; i < 8; i++)
7548       {
7549         int newx = x + check_xy[i].dx;
7550         int newy = y + check_xy[i].dy;
7551
7552         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7553         {
7554           can_clone = TRUE;
7555
7556           break;
7557         }
7558       }
7559
7560       if (can_clone)            /* randomly find an element to clone */
7561       {
7562         can_clone = FALSE;
7563
7564         start_pos = check_pos[RND(8)];
7565         check_order = (RND(2) ? -1 : +1);
7566
7567         for (i = 0; i < 8; i++)
7568         {
7569           int pos_raw = start_pos + i * check_order;
7570           int pos = (pos_raw + 8) % 8;
7571           int newx = x + check_xy[pos].dx;
7572           int newy = y + check_xy[pos].dy;
7573
7574           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7575           {
7576             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7577             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7578
7579             Store[x][y] = Feld[newx][newy];
7580
7581             can_clone = TRUE;
7582
7583             break;
7584           }
7585         }
7586       }
7587
7588       if (can_clone)            /* randomly find a direction to move */
7589       {
7590         can_clone = FALSE;
7591
7592         start_pos = check_pos[RND(8)];
7593         check_order = (RND(2) ? -1 : +1);
7594
7595         for (i = 0; i < 8; i++)
7596         {
7597           int pos_raw = start_pos + i * check_order;
7598           int pos = (pos_raw + 8) % 8;
7599           int newx = x + check_xy[pos].dx;
7600           int newy = y + check_xy[pos].dy;
7601           int new_move_dir = check_xy[pos].dir;
7602
7603           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7604           {
7605             MovDir[x][y] = new_move_dir;
7606             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7607
7608             can_clone = TRUE;
7609
7610             break;
7611           }
7612         }
7613       }
7614
7615       if (can_clone)            /* cloning and moving successful */
7616         return;
7617
7618       /* cannot clone -- try to move towards player */
7619
7620       start_pos = check_pos[MovDir[x][y] & 0x0f];
7621       check_order = (RND(2) ? -1 : +1);
7622
7623       for (i = 0; i < 3; i++)
7624       {
7625         /* first check start_pos, then previous/next or (next/previous) pos */
7626         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7627         int pos = (pos_raw + 8) % 8;
7628         int newx = x + check_xy[pos].dx;
7629         int newy = y + check_xy[pos].dy;
7630         int new_move_dir = check_xy[pos].dir;
7631
7632         if (IS_PLAYER(newx, newy))
7633           break;
7634
7635         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7636         {
7637           MovDir[x][y] = new_move_dir;
7638           MovDelay[x][y] = level.android_move_time * 8 + 1;
7639
7640           break;
7641         }
7642       }
7643     }
7644   }
7645   else if (move_pattern == MV_TURNING_LEFT ||
7646            move_pattern == MV_TURNING_RIGHT ||
7647            move_pattern == MV_TURNING_LEFT_RIGHT ||
7648            move_pattern == MV_TURNING_RIGHT_LEFT ||
7649            move_pattern == MV_TURNING_RANDOM ||
7650            move_pattern == MV_ALL_DIRECTIONS)
7651   {
7652     boolean can_turn_left =
7653       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7654     boolean can_turn_right =
7655       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7656
7657     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7658       return;
7659
7660     if (move_pattern == MV_TURNING_LEFT)
7661       MovDir[x][y] = left_dir;
7662     else if (move_pattern == MV_TURNING_RIGHT)
7663       MovDir[x][y] = right_dir;
7664     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7665       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7666     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7667       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7668     else if (move_pattern == MV_TURNING_RANDOM)
7669       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7670                       can_turn_right && !can_turn_left ? right_dir :
7671                       RND(2) ? left_dir : right_dir);
7672     else if (can_turn_left && can_turn_right)
7673       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7674     else if (can_turn_left)
7675       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7676     else if (can_turn_right)
7677       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7678     else
7679       MovDir[x][y] = back_dir;
7680
7681     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7682   }
7683   else if (move_pattern == MV_HORIZONTAL ||
7684            move_pattern == MV_VERTICAL)
7685   {
7686     if (move_pattern & old_move_dir)
7687       MovDir[x][y] = back_dir;
7688     else if (move_pattern == MV_HORIZONTAL)
7689       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7690     else if (move_pattern == MV_VERTICAL)
7691       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7692
7693     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7694   }
7695   else if (move_pattern & MV_ANY_DIRECTION)
7696   {
7697     MovDir[x][y] = move_pattern;
7698     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7699   }
7700   else if (move_pattern & MV_WIND_DIRECTION)
7701   {
7702     MovDir[x][y] = game.wind_direction;
7703     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7704   }
7705   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7706   {
7707     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7708       MovDir[x][y] = left_dir;
7709     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7710       MovDir[x][y] = right_dir;
7711
7712     if (MovDir[x][y] != old_move_dir)
7713       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7714   }
7715   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7716   {
7717     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7718       MovDir[x][y] = right_dir;
7719     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7720       MovDir[x][y] = left_dir;
7721
7722     if (MovDir[x][y] != old_move_dir)
7723       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7724   }
7725   else if (move_pattern == MV_TOWARDS_PLAYER ||
7726            move_pattern == MV_AWAY_FROM_PLAYER)
7727   {
7728     int attr_x = -1, attr_y = -1;
7729     int newx, newy;
7730     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7731
7732     if (AllPlayersGone)
7733     {
7734       attr_x = ExitX;
7735       attr_y = ExitY;
7736     }
7737     else
7738     {
7739       int i;
7740
7741       for (i = 0; i < MAX_PLAYERS; i++)
7742       {
7743         struct PlayerInfo *player = &stored_player[i];
7744         int jx = player->jx, jy = player->jy;
7745
7746         if (!player->active)
7747           continue;
7748
7749         if (attr_x == -1 ||
7750             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7751         {
7752           attr_x = jx;
7753           attr_y = jy;
7754         }
7755       }
7756     }
7757
7758     MovDir[x][y] = MV_NONE;
7759     if (attr_x < x)
7760       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7761     else if (attr_x > x)
7762       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7763     if (attr_y < y)
7764       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7765     else if (attr_y > y)
7766       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7767
7768     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7769
7770     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7771     {
7772       boolean first_horiz = RND(2);
7773       int new_move_dir = MovDir[x][y];
7774
7775       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7776       {
7777         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7778         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7779
7780         return;
7781       }
7782
7783       MovDir[x][y] =
7784         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7785       Moving2Blocked(x, y, &newx, &newy);
7786
7787       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7788         return;
7789
7790       MovDir[x][y] =
7791         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7792       Moving2Blocked(x, y, &newx, &newy);
7793
7794       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7795         return;
7796
7797       MovDir[x][y] = old_move_dir;
7798     }
7799   }
7800   else if (move_pattern == MV_WHEN_PUSHED ||
7801            move_pattern == MV_WHEN_DROPPED)
7802   {
7803     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7804       MovDir[x][y] = MV_NONE;
7805
7806     MovDelay[x][y] = 0;
7807   }
7808   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7809   {
7810     static int test_xy[7][2] =
7811     {
7812       { 0, -1 },
7813       { -1, 0 },
7814       { +1, 0 },
7815       { 0, +1 },
7816       { 0, -1 },
7817       { -1, 0 },
7818       { +1, 0 },
7819     };
7820     static int test_dir[7] =
7821     {
7822       MV_UP,
7823       MV_LEFT,
7824       MV_RIGHT,
7825       MV_DOWN,
7826       MV_UP,
7827       MV_LEFT,
7828       MV_RIGHT,
7829     };
7830     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7831     int move_preference = -1000000;     /* start with very low preference */
7832     int new_move_dir = MV_NONE;
7833     int start_test = RND(4);
7834     int i;
7835
7836     for (i = 0; i < NUM_DIRECTIONS; i++)
7837     {
7838       int move_dir = test_dir[start_test + i];
7839       int move_dir_preference;
7840
7841       xx = x + test_xy[start_test + i][0];
7842       yy = y + test_xy[start_test + i][1];
7843
7844       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7845           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7846       {
7847         new_move_dir = move_dir;
7848
7849         break;
7850       }
7851
7852       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7853         continue;
7854
7855       move_dir_preference = -1 * RunnerVisit[xx][yy];
7856       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7857         move_dir_preference = PlayerVisit[xx][yy];
7858
7859       if (move_dir_preference > move_preference)
7860       {
7861         /* prefer field that has not been visited for the longest time */
7862         move_preference = move_dir_preference;
7863         new_move_dir = move_dir;
7864       }
7865       else if (move_dir_preference == move_preference &&
7866                move_dir == old_move_dir)
7867       {
7868         /* prefer last direction when all directions are preferred equally */
7869         move_preference = move_dir_preference;
7870         new_move_dir = move_dir;
7871       }
7872     }
7873
7874     MovDir[x][y] = new_move_dir;
7875     if (old_move_dir != new_move_dir)
7876       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7877   }
7878 }
7879
7880 static void TurnRound(int x, int y)
7881 {
7882   int direction = MovDir[x][y];
7883
7884   TurnRoundExt(x, y);
7885
7886   GfxDir[x][y] = MovDir[x][y];
7887
7888   if (direction != MovDir[x][y])
7889     GfxFrame[x][y] = 0;
7890
7891   if (MovDelay[x][y])
7892     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7893
7894   ResetGfxFrame(x, y, FALSE);
7895 }
7896
7897 static boolean JustBeingPushed(int x, int y)
7898 {
7899   int i;
7900
7901   for (i = 0; i < MAX_PLAYERS; i++)
7902   {
7903     struct PlayerInfo *player = &stored_player[i];
7904
7905     if (player->active && player->is_pushing && player->MovPos)
7906     {
7907       int next_jx = player->jx + (player->jx - player->last_jx);
7908       int next_jy = player->jy + (player->jy - player->last_jy);
7909
7910       if (x == next_jx && y == next_jy)
7911         return TRUE;
7912     }
7913   }
7914
7915   return FALSE;
7916 }
7917
7918 void StartMoving(int x, int y)
7919 {
7920   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7921   int element = Feld[x][y];
7922
7923   if (Stop[x][y])
7924     return;
7925
7926   if (MovDelay[x][y] == 0)
7927     GfxAction[x][y] = ACTION_DEFAULT;
7928
7929   if (CAN_FALL(element) && y < lev_fieldy - 1)
7930   {
7931     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7932         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7933       if (JustBeingPushed(x, y))
7934         return;
7935
7936     if (element == EL_QUICKSAND_FULL)
7937     {
7938       if (IS_FREE(x, y + 1))
7939       {
7940         InitMovingField(x, y, MV_DOWN);
7941         started_moving = TRUE;
7942
7943         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7944 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7945         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7946           Store[x][y] = EL_ROCK;
7947 #else
7948         Store[x][y] = EL_ROCK;
7949 #endif
7950
7951         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7952       }
7953       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7954       {
7955         if (!MovDelay[x][y])
7956         {
7957           MovDelay[x][y] = TILEY + 1;
7958
7959           ResetGfxAnimation(x, y);
7960           ResetGfxAnimation(x, y + 1);
7961         }
7962
7963         if (MovDelay[x][y])
7964         {
7965           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7966           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7967
7968           MovDelay[x][y]--;
7969           if (MovDelay[x][y])
7970             return;
7971         }
7972
7973         Feld[x][y] = EL_QUICKSAND_EMPTY;
7974         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7975         Store[x][y + 1] = Store[x][y];
7976         Store[x][y] = 0;
7977
7978         PlayLevelSoundAction(x, y, ACTION_FILLING);
7979       }
7980       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7981       {
7982         if (!MovDelay[x][y])
7983         {
7984           MovDelay[x][y] = TILEY + 1;
7985
7986           ResetGfxAnimation(x, y);
7987           ResetGfxAnimation(x, y + 1);
7988         }
7989
7990         if (MovDelay[x][y])
7991         {
7992           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7993           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7994
7995           MovDelay[x][y]--;
7996           if (MovDelay[x][y])
7997             return;
7998         }
7999
8000         Feld[x][y] = EL_QUICKSAND_EMPTY;
8001         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8002         Store[x][y + 1] = Store[x][y];
8003         Store[x][y] = 0;
8004
8005         PlayLevelSoundAction(x, y, ACTION_FILLING);
8006       }
8007     }
8008     else if (element == EL_QUICKSAND_FAST_FULL)
8009     {
8010       if (IS_FREE(x, y + 1))
8011       {
8012         InitMovingField(x, y, MV_DOWN);
8013         started_moving = TRUE;
8014
8015         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8016 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8017         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8018           Store[x][y] = EL_ROCK;
8019 #else
8020         Store[x][y] = EL_ROCK;
8021 #endif
8022
8023         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8024       }
8025       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8026       {
8027         if (!MovDelay[x][y])
8028         {
8029           MovDelay[x][y] = TILEY + 1;
8030
8031           ResetGfxAnimation(x, y);
8032           ResetGfxAnimation(x, y + 1);
8033         }
8034
8035         if (MovDelay[x][y])
8036         {
8037           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8038           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8039
8040           MovDelay[x][y]--;
8041           if (MovDelay[x][y])
8042             return;
8043         }
8044
8045         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8046         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8047         Store[x][y + 1] = Store[x][y];
8048         Store[x][y] = 0;
8049
8050         PlayLevelSoundAction(x, y, ACTION_FILLING);
8051       }
8052       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8053       {
8054         if (!MovDelay[x][y])
8055         {
8056           MovDelay[x][y] = TILEY + 1;
8057
8058           ResetGfxAnimation(x, y);
8059           ResetGfxAnimation(x, y + 1);
8060         }
8061
8062         if (MovDelay[x][y])
8063         {
8064           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8065           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8066
8067           MovDelay[x][y]--;
8068           if (MovDelay[x][y])
8069             return;
8070         }
8071
8072         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8073         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8074         Store[x][y + 1] = Store[x][y];
8075         Store[x][y] = 0;
8076
8077         PlayLevelSoundAction(x, y, ACTION_FILLING);
8078       }
8079     }
8080     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8081              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8082     {
8083       InitMovingField(x, y, MV_DOWN);
8084       started_moving = TRUE;
8085
8086       Feld[x][y] = EL_QUICKSAND_FILLING;
8087       Store[x][y] = element;
8088
8089       PlayLevelSoundAction(x, y, ACTION_FILLING);
8090     }
8091     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8092              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8093     {
8094       InitMovingField(x, y, MV_DOWN);
8095       started_moving = TRUE;
8096
8097       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8098       Store[x][y] = element;
8099
8100       PlayLevelSoundAction(x, y, ACTION_FILLING);
8101     }
8102     else if (element == EL_MAGIC_WALL_FULL)
8103     {
8104       if (IS_FREE(x, y + 1))
8105       {
8106         InitMovingField(x, y, MV_DOWN);
8107         started_moving = TRUE;
8108
8109         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8110         Store[x][y] = EL_CHANGED(Store[x][y]);
8111       }
8112       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8113       {
8114         if (!MovDelay[x][y])
8115           MovDelay[x][y] = TILEY / 4 + 1;
8116
8117         if (MovDelay[x][y])
8118         {
8119           MovDelay[x][y]--;
8120           if (MovDelay[x][y])
8121             return;
8122         }
8123
8124         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8125         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8126         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8127         Store[x][y] = 0;
8128       }
8129     }
8130     else if (element == EL_BD_MAGIC_WALL_FULL)
8131     {
8132       if (IS_FREE(x, y + 1))
8133       {
8134         InitMovingField(x, y, MV_DOWN);
8135         started_moving = TRUE;
8136
8137         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8138         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8139       }
8140       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8141       {
8142         if (!MovDelay[x][y])
8143           MovDelay[x][y] = TILEY / 4 + 1;
8144
8145         if (MovDelay[x][y])
8146         {
8147           MovDelay[x][y]--;
8148           if (MovDelay[x][y])
8149             return;
8150         }
8151
8152         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8153         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8154         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8155         Store[x][y] = 0;
8156       }
8157     }
8158     else if (element == EL_DC_MAGIC_WALL_FULL)
8159     {
8160       if (IS_FREE(x, y + 1))
8161       {
8162         InitMovingField(x, y, MV_DOWN);
8163         started_moving = TRUE;
8164
8165         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8166         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8167       }
8168       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8169       {
8170         if (!MovDelay[x][y])
8171           MovDelay[x][y] = TILEY / 4 + 1;
8172
8173         if (MovDelay[x][y])
8174         {
8175           MovDelay[x][y]--;
8176           if (MovDelay[x][y])
8177             return;
8178         }
8179
8180         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8181         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8182         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8183         Store[x][y] = 0;
8184       }
8185     }
8186     else if ((CAN_PASS_MAGIC_WALL(element) &&
8187               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8188                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8189              (CAN_PASS_DC_MAGIC_WALL(element) &&
8190               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8191
8192     {
8193       InitMovingField(x, y, MV_DOWN);
8194       started_moving = TRUE;
8195
8196       Feld[x][y] =
8197         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8198          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8199          EL_DC_MAGIC_WALL_FILLING);
8200       Store[x][y] = element;
8201     }
8202     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8203     {
8204       SplashAcid(x, y + 1);
8205
8206       InitMovingField(x, y, MV_DOWN);
8207       started_moving = TRUE;
8208
8209       Store[x][y] = EL_ACID;
8210     }
8211     else if (
8212 #if USE_FIX_IMPACT_COLLISION
8213              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8214               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8215 #else
8216              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8217               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8218 #endif
8219              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8220               CAN_FALL(element) && WasJustFalling[x][y] &&
8221               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8222
8223              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8224               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8225               (Feld[x][y + 1] == EL_BLOCKED)))
8226     {
8227       /* this is needed for a special case not covered by calling "Impact()"
8228          from "ContinueMoving()": if an element moves to a tile directly below
8229          another element which was just falling on that tile (which was empty
8230          in the previous frame), the falling element above would just stop
8231          instead of smashing the element below (in previous version, the above
8232          element was just checked for "moving" instead of "falling", resulting
8233          in incorrect smashes caused by horizontal movement of the above
8234          element; also, the case of the player being the element to smash was
8235          simply not covered here... :-/ ) */
8236
8237       CheckCollision[x][y] = 0;
8238       CheckImpact[x][y] = 0;
8239
8240       Impact(x, y);
8241     }
8242     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8243     {
8244       if (MovDir[x][y] == MV_NONE)
8245       {
8246         InitMovingField(x, y, MV_DOWN);
8247         started_moving = TRUE;
8248       }
8249     }
8250     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8251     {
8252       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8253         MovDir[x][y] = MV_DOWN;
8254
8255       InitMovingField(x, y, MV_DOWN);
8256       started_moving = TRUE;
8257     }
8258     else if (element == EL_AMOEBA_DROP)
8259     {
8260       Feld[x][y] = EL_AMOEBA_GROWING;
8261       Store[x][y] = EL_AMOEBA_WET;
8262     }
8263     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8264               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8265              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8266              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8267     {
8268       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8269                                 (IS_FREE(x - 1, y + 1) ||
8270                                  Feld[x - 1][y + 1] == EL_ACID));
8271       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8272                                 (IS_FREE(x + 1, y + 1) ||
8273                                  Feld[x + 1][y + 1] == EL_ACID));
8274       boolean can_fall_any  = (can_fall_left || can_fall_right);
8275       boolean can_fall_both = (can_fall_left && can_fall_right);
8276       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8277
8278 #if USE_NEW_ALL_SLIPPERY
8279       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8280       {
8281         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8282           can_fall_right = FALSE;
8283         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8284           can_fall_left = FALSE;
8285         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8286           can_fall_right = FALSE;
8287         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8288           can_fall_left = FALSE;
8289
8290         can_fall_any  = (can_fall_left || can_fall_right);
8291         can_fall_both = FALSE;
8292       }
8293 #else
8294       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8295       {
8296         if (slippery_type == SLIPPERY_ONLY_LEFT)
8297           can_fall_right = FALSE;
8298         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8299           can_fall_left = FALSE;
8300         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8301           can_fall_right = FALSE;
8302         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8303           can_fall_left = FALSE;
8304
8305         can_fall_any  = (can_fall_left || can_fall_right);
8306         can_fall_both = (can_fall_left && can_fall_right);
8307       }
8308 #endif
8309
8310 #if USE_NEW_ALL_SLIPPERY
8311 #else
8312 #if USE_NEW_SP_SLIPPERY
8313       /* !!! better use the same properties as for custom elements here !!! */
8314       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8315                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8316       {
8317         can_fall_right = FALSE;         /* slip down on left side */
8318         can_fall_both = FALSE;
8319       }
8320 #endif
8321 #endif
8322
8323 #if USE_NEW_ALL_SLIPPERY
8324       if (can_fall_both)
8325       {
8326         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8327           can_fall_right = FALSE;       /* slip down on left side */
8328         else
8329           can_fall_left = !(can_fall_right = RND(2));
8330
8331         can_fall_both = FALSE;
8332       }
8333 #else
8334       if (can_fall_both)
8335       {
8336         if (game.emulation == EMU_BOULDERDASH ||
8337             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8338           can_fall_right = FALSE;       /* slip down on left side */
8339         else
8340           can_fall_left = !(can_fall_right = RND(2));
8341
8342         can_fall_both = FALSE;
8343       }
8344 #endif
8345
8346       if (can_fall_any)
8347       {
8348         /* if not determined otherwise, prefer left side for slipping down */
8349         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8350         started_moving = TRUE;
8351       }
8352     }
8353 #if 0
8354     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8355 #else
8356     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8357 #endif
8358     {
8359       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8360       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8361       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8362       int belt_dir = game.belt_dir[belt_nr];
8363
8364       if ((belt_dir == MV_LEFT  && left_is_free) ||
8365           (belt_dir == MV_RIGHT && right_is_free))
8366       {
8367         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8368
8369         InitMovingField(x, y, belt_dir);
8370         started_moving = TRUE;
8371
8372         Pushed[x][y] = TRUE;
8373         Pushed[nextx][y] = TRUE;
8374
8375         GfxAction[x][y] = ACTION_DEFAULT;
8376       }
8377       else
8378       {
8379         MovDir[x][y] = 0;       /* if element was moving, stop it */
8380       }
8381     }
8382   }
8383
8384   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8385 #if 0
8386   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8387 #else
8388   if (CAN_MOVE(element) && !started_moving)
8389 #endif
8390   {
8391     int move_pattern = element_info[element].move_pattern;
8392     int newx, newy;
8393
8394 #if 0
8395 #if DEBUG
8396     if (MovDir[x][y] == MV_NONE)
8397     {
8398       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8399              x, y, element, element_info[element].token_name);
8400       printf("StartMoving(): This should never happen!\n");
8401     }
8402 #endif
8403 #endif
8404
8405     Moving2Blocked(x, y, &newx, &newy);
8406
8407     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8408       return;
8409
8410     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8411         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8412     {
8413       WasJustMoving[x][y] = 0;
8414       CheckCollision[x][y] = 0;
8415
8416       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8417
8418       if (Feld[x][y] != element)        /* element has changed */
8419         return;
8420     }
8421
8422     if (!MovDelay[x][y])        /* start new movement phase */
8423     {
8424       /* all objects that can change their move direction after each step
8425          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8426
8427       if (element != EL_YAMYAM &&
8428           element != EL_DARK_YAMYAM &&
8429           element != EL_PACMAN &&
8430           !(move_pattern & MV_ANY_DIRECTION) &&
8431           move_pattern != MV_TURNING_LEFT &&
8432           move_pattern != MV_TURNING_RIGHT &&
8433           move_pattern != MV_TURNING_LEFT_RIGHT &&
8434           move_pattern != MV_TURNING_RIGHT_LEFT &&
8435           move_pattern != MV_TURNING_RANDOM)
8436       {
8437         TurnRound(x, y);
8438
8439         if (MovDelay[x][y] && (element == EL_BUG ||
8440                                element == EL_SPACESHIP ||
8441                                element == EL_SP_SNIKSNAK ||
8442                                element == EL_SP_ELECTRON ||
8443                                element == EL_MOLE))
8444           TEST_DrawLevelField(x, y);
8445       }
8446     }
8447
8448     if (MovDelay[x][y])         /* wait some time before next movement */
8449     {
8450       MovDelay[x][y]--;
8451
8452       if (element == EL_ROBOT ||
8453           element == EL_YAMYAM ||
8454           element == EL_DARK_YAMYAM)
8455       {
8456         DrawLevelElementAnimationIfNeeded(x, y, element);
8457         PlayLevelSoundAction(x, y, ACTION_WAITING);
8458       }
8459       else if (element == EL_SP_ELECTRON)
8460         DrawLevelElementAnimationIfNeeded(x, y, element);
8461       else if (element == EL_DRAGON)
8462       {
8463         int i;
8464         int dir = MovDir[x][y];
8465         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8466         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8467         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8468                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8469                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8470                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8471         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8472
8473         GfxAction[x][y] = ACTION_ATTACKING;
8474
8475         if (IS_PLAYER(x, y))
8476           DrawPlayerField(x, y);
8477         else
8478           TEST_DrawLevelField(x, y);
8479
8480         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8481
8482         for (i = 1; i <= 3; i++)
8483         {
8484           int xx = x + i * dx;
8485           int yy = y + i * dy;
8486           int sx = SCREENX(xx);
8487           int sy = SCREENY(yy);
8488           int flame_graphic = graphic + (i - 1);
8489
8490           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8491             break;
8492
8493           if (MovDelay[x][y])
8494           {
8495             int flamed = MovingOrBlocked2Element(xx, yy);
8496
8497             /* !!! */
8498 #if 0
8499             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8500               Bang(xx, yy);
8501             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8502               RemoveMovingField(xx, yy);
8503             else
8504               RemoveField(xx, yy);
8505 #else
8506             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8507               Bang(xx, yy);
8508             else
8509               RemoveMovingField(xx, yy);
8510 #endif
8511
8512             ChangeDelay[xx][yy] = 0;
8513
8514             Feld[xx][yy] = EL_FLAMES;
8515
8516             if (IN_SCR_FIELD(sx, sy))
8517             {
8518               TEST_DrawLevelFieldCrumbled(xx, yy);
8519               DrawGraphic(sx, sy, flame_graphic, frame);
8520             }
8521           }
8522           else
8523           {
8524             if (Feld[xx][yy] == EL_FLAMES)
8525               Feld[xx][yy] = EL_EMPTY;
8526             TEST_DrawLevelField(xx, yy);
8527           }
8528         }
8529       }
8530
8531       if (MovDelay[x][y])       /* element still has to wait some time */
8532       {
8533         PlayLevelSoundAction(x, y, ACTION_WAITING);
8534
8535         return;
8536       }
8537     }
8538
8539     /* now make next step */
8540
8541     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8542
8543     if (DONT_COLLIDE_WITH(element) &&
8544         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8545         !PLAYER_ENEMY_PROTECTED(newx, newy))
8546     {
8547       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8548
8549       return;
8550     }
8551
8552     else if (CAN_MOVE_INTO_ACID(element) &&
8553              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8554              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8555              (MovDir[x][y] == MV_DOWN ||
8556               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8557     {
8558       SplashAcid(newx, newy);
8559       Store[x][y] = EL_ACID;
8560     }
8561     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8562     {
8563       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8564           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8565           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8566           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8567       {
8568         RemoveField(x, y);
8569         TEST_DrawLevelField(x, y);
8570
8571         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8572         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8573           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8574
8575         local_player->friends_still_needed--;
8576         if (!local_player->friends_still_needed &&
8577             !local_player->GameOver && AllPlayersGone)
8578           PlayerWins(local_player);
8579
8580         return;
8581       }
8582       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8583       {
8584         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8585           TEST_DrawLevelField(newx, newy);
8586         else
8587           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8588       }
8589       else if (!IS_FREE(newx, newy))
8590       {
8591         GfxAction[x][y] = ACTION_WAITING;
8592
8593         if (IS_PLAYER(x, y))
8594           DrawPlayerField(x, y);
8595         else
8596           TEST_DrawLevelField(x, y);
8597
8598         return;
8599       }
8600     }
8601     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8602     {
8603       if (IS_FOOD_PIG(Feld[newx][newy]))
8604       {
8605         if (IS_MOVING(newx, newy))
8606           RemoveMovingField(newx, newy);
8607         else
8608         {
8609           Feld[newx][newy] = EL_EMPTY;
8610           TEST_DrawLevelField(newx, newy);
8611         }
8612
8613         PlayLevelSound(x, y, SND_PIG_DIGGING);
8614       }
8615       else if (!IS_FREE(newx, newy))
8616       {
8617         if (IS_PLAYER(x, y))
8618           DrawPlayerField(x, y);
8619         else
8620           TEST_DrawLevelField(x, y);
8621
8622         return;
8623       }
8624     }
8625     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8626     {
8627       if (Store[x][y] != EL_EMPTY)
8628       {
8629         boolean can_clone = FALSE;
8630         int xx, yy;
8631
8632         /* check if element to clone is still there */
8633         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8634         {
8635           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8636           {
8637             can_clone = TRUE;
8638
8639             break;
8640           }
8641         }
8642
8643         /* cannot clone or target field not free anymore -- do not clone */
8644         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8645           Store[x][y] = EL_EMPTY;
8646       }
8647
8648       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8649       {
8650         if (IS_MV_DIAGONAL(MovDir[x][y]))
8651         {
8652           int diagonal_move_dir = MovDir[x][y];
8653           int stored = Store[x][y];
8654           int change_delay = 8;
8655           int graphic;
8656
8657           /* android is moving diagonally */
8658
8659           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8660
8661           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8662           GfxElement[x][y] = EL_EMC_ANDROID;
8663           GfxAction[x][y] = ACTION_SHRINKING;
8664           GfxDir[x][y] = diagonal_move_dir;
8665           ChangeDelay[x][y] = change_delay;
8666
8667           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8668                                    GfxDir[x][y]);
8669
8670           DrawLevelGraphicAnimation(x, y, graphic);
8671           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8672
8673           if (Feld[newx][newy] == EL_ACID)
8674           {
8675             SplashAcid(newx, newy);
8676
8677             return;
8678           }
8679
8680           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8681
8682           Store[newx][newy] = EL_EMC_ANDROID;
8683           GfxElement[newx][newy] = EL_EMC_ANDROID;
8684           GfxAction[newx][newy] = ACTION_GROWING;
8685           GfxDir[newx][newy] = diagonal_move_dir;
8686           ChangeDelay[newx][newy] = change_delay;
8687
8688           graphic = el_act_dir2img(GfxElement[newx][newy],
8689                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8690
8691           DrawLevelGraphicAnimation(newx, newy, graphic);
8692           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8693
8694           return;
8695         }
8696         else
8697         {
8698           Feld[newx][newy] = EL_EMPTY;
8699           TEST_DrawLevelField(newx, newy);
8700
8701           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8702         }
8703       }
8704       else if (!IS_FREE(newx, newy))
8705       {
8706 #if 0
8707         if (IS_PLAYER(x, y))
8708           DrawPlayerField(x, y);
8709         else
8710           TEST_DrawLevelField(x, y);
8711 #endif
8712
8713         return;
8714       }
8715     }
8716     else if (IS_CUSTOM_ELEMENT(element) &&
8717              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8718     {
8719 #if 1
8720       if (!DigFieldByCE(newx, newy, element))
8721         return;
8722 #else
8723       int new_element = Feld[newx][newy];
8724
8725       if (!IS_FREE(newx, newy))
8726       {
8727         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8728                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8729                       ACTION_BREAKING);
8730
8731         /* no element can dig solid indestructible elements */
8732         if (IS_INDESTRUCTIBLE(new_element) &&
8733             !IS_DIGGABLE(new_element) &&
8734             !IS_COLLECTIBLE(new_element))
8735           return;
8736
8737         if (AmoebaNr[newx][newy] &&
8738             (new_element == EL_AMOEBA_FULL ||
8739              new_element == EL_BD_AMOEBA ||
8740              new_element == EL_AMOEBA_GROWING))
8741         {
8742           AmoebaCnt[AmoebaNr[newx][newy]]--;
8743           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8744         }
8745
8746         if (IS_MOVING(newx, newy))
8747           RemoveMovingField(newx, newy);
8748         else
8749         {
8750           RemoveField(newx, newy);
8751           TEST_DrawLevelField(newx, newy);
8752         }
8753
8754         /* if digged element was about to explode, prevent the explosion */
8755         ExplodeField[newx][newy] = EX_TYPE_NONE;
8756
8757         PlayLevelSoundAction(x, y, action);
8758       }
8759
8760       Store[newx][newy] = EL_EMPTY;
8761
8762 #if 1
8763       /* this makes it possible to leave the removed element again */
8764       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8765         Store[newx][newy] = new_element;
8766 #else
8767       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8768       {
8769         int move_leave_element = element_info[element].move_leave_element;
8770
8771         /* this makes it possible to leave the removed element again */
8772         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8773                              new_element : move_leave_element);
8774       }
8775 #endif
8776
8777 #endif
8778
8779       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8780       {
8781         RunnerVisit[x][y] = FrameCounter;
8782         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8783       }
8784     }
8785     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8786     {
8787       if (!IS_FREE(newx, newy))
8788       {
8789         if (IS_PLAYER(x, y))
8790           DrawPlayerField(x, y);
8791         else
8792           TEST_DrawLevelField(x, y);
8793
8794         return;
8795       }
8796       else
8797       {
8798         boolean wanna_flame = !RND(10);
8799         int dx = newx - x, dy = newy - y;
8800         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8801         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8802         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8803                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8804         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8805                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8806
8807         if ((wanna_flame ||
8808              IS_CLASSIC_ENEMY(element1) ||
8809              IS_CLASSIC_ENEMY(element2)) &&
8810             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8811             element1 != EL_FLAMES && element2 != EL_FLAMES)
8812         {
8813           ResetGfxAnimation(x, y);
8814           GfxAction[x][y] = ACTION_ATTACKING;
8815
8816           if (IS_PLAYER(x, y))
8817             DrawPlayerField(x, y);
8818           else
8819             TEST_DrawLevelField(x, y);
8820
8821           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8822
8823           MovDelay[x][y] = 50;
8824
8825           /* !!! */
8826 #if 0
8827           RemoveField(newx, newy);
8828 #endif
8829           Feld[newx][newy] = EL_FLAMES;
8830           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8831           {
8832 #if 0
8833             RemoveField(newx1, newy1);
8834 #endif
8835             Feld[newx1][newy1] = EL_FLAMES;
8836           }
8837           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8838           {
8839 #if 0
8840             RemoveField(newx2, newy2);
8841 #endif
8842             Feld[newx2][newy2] = EL_FLAMES;
8843           }
8844
8845           return;
8846         }
8847       }
8848     }
8849     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8850              Feld[newx][newy] == EL_DIAMOND)
8851     {
8852       if (IS_MOVING(newx, newy))
8853         RemoveMovingField(newx, newy);
8854       else
8855       {
8856         Feld[newx][newy] = EL_EMPTY;
8857         TEST_DrawLevelField(newx, newy);
8858       }
8859
8860       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8861     }
8862     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8863              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8864     {
8865       if (AmoebaNr[newx][newy])
8866       {
8867         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8868         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8869             Feld[newx][newy] == EL_BD_AMOEBA)
8870           AmoebaCnt[AmoebaNr[newx][newy]]--;
8871       }
8872
8873 #if 0
8874       /* !!! test !!! */
8875       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8876       {
8877         RemoveMovingField(newx, newy);
8878       }
8879 #else
8880       if (IS_MOVING(newx, newy))
8881       {
8882         RemoveMovingField(newx, newy);
8883       }
8884 #endif
8885       else
8886       {
8887         Feld[newx][newy] = EL_EMPTY;
8888         TEST_DrawLevelField(newx, newy);
8889       }
8890
8891       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8892     }
8893     else if ((element == EL_PACMAN || element == EL_MOLE)
8894              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8895     {
8896       if (AmoebaNr[newx][newy])
8897       {
8898         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8899         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8900             Feld[newx][newy] == EL_BD_AMOEBA)
8901           AmoebaCnt[AmoebaNr[newx][newy]]--;
8902       }
8903
8904       if (element == EL_MOLE)
8905       {
8906         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8907         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8908
8909         ResetGfxAnimation(x, y);
8910         GfxAction[x][y] = ACTION_DIGGING;
8911         TEST_DrawLevelField(x, y);
8912
8913         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8914
8915         return;                         /* wait for shrinking amoeba */
8916       }
8917       else      /* element == EL_PACMAN */
8918       {
8919         Feld[newx][newy] = EL_EMPTY;
8920         TEST_DrawLevelField(newx, newy);
8921         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8922       }
8923     }
8924     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8925              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8926               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8927     {
8928       /* wait for shrinking amoeba to completely disappear */
8929       return;
8930     }
8931     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8932     {
8933       /* object was running against a wall */
8934
8935       TurnRound(x, y);
8936
8937 #if 0
8938       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8939       if (move_pattern & MV_ANY_DIRECTION &&
8940           move_pattern == MovDir[x][y])
8941       {
8942         int blocking_element =
8943           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8944
8945         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8946                                  MovDir[x][y]);
8947
8948         element = Feld[x][y];   /* element might have changed */
8949       }
8950 #endif
8951
8952       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8953         DrawLevelElementAnimation(x, y, element);
8954
8955       if (DONT_TOUCH(element))
8956         TestIfBadThingTouchesPlayer(x, y);
8957
8958       return;
8959     }
8960
8961     InitMovingField(x, y, MovDir[x][y]);
8962
8963     PlayLevelSoundAction(x, y, ACTION_MOVING);
8964   }
8965
8966   if (MovDir[x][y])
8967     ContinueMoving(x, y);
8968 }
8969
8970 void ContinueMoving(int x, int y)
8971 {
8972   int element = Feld[x][y];
8973   struct ElementInfo *ei = &element_info[element];
8974   int direction = MovDir[x][y];
8975   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8976   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8977   int newx = x + dx, newy = y + dy;
8978   int stored = Store[x][y];
8979   int stored_new = Store[newx][newy];
8980   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8981   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8982   boolean last_line = (newy == lev_fieldy - 1);
8983
8984   MovPos[x][y] += getElementMoveStepsize(x, y);
8985
8986   if (pushed_by_player) /* special case: moving object pushed by player */
8987     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8988
8989   if (ABS(MovPos[x][y]) < TILEX)
8990   {
8991 #if 0
8992     int ee = Feld[x][y];
8993     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8994     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8995
8996     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8997            x, y, ABS(MovPos[x][y]),
8998            ee, gg, ff,
8999            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9000 #endif
9001
9002     TEST_DrawLevelField(x, y);
9003
9004     return;     /* element is still moving */
9005   }
9006
9007   /* element reached destination field */
9008
9009   Feld[x][y] = EL_EMPTY;
9010   Feld[newx][newy] = element;
9011   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
9012
9013   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
9014   {
9015     element = Feld[newx][newy] = EL_ACID;
9016   }
9017   else if (element == EL_MOLE)
9018   {
9019     Feld[x][y] = EL_SAND;
9020
9021     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9022   }
9023   else if (element == EL_QUICKSAND_FILLING)
9024   {
9025     element = Feld[newx][newy] = get_next_element(element);
9026     Store[newx][newy] = Store[x][y];
9027   }
9028   else if (element == EL_QUICKSAND_EMPTYING)
9029   {
9030     Feld[x][y] = get_next_element(element);
9031     element = Feld[newx][newy] = Store[x][y];
9032   }
9033   else if (element == EL_QUICKSAND_FAST_FILLING)
9034   {
9035     element = Feld[newx][newy] = get_next_element(element);
9036     Store[newx][newy] = Store[x][y];
9037   }
9038   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9039   {
9040     Feld[x][y] = get_next_element(element);
9041     element = Feld[newx][newy] = Store[x][y];
9042   }
9043   else if (element == EL_MAGIC_WALL_FILLING)
9044   {
9045     element = Feld[newx][newy] = get_next_element(element);
9046     if (!game.magic_wall_active)
9047       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9048     Store[newx][newy] = Store[x][y];
9049   }
9050   else if (element == EL_MAGIC_WALL_EMPTYING)
9051   {
9052     Feld[x][y] = get_next_element(element);
9053     if (!game.magic_wall_active)
9054       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9055     element = Feld[newx][newy] = Store[x][y];
9056
9057 #if USE_NEW_CUSTOM_VALUE
9058     InitField(newx, newy, FALSE);
9059 #endif
9060   }
9061   else if (element == EL_BD_MAGIC_WALL_FILLING)
9062   {
9063     element = Feld[newx][newy] = get_next_element(element);
9064     if (!game.magic_wall_active)
9065       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9066     Store[newx][newy] = Store[x][y];
9067   }
9068   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9069   {
9070     Feld[x][y] = get_next_element(element);
9071     if (!game.magic_wall_active)
9072       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9073     element = Feld[newx][newy] = Store[x][y];
9074
9075 #if USE_NEW_CUSTOM_VALUE
9076     InitField(newx, newy, FALSE);
9077 #endif
9078   }
9079   else if (element == EL_DC_MAGIC_WALL_FILLING)
9080   {
9081     element = Feld[newx][newy] = get_next_element(element);
9082     if (!game.magic_wall_active)
9083       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9084     Store[newx][newy] = Store[x][y];
9085   }
9086   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9087   {
9088     Feld[x][y] = get_next_element(element);
9089     if (!game.magic_wall_active)
9090       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9091     element = Feld[newx][newy] = Store[x][y];
9092
9093 #if USE_NEW_CUSTOM_VALUE
9094     InitField(newx, newy, FALSE);
9095 #endif
9096   }
9097   else if (element == EL_AMOEBA_DROPPING)
9098   {
9099     Feld[x][y] = get_next_element(element);
9100     element = Feld[newx][newy] = Store[x][y];
9101   }
9102   else if (element == EL_SOKOBAN_OBJECT)
9103   {
9104     if (Back[x][y])
9105       Feld[x][y] = Back[x][y];
9106
9107     if (Back[newx][newy])
9108       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9109
9110     Back[x][y] = Back[newx][newy] = 0;
9111   }
9112
9113   Store[x][y] = EL_EMPTY;
9114   MovPos[x][y] = 0;
9115   MovDir[x][y] = 0;
9116   MovDelay[x][y] = 0;
9117
9118   MovDelay[newx][newy] = 0;
9119
9120   if (CAN_CHANGE_OR_HAS_ACTION(element))
9121   {
9122     /* copy element change control values to new field */
9123     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9124     ChangePage[newx][newy]  = ChangePage[x][y];
9125     ChangeCount[newx][newy] = ChangeCount[x][y];
9126     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9127   }
9128
9129 #if USE_NEW_CUSTOM_VALUE
9130   CustomValue[newx][newy] = CustomValue[x][y];
9131 #endif
9132
9133   ChangeDelay[x][y] = 0;
9134   ChangePage[x][y] = -1;
9135   ChangeCount[x][y] = 0;
9136   ChangeEvent[x][y] = -1;
9137
9138 #if USE_NEW_CUSTOM_VALUE
9139   CustomValue[x][y] = 0;
9140 #endif
9141
9142   /* copy animation control values to new field */
9143   GfxFrame[newx][newy]  = GfxFrame[x][y];
9144   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9145   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9146   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9147
9148   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9149
9150   /* some elements can leave other elements behind after moving */
9151 #if 1
9152   if (ei->move_leave_element != EL_EMPTY &&
9153       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9154       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9155 #else
9156   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9157       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9158       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9159 #endif
9160   {
9161     int move_leave_element = ei->move_leave_element;
9162
9163 #if 1
9164 #if 1
9165     /* this makes it possible to leave the removed element again */
9166     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9167       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9168 #else
9169     /* this makes it possible to leave the removed element again */
9170     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9171       move_leave_element = stored;
9172 #endif
9173 #else
9174     /* this makes it possible to leave the removed element again */
9175     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9176         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9177       move_leave_element = stored;
9178 #endif
9179
9180     Feld[x][y] = move_leave_element;
9181
9182     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9183       MovDir[x][y] = direction;
9184
9185     InitField(x, y, FALSE);
9186
9187     if (GFX_CRUMBLED(Feld[x][y]))
9188       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9189
9190     if (ELEM_IS_PLAYER(move_leave_element))
9191       RelocatePlayer(x, y, move_leave_element);
9192   }
9193
9194   /* do this after checking for left-behind element */
9195   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9196
9197   if (!CAN_MOVE(element) ||
9198       (CAN_FALL(element) && direction == MV_DOWN &&
9199        (element == EL_SPRING ||
9200         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9201         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9202     GfxDir[x][y] = MovDir[newx][newy] = 0;
9203
9204   TEST_DrawLevelField(x, y);
9205   TEST_DrawLevelField(newx, newy);
9206
9207   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9208
9209   /* prevent pushed element from moving on in pushed direction */
9210   if (pushed_by_player && CAN_MOVE(element) &&
9211       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9212       !(element_info[element].move_pattern & direction))
9213     TurnRound(newx, newy);
9214
9215   /* prevent elements on conveyor belt from moving on in last direction */
9216   if (pushed_by_conveyor && CAN_FALL(element) &&
9217       direction & MV_HORIZONTAL)
9218     MovDir[newx][newy] = 0;
9219
9220   if (!pushed_by_player)
9221   {
9222     int nextx = newx + dx, nexty = newy + dy;
9223     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9224
9225     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9226
9227     if (CAN_FALL(element) && direction == MV_DOWN)
9228       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9229
9230     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9231       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9232
9233 #if USE_FIX_IMPACT_COLLISION
9234     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9235       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9236 #endif
9237   }
9238
9239   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9240   {
9241     TestIfBadThingTouchesPlayer(newx, newy);
9242     TestIfBadThingTouchesFriend(newx, newy);
9243
9244     if (!IS_CUSTOM_ELEMENT(element))
9245       TestIfBadThingTouchesOtherBadThing(newx, newy);
9246   }
9247   else if (element == EL_PENGUIN)
9248     TestIfFriendTouchesBadThing(newx, newy);
9249
9250   if (DONT_GET_HIT_BY(element))
9251   {
9252     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9253   }
9254
9255   /* give the player one last chance (one more frame) to move away */
9256   if (CAN_FALL(element) && direction == MV_DOWN &&
9257       (last_line || (!IS_FREE(x, newy + 1) &&
9258                      (!IS_PLAYER(x, newy + 1) ||
9259                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9260     Impact(x, newy);
9261
9262   if (pushed_by_player && !game.use_change_when_pushing_bug)
9263   {
9264     int push_side = MV_DIR_OPPOSITE(direction);
9265     struct PlayerInfo *player = PLAYERINFO(x, y);
9266
9267     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9268                                player->index_bit, push_side);
9269     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9270                                         player->index_bit, push_side);
9271   }
9272
9273   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9274     MovDelay[newx][newy] = 1;
9275
9276   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9277
9278   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9279
9280 #if 0
9281   if (ChangePage[newx][newy] != -1)             /* delayed change */
9282   {
9283     int page = ChangePage[newx][newy];
9284     struct ElementChangeInfo *change = &ei->change_page[page];
9285
9286     ChangePage[newx][newy] = -1;
9287
9288     if (change->can_change)
9289     {
9290       if (ChangeElement(newx, newy, element, page))
9291       {
9292         if (change->post_change_function)
9293           change->post_change_function(newx, newy);
9294       }
9295     }
9296
9297     if (change->has_action)
9298       ExecuteCustomElementAction(newx, newy, element, page);
9299   }
9300 #endif
9301
9302   TestIfElementHitsCustomElement(newx, newy, direction);
9303   TestIfPlayerTouchesCustomElement(newx, newy);
9304   TestIfElementTouchesCustomElement(newx, newy);
9305
9306   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9307       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9308     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9309                              MV_DIR_OPPOSITE(direction));
9310 }
9311
9312 int AmoebeNachbarNr(int ax, int ay)
9313 {
9314   int i;
9315   int element = Feld[ax][ay];
9316   int group_nr = 0;
9317   static int xy[4][2] =
9318   {
9319     { 0, -1 },
9320     { -1, 0 },
9321     { +1, 0 },
9322     { 0, +1 }
9323   };
9324
9325   for (i = 0; i < NUM_DIRECTIONS; i++)
9326   {
9327     int x = ax + xy[i][0];
9328     int y = ay + xy[i][1];
9329
9330     if (!IN_LEV_FIELD(x, y))
9331       continue;
9332
9333     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9334       group_nr = AmoebaNr[x][y];
9335   }
9336
9337   return group_nr;
9338 }
9339
9340 void AmoebenVereinigen(int ax, int ay)
9341 {
9342   int i, x, y, xx, yy;
9343   int new_group_nr = AmoebaNr[ax][ay];
9344   static int xy[4][2] =
9345   {
9346     { 0, -1 },
9347     { -1, 0 },
9348     { +1, 0 },
9349     { 0, +1 }
9350   };
9351
9352   if (new_group_nr == 0)
9353     return;
9354
9355   for (i = 0; i < NUM_DIRECTIONS; i++)
9356   {
9357     x = ax + xy[i][0];
9358     y = ay + xy[i][1];
9359
9360     if (!IN_LEV_FIELD(x, y))
9361       continue;
9362
9363     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9364          Feld[x][y] == EL_BD_AMOEBA ||
9365          Feld[x][y] == EL_AMOEBA_DEAD) &&
9366         AmoebaNr[x][y] != new_group_nr)
9367     {
9368       int old_group_nr = AmoebaNr[x][y];
9369
9370       if (old_group_nr == 0)
9371         return;
9372
9373       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9374       AmoebaCnt[old_group_nr] = 0;
9375       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9376       AmoebaCnt2[old_group_nr] = 0;
9377
9378       SCAN_PLAYFIELD(xx, yy)
9379       {
9380         if (AmoebaNr[xx][yy] == old_group_nr)
9381           AmoebaNr[xx][yy] = new_group_nr;
9382       }
9383     }
9384   }
9385 }
9386
9387 void AmoebeUmwandeln(int ax, int ay)
9388 {
9389   int i, x, y;
9390
9391   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9392   {
9393     int group_nr = AmoebaNr[ax][ay];
9394
9395 #ifdef DEBUG
9396     if (group_nr == 0)
9397     {
9398       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9399       printf("AmoebeUmwandeln(): This should never happen!\n");
9400       return;
9401     }
9402 #endif
9403
9404     SCAN_PLAYFIELD(x, y)
9405     {
9406       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9407       {
9408         AmoebaNr[x][y] = 0;
9409         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9410       }
9411     }
9412
9413     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9414                             SND_AMOEBA_TURNING_TO_GEM :
9415                             SND_AMOEBA_TURNING_TO_ROCK));
9416     Bang(ax, ay);
9417   }
9418   else
9419   {
9420     static int xy[4][2] =
9421     {
9422       { 0, -1 },
9423       { -1, 0 },
9424       { +1, 0 },
9425       { 0, +1 }
9426     };
9427
9428     for (i = 0; i < NUM_DIRECTIONS; i++)
9429     {
9430       x = ax + xy[i][0];
9431       y = ay + xy[i][1];
9432
9433       if (!IN_LEV_FIELD(x, y))
9434         continue;
9435
9436       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9437       {
9438         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9439                               SND_AMOEBA_TURNING_TO_GEM :
9440                               SND_AMOEBA_TURNING_TO_ROCK));
9441         Bang(x, y);
9442       }
9443     }
9444   }
9445 }
9446
9447 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9448 {
9449   int x, y;
9450   int group_nr = AmoebaNr[ax][ay];
9451   boolean done = FALSE;
9452
9453 #ifdef DEBUG
9454   if (group_nr == 0)
9455   {
9456     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9457     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9458     return;
9459   }
9460 #endif
9461
9462   SCAN_PLAYFIELD(x, y)
9463   {
9464     if (AmoebaNr[x][y] == group_nr &&
9465         (Feld[x][y] == EL_AMOEBA_DEAD ||
9466          Feld[x][y] == EL_BD_AMOEBA ||
9467          Feld[x][y] == EL_AMOEBA_GROWING))
9468     {
9469       AmoebaNr[x][y] = 0;
9470       Feld[x][y] = new_element;
9471       InitField(x, y, FALSE);
9472       TEST_DrawLevelField(x, y);
9473       done = TRUE;
9474     }
9475   }
9476
9477   if (done)
9478     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9479                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9480                             SND_BD_AMOEBA_TURNING_TO_GEM));
9481 }
9482
9483 void AmoebeWaechst(int x, int y)
9484 {
9485   static unsigned int sound_delay = 0;
9486   static unsigned int sound_delay_value = 0;
9487
9488   if (!MovDelay[x][y])          /* start new growing cycle */
9489   {
9490     MovDelay[x][y] = 7;
9491
9492     if (DelayReached(&sound_delay, sound_delay_value))
9493     {
9494       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9495       sound_delay_value = 30;
9496     }
9497   }
9498
9499   if (MovDelay[x][y])           /* wait some time before growing bigger */
9500   {
9501     MovDelay[x][y]--;
9502     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9503     {
9504       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9505                                            6 - MovDelay[x][y]);
9506
9507       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9508     }
9509
9510     if (!MovDelay[x][y])
9511     {
9512       Feld[x][y] = Store[x][y];
9513       Store[x][y] = 0;
9514       TEST_DrawLevelField(x, y);
9515     }
9516   }
9517 }
9518
9519 void AmoebaDisappearing(int x, int y)
9520 {
9521   static unsigned int sound_delay = 0;
9522   static unsigned int sound_delay_value = 0;
9523
9524   if (!MovDelay[x][y])          /* start new shrinking cycle */
9525   {
9526     MovDelay[x][y] = 7;
9527
9528     if (DelayReached(&sound_delay, sound_delay_value))
9529       sound_delay_value = 30;
9530   }
9531
9532   if (MovDelay[x][y])           /* wait some time before shrinking */
9533   {
9534     MovDelay[x][y]--;
9535     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9536     {
9537       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9538                                            6 - MovDelay[x][y]);
9539
9540       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9541     }
9542
9543     if (!MovDelay[x][y])
9544     {
9545       Feld[x][y] = EL_EMPTY;
9546       TEST_DrawLevelField(x, y);
9547
9548       /* don't let mole enter this field in this cycle;
9549          (give priority to objects falling to this field from above) */
9550       Stop[x][y] = TRUE;
9551     }
9552   }
9553 }
9554
9555 void AmoebeAbleger(int ax, int ay)
9556 {
9557   int i;
9558   int element = Feld[ax][ay];
9559   int graphic = el2img(element);
9560   int newax = ax, neway = ay;
9561   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9562   static int xy[4][2] =
9563   {
9564     { 0, -1 },
9565     { -1, 0 },
9566     { +1, 0 },
9567     { 0, +1 }
9568   };
9569
9570   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9571   {
9572     Feld[ax][ay] = EL_AMOEBA_DEAD;
9573     TEST_DrawLevelField(ax, ay);
9574     return;
9575   }
9576
9577   if (IS_ANIMATED(graphic))
9578     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9579
9580   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9581     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9582
9583   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9584   {
9585     MovDelay[ax][ay]--;
9586     if (MovDelay[ax][ay])
9587       return;
9588   }
9589
9590   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9591   {
9592     int start = RND(4);
9593     int x = ax + xy[start][0];
9594     int y = ay + xy[start][1];
9595
9596     if (!IN_LEV_FIELD(x, y))
9597       return;
9598
9599     if (IS_FREE(x, y) ||
9600         CAN_GROW_INTO(Feld[x][y]) ||
9601         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9602         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9603     {
9604       newax = x;
9605       neway = y;
9606     }
9607
9608     if (newax == ax && neway == ay)
9609       return;
9610   }
9611   else                          /* normal or "filled" (BD style) amoeba */
9612   {
9613     int start = RND(4);
9614     boolean waiting_for_player = FALSE;
9615
9616     for (i = 0; i < NUM_DIRECTIONS; i++)
9617     {
9618       int j = (start + i) % 4;
9619       int x = ax + xy[j][0];
9620       int y = ay + xy[j][1];
9621
9622       if (!IN_LEV_FIELD(x, y))
9623         continue;
9624
9625       if (IS_FREE(x, y) ||
9626           CAN_GROW_INTO(Feld[x][y]) ||
9627           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9628           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9629       {
9630         newax = x;
9631         neway = y;
9632         break;
9633       }
9634       else if (IS_PLAYER(x, y))
9635         waiting_for_player = TRUE;
9636     }
9637
9638     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9639     {
9640       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9641       {
9642         Feld[ax][ay] = EL_AMOEBA_DEAD;
9643         TEST_DrawLevelField(ax, ay);
9644         AmoebaCnt[AmoebaNr[ax][ay]]--;
9645
9646         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9647         {
9648           if (element == EL_AMOEBA_FULL)
9649             AmoebeUmwandeln(ax, ay);
9650           else if (element == EL_BD_AMOEBA)
9651             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9652         }
9653       }
9654       return;
9655     }
9656     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9657     {
9658       /* amoeba gets larger by growing in some direction */
9659
9660       int new_group_nr = AmoebaNr[ax][ay];
9661
9662 #ifdef DEBUG
9663   if (new_group_nr == 0)
9664   {
9665     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9666     printf("AmoebeAbleger(): This should never happen!\n");
9667     return;
9668   }
9669 #endif
9670
9671       AmoebaNr[newax][neway] = new_group_nr;
9672       AmoebaCnt[new_group_nr]++;
9673       AmoebaCnt2[new_group_nr]++;
9674
9675       /* if amoeba touches other amoeba(s) after growing, unify them */
9676       AmoebenVereinigen(newax, neway);
9677
9678       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9679       {
9680         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9681         return;
9682       }
9683     }
9684   }
9685
9686   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9687       (neway == lev_fieldy - 1 && newax != ax))
9688   {
9689     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9690     Store[newax][neway] = element;
9691   }
9692   else if (neway == ay || element == EL_EMC_DRIPPER)
9693   {
9694     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9695
9696     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9697   }
9698   else
9699   {
9700     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9701     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9702     Store[ax][ay] = EL_AMOEBA_DROP;
9703     ContinueMoving(ax, ay);
9704     return;
9705   }
9706
9707   TEST_DrawLevelField(newax, neway);
9708 }
9709
9710 void Life(int ax, int ay)
9711 {
9712   int x1, y1, x2, y2;
9713   int life_time = 40;
9714   int element = Feld[ax][ay];
9715   int graphic = el2img(element);
9716   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9717                          level.biomaze);
9718   boolean changed = FALSE;
9719
9720   if (IS_ANIMATED(graphic))
9721     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9722
9723   if (Stop[ax][ay])
9724     return;
9725
9726   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9727     MovDelay[ax][ay] = life_time;
9728
9729   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9730   {
9731     MovDelay[ax][ay]--;
9732     if (MovDelay[ax][ay])
9733       return;
9734   }
9735
9736   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9737   {
9738     int xx = ax+x1, yy = ay+y1;
9739     int nachbarn = 0;
9740
9741     if (!IN_LEV_FIELD(xx, yy))
9742       continue;
9743
9744     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9745     {
9746       int x = xx+x2, y = yy+y2;
9747
9748       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9749         continue;
9750
9751       if (((Feld[x][y] == element ||
9752             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9753            !Stop[x][y]) ||
9754           (IS_FREE(x, y) && Stop[x][y]))
9755         nachbarn++;
9756     }
9757
9758     if (xx == ax && yy == ay)           /* field in the middle */
9759     {
9760       if (nachbarn < life_parameter[0] ||
9761           nachbarn > life_parameter[1])
9762       {
9763         Feld[xx][yy] = EL_EMPTY;
9764         if (!Stop[xx][yy])
9765           TEST_DrawLevelField(xx, yy);
9766         Stop[xx][yy] = TRUE;
9767         changed = TRUE;
9768       }
9769     }
9770     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9771     {                                   /* free border field */
9772       if (nachbarn >= life_parameter[2] &&
9773           nachbarn <= life_parameter[3])
9774       {
9775         Feld[xx][yy] = element;
9776         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9777         if (!Stop[xx][yy])
9778           TEST_DrawLevelField(xx, yy);
9779         Stop[xx][yy] = TRUE;
9780         changed = TRUE;
9781       }
9782     }
9783   }
9784
9785   if (changed)
9786     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9787                    SND_GAME_OF_LIFE_GROWING);
9788 }
9789
9790 static void InitRobotWheel(int x, int y)
9791 {
9792   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9793 }
9794
9795 static void RunRobotWheel(int x, int y)
9796 {
9797   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9798 }
9799
9800 static void StopRobotWheel(int x, int y)
9801 {
9802   if (ZX == x && ZY == y)
9803   {
9804     ZX = ZY = -1;
9805
9806     game.robot_wheel_active = FALSE;
9807   }
9808 }
9809
9810 static void InitTimegateWheel(int x, int y)
9811 {
9812   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9813 }
9814
9815 static void RunTimegateWheel(int x, int y)
9816 {
9817   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9818 }
9819
9820 static void InitMagicBallDelay(int x, int y)
9821 {
9822 #if 1
9823   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9824 #else
9825   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9826 #endif
9827 }
9828
9829 static void ActivateMagicBall(int bx, int by)
9830 {
9831   int x, y;
9832
9833   if (level.ball_random)
9834   {
9835     int pos_border = RND(8);    /* select one of the eight border elements */
9836     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9837     int xx = pos_content % 3;
9838     int yy = pos_content / 3;
9839
9840     x = bx - 1 + xx;
9841     y = by - 1 + yy;
9842
9843     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9844       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9845   }
9846   else
9847   {
9848     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9849     {
9850       int xx = x - bx + 1;
9851       int yy = y - by + 1;
9852
9853       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9854         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9855     }
9856   }
9857
9858   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9859 }
9860
9861 void CheckExit(int x, int y)
9862 {
9863   if (local_player->gems_still_needed > 0 ||
9864       local_player->sokobanfields_still_needed > 0 ||
9865       local_player->lights_still_needed > 0)
9866   {
9867     int element = Feld[x][y];
9868     int graphic = el2img(element);
9869
9870     if (IS_ANIMATED(graphic))
9871       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9872
9873     return;
9874   }
9875
9876   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9877     return;
9878
9879   Feld[x][y] = EL_EXIT_OPENING;
9880
9881   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9882 }
9883
9884 void CheckExitEM(int x, int y)
9885 {
9886   if (local_player->gems_still_needed > 0 ||
9887       local_player->sokobanfields_still_needed > 0 ||
9888       local_player->lights_still_needed > 0)
9889   {
9890     int element = Feld[x][y];
9891     int graphic = el2img(element);
9892
9893     if (IS_ANIMATED(graphic))
9894       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9895
9896     return;
9897   }
9898
9899   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9900     return;
9901
9902   Feld[x][y] = EL_EM_EXIT_OPENING;
9903
9904   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9905 }
9906
9907 void CheckExitSteel(int x, int y)
9908 {
9909   if (local_player->gems_still_needed > 0 ||
9910       local_player->sokobanfields_still_needed > 0 ||
9911       local_player->lights_still_needed > 0)
9912   {
9913     int element = Feld[x][y];
9914     int graphic = el2img(element);
9915
9916     if (IS_ANIMATED(graphic))
9917       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9918
9919     return;
9920   }
9921
9922   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9923     return;
9924
9925   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9926
9927   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9928 }
9929
9930 void CheckExitSteelEM(int x, int y)
9931 {
9932   if (local_player->gems_still_needed > 0 ||
9933       local_player->sokobanfields_still_needed > 0 ||
9934       local_player->lights_still_needed > 0)
9935   {
9936     int element = Feld[x][y];
9937     int graphic = el2img(element);
9938
9939     if (IS_ANIMATED(graphic))
9940       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9941
9942     return;
9943   }
9944
9945   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9946     return;
9947
9948   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9949
9950   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9951 }
9952
9953 void CheckExitSP(int x, int y)
9954 {
9955   if (local_player->gems_still_needed > 0)
9956   {
9957     int element = Feld[x][y];
9958     int graphic = el2img(element);
9959
9960     if (IS_ANIMATED(graphic))
9961       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9962
9963     return;
9964   }
9965
9966   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9967     return;
9968
9969   Feld[x][y] = EL_SP_EXIT_OPENING;
9970
9971   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9972 }
9973
9974 static void CloseAllOpenTimegates()
9975 {
9976   int x, y;
9977
9978   SCAN_PLAYFIELD(x, y)
9979   {
9980     int element = Feld[x][y];
9981
9982     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9983     {
9984       Feld[x][y] = EL_TIMEGATE_CLOSING;
9985
9986       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9987     }
9988   }
9989 }
9990
9991 void DrawTwinkleOnField(int x, int y)
9992 {
9993   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9994     return;
9995
9996   if (Feld[x][y] == EL_BD_DIAMOND)
9997     return;
9998
9999   if (MovDelay[x][y] == 0)      /* next animation frame */
10000     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10001
10002   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
10003   {
10004     MovDelay[x][y]--;
10005
10006     DrawLevelElementAnimation(x, y, Feld[x][y]);
10007
10008     if (MovDelay[x][y] != 0)
10009     {
10010       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10011                                            10 - MovDelay[x][y]);
10012
10013       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10014     }
10015   }
10016 }
10017
10018 void MauerWaechst(int x, int y)
10019 {
10020   int delay = 6;
10021
10022   if (!MovDelay[x][y])          /* next animation frame */
10023     MovDelay[x][y] = 3 * delay;
10024
10025   if (MovDelay[x][y])           /* wait some time before next frame */
10026   {
10027     MovDelay[x][y]--;
10028
10029     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10030     {
10031       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10032       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10033
10034       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10035     }
10036
10037     if (!MovDelay[x][y])
10038     {
10039       if (MovDir[x][y] == MV_LEFT)
10040       {
10041         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10042           TEST_DrawLevelField(x - 1, y);
10043       }
10044       else if (MovDir[x][y] == MV_RIGHT)
10045       {
10046         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10047           TEST_DrawLevelField(x + 1, y);
10048       }
10049       else if (MovDir[x][y] == MV_UP)
10050       {
10051         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10052           TEST_DrawLevelField(x, y - 1);
10053       }
10054       else
10055       {
10056         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10057           TEST_DrawLevelField(x, y + 1);
10058       }
10059
10060       Feld[x][y] = Store[x][y];
10061       Store[x][y] = 0;
10062       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10063       TEST_DrawLevelField(x, y);
10064     }
10065   }
10066 }
10067
10068 void MauerAbleger(int ax, int ay)
10069 {
10070   int element = Feld[ax][ay];
10071   int graphic = el2img(element);
10072   boolean oben_frei = FALSE, unten_frei = FALSE;
10073   boolean links_frei = FALSE, rechts_frei = FALSE;
10074   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10075   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10076   boolean new_wall = FALSE;
10077
10078   if (IS_ANIMATED(graphic))
10079     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10080
10081   if (!MovDelay[ax][ay])        /* start building new wall */
10082     MovDelay[ax][ay] = 6;
10083
10084   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10085   {
10086     MovDelay[ax][ay]--;
10087     if (MovDelay[ax][ay])
10088       return;
10089   }
10090
10091   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10092     oben_frei = TRUE;
10093   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10094     unten_frei = TRUE;
10095   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10096     links_frei = TRUE;
10097   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10098     rechts_frei = TRUE;
10099
10100   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10101       element == EL_EXPANDABLE_WALL_ANY)
10102   {
10103     if (oben_frei)
10104     {
10105       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10106       Store[ax][ay-1] = element;
10107       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10108       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10109         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10110                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10111       new_wall = TRUE;
10112     }
10113     if (unten_frei)
10114     {
10115       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10116       Store[ax][ay+1] = element;
10117       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10118       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10119         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10120                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10121       new_wall = TRUE;
10122     }
10123   }
10124
10125   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10126       element == EL_EXPANDABLE_WALL_ANY ||
10127       element == EL_EXPANDABLE_WALL ||
10128       element == EL_BD_EXPANDABLE_WALL)
10129   {
10130     if (links_frei)
10131     {
10132       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10133       Store[ax-1][ay] = element;
10134       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10135       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10136         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10137                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10138       new_wall = TRUE;
10139     }
10140
10141     if (rechts_frei)
10142     {
10143       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10144       Store[ax+1][ay] = element;
10145       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10146       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10147         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10148                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10149       new_wall = TRUE;
10150     }
10151   }
10152
10153   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10154     TEST_DrawLevelField(ax, ay);
10155
10156   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10157     oben_massiv = TRUE;
10158   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10159     unten_massiv = TRUE;
10160   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10161     links_massiv = TRUE;
10162   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10163     rechts_massiv = TRUE;
10164
10165   if (((oben_massiv && unten_massiv) ||
10166        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10167        element == EL_EXPANDABLE_WALL) &&
10168       ((links_massiv && rechts_massiv) ||
10169        element == EL_EXPANDABLE_WALL_VERTICAL))
10170     Feld[ax][ay] = EL_WALL;
10171
10172   if (new_wall)
10173     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10174 }
10175
10176 void MauerAblegerStahl(int ax, int ay)
10177 {
10178   int element = Feld[ax][ay];
10179   int graphic = el2img(element);
10180   boolean oben_frei = FALSE, unten_frei = FALSE;
10181   boolean links_frei = FALSE, rechts_frei = FALSE;
10182   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10183   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10184   boolean new_wall = FALSE;
10185
10186   if (IS_ANIMATED(graphic))
10187     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10188
10189   if (!MovDelay[ax][ay])        /* start building new wall */
10190     MovDelay[ax][ay] = 6;
10191
10192   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10193   {
10194     MovDelay[ax][ay]--;
10195     if (MovDelay[ax][ay])
10196       return;
10197   }
10198
10199   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10200     oben_frei = TRUE;
10201   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10202     unten_frei = TRUE;
10203   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10204     links_frei = TRUE;
10205   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10206     rechts_frei = TRUE;
10207
10208   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10209       element == EL_EXPANDABLE_STEELWALL_ANY)
10210   {
10211     if (oben_frei)
10212     {
10213       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10214       Store[ax][ay-1] = element;
10215       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10216       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10217         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10218                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10219       new_wall = TRUE;
10220     }
10221     if (unten_frei)
10222     {
10223       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10224       Store[ax][ay+1] = element;
10225       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10226       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10227         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10228                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10229       new_wall = TRUE;
10230     }
10231   }
10232
10233   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10234       element == EL_EXPANDABLE_STEELWALL_ANY)
10235   {
10236     if (links_frei)
10237     {
10238       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10239       Store[ax-1][ay] = element;
10240       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10241       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10242         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10243                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10244       new_wall = TRUE;
10245     }
10246
10247     if (rechts_frei)
10248     {
10249       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10250       Store[ax+1][ay] = element;
10251       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10252       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10253         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10254                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10255       new_wall = TRUE;
10256     }
10257   }
10258
10259   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10260     oben_massiv = TRUE;
10261   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10262     unten_massiv = TRUE;
10263   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10264     links_massiv = TRUE;
10265   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10266     rechts_massiv = TRUE;
10267
10268   if (((oben_massiv && unten_massiv) ||
10269        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10270       ((links_massiv && rechts_massiv) ||
10271        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10272     Feld[ax][ay] = EL_STEELWALL;
10273
10274   if (new_wall)
10275     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10276 }
10277
10278 void CheckForDragon(int x, int y)
10279 {
10280   int i, j;
10281   boolean dragon_found = FALSE;
10282   static int xy[4][2] =
10283   {
10284     { 0, -1 },
10285     { -1, 0 },
10286     { +1, 0 },
10287     { 0, +1 }
10288   };
10289
10290   for (i = 0; i < NUM_DIRECTIONS; i++)
10291   {
10292     for (j = 0; j < 4; j++)
10293     {
10294       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10295
10296       if (IN_LEV_FIELD(xx, yy) &&
10297           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10298       {
10299         if (Feld[xx][yy] == EL_DRAGON)
10300           dragon_found = TRUE;
10301       }
10302       else
10303         break;
10304     }
10305   }
10306
10307   if (!dragon_found)
10308   {
10309     for (i = 0; i < NUM_DIRECTIONS; i++)
10310     {
10311       for (j = 0; j < 3; j++)
10312       {
10313         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10314   
10315         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10316         {
10317           Feld[xx][yy] = EL_EMPTY;
10318           TEST_DrawLevelField(xx, yy);
10319         }
10320         else
10321           break;
10322       }
10323     }
10324   }
10325 }
10326
10327 static void InitBuggyBase(int x, int y)
10328 {
10329   int element = Feld[x][y];
10330   int activating_delay = FRAMES_PER_SECOND / 4;
10331
10332   ChangeDelay[x][y] =
10333     (element == EL_SP_BUGGY_BASE ?
10334      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10335      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10336      activating_delay :
10337      element == EL_SP_BUGGY_BASE_ACTIVE ?
10338      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10339 }
10340
10341 static void WarnBuggyBase(int x, int y)
10342 {
10343   int i;
10344   static int xy[4][2] =
10345   {
10346     { 0, -1 },
10347     { -1, 0 },
10348     { +1, 0 },
10349     { 0, +1 }
10350   };
10351
10352   for (i = 0; i < NUM_DIRECTIONS; i++)
10353   {
10354     int xx = x + xy[i][0];
10355     int yy = y + xy[i][1];
10356
10357     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10358     {
10359       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10360
10361       break;
10362     }
10363   }
10364 }
10365
10366 static void InitTrap(int x, int y)
10367 {
10368   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10369 }
10370
10371 static void ActivateTrap(int x, int y)
10372 {
10373   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10374 }
10375
10376 static void ChangeActiveTrap(int x, int y)
10377 {
10378   int graphic = IMG_TRAP_ACTIVE;
10379
10380   /* if new animation frame was drawn, correct crumbled sand border */
10381   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10382     TEST_DrawLevelFieldCrumbled(x, y);
10383 }
10384
10385 static int getSpecialActionElement(int element, int number, int base_element)
10386 {
10387   return (element != EL_EMPTY ? element :
10388           number != -1 ? base_element + number - 1 :
10389           EL_EMPTY);
10390 }
10391
10392 static int getModifiedActionNumber(int value_old, int operator, int operand,
10393                                    int value_min, int value_max)
10394 {
10395   int value_new = (operator == CA_MODE_SET      ? operand :
10396                    operator == CA_MODE_ADD      ? value_old + operand :
10397                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10398                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10399                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10400                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10401                    value_old);
10402
10403   return (value_new < value_min ? value_min :
10404           value_new > value_max ? value_max :
10405           value_new);
10406 }
10407
10408 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10409 {
10410   struct ElementInfo *ei = &element_info[element];
10411   struct ElementChangeInfo *change = &ei->change_page[page];
10412   int target_element = change->target_element;
10413   int action_type = change->action_type;
10414   int action_mode = change->action_mode;
10415   int action_arg = change->action_arg;
10416   int action_element = change->action_element;
10417   int i;
10418
10419   if (!change->has_action)
10420     return;
10421
10422   /* ---------- determine action paramater values -------------------------- */
10423
10424   int level_time_value =
10425     (level.time > 0 ? TimeLeft :
10426      TimePlayed);
10427
10428   int action_arg_element_raw =
10429     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10430      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10431      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10432      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10433      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10434      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10435      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10436      EL_EMPTY);
10437   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10438
10439 #if 0
10440   if (action_arg_element_raw == EL_GROUP_START)
10441     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10442 #endif
10443
10444   int action_arg_direction =
10445     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10446      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10447      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10448      change->actual_trigger_side :
10449      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10450      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10451      MV_NONE);
10452
10453   int action_arg_number_min =
10454     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10455      CA_ARG_MIN);
10456
10457   int action_arg_number_max =
10458     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10459      action_type == CA_SET_LEVEL_GEMS ? 999 :
10460      action_type == CA_SET_LEVEL_TIME ? 9999 :
10461      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10462      action_type == CA_SET_CE_VALUE ? 9999 :
10463      action_type == CA_SET_CE_SCORE ? 9999 :
10464      CA_ARG_MAX);
10465
10466   int action_arg_number_reset =
10467     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10468      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10469      action_type == CA_SET_LEVEL_TIME ? level.time :
10470      action_type == CA_SET_LEVEL_SCORE ? 0 :
10471 #if USE_NEW_CUSTOM_VALUE
10472      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10473 #else
10474      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10475 #endif
10476      action_type == CA_SET_CE_SCORE ? 0 :
10477      0);
10478
10479   int action_arg_number =
10480     (action_arg <= CA_ARG_MAX ? action_arg :
10481      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10482      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10483      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10484      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10485      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10486      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10487 #if USE_NEW_CUSTOM_VALUE
10488      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10489 #else
10490      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10491 #endif
10492      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10493      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10494      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10495      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10496      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10497      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10498      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10499      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10500      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10501      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10502      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10503      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10504      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10505      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10506      -1);
10507
10508   int action_arg_number_old =
10509     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10510      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10511      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10512      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10513      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10514      0);
10515
10516   int action_arg_number_new =
10517     getModifiedActionNumber(action_arg_number_old,
10518                             action_mode, action_arg_number,
10519                             action_arg_number_min, action_arg_number_max);
10520
10521 #if 1
10522   int trigger_player_bits =
10523     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10524      change->actual_trigger_player_bits : change->trigger_player);
10525 #else
10526   int trigger_player_bits =
10527     (change->actual_trigger_player >= EL_PLAYER_1 &&
10528      change->actual_trigger_player <= EL_PLAYER_4 ?
10529      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10530      PLAYER_BITS_ANY);
10531 #endif
10532
10533   int action_arg_player_bits =
10534     (action_arg >= CA_ARG_PLAYER_1 &&
10535      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10536      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10537      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10538      PLAYER_BITS_ANY);
10539
10540   /* ---------- execute action  -------------------------------------------- */
10541
10542   switch (action_type)
10543   {
10544     case CA_NO_ACTION:
10545     {
10546       return;
10547     }
10548
10549     /* ---------- level actions  ------------------------------------------- */
10550
10551     case CA_RESTART_LEVEL:
10552     {
10553       game.restart_level = TRUE;
10554
10555       break;
10556     }
10557
10558     case CA_SHOW_ENVELOPE:
10559     {
10560       int element = getSpecialActionElement(action_arg_element,
10561                                             action_arg_number, EL_ENVELOPE_1);
10562
10563       if (IS_ENVELOPE(element))
10564         local_player->show_envelope = element;
10565
10566       break;
10567     }
10568
10569     case CA_SET_LEVEL_TIME:
10570     {
10571       if (level.time > 0)       /* only modify limited time value */
10572       {
10573         TimeLeft = action_arg_number_new;
10574
10575 #if 1
10576         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10577
10578         DisplayGameControlValues();
10579 #else
10580         DrawGameValue_Time(TimeLeft);
10581 #endif
10582
10583         if (!TimeLeft && setup.time_limit)
10584           for (i = 0; i < MAX_PLAYERS; i++)
10585             KillPlayer(&stored_player[i]);
10586       }
10587
10588       break;
10589     }
10590
10591     case CA_SET_LEVEL_SCORE:
10592     {
10593       local_player->score = action_arg_number_new;
10594
10595 #if 1
10596       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10597
10598       DisplayGameControlValues();
10599 #else
10600       DrawGameValue_Score(local_player->score);
10601 #endif
10602
10603       break;
10604     }
10605
10606     case CA_SET_LEVEL_GEMS:
10607     {
10608       local_player->gems_still_needed = action_arg_number_new;
10609
10610 #if 1
10611       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10612
10613       DisplayGameControlValues();
10614 #else
10615       DrawGameValue_Emeralds(local_player->gems_still_needed);
10616 #endif
10617
10618       break;
10619     }
10620
10621 #if !USE_PLAYER_GRAVITY
10622     case CA_SET_LEVEL_GRAVITY:
10623     {
10624       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10625                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10626                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10627                       game.gravity);
10628       break;
10629     }
10630 #endif
10631
10632     case CA_SET_LEVEL_WIND:
10633     {
10634       game.wind_direction = action_arg_direction;
10635
10636       break;
10637     }
10638
10639     case CA_SET_LEVEL_RANDOM_SEED:
10640     {
10641 #if 1
10642       /* ensure that setting a new random seed while playing is predictable */
10643       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10644 #else
10645       InitRND(action_arg_number_new);
10646 #endif
10647
10648 #if 0
10649       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10650 #endif
10651
10652 #if 0
10653       {
10654         int i;
10655
10656         printf("::: ");
10657         for (i = 0; i < 9; i++)
10658           printf("%d, ", RND(2));
10659         printf("\n");
10660       }
10661 #endif
10662
10663       break;
10664     }
10665
10666     /* ---------- player actions  ------------------------------------------ */
10667
10668     case CA_MOVE_PLAYER:
10669     {
10670       /* automatically move to the next field in specified direction */
10671       for (i = 0; i < MAX_PLAYERS; i++)
10672         if (trigger_player_bits & (1 << i))
10673           stored_player[i].programmed_action = action_arg_direction;
10674
10675       break;
10676     }
10677
10678     case CA_EXIT_PLAYER:
10679     {
10680       for (i = 0; i < MAX_PLAYERS; i++)
10681         if (action_arg_player_bits & (1 << i))
10682           PlayerWins(&stored_player[i]);
10683
10684       break;
10685     }
10686
10687     case CA_KILL_PLAYER:
10688     {
10689       for (i = 0; i < MAX_PLAYERS; i++)
10690         if (action_arg_player_bits & (1 << i))
10691           KillPlayer(&stored_player[i]);
10692
10693       break;
10694     }
10695
10696     case CA_SET_PLAYER_KEYS:
10697     {
10698       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10699       int element = getSpecialActionElement(action_arg_element,
10700                                             action_arg_number, EL_KEY_1);
10701
10702       if (IS_KEY(element))
10703       {
10704         for (i = 0; i < MAX_PLAYERS; i++)
10705         {
10706           if (trigger_player_bits & (1 << i))
10707           {
10708             stored_player[i].key[KEY_NR(element)] = key_state;
10709
10710             DrawGameDoorValues();
10711           }
10712         }
10713       }
10714
10715       break;
10716     }
10717
10718     case CA_SET_PLAYER_SPEED:
10719     {
10720 #if 0
10721       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10722 #endif
10723
10724       for (i = 0; i < MAX_PLAYERS; i++)
10725       {
10726         if (trigger_player_bits & (1 << i))
10727         {
10728           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10729
10730           if (action_arg == CA_ARG_SPEED_FASTER &&
10731               stored_player[i].cannot_move)
10732           {
10733             action_arg_number = STEPSIZE_VERY_SLOW;
10734           }
10735           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10736                    action_arg == CA_ARG_SPEED_FASTER)
10737           {
10738             action_arg_number = 2;
10739             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10740                            CA_MODE_MULTIPLY);
10741           }
10742           else if (action_arg == CA_ARG_NUMBER_RESET)
10743           {
10744             action_arg_number = level.initial_player_stepsize[i];
10745           }
10746
10747           move_stepsize =
10748             getModifiedActionNumber(move_stepsize,
10749                                     action_mode,
10750                                     action_arg_number,
10751                                     action_arg_number_min,
10752                                     action_arg_number_max);
10753
10754           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10755         }
10756       }
10757
10758       break;
10759     }
10760
10761     case CA_SET_PLAYER_SHIELD:
10762     {
10763       for (i = 0; i < MAX_PLAYERS; i++)
10764       {
10765         if (trigger_player_bits & (1 << i))
10766         {
10767           if (action_arg == CA_ARG_SHIELD_OFF)
10768           {
10769             stored_player[i].shield_normal_time_left = 0;
10770             stored_player[i].shield_deadly_time_left = 0;
10771           }
10772           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10773           {
10774             stored_player[i].shield_normal_time_left = 999999;
10775           }
10776           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10777           {
10778             stored_player[i].shield_normal_time_left = 999999;
10779             stored_player[i].shield_deadly_time_left = 999999;
10780           }
10781         }
10782       }
10783
10784       break;
10785     }
10786
10787 #if USE_PLAYER_GRAVITY
10788     case CA_SET_PLAYER_GRAVITY:
10789     {
10790       for (i = 0; i < MAX_PLAYERS; i++)
10791       {
10792         if (trigger_player_bits & (1 << i))
10793         {
10794           stored_player[i].gravity =
10795             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10796              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10797              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10798              stored_player[i].gravity);
10799         }
10800       }
10801
10802       break;
10803     }
10804 #endif
10805
10806     case CA_SET_PLAYER_ARTWORK:
10807     {
10808       for (i = 0; i < MAX_PLAYERS; i++)
10809       {
10810         if (trigger_player_bits & (1 << i))
10811         {
10812           int artwork_element = action_arg_element;
10813
10814           if (action_arg == CA_ARG_ELEMENT_RESET)
10815             artwork_element =
10816               (level.use_artwork_element[i] ? level.artwork_element[i] :
10817                stored_player[i].element_nr);
10818
10819 #if USE_GFX_RESET_PLAYER_ARTWORK
10820           if (stored_player[i].artwork_element != artwork_element)
10821             stored_player[i].Frame = 0;
10822 #endif
10823
10824           stored_player[i].artwork_element = artwork_element;
10825
10826           SetPlayerWaiting(&stored_player[i], FALSE);
10827
10828           /* set number of special actions for bored and sleeping animation */
10829           stored_player[i].num_special_action_bored =
10830             get_num_special_action(artwork_element,
10831                                    ACTION_BORING_1, ACTION_BORING_LAST);
10832           stored_player[i].num_special_action_sleeping =
10833             get_num_special_action(artwork_element,
10834                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10835         }
10836       }
10837
10838       break;
10839     }
10840
10841     case CA_SET_PLAYER_INVENTORY:
10842     {
10843       for (i = 0; i < MAX_PLAYERS; i++)
10844       {
10845         struct PlayerInfo *player = &stored_player[i];
10846         int j, k;
10847
10848         if (trigger_player_bits & (1 << i))
10849         {
10850           int inventory_element = action_arg_element;
10851
10852           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10853               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10854               action_arg == CA_ARG_ELEMENT_ACTION)
10855           {
10856             int element = inventory_element;
10857             int collect_count = element_info[element].collect_count_initial;
10858
10859             if (!IS_CUSTOM_ELEMENT(element))
10860               collect_count = 1;
10861
10862             if (collect_count == 0)
10863               player->inventory_infinite_element = element;
10864             else
10865               for (k = 0; k < collect_count; k++)
10866                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10867                   player->inventory_element[player->inventory_size++] =
10868                     element;
10869           }
10870           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10871                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10872                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10873           {
10874             if (player->inventory_infinite_element != EL_UNDEFINED &&
10875                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10876                                      action_arg_element_raw))
10877               player->inventory_infinite_element = EL_UNDEFINED;
10878
10879             for (k = 0, j = 0; j < player->inventory_size; j++)
10880             {
10881               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10882                                         action_arg_element_raw))
10883                 player->inventory_element[k++] = player->inventory_element[j];
10884             }
10885
10886             player->inventory_size = k;
10887           }
10888           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10889           {
10890             if (player->inventory_size > 0)
10891             {
10892               for (j = 0; j < player->inventory_size - 1; j++)
10893                 player->inventory_element[j] = player->inventory_element[j + 1];
10894
10895               player->inventory_size--;
10896             }
10897           }
10898           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10899           {
10900             if (player->inventory_size > 0)
10901               player->inventory_size--;
10902           }
10903           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10904           {
10905             player->inventory_infinite_element = EL_UNDEFINED;
10906             player->inventory_size = 0;
10907           }
10908           else if (action_arg == CA_ARG_INVENTORY_RESET)
10909           {
10910             player->inventory_infinite_element = EL_UNDEFINED;
10911             player->inventory_size = 0;
10912
10913             if (level.use_initial_inventory[i])
10914             {
10915               for (j = 0; j < level.initial_inventory_size[i]; j++)
10916               {
10917                 int element = level.initial_inventory_content[i][j];
10918                 int collect_count = element_info[element].collect_count_initial;
10919
10920                 if (!IS_CUSTOM_ELEMENT(element))
10921                   collect_count = 1;
10922
10923                 if (collect_count == 0)
10924                   player->inventory_infinite_element = element;
10925                 else
10926                   for (k = 0; k < collect_count; k++)
10927                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10928                       player->inventory_element[player->inventory_size++] =
10929                         element;
10930               }
10931             }
10932           }
10933         }
10934       }
10935
10936       break;
10937     }
10938
10939     /* ---------- CE actions  ---------------------------------------------- */
10940
10941     case CA_SET_CE_VALUE:
10942     {
10943 #if USE_NEW_CUSTOM_VALUE
10944       int last_ce_value = CustomValue[x][y];
10945
10946       CustomValue[x][y] = action_arg_number_new;
10947
10948       if (CustomValue[x][y] != last_ce_value)
10949       {
10950         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10951         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10952
10953         if (CustomValue[x][y] == 0)
10954         {
10955           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10956           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10957         }
10958       }
10959 #endif
10960
10961       break;
10962     }
10963
10964     case CA_SET_CE_SCORE:
10965     {
10966 #if USE_NEW_CUSTOM_VALUE
10967       int last_ce_score = ei->collect_score;
10968
10969       ei->collect_score = action_arg_number_new;
10970
10971       if (ei->collect_score != last_ce_score)
10972       {
10973         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10974         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10975
10976         if (ei->collect_score == 0)
10977         {
10978           int xx, yy;
10979
10980           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10981           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10982
10983           /*
10984             This is a very special case that seems to be a mixture between
10985             CheckElementChange() and CheckTriggeredElementChange(): while
10986             the first one only affects single elements that are triggered
10987             directly, the second one affects multiple elements in the playfield
10988             that are triggered indirectly by another element. This is a third
10989             case: Changing the CE score always affects multiple identical CEs,
10990             so every affected CE must be checked, not only the single CE for
10991             which the CE score was changed in the first place (as every instance
10992             of that CE shares the same CE score, and therefore also can change)!
10993           */
10994           SCAN_PLAYFIELD(xx, yy)
10995           {
10996             if (Feld[xx][yy] == element)
10997               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10998                                  CE_SCORE_GETS_ZERO);
10999           }
11000         }
11001       }
11002 #endif
11003
11004       break;
11005     }
11006
11007     case CA_SET_CE_ARTWORK:
11008     {
11009       int artwork_element = action_arg_element;
11010       boolean reset_frame = FALSE;
11011       int xx, yy;
11012
11013       if (action_arg == CA_ARG_ELEMENT_RESET)
11014         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11015                            element);
11016
11017       if (ei->gfx_element != artwork_element)
11018         reset_frame = TRUE;
11019
11020       ei->gfx_element = artwork_element;
11021
11022       SCAN_PLAYFIELD(xx, yy)
11023       {
11024         if (Feld[xx][yy] == element)
11025         {
11026           if (reset_frame)
11027           {
11028             ResetGfxAnimation(xx, yy);
11029             ResetRandomAnimationValue(xx, yy);
11030           }
11031
11032           TEST_DrawLevelField(xx, yy);
11033         }
11034       }
11035
11036       break;
11037     }
11038
11039     /* ---------- engine actions  ------------------------------------------ */
11040
11041     case CA_SET_ENGINE_SCAN_MODE:
11042     {
11043       InitPlayfieldScanMode(action_arg);
11044
11045       break;
11046     }
11047
11048     default:
11049       break;
11050   }
11051 }
11052
11053 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11054 {
11055   int old_element = Feld[x][y];
11056   int new_element = GetElementFromGroupElement(element);
11057   int previous_move_direction = MovDir[x][y];
11058 #if USE_NEW_CUSTOM_VALUE
11059   int last_ce_value = CustomValue[x][y];
11060 #endif
11061   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11062   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11063   boolean add_player_onto_element = (new_element_is_player &&
11064 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11065                                      /* this breaks SnakeBite when a snake is
11066                                         halfway through a door that closes */
11067                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11068                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11069 #endif
11070                                      IS_WALKABLE(old_element));
11071
11072 #if 0
11073   /* check if element under the player changes from accessible to unaccessible
11074      (needed for special case of dropping element which then changes) */
11075   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11076       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11077   {
11078     Bang(x, y);
11079
11080     return;
11081   }
11082 #endif
11083
11084   if (!add_player_onto_element)
11085   {
11086     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11087       RemoveMovingField(x, y);
11088     else
11089       RemoveField(x, y);
11090
11091     Feld[x][y] = new_element;
11092
11093 #if !USE_GFX_RESET_GFX_ANIMATION
11094     ResetGfxAnimation(x, y);
11095     ResetRandomAnimationValue(x, y);
11096 #endif
11097
11098     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11099       MovDir[x][y] = previous_move_direction;
11100
11101 #if USE_NEW_CUSTOM_VALUE
11102     if (element_info[new_element].use_last_ce_value)
11103       CustomValue[x][y] = last_ce_value;
11104 #endif
11105
11106     InitField_WithBug1(x, y, FALSE);
11107
11108     new_element = Feld[x][y];   /* element may have changed */
11109
11110 #if USE_GFX_RESET_GFX_ANIMATION
11111     ResetGfxAnimation(x, y);
11112     ResetRandomAnimationValue(x, y);
11113 #endif
11114
11115     TEST_DrawLevelField(x, y);
11116
11117     if (GFX_CRUMBLED(new_element))
11118       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11119   }
11120
11121 #if 1
11122   /* check if element under the player changes from accessible to unaccessible
11123      (needed for special case of dropping element which then changes) */
11124   /* (must be checked after creating new element for walkable group elements) */
11125 #if USE_FIX_KILLED_BY_NON_WALKABLE
11126   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11127       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11128   {
11129     Bang(x, y);
11130
11131     return;
11132   }
11133 #else
11134   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11135       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11136   {
11137     Bang(x, y);
11138
11139     return;
11140   }
11141 #endif
11142 #endif
11143
11144   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11145   if (new_element_is_player)
11146     RelocatePlayer(x, y, new_element);
11147
11148   if (is_change)
11149     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11150
11151   TestIfBadThingTouchesPlayer(x, y);
11152   TestIfPlayerTouchesCustomElement(x, y);
11153   TestIfElementTouchesCustomElement(x, y);
11154 }
11155
11156 static void CreateField(int x, int y, int element)
11157 {
11158   CreateFieldExt(x, y, element, FALSE);
11159 }
11160
11161 static void CreateElementFromChange(int x, int y, int element)
11162 {
11163   element = GET_VALID_RUNTIME_ELEMENT(element);
11164
11165 #if USE_STOP_CHANGED_ELEMENTS
11166   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11167   {
11168     int old_element = Feld[x][y];
11169
11170     /* prevent changed element from moving in same engine frame
11171        unless both old and new element can either fall or move */
11172     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11173         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11174       Stop[x][y] = TRUE;
11175   }
11176 #endif
11177
11178   CreateFieldExt(x, y, element, TRUE);
11179 }
11180
11181 static boolean ChangeElement(int x, int y, int element, int page)
11182 {
11183   struct ElementInfo *ei = &element_info[element];
11184   struct ElementChangeInfo *change = &ei->change_page[page];
11185   int ce_value = CustomValue[x][y];
11186   int ce_score = ei->collect_score;
11187   int target_element;
11188   int old_element = Feld[x][y];
11189
11190   /* always use default change event to prevent running into a loop */
11191   if (ChangeEvent[x][y] == -1)
11192     ChangeEvent[x][y] = CE_DELAY;
11193
11194   if (ChangeEvent[x][y] == CE_DELAY)
11195   {
11196     /* reset actual trigger element, trigger player and action element */
11197     change->actual_trigger_element = EL_EMPTY;
11198     change->actual_trigger_player = EL_EMPTY;
11199     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11200     change->actual_trigger_side = CH_SIDE_NONE;
11201     change->actual_trigger_ce_value = 0;
11202     change->actual_trigger_ce_score = 0;
11203   }
11204
11205   /* do not change elements more than a specified maximum number of changes */
11206   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11207     return FALSE;
11208
11209   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11210
11211   if (change->explode)
11212   {
11213     Bang(x, y);
11214
11215     return TRUE;
11216   }
11217
11218   if (change->use_target_content)
11219   {
11220     boolean complete_replace = TRUE;
11221     boolean can_replace[3][3];
11222     int xx, yy;
11223
11224     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11225     {
11226       boolean is_empty;
11227       boolean is_walkable;
11228       boolean is_diggable;
11229       boolean is_collectible;
11230       boolean is_removable;
11231       boolean is_destructible;
11232       int ex = x + xx - 1;
11233       int ey = y + yy - 1;
11234       int content_element = change->target_content.e[xx][yy];
11235       int e;
11236
11237       can_replace[xx][yy] = TRUE;
11238
11239       if (ex == x && ey == y)   /* do not check changing element itself */
11240         continue;
11241
11242       if (content_element == EL_EMPTY_SPACE)
11243       {
11244         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11245
11246         continue;
11247       }
11248
11249       if (!IN_LEV_FIELD(ex, ey))
11250       {
11251         can_replace[xx][yy] = FALSE;
11252         complete_replace = FALSE;
11253
11254         continue;
11255       }
11256
11257       e = Feld[ex][ey];
11258
11259       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11260         e = MovingOrBlocked2Element(ex, ey);
11261
11262       is_empty = (IS_FREE(ex, ey) ||
11263                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11264
11265       is_walkable     = (is_empty || IS_WALKABLE(e));
11266       is_diggable     = (is_empty || IS_DIGGABLE(e));
11267       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11268       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11269       is_removable    = (is_diggable || is_collectible);
11270
11271       can_replace[xx][yy] =
11272         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11273           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11274           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11275           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11276           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11277           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11278          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11279
11280       if (!can_replace[xx][yy])
11281         complete_replace = FALSE;
11282     }
11283
11284     if (!change->only_if_complete || complete_replace)
11285     {
11286       boolean something_has_changed = FALSE;
11287
11288       if (change->only_if_complete && change->use_random_replace &&
11289           RND(100) < change->random_percentage)
11290         return FALSE;
11291
11292       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11293       {
11294         int ex = x + xx - 1;
11295         int ey = y + yy - 1;
11296         int content_element;
11297
11298         if (can_replace[xx][yy] && (!change->use_random_replace ||
11299                                     RND(100) < change->random_percentage))
11300         {
11301           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11302             RemoveMovingField(ex, ey);
11303
11304           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11305
11306           content_element = change->target_content.e[xx][yy];
11307           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11308                                               ce_value, ce_score);
11309
11310           CreateElementFromChange(ex, ey, target_element);
11311
11312           something_has_changed = TRUE;
11313
11314           /* for symmetry reasons, freeze newly created border elements */
11315           if (ex != x || ey != y)
11316             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11317         }
11318       }
11319
11320       if (something_has_changed)
11321       {
11322         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11323         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11324       }
11325     }
11326   }
11327   else
11328   {
11329     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11330                                         ce_value, ce_score);
11331
11332     if (element == EL_DIAGONAL_GROWING ||
11333         element == EL_DIAGONAL_SHRINKING)
11334     {
11335       target_element = Store[x][y];
11336
11337       Store[x][y] = EL_EMPTY;
11338     }
11339
11340     CreateElementFromChange(x, y, target_element);
11341
11342     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11343     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11344   }
11345
11346   /* this uses direct change before indirect change */
11347   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11348
11349   return TRUE;
11350 }
11351
11352 #if USE_NEW_DELAYED_ACTION
11353
11354 static void HandleElementChange(int x, int y, int page)
11355 {
11356   int element = MovingOrBlocked2Element(x, y);
11357   struct ElementInfo *ei = &element_info[element];
11358   struct ElementChangeInfo *change = &ei->change_page[page];
11359   boolean handle_action_before_change = FALSE;
11360
11361 #ifdef DEBUG
11362   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11363       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11364   {
11365     printf("\n\n");
11366     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11367            x, y, element, element_info[element].token_name);
11368     printf("HandleElementChange(): This should never happen!\n");
11369     printf("\n\n");
11370   }
11371 #endif
11372
11373   /* this can happen with classic bombs on walkable, changing elements */
11374   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11375   {
11376 #if 0
11377     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11378       ChangeDelay[x][y] = 0;
11379 #endif
11380
11381     return;
11382   }
11383
11384   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11385   {
11386     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11387
11388     if (change->can_change)
11389     {
11390 #if 1
11391       /* !!! not clear why graphic animation should be reset at all here !!! */
11392       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11393 #if USE_GFX_RESET_WHEN_NOT_MOVING
11394       /* when a custom element is about to change (for example by change delay),
11395          do not reset graphic animation when the custom element is moving */
11396       if (!IS_MOVING(x, y))
11397 #endif
11398       {
11399         ResetGfxAnimation(x, y);
11400         ResetRandomAnimationValue(x, y);
11401       }
11402 #endif
11403
11404       if (change->pre_change_function)
11405         change->pre_change_function(x, y);
11406     }
11407   }
11408
11409   ChangeDelay[x][y]--;
11410
11411   if (ChangeDelay[x][y] != 0)           /* continue element change */
11412   {
11413     if (change->can_change)
11414     {
11415       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11416
11417       if (IS_ANIMATED(graphic))
11418         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11419
11420       if (change->change_function)
11421         change->change_function(x, y);
11422     }
11423   }
11424   else                                  /* finish element change */
11425   {
11426     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11427     {
11428       page = ChangePage[x][y];
11429       ChangePage[x][y] = -1;
11430
11431       change = &ei->change_page[page];
11432     }
11433
11434     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11435     {
11436       ChangeDelay[x][y] = 1;            /* try change after next move step */
11437       ChangePage[x][y] = page;          /* remember page to use for change */
11438
11439       return;
11440     }
11441
11442 #if 1
11443     /* special case: set new level random seed before changing element */
11444     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11445       handle_action_before_change = TRUE;
11446
11447     if (change->has_action && handle_action_before_change)
11448       ExecuteCustomElementAction(x, y, element, page);
11449 #endif
11450
11451     if (change->can_change)
11452     {
11453       if (ChangeElement(x, y, element, page))
11454       {
11455         if (change->post_change_function)
11456           change->post_change_function(x, y);
11457       }
11458     }
11459
11460     if (change->has_action && !handle_action_before_change)
11461       ExecuteCustomElementAction(x, y, element, page);
11462   }
11463 }
11464
11465 #else
11466
11467 static void HandleElementChange(int x, int y, int page)
11468 {
11469   int element = MovingOrBlocked2Element(x, y);
11470   struct ElementInfo *ei = &element_info[element];
11471   struct ElementChangeInfo *change = &ei->change_page[page];
11472
11473 #ifdef DEBUG
11474   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11475   {
11476     printf("\n\n");
11477     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11478            x, y, element, element_info[element].token_name);
11479     printf("HandleElementChange(): This should never happen!\n");
11480     printf("\n\n");
11481   }
11482 #endif
11483
11484   /* this can happen with classic bombs on walkable, changing elements */
11485   if (!CAN_CHANGE(element))
11486   {
11487 #if 0
11488     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11489       ChangeDelay[x][y] = 0;
11490 #endif
11491
11492     return;
11493   }
11494
11495   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11496   {
11497     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11498
11499     ResetGfxAnimation(x, y);
11500     ResetRandomAnimationValue(x, y);
11501
11502     if (change->pre_change_function)
11503       change->pre_change_function(x, y);
11504   }
11505
11506   ChangeDelay[x][y]--;
11507
11508   if (ChangeDelay[x][y] != 0)           /* continue element change */
11509   {
11510     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11511
11512     if (IS_ANIMATED(graphic))
11513       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11514
11515     if (change->change_function)
11516       change->change_function(x, y);
11517   }
11518   else                                  /* finish element change */
11519   {
11520     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11521     {
11522       page = ChangePage[x][y];
11523       ChangePage[x][y] = -1;
11524
11525       change = &ei->change_page[page];
11526     }
11527
11528     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11529     {
11530       ChangeDelay[x][y] = 1;            /* try change after next move step */
11531       ChangePage[x][y] = page;          /* remember page to use for change */
11532
11533       return;
11534     }
11535
11536     if (ChangeElement(x, y, element, page))
11537     {
11538       if (change->post_change_function)
11539         change->post_change_function(x, y);
11540     }
11541   }
11542 }
11543
11544 #endif
11545
11546 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11547                                               int trigger_element,
11548                                               int trigger_event,
11549                                               int trigger_player,
11550                                               int trigger_side,
11551                                               int trigger_page)
11552 {
11553   boolean change_done_any = FALSE;
11554   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11555   int i;
11556
11557   if (!(trigger_events[trigger_element][trigger_event]))
11558     return FALSE;
11559
11560 #if 0
11561   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11562          trigger_event, recursion_loop_depth, recursion_loop_detected,
11563          recursion_loop_element, EL_NAME(recursion_loop_element));
11564 #endif
11565
11566   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11567
11568   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11569   {
11570     int element = EL_CUSTOM_START + i;
11571     boolean change_done = FALSE;
11572     int p;
11573
11574     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11575         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11576       continue;
11577
11578     for (p = 0; p < element_info[element].num_change_pages; p++)
11579     {
11580       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11581
11582       if (change->can_change_or_has_action &&
11583           change->has_event[trigger_event] &&
11584           change->trigger_side & trigger_side &&
11585           change->trigger_player & trigger_player &&
11586           change->trigger_page & trigger_page_bits &&
11587           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11588       {
11589         change->actual_trigger_element = trigger_element;
11590         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11591         change->actual_trigger_player_bits = trigger_player;
11592         change->actual_trigger_side = trigger_side;
11593         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11594         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11595
11596 #if 0
11597         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11598                element, EL_NAME(element), p);
11599 #endif
11600
11601         if ((change->can_change && !change_done) || change->has_action)
11602         {
11603           int x, y;
11604
11605           SCAN_PLAYFIELD(x, y)
11606           {
11607             if (Feld[x][y] == element)
11608             {
11609               if (change->can_change && !change_done)
11610               {
11611 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11612                 /* if element already changed in this frame, not only prevent
11613                    another element change (checked in ChangeElement()), but
11614                    also prevent additional element actions for this element */
11615
11616                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11617                     !level.use_action_after_change_bug)
11618                   continue;
11619 #endif
11620
11621 #if 0
11622                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11623                        element, EL_NAME(element), p);
11624 #endif
11625
11626                 ChangeDelay[x][y] = 1;
11627                 ChangeEvent[x][y] = trigger_event;
11628
11629                 HandleElementChange(x, y, p);
11630               }
11631 #if USE_NEW_DELAYED_ACTION
11632               else if (change->has_action)
11633               {
11634 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11635                 /* if element already changed in this frame, not only prevent
11636                    another element change (checked in ChangeElement()), but
11637                    also prevent additional element actions for this element */
11638
11639                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11640                     !level.use_action_after_change_bug)
11641                   continue;
11642 #endif
11643
11644
11645 #if 0
11646                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11647                        element, EL_NAME(element), p);
11648 #endif
11649
11650                 ExecuteCustomElementAction(x, y, element, p);
11651                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11652               }
11653 #else
11654               if (change->has_action)
11655               {
11656                 ExecuteCustomElementAction(x, y, element, p);
11657                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11658               }
11659 #endif
11660             }
11661           }
11662
11663           if (change->can_change)
11664           {
11665             change_done = TRUE;
11666             change_done_any = TRUE;
11667
11668 #if 0
11669             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11670                    element, EL_NAME(element), p);
11671 #endif
11672
11673           }
11674         }
11675       }
11676     }
11677   }
11678
11679   RECURSION_LOOP_DETECTION_END();
11680
11681   return change_done_any;
11682 }
11683
11684 static boolean CheckElementChangeExt(int x, int y,
11685                                      int element,
11686                                      int trigger_element,
11687                                      int trigger_event,
11688                                      int trigger_player,
11689                                      int trigger_side)
11690 {
11691   boolean change_done = FALSE;
11692   int p;
11693
11694   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11695       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11696     return FALSE;
11697
11698   if (Feld[x][y] == EL_BLOCKED)
11699   {
11700     Blocked2Moving(x, y, &x, &y);
11701     element = Feld[x][y];
11702   }
11703
11704 #if 0
11705   /* check if element has already changed */
11706   if (Feld[x][y] != element)
11707     return FALSE;
11708 #else
11709   /* check if element has already changed or is about to change after moving */
11710   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11711        Feld[x][y] != element) ||
11712
11713       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11714        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11715         ChangePage[x][y] != -1)))
11716     return FALSE;
11717 #endif
11718
11719 #if 0
11720   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11721          trigger_event, recursion_loop_depth, recursion_loop_detected,
11722          recursion_loop_element, EL_NAME(recursion_loop_element));
11723 #endif
11724
11725   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11726
11727 #if 0
11728   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11729 #endif
11730
11731   for (p = 0; p < element_info[element].num_change_pages; p++)
11732   {
11733     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11734
11735     /* check trigger element for all events where the element that is checked
11736        for changing interacts with a directly adjacent element -- this is
11737        different to element changes that affect other elements to change on the
11738        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11739     boolean check_trigger_element =
11740       (trigger_event == CE_TOUCHING_X ||
11741        trigger_event == CE_HITTING_X ||
11742        trigger_event == CE_HIT_BY_X ||
11743 #if 1
11744        /* this one was forgotten until 3.2.3 */
11745        trigger_event == CE_DIGGING_X);
11746 #endif
11747
11748     if (change->can_change_or_has_action &&
11749         change->has_event[trigger_event] &&
11750         change->trigger_side & trigger_side &&
11751         change->trigger_player & trigger_player &&
11752         (!check_trigger_element ||
11753          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11754     {
11755       change->actual_trigger_element = trigger_element;
11756       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11757       change->actual_trigger_player_bits = trigger_player;
11758       change->actual_trigger_side = trigger_side;
11759       change->actual_trigger_ce_value = CustomValue[x][y];
11760       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11761
11762       /* special case: trigger element not at (x,y) position for some events */
11763       if (check_trigger_element)
11764       {
11765         static struct
11766         {
11767           int dx, dy;
11768         } move_xy[] =
11769           {
11770             {  0,  0 },
11771             { -1,  0 },
11772             { +1,  0 },
11773             {  0,  0 },
11774             {  0, -1 },
11775             {  0,  0 }, { 0, 0 }, { 0, 0 },
11776             {  0, +1 }
11777           };
11778
11779         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11780         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11781
11782         change->actual_trigger_ce_value = CustomValue[xx][yy];
11783         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11784       }
11785
11786       if (change->can_change && !change_done)
11787       {
11788         ChangeDelay[x][y] = 1;
11789         ChangeEvent[x][y] = trigger_event;
11790
11791         HandleElementChange(x, y, p);
11792
11793         change_done = TRUE;
11794       }
11795 #if USE_NEW_DELAYED_ACTION
11796       else if (change->has_action)
11797       {
11798         ExecuteCustomElementAction(x, y, element, p);
11799         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11800       }
11801 #else
11802       if (change->has_action)
11803       {
11804         ExecuteCustomElementAction(x, y, element, p);
11805         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11806       }
11807 #endif
11808     }
11809   }
11810
11811   RECURSION_LOOP_DETECTION_END();
11812
11813   return change_done;
11814 }
11815
11816 static void PlayPlayerSound(struct PlayerInfo *player)
11817 {
11818   int jx = player->jx, jy = player->jy;
11819   int sound_element = player->artwork_element;
11820   int last_action = player->last_action_waiting;
11821   int action = player->action_waiting;
11822
11823   if (player->is_waiting)
11824   {
11825     if (action != last_action)
11826       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11827     else
11828       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11829   }
11830   else
11831   {
11832     if (action != last_action)
11833       StopSound(element_info[sound_element].sound[last_action]);
11834
11835     if (last_action == ACTION_SLEEPING)
11836       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11837   }
11838 }
11839
11840 static void PlayAllPlayersSound()
11841 {
11842   int i;
11843
11844   for (i = 0; i < MAX_PLAYERS; i++)
11845     if (stored_player[i].active)
11846       PlayPlayerSound(&stored_player[i]);
11847 }
11848
11849 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11850 {
11851   boolean last_waiting = player->is_waiting;
11852   int move_dir = player->MovDir;
11853
11854   player->dir_waiting = move_dir;
11855   player->last_action_waiting = player->action_waiting;
11856
11857   if (is_waiting)
11858   {
11859     if (!last_waiting)          /* not waiting -> waiting */
11860     {
11861       player->is_waiting = TRUE;
11862
11863       player->frame_counter_bored =
11864         FrameCounter +
11865         game.player_boring_delay_fixed +
11866         GetSimpleRandom(game.player_boring_delay_random);
11867       player->frame_counter_sleeping =
11868         FrameCounter +
11869         game.player_sleeping_delay_fixed +
11870         GetSimpleRandom(game.player_sleeping_delay_random);
11871
11872       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11873     }
11874
11875     if (game.player_sleeping_delay_fixed +
11876         game.player_sleeping_delay_random > 0 &&
11877         player->anim_delay_counter == 0 &&
11878         player->post_delay_counter == 0 &&
11879         FrameCounter >= player->frame_counter_sleeping)
11880       player->is_sleeping = TRUE;
11881     else if (game.player_boring_delay_fixed +
11882              game.player_boring_delay_random > 0 &&
11883              FrameCounter >= player->frame_counter_bored)
11884       player->is_bored = TRUE;
11885
11886     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11887                               player->is_bored ? ACTION_BORING :
11888                               ACTION_WAITING);
11889
11890     if (player->is_sleeping && player->use_murphy)
11891     {
11892       /* special case for sleeping Murphy when leaning against non-free tile */
11893
11894       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11895           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11896            !IS_MOVING(player->jx - 1, player->jy)))
11897         move_dir = MV_LEFT;
11898       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11899                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11900                 !IS_MOVING(player->jx + 1, player->jy)))
11901         move_dir = MV_RIGHT;
11902       else
11903         player->is_sleeping = FALSE;
11904
11905       player->dir_waiting = move_dir;
11906     }
11907
11908     if (player->is_sleeping)
11909     {
11910       if (player->num_special_action_sleeping > 0)
11911       {
11912         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11913         {
11914           int last_special_action = player->special_action_sleeping;
11915           int num_special_action = player->num_special_action_sleeping;
11916           int special_action =
11917             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11918              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11919              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11920              last_special_action + 1 : ACTION_SLEEPING);
11921           int special_graphic =
11922             el_act_dir2img(player->artwork_element, special_action, move_dir);
11923
11924           player->anim_delay_counter =
11925             graphic_info[special_graphic].anim_delay_fixed +
11926             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11927           player->post_delay_counter =
11928             graphic_info[special_graphic].post_delay_fixed +
11929             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11930
11931           player->special_action_sleeping = special_action;
11932         }
11933
11934         if (player->anim_delay_counter > 0)
11935         {
11936           player->action_waiting = player->special_action_sleeping;
11937           player->anim_delay_counter--;
11938         }
11939         else if (player->post_delay_counter > 0)
11940         {
11941           player->post_delay_counter--;
11942         }
11943       }
11944     }
11945     else if (player->is_bored)
11946     {
11947       if (player->num_special_action_bored > 0)
11948       {
11949         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11950         {
11951           int special_action =
11952             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11953           int special_graphic =
11954             el_act_dir2img(player->artwork_element, special_action, move_dir);
11955
11956           player->anim_delay_counter =
11957             graphic_info[special_graphic].anim_delay_fixed +
11958             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11959           player->post_delay_counter =
11960             graphic_info[special_graphic].post_delay_fixed +
11961             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11962
11963           player->special_action_bored = special_action;
11964         }
11965
11966         if (player->anim_delay_counter > 0)
11967         {
11968           player->action_waiting = player->special_action_bored;
11969           player->anim_delay_counter--;
11970         }
11971         else if (player->post_delay_counter > 0)
11972         {
11973           player->post_delay_counter--;
11974         }
11975       }
11976     }
11977   }
11978   else if (last_waiting)        /* waiting -> not waiting */
11979   {
11980     player->is_waiting = FALSE;
11981     player->is_bored = FALSE;
11982     player->is_sleeping = FALSE;
11983
11984     player->frame_counter_bored = -1;
11985     player->frame_counter_sleeping = -1;
11986
11987     player->anim_delay_counter = 0;
11988     player->post_delay_counter = 0;
11989
11990     player->dir_waiting = player->MovDir;
11991     player->action_waiting = ACTION_DEFAULT;
11992
11993     player->special_action_bored = ACTION_DEFAULT;
11994     player->special_action_sleeping = ACTION_DEFAULT;
11995   }
11996 }
11997
11998 static void CheckSingleStepMode(struct PlayerInfo *player)
11999 {
12000   if (tape.single_step && tape.recording && !tape.pausing)
12001   {
12002     /* as it is called "single step mode", just return to pause mode when the
12003        player stopped moving after one tile (or never starts moving at all) */
12004     if (!player->is_moving && !player->is_pushing)
12005     {
12006       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12007       SnapField(player, 0, 0);                  /* stop snapping */
12008     }
12009   }
12010 }
12011
12012 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12013 {
12014   int left      = player_action & JOY_LEFT;
12015   int right     = player_action & JOY_RIGHT;
12016   int up        = player_action & JOY_UP;
12017   int down      = player_action & JOY_DOWN;
12018   int button1   = player_action & JOY_BUTTON_1;
12019   int button2   = player_action & JOY_BUTTON_2;
12020   int dx        = (left ? -1 : right ? 1 : 0);
12021   int dy        = (up   ? -1 : down  ? 1 : 0);
12022
12023   if (!player->active || tape.pausing)
12024     return 0;
12025
12026   if (player_action)
12027   {
12028     if (button1)
12029       SnapField(player, dx, dy);
12030     else
12031     {
12032       if (button2)
12033         DropElement(player);
12034
12035       MovePlayer(player, dx, dy);
12036     }
12037
12038     CheckSingleStepMode(player);
12039
12040     SetPlayerWaiting(player, FALSE);
12041
12042     return player_action;
12043   }
12044   else
12045   {
12046     /* no actions for this player (no input at player's configured device) */
12047
12048     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12049     SnapField(player, 0, 0);
12050     CheckGravityMovementWhenNotMoving(player);
12051
12052     if (player->MovPos == 0)
12053       SetPlayerWaiting(player, TRUE);
12054
12055     if (player->MovPos == 0)    /* needed for tape.playing */
12056       player->is_moving = FALSE;
12057
12058     player->is_dropping = FALSE;
12059     player->is_dropping_pressed = FALSE;
12060     player->drop_pressed_delay = 0;
12061
12062     CheckSingleStepMode(player);
12063
12064     return 0;
12065   }
12066 }
12067
12068 static void CheckLevelTime()
12069 {
12070   int i;
12071
12072   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12073   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12074   {
12075     if (level.native_em_level->lev->home == 0)  /* all players at home */
12076     {
12077       PlayerWins(local_player);
12078
12079       AllPlayersGone = TRUE;
12080
12081       level.native_em_level->lev->home = -1;
12082     }
12083
12084     if (level.native_em_level->ply[0]->alive == 0 &&
12085         level.native_em_level->ply[1]->alive == 0 &&
12086         level.native_em_level->ply[2]->alive == 0 &&
12087         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12088       AllPlayersGone = TRUE;
12089   }
12090   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12091   {
12092     if (game_sp.LevelSolved &&
12093         !game_sp.GameOver)                              /* game won */
12094     {
12095       PlayerWins(local_player);
12096
12097       game_sp.GameOver = TRUE;
12098
12099       AllPlayersGone = TRUE;
12100     }
12101
12102     if (game_sp.GameOver)                               /* game lost */
12103       AllPlayersGone = TRUE;
12104   }
12105
12106   if (TimeFrames >= FRAMES_PER_SECOND)
12107   {
12108     TimeFrames = 0;
12109     TapeTime++;
12110
12111     for (i = 0; i < MAX_PLAYERS; i++)
12112     {
12113       struct PlayerInfo *player = &stored_player[i];
12114
12115       if (SHIELD_ON(player))
12116       {
12117         player->shield_normal_time_left--;
12118
12119         if (player->shield_deadly_time_left > 0)
12120           player->shield_deadly_time_left--;
12121       }
12122     }
12123
12124     if (!local_player->LevelSolved && !level.use_step_counter)
12125     {
12126       TimePlayed++;
12127
12128       if (TimeLeft > 0)
12129       {
12130         TimeLeft--;
12131
12132         if (TimeLeft <= 10 && setup.time_limit)
12133           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12134
12135 #if 1
12136         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12137            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12138
12139         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12140
12141         /* (already called by UpdateAndDisplayGameControlValues() below) */
12142         // DisplayGameControlValues();
12143 #else
12144         DrawGameValue_Time(TimeLeft);
12145 #endif
12146
12147         if (!TimeLeft && setup.time_limit)
12148         {
12149           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12150             level.native_em_level->lev->killed_out_of_time = TRUE;
12151           else
12152             for (i = 0; i < MAX_PLAYERS; i++)
12153               KillPlayer(&stored_player[i]);
12154         }
12155       }
12156 #if 1
12157       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12158       {
12159         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12160
12161         /* (already called by UpdateAndDisplayGameControlValues() below) */
12162         // DisplayGameControlValues();
12163       }
12164 #else
12165       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12166         DrawGameValue_Time(TimePlayed);
12167 #endif
12168
12169       level.native_em_level->lev->time =
12170         (game.no_time_limit ? TimePlayed : TimeLeft);
12171     }
12172
12173     if (tape.recording || tape.playing)
12174       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12175   }
12176
12177 #if 1
12178   UpdateAndDisplayGameControlValues();
12179 #else
12180   UpdateGameDoorValues();
12181   DrawGameDoorValues();
12182 #endif
12183 }
12184
12185 void AdvanceFrameAndPlayerCounters(int player_nr)
12186 {
12187   int i;
12188
12189   /* advance frame counters (global frame counter and time frame counter) */
12190   FrameCounter++;
12191   TimeFrames++;
12192
12193   /* advance player counters (counters for move delay, move animation etc.) */
12194   for (i = 0; i < MAX_PLAYERS; i++)
12195   {
12196     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12197     int move_delay_value = stored_player[i].move_delay_value;
12198     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12199
12200     if (!advance_player_counters)       /* not all players may be affected */
12201       continue;
12202
12203 #if USE_NEW_PLAYER_ANIM
12204     if (move_frames == 0)       /* less than one move per game frame */
12205     {
12206       int stepsize = TILEX / move_delay_value;
12207       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12208       int count = (stored_player[i].is_moving ?
12209                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12210
12211       if (count % delay == 0)
12212         move_frames = 1;
12213     }
12214 #endif
12215
12216     stored_player[i].Frame += move_frames;
12217
12218     if (stored_player[i].MovPos != 0)
12219       stored_player[i].StepFrame += move_frames;
12220
12221     if (stored_player[i].move_delay > 0)
12222       stored_player[i].move_delay--;
12223
12224     /* due to bugs in previous versions, counter must count up, not down */
12225     if (stored_player[i].push_delay != -1)
12226       stored_player[i].push_delay++;
12227
12228     if (stored_player[i].drop_delay > 0)
12229       stored_player[i].drop_delay--;
12230
12231     if (stored_player[i].is_dropping_pressed)
12232       stored_player[i].drop_pressed_delay++;
12233   }
12234 }
12235
12236 void StartGameActions(boolean init_network_game, boolean record_tape,
12237                       int random_seed)
12238 {
12239   unsigned int new_random_seed = InitRND(random_seed);
12240
12241   if (record_tape)
12242     TapeStartRecording(new_random_seed);
12243
12244 #if defined(NETWORK_AVALIABLE)
12245   if (init_network_game)
12246   {
12247     SendToServer_StartPlaying();
12248
12249     return;
12250   }
12251 #endif
12252
12253   InitGame();
12254 }
12255
12256 void GameActions()
12257 {
12258   static unsigned int game_frame_delay = 0;
12259   unsigned int game_frame_delay_value;
12260   byte *recorded_player_action;
12261   byte summarized_player_action = 0;
12262   byte tape_action[MAX_PLAYERS];
12263   int i;
12264
12265   /* detect endless loops, caused by custom element programming */
12266   if (recursion_loop_detected && recursion_loop_depth == 0)
12267   {
12268     char *message = getStringCat3("Internal Error! Element ",
12269                                   EL_NAME(recursion_loop_element),
12270                                   " caused endless loop! Quit the game?");
12271
12272     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12273           EL_NAME(recursion_loop_element));
12274
12275     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12276
12277     recursion_loop_detected = FALSE;    /* if game should be continued */
12278
12279     free(message);
12280
12281     return;
12282   }
12283
12284   if (game.restart_level)
12285     StartGameActions(options.network, setup.autorecord, level.random_seed);
12286
12287   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12288   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12289   {
12290     if (level.native_em_level->lev->home == 0)  /* all players at home */
12291     {
12292       PlayerWins(local_player);
12293
12294       AllPlayersGone = TRUE;
12295
12296       level.native_em_level->lev->home = -1;
12297     }
12298
12299     if (level.native_em_level->ply[0]->alive == 0 &&
12300         level.native_em_level->ply[1]->alive == 0 &&
12301         level.native_em_level->ply[2]->alive == 0 &&
12302         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12303       AllPlayersGone = TRUE;
12304   }
12305   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12306   {
12307     if (game_sp.LevelSolved &&
12308         !game_sp.GameOver)                              /* game won */
12309     {
12310       PlayerWins(local_player);
12311
12312       game_sp.GameOver = TRUE;
12313
12314       AllPlayersGone = TRUE;
12315     }
12316
12317     if (game_sp.GameOver)                               /* game lost */
12318       AllPlayersGone = TRUE;
12319   }
12320
12321   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12322     GameWon();
12323
12324   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12325     TapeStop();
12326
12327   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12328     return;
12329
12330   game_frame_delay_value =
12331     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12332
12333   if (tape.playing && tape.warp_forward && !tape.pausing)
12334     game_frame_delay_value = 0;
12335
12336   /* ---------- main game synchronization point ---------- */
12337
12338   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12339
12340   if (network_playing && !network_player_action_received)
12341   {
12342     /* try to get network player actions in time */
12343
12344 #if defined(NETWORK_AVALIABLE)
12345     /* last chance to get network player actions without main loop delay */
12346     HandleNetworking();
12347 #endif
12348
12349     /* game was quit by network peer */
12350     if (game_status != GAME_MODE_PLAYING)
12351       return;
12352
12353     if (!network_player_action_received)
12354       return;           /* failed to get network player actions in time */
12355
12356     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12357   }
12358
12359   if (tape.pausing)
12360     return;
12361
12362   /* at this point we know that we really continue executing the game */
12363
12364   network_player_action_received = FALSE;
12365
12366   /* when playing tape, read previously recorded player input from tape data */
12367   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12368
12369 #if 1
12370   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12371   if (tape.pausing)
12372     return;
12373 #endif
12374
12375   if (tape.set_centered_player)
12376   {
12377     game.centered_player_nr_next = tape.centered_player_nr_next;
12378     game.set_centered_player = TRUE;
12379   }
12380
12381   for (i = 0; i < MAX_PLAYERS; i++)
12382   {
12383     summarized_player_action |= stored_player[i].action;
12384
12385 #if 1
12386     if (!network_playing && (game.team_mode || tape.playing))
12387       stored_player[i].effective_action = stored_player[i].action;
12388 #else
12389     if (!network_playing)
12390       stored_player[i].effective_action = stored_player[i].action;
12391 #endif
12392   }
12393
12394 #if defined(NETWORK_AVALIABLE)
12395   if (network_playing)
12396     SendToServer_MovePlayer(summarized_player_action);
12397 #endif
12398
12399   if (!options.network && !game.team_mode)
12400     local_player->effective_action = summarized_player_action;
12401
12402   if (tape.recording &&
12403       setup.team_mode &&
12404       setup.input_on_focus &&
12405       game.centered_player_nr != -1)
12406   {
12407     for (i = 0; i < MAX_PLAYERS; i++)
12408       stored_player[i].effective_action =
12409         (i == game.centered_player_nr ? summarized_player_action : 0);
12410   }
12411
12412   if (recorded_player_action != NULL)
12413     for (i = 0; i < MAX_PLAYERS; i++)
12414       stored_player[i].effective_action = recorded_player_action[i];
12415
12416   for (i = 0; i < MAX_PLAYERS; i++)
12417   {
12418     tape_action[i] = stored_player[i].effective_action;
12419
12420 #if 1
12421     /* (this may happen in the RND game engine if a player was not present on
12422        the playfield on level start, but appeared later from a custom element */
12423     if (tape.recording &&
12424         setup.team_mode &&
12425         tape_action[i] &&
12426         !tape.player_participates[i])
12427       tape.player_participates[i] = TRUE;
12428 #else
12429     /* (this can only happen in the R'n'D game engine) */
12430     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12431       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12432 #endif
12433   }
12434
12435   /* only record actions from input devices, but not programmed actions */
12436   if (tape.recording)
12437     TapeRecordAction(tape_action);
12438
12439 #if USE_NEW_PLAYER_ASSIGNMENTS
12440 #if 1
12441   if (game.team_mode)
12442 #endif
12443   {
12444     byte mapped_action[MAX_PLAYERS];
12445
12446 #if DEBUG_PLAYER_ACTIONS
12447     printf(":::");
12448     for (i = 0; i < MAX_PLAYERS; i++)
12449       printf(" %d, ", stored_player[i].effective_action);
12450 #endif
12451
12452     for (i = 0; i < MAX_PLAYERS; i++)
12453       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12454
12455     for (i = 0; i < MAX_PLAYERS; i++)
12456       stored_player[i].effective_action = mapped_action[i];
12457
12458 #if DEBUG_PLAYER_ACTIONS
12459     printf(" =>");
12460     for (i = 0; i < MAX_PLAYERS; i++)
12461       printf(" %d, ", stored_player[i].effective_action);
12462     printf("\n");
12463 #endif
12464   }
12465 #if DEBUG_PLAYER_ACTIONS
12466   else
12467   {
12468     printf(":::");
12469     for (i = 0; i < MAX_PLAYERS; i++)
12470       printf(" %d, ", stored_player[i].effective_action);
12471     printf("\n");
12472   }
12473 #endif
12474 #endif
12475
12476 #if 0
12477   printf("::: summarized_player_action == %d\n",
12478          local_player->effective_action);
12479 #endif
12480
12481
12482
12483
12484 #if 0
12485 #if DEBUG_INIT_PLAYER
12486     if (options.debug)
12487     {
12488       printf("Player status (final):\n");
12489
12490       for (i = 0; i < MAX_PLAYERS; i++)
12491       {
12492         struct PlayerInfo *player = &stored_player[i];
12493
12494         printf("- player %d: present == %d, connected == %d, active == %d",
12495                i + 1,
12496                player->present,
12497                player->connected,
12498                player->active);
12499
12500         if (local_player == player)
12501           printf(" (local player)");
12502
12503         printf("\n");
12504       }
12505     }
12506 #endif
12507 #endif
12508
12509
12510
12511   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12512   {
12513     GameActions_EM_Main();
12514   }
12515   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12516   {
12517     GameActions_SP_Main();
12518   }
12519   else
12520   {
12521     GameActions_RND();
12522   }
12523 }
12524
12525 void GameActions_EM_Main()
12526 {
12527   byte effective_action[MAX_PLAYERS];
12528   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12529   int i;
12530
12531   for (i = 0; i < MAX_PLAYERS; i++)
12532     effective_action[i] = stored_player[i].effective_action;
12533
12534   GameActions_EM(effective_action, warp_mode);
12535
12536   CheckLevelTime();
12537
12538   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12539 }
12540
12541 void GameActions_SP_Main()
12542 {
12543   byte effective_action[MAX_PLAYERS];
12544   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12545   int i;
12546
12547   for (i = 0; i < MAX_PLAYERS; i++)
12548     effective_action[i] = stored_player[i].effective_action;
12549
12550   GameActions_SP(effective_action, warp_mode);
12551
12552   CheckLevelTime();
12553
12554   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12555 }
12556
12557 void GameActions_RND()
12558 {
12559   int magic_wall_x = 0, magic_wall_y = 0;
12560   int i, x, y, element, graphic;
12561
12562   InitPlayfieldScanModeVars();
12563
12564 #if USE_ONE_MORE_CHANGE_PER_FRAME
12565   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12566   {
12567     SCAN_PLAYFIELD(x, y)
12568     {
12569       ChangeCount[x][y] = 0;
12570       ChangeEvent[x][y] = -1;
12571     }
12572   }
12573 #endif
12574
12575   if (game.set_centered_player)
12576   {
12577     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12578
12579     /* switching to "all players" only possible if all players fit to screen */
12580     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12581     {
12582       game.centered_player_nr_next = game.centered_player_nr;
12583       game.set_centered_player = FALSE;
12584     }
12585
12586     /* do not switch focus to non-existing (or non-active) player */
12587     if (game.centered_player_nr_next >= 0 &&
12588         !stored_player[game.centered_player_nr_next].active)
12589     {
12590       game.centered_player_nr_next = game.centered_player_nr;
12591       game.set_centered_player = FALSE;
12592     }
12593   }
12594
12595   if (game.set_centered_player &&
12596       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12597   {
12598     int sx, sy;
12599
12600     if (game.centered_player_nr_next == -1)
12601     {
12602       setScreenCenteredToAllPlayers(&sx, &sy);
12603     }
12604     else
12605     {
12606       sx = stored_player[game.centered_player_nr_next].jx;
12607       sy = stored_player[game.centered_player_nr_next].jy;
12608     }
12609
12610     game.centered_player_nr = game.centered_player_nr_next;
12611     game.set_centered_player = FALSE;
12612
12613     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12614     DrawGameDoorValues();
12615   }
12616
12617   for (i = 0; i < MAX_PLAYERS; i++)
12618   {
12619     int actual_player_action = stored_player[i].effective_action;
12620
12621 #if 1
12622     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12623        - rnd_equinox_tetrachloride 048
12624        - rnd_equinox_tetrachloride_ii 096
12625        - rnd_emanuel_schmieg 002
12626        - doctor_sloan_ww 001, 020
12627     */
12628     if (stored_player[i].MovPos == 0)
12629       CheckGravityMovement(&stored_player[i]);
12630 #endif
12631
12632     /* overwrite programmed action with tape action */
12633     if (stored_player[i].programmed_action)
12634       actual_player_action = stored_player[i].programmed_action;
12635
12636     PlayerActions(&stored_player[i], actual_player_action);
12637
12638     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12639   }
12640
12641   ScrollScreen(NULL, SCROLL_GO_ON);
12642
12643   /* for backwards compatibility, the following code emulates a fixed bug that
12644      occured when pushing elements (causing elements that just made their last
12645      pushing step to already (if possible) make their first falling step in the
12646      same game frame, which is bad); this code is also needed to use the famous
12647      "spring push bug" which is used in older levels and might be wanted to be
12648      used also in newer levels, but in this case the buggy pushing code is only
12649      affecting the "spring" element and no other elements */
12650
12651   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12652   {
12653     for (i = 0; i < MAX_PLAYERS; i++)
12654     {
12655       struct PlayerInfo *player = &stored_player[i];
12656       int x = player->jx;
12657       int y = player->jy;
12658
12659       if (player->active && player->is_pushing && player->is_moving &&
12660           IS_MOVING(x, y) &&
12661           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12662            Feld[x][y] == EL_SPRING))
12663       {
12664         ContinueMoving(x, y);
12665
12666         /* continue moving after pushing (this is actually a bug) */
12667         if (!IS_MOVING(x, y))
12668           Stop[x][y] = FALSE;
12669       }
12670     }
12671   }
12672
12673 #if 0
12674   debug_print_timestamp(0, "start main loop profiling");
12675 #endif
12676
12677   SCAN_PLAYFIELD(x, y)
12678   {
12679     ChangeCount[x][y] = 0;
12680     ChangeEvent[x][y] = -1;
12681
12682     /* this must be handled before main playfield loop */
12683     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12684     {
12685       MovDelay[x][y]--;
12686       if (MovDelay[x][y] <= 0)
12687         RemoveField(x, y);
12688     }
12689
12690 #if USE_NEW_SNAP_DELAY
12691     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12692     {
12693       MovDelay[x][y]--;
12694       if (MovDelay[x][y] <= 0)
12695       {
12696         RemoveField(x, y);
12697         TEST_DrawLevelField(x, y);
12698
12699         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12700       }
12701     }
12702 #endif
12703
12704 #if DEBUG
12705     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12706     {
12707       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12708       printf("GameActions(): This should never happen!\n");
12709
12710       ChangePage[x][y] = -1;
12711     }
12712 #endif
12713
12714     Stop[x][y] = FALSE;
12715     if (WasJustMoving[x][y] > 0)
12716       WasJustMoving[x][y]--;
12717     if (WasJustFalling[x][y] > 0)
12718       WasJustFalling[x][y]--;
12719     if (CheckCollision[x][y] > 0)
12720       CheckCollision[x][y]--;
12721     if (CheckImpact[x][y] > 0)
12722       CheckImpact[x][y]--;
12723
12724     GfxFrame[x][y]++;
12725
12726     /* reset finished pushing action (not done in ContinueMoving() to allow
12727        continuous pushing animation for elements with zero push delay) */
12728     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12729     {
12730       ResetGfxAnimation(x, y);
12731       TEST_DrawLevelField(x, y);
12732     }
12733
12734 #if DEBUG
12735     if (IS_BLOCKED(x, y))
12736     {
12737       int oldx, oldy;
12738
12739       Blocked2Moving(x, y, &oldx, &oldy);
12740       if (!IS_MOVING(oldx, oldy))
12741       {
12742         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12743         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12744         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12745         printf("GameActions(): This should never happen!\n");
12746       }
12747     }
12748 #endif
12749   }
12750
12751 #if 0
12752   debug_print_timestamp(0, "- time for pre-main loop:");
12753 #endif
12754
12755 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12756   SCAN_PLAYFIELD(x, y)
12757   {
12758     element = Feld[x][y];
12759     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12760
12761 #if 1
12762     {
12763 #if 1
12764       int element2 = element;
12765       int graphic2 = graphic;
12766 #else
12767       int element2 = Feld[x][y];
12768       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12769 #endif
12770       int last_gfx_frame = GfxFrame[x][y];
12771
12772       if (graphic_info[graphic2].anim_global_sync)
12773         GfxFrame[x][y] = FrameCounter;
12774       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12775         GfxFrame[x][y] = CustomValue[x][y];
12776       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12777         GfxFrame[x][y] = element_info[element2].collect_score;
12778       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12779         GfxFrame[x][y] = ChangeDelay[x][y];
12780
12781       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12782         DrawLevelGraphicAnimation(x, y, graphic2);
12783     }
12784 #else
12785     ResetGfxFrame(x, y, TRUE);
12786 #endif
12787
12788 #if 1
12789     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12790         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12791       ResetRandomAnimationValue(x, y);
12792 #endif
12793
12794 #if 1
12795     SetRandomAnimationValue(x, y);
12796 #endif
12797
12798 #if 1
12799     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12800 #endif
12801   }
12802 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12803
12804 #if 0
12805   debug_print_timestamp(0, "- time for TEST loop:     -->");
12806 #endif
12807
12808   SCAN_PLAYFIELD(x, y)
12809   {
12810     element = Feld[x][y];
12811     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12812
12813     ResetGfxFrame(x, y, TRUE);
12814
12815     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12816         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12817       ResetRandomAnimationValue(x, y);
12818
12819     SetRandomAnimationValue(x, y);
12820
12821     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12822
12823     if (IS_INACTIVE(element))
12824     {
12825       if (IS_ANIMATED(graphic))
12826         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12827
12828       continue;
12829     }
12830
12831     /* this may take place after moving, so 'element' may have changed */
12832     if (IS_CHANGING(x, y) &&
12833         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12834     {
12835       int page = element_info[element].event_page_nr[CE_DELAY];
12836
12837 #if 1
12838       HandleElementChange(x, y, page);
12839 #else
12840       if (CAN_CHANGE(element))
12841         HandleElementChange(x, y, page);
12842
12843       if (HAS_ACTION(element))
12844         ExecuteCustomElementAction(x, y, element, page);
12845 #endif
12846
12847       element = Feld[x][y];
12848       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12849     }
12850
12851 #if 0   // ---------------------------------------------------------------------
12852
12853     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12854     {
12855       StartMoving(x, y);
12856
12857       element = Feld[x][y];
12858       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12859
12860       if (IS_ANIMATED(graphic) &&
12861           !IS_MOVING(x, y) &&
12862           !Stop[x][y])
12863         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12864
12865       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12866         TEST_DrawTwinkleOnField(x, y);
12867     }
12868     else if (IS_MOVING(x, y))
12869       ContinueMoving(x, y);
12870     else
12871     {
12872       switch (element)
12873       {
12874         case EL_ACID:
12875         case EL_EXIT_OPEN:
12876         case EL_EM_EXIT_OPEN:
12877         case EL_SP_EXIT_OPEN:
12878         case EL_STEEL_EXIT_OPEN:
12879         case EL_EM_STEEL_EXIT_OPEN:
12880         case EL_SP_TERMINAL:
12881         case EL_SP_TERMINAL_ACTIVE:
12882         case EL_EXTRA_TIME:
12883         case EL_SHIELD_NORMAL:
12884         case EL_SHIELD_DEADLY:
12885           if (IS_ANIMATED(graphic))
12886             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12887           break;
12888
12889         case EL_DYNAMITE_ACTIVE:
12890         case EL_EM_DYNAMITE_ACTIVE:
12891         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12892         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12893         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12894         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12895         case EL_SP_DISK_RED_ACTIVE:
12896           CheckDynamite(x, y);
12897           break;
12898
12899         case EL_AMOEBA_GROWING:
12900           AmoebeWaechst(x, y);
12901           break;
12902
12903         case EL_AMOEBA_SHRINKING:
12904           AmoebaDisappearing(x, y);
12905           break;
12906
12907 #if !USE_NEW_AMOEBA_CODE
12908         case EL_AMOEBA_WET:
12909         case EL_AMOEBA_DRY:
12910         case EL_AMOEBA_FULL:
12911         case EL_BD_AMOEBA:
12912         case EL_EMC_DRIPPER:
12913           AmoebeAbleger(x, y);
12914           break;
12915 #endif
12916
12917         case EL_GAME_OF_LIFE:
12918         case EL_BIOMAZE:
12919           Life(x, y);
12920           break;
12921
12922         case EL_EXIT_CLOSED:
12923           CheckExit(x, y);
12924           break;
12925
12926         case EL_EM_EXIT_CLOSED:
12927           CheckExitEM(x, y);
12928           break;
12929
12930         case EL_STEEL_EXIT_CLOSED:
12931           CheckExitSteel(x, y);
12932           break;
12933
12934         case EL_EM_STEEL_EXIT_CLOSED:
12935           CheckExitSteelEM(x, y);
12936           break;
12937
12938         case EL_SP_EXIT_CLOSED:
12939           CheckExitSP(x, y);
12940           break;
12941
12942         case EL_EXPANDABLE_WALL_GROWING:
12943         case EL_EXPANDABLE_STEELWALL_GROWING:
12944           MauerWaechst(x, y);
12945           break;
12946
12947         case EL_EXPANDABLE_WALL:
12948         case EL_EXPANDABLE_WALL_HORIZONTAL:
12949         case EL_EXPANDABLE_WALL_VERTICAL:
12950         case EL_EXPANDABLE_WALL_ANY:
12951         case EL_BD_EXPANDABLE_WALL:
12952           MauerAbleger(x, y);
12953           break;
12954
12955         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12956         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12957         case EL_EXPANDABLE_STEELWALL_ANY:
12958           MauerAblegerStahl(x, y);
12959           break;
12960
12961         case EL_FLAMES:
12962           CheckForDragon(x, y);
12963           break;
12964
12965         case EL_EXPLOSION:
12966           break;
12967
12968         case EL_ELEMENT_SNAPPING:
12969         case EL_DIAGONAL_SHRINKING:
12970         case EL_DIAGONAL_GROWING:
12971         {
12972           graphic =
12973             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12974
12975           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12976           break;
12977         }
12978
12979         default:
12980           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12981             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12982           break;
12983       }
12984     }
12985
12986 #else   // ---------------------------------------------------------------------
12987
12988     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12989     {
12990       StartMoving(x, y);
12991
12992       element = Feld[x][y];
12993       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12994
12995       if (IS_ANIMATED(graphic) &&
12996           !IS_MOVING(x, y) &&
12997           !Stop[x][y])
12998         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12999
13000       if (IS_GEM(element) || element == EL_SP_INFOTRON)
13001         TEST_DrawTwinkleOnField(x, y);
13002     }
13003     else if ((element == EL_ACID ||
13004               element == EL_EXIT_OPEN ||
13005               element == EL_EM_EXIT_OPEN ||
13006               element == EL_SP_EXIT_OPEN ||
13007               element == EL_STEEL_EXIT_OPEN ||
13008               element == EL_EM_STEEL_EXIT_OPEN ||
13009               element == EL_SP_TERMINAL ||
13010               element == EL_SP_TERMINAL_ACTIVE ||
13011               element == EL_EXTRA_TIME ||
13012               element == EL_SHIELD_NORMAL ||
13013               element == EL_SHIELD_DEADLY) &&
13014              IS_ANIMATED(graphic))
13015       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13016     else if (IS_MOVING(x, y))
13017       ContinueMoving(x, y);
13018     else if (IS_ACTIVE_BOMB(element))
13019       CheckDynamite(x, y);
13020     else if (element == EL_AMOEBA_GROWING)
13021       AmoebeWaechst(x, y);
13022     else if (element == EL_AMOEBA_SHRINKING)
13023       AmoebaDisappearing(x, y);
13024
13025 #if !USE_NEW_AMOEBA_CODE
13026     else if (IS_AMOEBALIVE(element))
13027       AmoebeAbleger(x, y);
13028 #endif
13029
13030     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13031       Life(x, y);
13032     else if (element == EL_EXIT_CLOSED)
13033       CheckExit(x, y);
13034     else if (element == EL_EM_EXIT_CLOSED)
13035       CheckExitEM(x, y);
13036     else if (element == EL_STEEL_EXIT_CLOSED)
13037       CheckExitSteel(x, y);
13038     else if (element == EL_EM_STEEL_EXIT_CLOSED)
13039       CheckExitSteelEM(x, y);
13040     else if (element == EL_SP_EXIT_CLOSED)
13041       CheckExitSP(x, y);
13042     else if (element == EL_EXPANDABLE_WALL_GROWING ||
13043              element == EL_EXPANDABLE_STEELWALL_GROWING)
13044       MauerWaechst(x, y);
13045     else if (element == EL_EXPANDABLE_WALL ||
13046              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13047              element == EL_EXPANDABLE_WALL_VERTICAL ||
13048              element == EL_EXPANDABLE_WALL_ANY ||
13049              element == EL_BD_EXPANDABLE_WALL)
13050       MauerAbleger(x, y);
13051     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13052              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13053              element == EL_EXPANDABLE_STEELWALL_ANY)
13054       MauerAblegerStahl(x, y);
13055     else if (element == EL_FLAMES)
13056       CheckForDragon(x, y);
13057     else if (element == EL_EXPLOSION)
13058       ; /* drawing of correct explosion animation is handled separately */
13059     else if (element == EL_ELEMENT_SNAPPING ||
13060              element == EL_DIAGONAL_SHRINKING ||
13061              element == EL_DIAGONAL_GROWING)
13062     {
13063       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13064
13065       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13066     }
13067     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13068       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13069
13070 #endif  // ---------------------------------------------------------------------
13071
13072     if (IS_BELT_ACTIVE(element))
13073       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13074
13075     if (game.magic_wall_active)
13076     {
13077       int jx = local_player->jx, jy = local_player->jy;
13078
13079       /* play the element sound at the position nearest to the player */
13080       if ((element == EL_MAGIC_WALL_FULL ||
13081            element == EL_MAGIC_WALL_ACTIVE ||
13082            element == EL_MAGIC_WALL_EMPTYING ||
13083            element == EL_BD_MAGIC_WALL_FULL ||
13084            element == EL_BD_MAGIC_WALL_ACTIVE ||
13085            element == EL_BD_MAGIC_WALL_EMPTYING ||
13086            element == EL_DC_MAGIC_WALL_FULL ||
13087            element == EL_DC_MAGIC_WALL_ACTIVE ||
13088            element == EL_DC_MAGIC_WALL_EMPTYING) &&
13089           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13090       {
13091         magic_wall_x = x;
13092         magic_wall_y = y;
13093       }
13094     }
13095   }
13096
13097 #if 0
13098   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13099 #endif
13100
13101 #if USE_NEW_AMOEBA_CODE
13102   /* new experimental amoeba growth stuff */
13103   if (!(FrameCounter % 8))
13104   {
13105     static unsigned int random = 1684108901;
13106
13107     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13108     {
13109       x = RND(lev_fieldx);
13110       y = RND(lev_fieldy);
13111       element = Feld[x][y];
13112
13113       if (!IS_PLAYER(x,y) &&
13114           (element == EL_EMPTY ||
13115            CAN_GROW_INTO(element) ||
13116            element == EL_QUICKSAND_EMPTY ||
13117            element == EL_QUICKSAND_FAST_EMPTY ||
13118            element == EL_ACID_SPLASH_LEFT ||
13119            element == EL_ACID_SPLASH_RIGHT))
13120       {
13121         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13122             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13123             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13124             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13125           Feld[x][y] = EL_AMOEBA_DROP;
13126       }
13127
13128       random = random * 129 + 1;
13129     }
13130   }
13131 #endif
13132
13133 #if 0
13134   if (game.explosions_delayed)
13135 #endif
13136   {
13137     game.explosions_delayed = FALSE;
13138
13139     SCAN_PLAYFIELD(x, y)
13140     {
13141       element = Feld[x][y];
13142
13143       if (ExplodeField[x][y])
13144         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13145       else if (element == EL_EXPLOSION)
13146         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13147
13148       ExplodeField[x][y] = EX_TYPE_NONE;
13149     }
13150
13151     game.explosions_delayed = TRUE;
13152   }
13153
13154   if (game.magic_wall_active)
13155   {
13156     if (!(game.magic_wall_time_left % 4))
13157     {
13158       int element = Feld[magic_wall_x][magic_wall_y];
13159
13160       if (element == EL_BD_MAGIC_WALL_FULL ||
13161           element == EL_BD_MAGIC_WALL_ACTIVE ||
13162           element == EL_BD_MAGIC_WALL_EMPTYING)
13163         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13164       else if (element == EL_DC_MAGIC_WALL_FULL ||
13165                element == EL_DC_MAGIC_WALL_ACTIVE ||
13166                element == EL_DC_MAGIC_WALL_EMPTYING)
13167         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13168       else
13169         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13170     }
13171
13172     if (game.magic_wall_time_left > 0)
13173     {
13174       game.magic_wall_time_left--;
13175
13176       if (!game.magic_wall_time_left)
13177       {
13178         SCAN_PLAYFIELD(x, y)
13179         {
13180           element = Feld[x][y];
13181
13182           if (element == EL_MAGIC_WALL_ACTIVE ||
13183               element == EL_MAGIC_WALL_FULL)
13184           {
13185             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13186             TEST_DrawLevelField(x, y);
13187           }
13188           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13189                    element == EL_BD_MAGIC_WALL_FULL)
13190           {
13191             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13192             TEST_DrawLevelField(x, y);
13193           }
13194           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13195                    element == EL_DC_MAGIC_WALL_FULL)
13196           {
13197             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13198             TEST_DrawLevelField(x, y);
13199           }
13200         }
13201
13202         game.magic_wall_active = FALSE;
13203       }
13204     }
13205   }
13206
13207   if (game.light_time_left > 0)
13208   {
13209     game.light_time_left--;
13210
13211     if (game.light_time_left == 0)
13212       RedrawAllLightSwitchesAndInvisibleElements();
13213   }
13214
13215   if (game.timegate_time_left > 0)
13216   {
13217     game.timegate_time_left--;
13218
13219     if (game.timegate_time_left == 0)
13220       CloseAllOpenTimegates();
13221   }
13222
13223   if (game.lenses_time_left > 0)
13224   {
13225     game.lenses_time_left--;
13226
13227     if (game.lenses_time_left == 0)
13228       RedrawAllInvisibleElementsForLenses();
13229   }
13230
13231   if (game.magnify_time_left > 0)
13232   {
13233     game.magnify_time_left--;
13234
13235     if (game.magnify_time_left == 0)
13236       RedrawAllInvisibleElementsForMagnifier();
13237   }
13238
13239   for (i = 0; i < MAX_PLAYERS; i++)
13240   {
13241     struct PlayerInfo *player = &stored_player[i];
13242
13243     if (SHIELD_ON(player))
13244     {
13245       if (player->shield_deadly_time_left)
13246         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13247       else if (player->shield_normal_time_left)
13248         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13249     }
13250   }
13251
13252 #if USE_DELAYED_GFX_REDRAW
13253   SCAN_PLAYFIELD(x, y)
13254   {
13255 #if 1
13256     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13257 #else
13258     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13259         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13260 #endif
13261     {
13262       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13263          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13264
13265       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13266         DrawLevelField(x, y);
13267
13268       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13269         DrawLevelFieldCrumbled(x, y);
13270
13271       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13272         DrawLevelFieldCrumbledNeighbours(x, y);
13273
13274       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13275         DrawTwinkleOnField(x, y);
13276     }
13277
13278     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13279   }
13280 #endif
13281
13282   CheckLevelTime();
13283
13284   DrawAllPlayers();
13285   PlayAllPlayersSound();
13286
13287   if (options.debug)                    /* calculate frames per second */
13288   {
13289     static unsigned int fps_counter = 0;
13290     static int fps_frames = 0;
13291     unsigned int fps_delay_ms = Counter() - fps_counter;
13292
13293     fps_frames++;
13294
13295     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13296     {
13297       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13298
13299       fps_frames = 0;
13300       fps_counter = Counter();
13301     }
13302
13303     redraw_mask |= REDRAW_FPS;
13304   }
13305
13306   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13307
13308   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13309   {
13310     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13311
13312     local_player->show_envelope = 0;
13313   }
13314
13315 #if 0
13316   debug_print_timestamp(0, "stop main loop profiling ");
13317   printf("----------------------------------------------------------\n");
13318 #endif
13319
13320   /* use random number generator in every frame to make it less predictable */
13321   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13322     RND(1);
13323 }
13324
13325 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13326 {
13327   int min_x = x, min_y = y, max_x = x, max_y = y;
13328   int i;
13329
13330   for (i = 0; i < MAX_PLAYERS; i++)
13331   {
13332     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13333
13334     if (!stored_player[i].active || &stored_player[i] == player)
13335       continue;
13336
13337     min_x = MIN(min_x, jx);
13338     min_y = MIN(min_y, jy);
13339     max_x = MAX(max_x, jx);
13340     max_y = MAX(max_y, jy);
13341   }
13342
13343   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13344 }
13345
13346 static boolean AllPlayersInVisibleScreen()
13347 {
13348   int i;
13349
13350   for (i = 0; i < MAX_PLAYERS; i++)
13351   {
13352     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13353
13354     if (!stored_player[i].active)
13355       continue;
13356
13357     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13358       return FALSE;
13359   }
13360
13361   return TRUE;
13362 }
13363
13364 void ScrollLevel(int dx, int dy)
13365 {
13366 #if 0
13367   /* (directly solved in BlitBitmap() now) */
13368   static Bitmap *bitmap_db_field2 = NULL;
13369   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13370   int x, y;
13371 #else
13372   int x, y;
13373 #endif
13374
13375 #if 0
13376   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13377   /* only horizontal XOR vertical scroll direction allowed */
13378   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13379     return;
13380 #endif
13381
13382 #if 0
13383   /* (directly solved in BlitBitmap() now) */
13384   if (bitmap_db_field2 == NULL)
13385     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13386
13387   /* needed when blitting directly to same bitmap -- should not be needed with
13388      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13389   BlitBitmap(drawto_field, bitmap_db_field2,
13390              FX + TILEX * (dx == -1) - softscroll_offset,
13391              FY + TILEY * (dy == -1) - softscroll_offset,
13392              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13393              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13394              FX + TILEX * (dx == 1) - softscroll_offset,
13395              FY + TILEY * (dy == 1) - softscroll_offset);
13396   BlitBitmap(bitmap_db_field2, drawto_field,
13397              FX + TILEX * (dx == 1) - softscroll_offset,
13398              FY + TILEY * (dy == 1) - softscroll_offset,
13399              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13400              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13401              FX + TILEX * (dx == 1) - softscroll_offset,
13402              FY + TILEY * (dy == 1) - softscroll_offset);
13403
13404 #else
13405
13406 #if 0
13407   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13408   int xsize = (BX2 - BX1 + 1);
13409   int ysize = (BY2 - BY1 + 1);
13410   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13411   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13412   int step  = (start < end ? +1 : -1);
13413
13414   for (i = start; i != end; i += step)
13415   {
13416     BlitBitmap(drawto_field, drawto_field,
13417                FX + TILEX * (dx != 0 ? i + step : 0),
13418                FY + TILEY * (dy != 0 ? i + step : 0),
13419                TILEX * (dx != 0 ? 1 : xsize),
13420                TILEY * (dy != 0 ? 1 : ysize),
13421                FX + TILEX * (dx != 0 ? i : 0),
13422                FY + TILEY * (dy != 0 ? i : 0));
13423   }
13424
13425 #else
13426
13427 #if NEW_TILESIZE
13428 #if NEW_SCROLL
13429   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13430 #else
13431   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13432 #endif
13433 #else
13434 #if NEW_SCROLL
13435   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13436 #else
13437   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13438 #endif
13439 #endif
13440
13441 #if NEW_TILESIZE
13442   BlitBitmap(drawto_field, drawto_field,
13443              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13444              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13445              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13446              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13447              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13448              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13449 #else
13450   BlitBitmap(drawto_field, drawto_field,
13451              FX + TILEX * (dx == -1) - softscroll_offset,
13452              FY + TILEY * (dy == -1) - softscroll_offset,
13453              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13454              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13455              FX + TILEX * (dx == 1) - softscroll_offset,
13456              FY + TILEY * (dy == 1) - softscroll_offset);
13457 #endif
13458
13459 #endif
13460 #endif
13461
13462   if (dx != 0)
13463   {
13464     x = (dx == 1 ? BX1 : BX2);
13465     for (y = BY1; y <= BY2; y++)
13466       DrawScreenField(x, y);
13467   }
13468
13469   if (dy != 0)
13470   {
13471     y = (dy == 1 ? BY1 : BY2);
13472     for (x = BX1; x <= BX2; x++)
13473       DrawScreenField(x, y);
13474   }
13475
13476   redraw_mask |= REDRAW_FIELD;
13477 }
13478
13479 static boolean canFallDown(struct PlayerInfo *player)
13480 {
13481   int jx = player->jx, jy = player->jy;
13482
13483   return (IN_LEV_FIELD(jx, jy + 1) &&
13484           (IS_FREE(jx, jy + 1) ||
13485            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13486           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13487           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13488 }
13489
13490 static boolean canPassField(int x, int y, int move_dir)
13491 {
13492   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13493   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13494   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13495   int nextx = x + dx;
13496   int nexty = y + dy;
13497   int element = Feld[x][y];
13498
13499   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13500           !CAN_MOVE(element) &&
13501           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13502           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13503           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13504 }
13505
13506 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13507 {
13508   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13509   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13510   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13511   int newx = x + dx;
13512   int newy = y + dy;
13513
13514   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13515           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13516           (IS_DIGGABLE(Feld[newx][newy]) ||
13517            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13518            canPassField(newx, newy, move_dir)));
13519 }
13520
13521 static void CheckGravityMovement(struct PlayerInfo *player)
13522 {
13523 #if USE_PLAYER_GRAVITY
13524   if (player->gravity && !player->programmed_action)
13525 #else
13526   if (game.gravity && !player->programmed_action)
13527 #endif
13528   {
13529     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13530     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13531     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13532     int jx = player->jx, jy = player->jy;
13533     boolean player_is_moving_to_valid_field =
13534       (!player_is_snapping &&
13535        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13536         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13537     boolean player_can_fall_down = canFallDown(player);
13538
13539     if (player_can_fall_down &&
13540         !player_is_moving_to_valid_field)
13541       player->programmed_action = MV_DOWN;
13542   }
13543 }
13544
13545 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13546 {
13547   return CheckGravityMovement(player);
13548
13549 #if USE_PLAYER_GRAVITY
13550   if (player->gravity && !player->programmed_action)
13551 #else
13552   if (game.gravity && !player->programmed_action)
13553 #endif
13554   {
13555     int jx = player->jx, jy = player->jy;
13556     boolean field_under_player_is_free =
13557       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13558     boolean player_is_standing_on_valid_field =
13559       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13560        (IS_WALKABLE(Feld[jx][jy]) &&
13561         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13562
13563     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13564       player->programmed_action = MV_DOWN;
13565   }
13566 }
13567
13568 /*
13569   MovePlayerOneStep()
13570   -----------------------------------------------------------------------------
13571   dx, dy:               direction (non-diagonal) to try to move the player to
13572   real_dx, real_dy:     direction as read from input device (can be diagonal)
13573 */
13574
13575 boolean MovePlayerOneStep(struct PlayerInfo *player,
13576                           int dx, int dy, int real_dx, int real_dy)
13577 {
13578   int jx = player->jx, jy = player->jy;
13579   int new_jx = jx + dx, new_jy = jy + dy;
13580 #if !USE_FIXED_DONT_RUN_INTO
13581   int element;
13582 #endif
13583   int can_move;
13584   boolean player_can_move = !player->cannot_move;
13585
13586   if (!player->active || (!dx && !dy))
13587     return MP_NO_ACTION;
13588
13589   player->MovDir = (dx < 0 ? MV_LEFT :
13590                     dx > 0 ? MV_RIGHT :
13591                     dy < 0 ? MV_UP :
13592                     dy > 0 ? MV_DOWN :  MV_NONE);
13593
13594   if (!IN_LEV_FIELD(new_jx, new_jy))
13595     return MP_NO_ACTION;
13596
13597   if (!player_can_move)
13598   {
13599     if (player->MovPos == 0)
13600     {
13601       player->is_moving = FALSE;
13602       player->is_digging = FALSE;
13603       player->is_collecting = FALSE;
13604       player->is_snapping = FALSE;
13605       player->is_pushing = FALSE;
13606     }
13607   }
13608
13609 #if 1
13610   if (!options.network && game.centered_player_nr == -1 &&
13611       !AllPlayersInSight(player, new_jx, new_jy))
13612     return MP_NO_ACTION;
13613 #else
13614   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13615     return MP_NO_ACTION;
13616 #endif
13617
13618 #if !USE_FIXED_DONT_RUN_INTO
13619   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13620
13621   /* (moved to DigField()) */
13622   if (player_can_move && DONT_RUN_INTO(element))
13623   {
13624     if (element == EL_ACID && dx == 0 && dy == 1)
13625     {
13626       SplashAcid(new_jx, new_jy);
13627       Feld[jx][jy] = EL_PLAYER_1;
13628       InitMovingField(jx, jy, MV_DOWN);
13629       Store[jx][jy] = EL_ACID;
13630       ContinueMoving(jx, jy);
13631       BuryPlayer(player);
13632     }
13633     else
13634       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13635
13636     return MP_MOVING;
13637   }
13638 #endif
13639
13640   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13641   if (can_move != MP_MOVING)
13642     return can_move;
13643
13644   /* check if DigField() has caused relocation of the player */
13645   if (player->jx != jx || player->jy != jy)
13646     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13647
13648   StorePlayer[jx][jy] = 0;
13649   player->last_jx = jx;
13650   player->last_jy = jy;
13651   player->jx = new_jx;
13652   player->jy = new_jy;
13653   StorePlayer[new_jx][new_jy] = player->element_nr;
13654
13655   if (player->move_delay_value_next != -1)
13656   {
13657     player->move_delay_value = player->move_delay_value_next;
13658     player->move_delay_value_next = -1;
13659   }
13660
13661   player->MovPos =
13662     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13663
13664   player->step_counter++;
13665
13666   PlayerVisit[jx][jy] = FrameCounter;
13667
13668 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13669   player->is_moving = TRUE;
13670 #endif
13671
13672 #if 1
13673   /* should better be called in MovePlayer(), but this breaks some tapes */
13674   ScrollPlayer(player, SCROLL_INIT);
13675 #endif
13676
13677   return MP_MOVING;
13678 }
13679
13680 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13681 {
13682   int jx = player->jx, jy = player->jy;
13683   int old_jx = jx, old_jy = jy;
13684   int moved = MP_NO_ACTION;
13685
13686   if (!player->active)
13687     return FALSE;
13688
13689   if (!dx && !dy)
13690   {
13691     if (player->MovPos == 0)
13692     {
13693       player->is_moving = FALSE;
13694       player->is_digging = FALSE;
13695       player->is_collecting = FALSE;
13696       player->is_snapping = FALSE;
13697       player->is_pushing = FALSE;
13698     }
13699
13700     return FALSE;
13701   }
13702
13703   if (player->move_delay > 0)
13704     return FALSE;
13705
13706   player->move_delay = -1;              /* set to "uninitialized" value */
13707
13708   /* store if player is automatically moved to next field */
13709   player->is_auto_moving = (player->programmed_action != MV_NONE);
13710
13711   /* remove the last programmed player action */
13712   player->programmed_action = 0;
13713
13714   if (player->MovPos)
13715   {
13716     /* should only happen if pre-1.2 tape recordings are played */
13717     /* this is only for backward compatibility */
13718
13719     int original_move_delay_value = player->move_delay_value;
13720
13721 #if DEBUG
13722     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13723            tape.counter);
13724 #endif
13725
13726     /* scroll remaining steps with finest movement resolution */
13727     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13728
13729     while (player->MovPos)
13730     {
13731       ScrollPlayer(player, SCROLL_GO_ON);
13732       ScrollScreen(NULL, SCROLL_GO_ON);
13733
13734       AdvanceFrameAndPlayerCounters(player->index_nr);
13735
13736       DrawAllPlayers();
13737       BackToFront();
13738     }
13739
13740     player->move_delay_value = original_move_delay_value;
13741   }
13742
13743   player->is_active = FALSE;
13744
13745   if (player->last_move_dir & MV_HORIZONTAL)
13746   {
13747     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13748       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13749   }
13750   else
13751   {
13752     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13753       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13754   }
13755
13756 #if USE_FIXED_BORDER_RUNNING_GFX
13757   if (!moved && !player->is_active)
13758   {
13759     player->is_moving = FALSE;
13760     player->is_digging = FALSE;
13761     player->is_collecting = FALSE;
13762     player->is_snapping = FALSE;
13763     player->is_pushing = FALSE;
13764   }
13765 #endif
13766
13767   jx = player->jx;
13768   jy = player->jy;
13769
13770 #if 1
13771   if (moved & MP_MOVING && !ScreenMovPos &&
13772       (player->index_nr == game.centered_player_nr ||
13773        game.centered_player_nr == -1))
13774 #else
13775   if (moved & MP_MOVING && !ScreenMovPos &&
13776       (player == local_player || !options.network))
13777 #endif
13778   {
13779     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13780     int offset = game.scroll_delay_value;
13781
13782     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13783     {
13784       /* actual player has left the screen -- scroll in that direction */
13785       if (jx != old_jx)         /* player has moved horizontally */
13786         scroll_x += (jx - old_jx);
13787       else                      /* player has moved vertically */
13788         scroll_y += (jy - old_jy);
13789     }
13790     else
13791     {
13792       if (jx != old_jx)         /* player has moved horizontally */
13793       {
13794         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13795             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13796           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13797
13798         /* don't scroll over playfield boundaries */
13799         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13800           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13801
13802         /* don't scroll more than one field at a time */
13803         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13804
13805         /* don't scroll against the player's moving direction */
13806         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13807             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13808           scroll_x = old_scroll_x;
13809       }
13810       else                      /* player has moved vertically */
13811       {
13812         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13813             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13814           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13815
13816         /* don't scroll over playfield boundaries */
13817         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13818           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13819
13820         /* don't scroll more than one field at a time */
13821         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13822
13823         /* don't scroll against the player's moving direction */
13824         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13825             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13826           scroll_y = old_scroll_y;
13827       }
13828     }
13829
13830     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13831     {
13832 #if 1
13833       if (!options.network && game.centered_player_nr == -1 &&
13834           !AllPlayersInVisibleScreen())
13835       {
13836         scroll_x = old_scroll_x;
13837         scroll_y = old_scroll_y;
13838       }
13839       else
13840 #else
13841       if (!options.network && !AllPlayersInVisibleScreen())
13842       {
13843         scroll_x = old_scroll_x;
13844         scroll_y = old_scroll_y;
13845       }
13846       else
13847 #endif
13848       {
13849         ScrollScreen(player, SCROLL_INIT);
13850         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13851       }
13852     }
13853   }
13854
13855   player->StepFrame = 0;
13856
13857   if (moved & MP_MOVING)
13858   {
13859     if (old_jx != jx && old_jy == jy)
13860       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13861     else if (old_jx == jx && old_jy != jy)
13862       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13863
13864     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13865
13866     player->last_move_dir = player->MovDir;
13867     player->is_moving = TRUE;
13868     player->is_snapping = FALSE;
13869     player->is_switching = FALSE;
13870     player->is_dropping = FALSE;
13871     player->is_dropping_pressed = FALSE;
13872     player->drop_pressed_delay = 0;
13873
13874 #if 0
13875     /* should better be called here than above, but this breaks some tapes */
13876     ScrollPlayer(player, SCROLL_INIT);
13877 #endif
13878   }
13879   else
13880   {
13881     CheckGravityMovementWhenNotMoving(player);
13882
13883     player->is_moving = FALSE;
13884
13885     /* at this point, the player is allowed to move, but cannot move right now
13886        (e.g. because of something blocking the way) -- ensure that the player
13887        is also allowed to move in the next frame (in old versions before 3.1.1,
13888        the player was forced to wait again for eight frames before next try) */
13889
13890     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13891       player->move_delay = 0;   /* allow direct movement in the next frame */
13892   }
13893
13894   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13895     player->move_delay = player->move_delay_value;
13896
13897   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13898   {
13899     TestIfPlayerTouchesBadThing(jx, jy);
13900     TestIfPlayerTouchesCustomElement(jx, jy);
13901   }
13902
13903   if (!player->active)
13904     RemovePlayer(player);
13905
13906   return moved;
13907 }
13908
13909 void ScrollPlayer(struct PlayerInfo *player, int mode)
13910 {
13911   int jx = player->jx, jy = player->jy;
13912   int last_jx = player->last_jx, last_jy = player->last_jy;
13913   int move_stepsize = TILEX / player->move_delay_value;
13914
13915 #if USE_NEW_PLAYER_SPEED
13916   if (!player->active)
13917     return;
13918
13919   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13920     return;
13921 #else
13922   if (!player->active || player->MovPos == 0)
13923     return;
13924 #endif
13925
13926   if (mode == SCROLL_INIT)
13927   {
13928     player->actual_frame_counter = FrameCounter;
13929     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13930
13931     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13932         Feld[last_jx][last_jy] == EL_EMPTY)
13933     {
13934       int last_field_block_delay = 0;   /* start with no blocking at all */
13935       int block_delay_adjustment = player->block_delay_adjustment;
13936
13937       /* if player blocks last field, add delay for exactly one move */
13938       if (player->block_last_field)
13939       {
13940         last_field_block_delay += player->move_delay_value;
13941
13942         /* when blocking enabled, prevent moving up despite gravity */
13943 #if USE_PLAYER_GRAVITY
13944         if (player->gravity && player->MovDir == MV_UP)
13945           block_delay_adjustment = -1;
13946 #else
13947         if (game.gravity && player->MovDir == MV_UP)
13948           block_delay_adjustment = -1;
13949 #endif
13950       }
13951
13952       /* add block delay adjustment (also possible when not blocking) */
13953       last_field_block_delay += block_delay_adjustment;
13954
13955       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13956       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13957     }
13958
13959 #if USE_NEW_PLAYER_SPEED
13960     if (player->MovPos != 0)    /* player has not yet reached destination */
13961       return;
13962 #else
13963     return;
13964 #endif
13965   }
13966   else if (!FrameReached(&player->actual_frame_counter, 1))
13967     return;
13968
13969 #if USE_NEW_PLAYER_SPEED
13970   if (player->MovPos != 0)
13971   {
13972     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13973     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13974
13975     /* before DrawPlayer() to draw correct player graphic for this case */
13976     if (player->MovPos == 0)
13977       CheckGravityMovement(player);
13978   }
13979 #else
13980   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13981   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13982
13983   /* before DrawPlayer() to draw correct player graphic for this case */
13984   if (player->MovPos == 0)
13985     CheckGravityMovement(player);
13986 #endif
13987
13988   if (player->MovPos == 0)      /* player reached destination field */
13989   {
13990     if (player->move_delay_reset_counter > 0)
13991     {
13992       player->move_delay_reset_counter--;
13993
13994       if (player->move_delay_reset_counter == 0)
13995       {
13996         /* continue with normal speed after quickly moving through gate */
13997         HALVE_PLAYER_SPEED(player);
13998
13999         /* be able to make the next move without delay */
14000         player->move_delay = 0;
14001       }
14002     }
14003
14004     player->last_jx = jx;
14005     player->last_jy = jy;
14006
14007     if (Feld[jx][jy] == EL_EXIT_OPEN ||
14008         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14009 #if 1
14010         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14011 #endif
14012         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14013         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14014 #if 1
14015         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14016 #endif
14017         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14018         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
14019     {
14020       DrawPlayer(player);       /* needed here only to cleanup last field */
14021       RemovePlayer(player);
14022
14023       if (local_player->friends_still_needed == 0 ||
14024           IS_SP_ELEMENT(Feld[jx][jy]))
14025         PlayerWins(player);
14026     }
14027
14028     /* this breaks one level: "machine", level 000 */
14029     {
14030       int move_direction = player->MovDir;
14031       int enter_side = MV_DIR_OPPOSITE(move_direction);
14032       int leave_side = move_direction;
14033       int old_jx = last_jx;
14034       int old_jy = last_jy;
14035       int old_element = Feld[old_jx][old_jy];
14036       int new_element = Feld[jx][jy];
14037
14038       if (IS_CUSTOM_ELEMENT(old_element))
14039         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14040                                    CE_LEFT_BY_PLAYER,
14041                                    player->index_bit, leave_side);
14042
14043       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14044                                           CE_PLAYER_LEAVES_X,
14045                                           player->index_bit, leave_side);
14046
14047       if (IS_CUSTOM_ELEMENT(new_element))
14048         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14049                                    player->index_bit, enter_side);
14050
14051       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14052                                           CE_PLAYER_ENTERS_X,
14053                                           player->index_bit, enter_side);
14054
14055 #if USE_FIX_CE_ACTION_WITH_PLAYER
14056       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14057                                         CE_MOVE_OF_X, move_direction);
14058 #else
14059       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14060                                         CE_MOVE_OF_X, move_direction);
14061 #endif
14062     }
14063
14064     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14065     {
14066       TestIfPlayerTouchesBadThing(jx, jy);
14067       TestIfPlayerTouchesCustomElement(jx, jy);
14068
14069       /* needed because pushed element has not yet reached its destination,
14070          so it would trigger a change event at its previous field location */
14071       if (!player->is_pushing)
14072         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
14073
14074       if (!player->active)
14075         RemovePlayer(player);
14076     }
14077
14078     if (!local_player->LevelSolved && level.use_step_counter)
14079     {
14080       int i;
14081
14082       TimePlayed++;
14083
14084       if (TimeLeft > 0)
14085       {
14086         TimeLeft--;
14087
14088         if (TimeLeft <= 10 && setup.time_limit)
14089           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14090
14091 #if 1
14092         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14093
14094         DisplayGameControlValues();
14095 #else
14096         DrawGameValue_Time(TimeLeft);
14097 #endif
14098
14099         if (!TimeLeft && setup.time_limit)
14100           for (i = 0; i < MAX_PLAYERS; i++)
14101             KillPlayer(&stored_player[i]);
14102       }
14103 #if 1
14104       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14105       {
14106         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14107
14108         DisplayGameControlValues();
14109       }
14110 #else
14111       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14112         DrawGameValue_Time(TimePlayed);
14113 #endif
14114     }
14115
14116     if (tape.single_step && tape.recording && !tape.pausing &&
14117         !player->programmed_action)
14118       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14119   }
14120 }
14121
14122 void ScrollScreen(struct PlayerInfo *player, int mode)
14123 {
14124   static unsigned int screen_frame_counter = 0;
14125
14126   if (mode == SCROLL_INIT)
14127   {
14128     /* set scrolling step size according to actual player's moving speed */
14129     ScrollStepSize = TILEX / player->move_delay_value;
14130
14131     screen_frame_counter = FrameCounter;
14132     ScreenMovDir = player->MovDir;
14133     ScreenMovPos = player->MovPos;
14134     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14135     return;
14136   }
14137   else if (!FrameReached(&screen_frame_counter, 1))
14138     return;
14139
14140   if (ScreenMovPos)
14141   {
14142     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14143     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14144     redraw_mask |= REDRAW_FIELD;
14145   }
14146   else
14147     ScreenMovDir = MV_NONE;
14148 }
14149
14150 void TestIfPlayerTouchesCustomElement(int x, int y)
14151 {
14152   static int xy[4][2] =
14153   {
14154     { 0, -1 },
14155     { -1, 0 },
14156     { +1, 0 },
14157     { 0, +1 }
14158   };
14159   static int trigger_sides[4][2] =
14160   {
14161     /* center side       border side */
14162     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14163     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14164     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14165     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14166   };
14167   static int touch_dir[4] =
14168   {
14169     MV_LEFT | MV_RIGHT,
14170     MV_UP   | MV_DOWN,
14171     MV_UP   | MV_DOWN,
14172     MV_LEFT | MV_RIGHT
14173   };
14174   int center_element = Feld[x][y];      /* should always be non-moving! */
14175   int i;
14176
14177   for (i = 0; i < NUM_DIRECTIONS; i++)
14178   {
14179     int xx = x + xy[i][0];
14180     int yy = y + xy[i][1];
14181     int center_side = trigger_sides[i][0];
14182     int border_side = trigger_sides[i][1];
14183     int border_element;
14184
14185     if (!IN_LEV_FIELD(xx, yy))
14186       continue;
14187
14188     if (IS_PLAYER(x, y))                /* player found at center element */
14189     {
14190       struct PlayerInfo *player = PLAYERINFO(x, y);
14191
14192       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14193         border_element = Feld[xx][yy];          /* may be moving! */
14194       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14195         border_element = Feld[xx][yy];
14196       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14197         border_element = MovingOrBlocked2Element(xx, yy);
14198       else
14199         continue;               /* center and border element do not touch */
14200
14201       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14202                                  player->index_bit, border_side);
14203       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14204                                           CE_PLAYER_TOUCHES_X,
14205                                           player->index_bit, border_side);
14206
14207 #if USE_FIX_CE_ACTION_WITH_PLAYER
14208       {
14209         /* use player element that is initially defined in the level playfield,
14210            not the player element that corresponds to the runtime player number
14211            (example: a level that contains EL_PLAYER_3 as the only player would
14212            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14213         int player_element = PLAYERINFO(x, y)->initial_element;
14214
14215         CheckElementChangeBySide(xx, yy, border_element, player_element,
14216                                  CE_TOUCHING_X, border_side);
14217       }
14218 #endif
14219     }
14220     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14221     {
14222       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14223
14224       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14225       {
14226         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14227           continue;             /* center and border element do not touch */
14228       }
14229
14230       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14231                                  player->index_bit, center_side);
14232       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14233                                           CE_PLAYER_TOUCHES_X,
14234                                           player->index_bit, center_side);
14235
14236 #if USE_FIX_CE_ACTION_WITH_PLAYER
14237       {
14238         /* use player element that is initially defined in the level playfield,
14239            not the player element that corresponds to the runtime player number
14240            (example: a level that contains EL_PLAYER_3 as the only player would
14241            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14242         int player_element = PLAYERINFO(xx, yy)->initial_element;
14243
14244         CheckElementChangeBySide(x, y, center_element, player_element,
14245                                  CE_TOUCHING_X, center_side);
14246       }
14247 #endif
14248
14249       break;
14250     }
14251   }
14252 }
14253
14254 #if USE_ELEMENT_TOUCHING_BUGFIX
14255
14256 void TestIfElementTouchesCustomElement(int x, int y)
14257 {
14258   static int xy[4][2] =
14259   {
14260     { 0, -1 },
14261     { -1, 0 },
14262     { +1, 0 },
14263     { 0, +1 }
14264   };
14265   static int trigger_sides[4][2] =
14266   {
14267     /* center side      border side */
14268     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14269     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14270     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14271     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14272   };
14273   static int touch_dir[4] =
14274   {
14275     MV_LEFT | MV_RIGHT,
14276     MV_UP   | MV_DOWN,
14277     MV_UP   | MV_DOWN,
14278     MV_LEFT | MV_RIGHT
14279   };
14280   boolean change_center_element = FALSE;
14281   int center_element = Feld[x][y];      /* should always be non-moving! */
14282   int border_element_old[NUM_DIRECTIONS];
14283   int i;
14284
14285   for (i = 0; i < NUM_DIRECTIONS; i++)
14286   {
14287     int xx = x + xy[i][0];
14288     int yy = y + xy[i][1];
14289     int border_element;
14290
14291     border_element_old[i] = -1;
14292
14293     if (!IN_LEV_FIELD(xx, yy))
14294       continue;
14295
14296     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14297       border_element = Feld[xx][yy];    /* may be moving! */
14298     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14299       border_element = Feld[xx][yy];
14300     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14301       border_element = MovingOrBlocked2Element(xx, yy);
14302     else
14303       continue;                 /* center and border element do not touch */
14304
14305     border_element_old[i] = border_element;
14306   }
14307
14308   for (i = 0; i < NUM_DIRECTIONS; i++)
14309   {
14310     int xx = x + xy[i][0];
14311     int yy = y + xy[i][1];
14312     int center_side = trigger_sides[i][0];
14313     int border_element = border_element_old[i];
14314
14315     if (border_element == -1)
14316       continue;
14317
14318     /* check for change of border element */
14319     CheckElementChangeBySide(xx, yy, border_element, center_element,
14320                              CE_TOUCHING_X, center_side);
14321
14322     /* (center element cannot be player, so we dont have to check this here) */
14323   }
14324
14325   for (i = 0; i < NUM_DIRECTIONS; i++)
14326   {
14327     int xx = x + xy[i][0];
14328     int yy = y + xy[i][1];
14329     int border_side = trigger_sides[i][1];
14330     int border_element = border_element_old[i];
14331
14332     if (border_element == -1)
14333       continue;
14334
14335     /* check for change of center element (but change it only once) */
14336     if (!change_center_element)
14337       change_center_element =
14338         CheckElementChangeBySide(x, y, center_element, border_element,
14339                                  CE_TOUCHING_X, border_side);
14340
14341 #if USE_FIX_CE_ACTION_WITH_PLAYER
14342     if (IS_PLAYER(xx, yy))
14343     {
14344       /* use player element that is initially defined in the level playfield,
14345          not the player element that corresponds to the runtime player number
14346          (example: a level that contains EL_PLAYER_3 as the only player would
14347          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14348       int player_element = PLAYERINFO(xx, yy)->initial_element;
14349
14350       CheckElementChangeBySide(x, y, center_element, player_element,
14351                                CE_TOUCHING_X, border_side);
14352     }
14353 #endif
14354   }
14355 }
14356
14357 #else
14358
14359 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14360 {
14361   static int xy[4][2] =
14362   {
14363     { 0, -1 },
14364     { -1, 0 },
14365     { +1, 0 },
14366     { 0, +1 }
14367   };
14368   static int trigger_sides[4][2] =
14369   {
14370     /* center side      border side */
14371     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14372     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14373     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14374     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14375   };
14376   static int touch_dir[4] =
14377   {
14378     MV_LEFT | MV_RIGHT,
14379     MV_UP   | MV_DOWN,
14380     MV_UP   | MV_DOWN,
14381     MV_LEFT | MV_RIGHT
14382   };
14383   boolean change_center_element = FALSE;
14384   int center_element = Feld[x][y];      /* should always be non-moving! */
14385   int i;
14386
14387   for (i = 0; i < NUM_DIRECTIONS; i++)
14388   {
14389     int xx = x + xy[i][0];
14390     int yy = y + xy[i][1];
14391     int center_side = trigger_sides[i][0];
14392     int border_side = trigger_sides[i][1];
14393     int border_element;
14394
14395     if (!IN_LEV_FIELD(xx, yy))
14396       continue;
14397
14398     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14399       border_element = Feld[xx][yy];    /* may be moving! */
14400     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14401       border_element = Feld[xx][yy];
14402     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14403       border_element = MovingOrBlocked2Element(xx, yy);
14404     else
14405       continue;                 /* center and border element do not touch */
14406
14407     /* check for change of center element (but change it only once) */
14408     if (!change_center_element)
14409       change_center_element =
14410         CheckElementChangeBySide(x, y, center_element, border_element,
14411                                  CE_TOUCHING_X, border_side);
14412
14413     /* check for change of border element */
14414     CheckElementChangeBySide(xx, yy, border_element, center_element,
14415                              CE_TOUCHING_X, center_side);
14416   }
14417 }
14418
14419 #endif
14420
14421 void TestIfElementHitsCustomElement(int x, int y, int direction)
14422 {
14423   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14424   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14425   int hitx = x + dx, hity = y + dy;
14426   int hitting_element = Feld[x][y];
14427   int touched_element;
14428
14429   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14430     return;
14431
14432   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14433                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14434
14435   if (IN_LEV_FIELD(hitx, hity))
14436   {
14437     int opposite_direction = MV_DIR_OPPOSITE(direction);
14438     int hitting_side = direction;
14439     int touched_side = opposite_direction;
14440     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14441                           MovDir[hitx][hity] != direction ||
14442                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14443
14444     object_hit = TRUE;
14445
14446     if (object_hit)
14447     {
14448       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14449                                CE_HITTING_X, touched_side);
14450
14451       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14452                                CE_HIT_BY_X, hitting_side);
14453
14454       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14455                                CE_HIT_BY_SOMETHING, opposite_direction);
14456
14457 #if USE_FIX_CE_ACTION_WITH_PLAYER
14458       if (IS_PLAYER(hitx, hity))
14459       {
14460         /* use player element that is initially defined in the level playfield,
14461            not the player element that corresponds to the runtime player number
14462            (example: a level that contains EL_PLAYER_3 as the only player would
14463            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14464         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14465
14466         CheckElementChangeBySide(x, y, hitting_element, player_element,
14467                                  CE_HITTING_X, touched_side);
14468       }
14469 #endif
14470     }
14471   }
14472
14473   /* "hitting something" is also true when hitting the playfield border */
14474   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14475                            CE_HITTING_SOMETHING, direction);
14476 }
14477
14478 #if 0
14479 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14480 {
14481   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14482   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14483   int hitx = x + dx, hity = y + dy;
14484   int hitting_element = Feld[x][y];
14485   int touched_element;
14486 #if 0
14487   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14488                         !IS_FREE(hitx, hity) &&
14489                         (!IS_MOVING(hitx, hity) ||
14490                          MovDir[hitx][hity] != direction ||
14491                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14492 #endif
14493
14494   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14495     return;
14496
14497 #if 0
14498   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14499     return;
14500 #endif
14501
14502   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14503                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14504
14505   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14506                            EP_CAN_SMASH_EVERYTHING, direction);
14507
14508   if (IN_LEV_FIELD(hitx, hity))
14509   {
14510     int opposite_direction = MV_DIR_OPPOSITE(direction);
14511     int hitting_side = direction;
14512     int touched_side = opposite_direction;
14513 #if 0
14514     int touched_element = MovingOrBlocked2Element(hitx, hity);
14515 #endif
14516 #if 1
14517     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14518                           MovDir[hitx][hity] != direction ||
14519                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14520
14521     object_hit = TRUE;
14522 #endif
14523
14524     if (object_hit)
14525     {
14526       int i;
14527
14528       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14529                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14530
14531       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14532                                CE_OTHER_IS_SMASHING, touched_side);
14533
14534       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14535                                CE_OTHER_GETS_SMASHED, hitting_side);
14536     }
14537   }
14538 }
14539 #endif
14540
14541 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14542 {
14543   int i, kill_x = -1, kill_y = -1;
14544
14545   int bad_element = -1;
14546   static int test_xy[4][2] =
14547   {
14548     { 0, -1 },
14549     { -1, 0 },
14550     { +1, 0 },
14551     { 0, +1 }
14552   };
14553   static int test_dir[4] =
14554   {
14555     MV_UP,
14556     MV_LEFT,
14557     MV_RIGHT,
14558     MV_DOWN
14559   };
14560
14561   for (i = 0; i < NUM_DIRECTIONS; i++)
14562   {
14563     int test_x, test_y, test_move_dir, test_element;
14564
14565     test_x = good_x + test_xy[i][0];
14566     test_y = good_y + test_xy[i][1];
14567
14568     if (!IN_LEV_FIELD(test_x, test_y))
14569       continue;
14570
14571     test_move_dir =
14572       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14573
14574     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14575
14576     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14577        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14578     */
14579     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14580         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14581     {
14582       kill_x = test_x;
14583       kill_y = test_y;
14584       bad_element = test_element;
14585
14586       break;
14587     }
14588   }
14589
14590   if (kill_x != -1 || kill_y != -1)
14591   {
14592     if (IS_PLAYER(good_x, good_y))
14593     {
14594       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14595
14596       if (player->shield_deadly_time_left > 0 &&
14597           !IS_INDESTRUCTIBLE(bad_element))
14598         Bang(kill_x, kill_y);
14599       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14600         KillPlayer(player);
14601     }
14602     else
14603       Bang(good_x, good_y);
14604   }
14605 }
14606
14607 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14608 {
14609   int i, kill_x = -1, kill_y = -1;
14610   int bad_element = Feld[bad_x][bad_y];
14611   static int test_xy[4][2] =
14612   {
14613     { 0, -1 },
14614     { -1, 0 },
14615     { +1, 0 },
14616     { 0, +1 }
14617   };
14618   static int touch_dir[4] =
14619   {
14620     MV_LEFT | MV_RIGHT,
14621     MV_UP   | MV_DOWN,
14622     MV_UP   | MV_DOWN,
14623     MV_LEFT | MV_RIGHT
14624   };
14625   static int test_dir[4] =
14626   {
14627     MV_UP,
14628     MV_LEFT,
14629     MV_RIGHT,
14630     MV_DOWN
14631   };
14632
14633   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14634     return;
14635
14636   for (i = 0; i < NUM_DIRECTIONS; i++)
14637   {
14638     int test_x, test_y, test_move_dir, test_element;
14639
14640     test_x = bad_x + test_xy[i][0];
14641     test_y = bad_y + test_xy[i][1];
14642
14643     if (!IN_LEV_FIELD(test_x, test_y))
14644       continue;
14645
14646     test_move_dir =
14647       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14648
14649     test_element = Feld[test_x][test_y];
14650
14651     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14652        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14653     */
14654     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14655         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14656     {
14657       /* good thing is player or penguin that does not move away */
14658       if (IS_PLAYER(test_x, test_y))
14659       {
14660         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14661
14662         if (bad_element == EL_ROBOT && player->is_moving)
14663           continue;     /* robot does not kill player if he is moving */
14664
14665         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14666         {
14667           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14668             continue;           /* center and border element do not touch */
14669         }
14670
14671         kill_x = test_x;
14672         kill_y = test_y;
14673
14674         break;
14675       }
14676       else if (test_element == EL_PENGUIN)
14677       {
14678         kill_x = test_x;
14679         kill_y = test_y;
14680
14681         break;
14682       }
14683     }
14684   }
14685
14686   if (kill_x != -1 || kill_y != -1)
14687   {
14688     if (IS_PLAYER(kill_x, kill_y))
14689     {
14690       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14691
14692       if (player->shield_deadly_time_left > 0 &&
14693           !IS_INDESTRUCTIBLE(bad_element))
14694         Bang(bad_x, bad_y);
14695       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14696         KillPlayer(player);
14697     }
14698     else
14699       Bang(kill_x, kill_y);
14700   }
14701 }
14702
14703 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14704 {
14705   int bad_element = Feld[bad_x][bad_y];
14706   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14707   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14708   int test_x = bad_x + dx, test_y = bad_y + dy;
14709   int test_move_dir, test_element;
14710   int kill_x = -1, kill_y = -1;
14711
14712   if (!IN_LEV_FIELD(test_x, test_y))
14713     return;
14714
14715   test_move_dir =
14716     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14717
14718   test_element = Feld[test_x][test_y];
14719
14720   if (test_move_dir != bad_move_dir)
14721   {
14722     /* good thing can be player or penguin that does not move away */
14723     if (IS_PLAYER(test_x, test_y))
14724     {
14725       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14726
14727       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14728          player as being hit when he is moving towards the bad thing, because
14729          the "get hit by" condition would be lost after the player stops) */
14730       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14731         return;         /* player moves away from bad thing */
14732
14733       kill_x = test_x;
14734       kill_y = test_y;
14735     }
14736     else if (test_element == EL_PENGUIN)
14737     {
14738       kill_x = test_x;
14739       kill_y = test_y;
14740     }
14741   }
14742
14743   if (kill_x != -1 || kill_y != -1)
14744   {
14745     if (IS_PLAYER(kill_x, kill_y))
14746     {
14747       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14748
14749       if (player->shield_deadly_time_left > 0 &&
14750           !IS_INDESTRUCTIBLE(bad_element))
14751         Bang(bad_x, bad_y);
14752       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14753         KillPlayer(player);
14754     }
14755     else
14756       Bang(kill_x, kill_y);
14757   }
14758 }
14759
14760 void TestIfPlayerTouchesBadThing(int x, int y)
14761 {
14762   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14763 }
14764
14765 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14766 {
14767   TestIfGoodThingHitsBadThing(x, y, move_dir);
14768 }
14769
14770 void TestIfBadThingTouchesPlayer(int x, int y)
14771 {
14772   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14773 }
14774
14775 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14776 {
14777   TestIfBadThingHitsGoodThing(x, y, move_dir);
14778 }
14779
14780 void TestIfFriendTouchesBadThing(int x, int y)
14781 {
14782   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14783 }
14784
14785 void TestIfBadThingTouchesFriend(int x, int y)
14786 {
14787   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14788 }
14789
14790 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14791 {
14792   int i, kill_x = bad_x, kill_y = bad_y;
14793   static int xy[4][2] =
14794   {
14795     { 0, -1 },
14796     { -1, 0 },
14797     { +1, 0 },
14798     { 0, +1 }
14799   };
14800
14801   for (i = 0; i < NUM_DIRECTIONS; i++)
14802   {
14803     int x, y, element;
14804
14805     x = bad_x + xy[i][0];
14806     y = bad_y + xy[i][1];
14807     if (!IN_LEV_FIELD(x, y))
14808       continue;
14809
14810     element = Feld[x][y];
14811     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14812         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14813     {
14814       kill_x = x;
14815       kill_y = y;
14816       break;
14817     }
14818   }
14819
14820   if (kill_x != bad_x || kill_y != bad_y)
14821     Bang(bad_x, bad_y);
14822 }
14823
14824 void KillPlayer(struct PlayerInfo *player)
14825 {
14826   int jx = player->jx, jy = player->jy;
14827
14828   if (!player->active)
14829     return;
14830
14831 #if 0
14832   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14833          player->killed, player->active, player->reanimated);
14834 #endif
14835
14836   /* the following code was introduced to prevent an infinite loop when calling
14837      -> Bang()
14838      -> CheckTriggeredElementChangeExt()
14839      -> ExecuteCustomElementAction()
14840      -> KillPlayer()
14841      -> (infinitely repeating the above sequence of function calls)
14842      which occurs when killing the player while having a CE with the setting
14843      "kill player X when explosion of <player X>"; the solution using a new
14844      field "player->killed" was chosen for backwards compatibility, although
14845      clever use of the fields "player->active" etc. would probably also work */
14846 #if 1
14847   if (player->killed)
14848     return;
14849 #endif
14850
14851   player->killed = TRUE;
14852
14853   /* remove accessible field at the player's position */
14854   Feld[jx][jy] = EL_EMPTY;
14855
14856   /* deactivate shield (else Bang()/Explode() would not work right) */
14857   player->shield_normal_time_left = 0;
14858   player->shield_deadly_time_left = 0;
14859
14860 #if 0
14861   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14862          player->killed, player->active, player->reanimated);
14863 #endif
14864
14865   Bang(jx, jy);
14866
14867 #if 0
14868   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14869          player->killed, player->active, player->reanimated);
14870 #endif
14871
14872 #if USE_PLAYER_REANIMATION
14873 #if 1
14874   if (player->reanimated)       /* killed player may have been reanimated */
14875     player->killed = player->reanimated = FALSE;
14876   else
14877     BuryPlayer(player);
14878 #else
14879   if (player->killed)           /* player may have been reanimated */
14880     BuryPlayer(player);
14881 #endif
14882 #else
14883   BuryPlayer(player);
14884 #endif
14885 }
14886
14887 static void KillPlayerUnlessEnemyProtected(int x, int y)
14888 {
14889   if (!PLAYER_ENEMY_PROTECTED(x, y))
14890     KillPlayer(PLAYERINFO(x, y));
14891 }
14892
14893 static void KillPlayerUnlessExplosionProtected(int x, int y)
14894 {
14895   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14896     KillPlayer(PLAYERINFO(x, y));
14897 }
14898
14899 void BuryPlayer(struct PlayerInfo *player)
14900 {
14901   int jx = player->jx, jy = player->jy;
14902
14903   if (!player->active)
14904     return;
14905
14906   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14907   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14908
14909   player->GameOver = TRUE;
14910   RemovePlayer(player);
14911 }
14912
14913 void RemovePlayer(struct PlayerInfo *player)
14914 {
14915   int jx = player->jx, jy = player->jy;
14916   int i, found = FALSE;
14917
14918   player->present = FALSE;
14919   player->active = FALSE;
14920
14921   if (!ExplodeField[jx][jy])
14922     StorePlayer[jx][jy] = 0;
14923
14924   if (player->is_moving)
14925     TEST_DrawLevelField(player->last_jx, player->last_jy);
14926
14927   for (i = 0; i < MAX_PLAYERS; i++)
14928     if (stored_player[i].active)
14929       found = TRUE;
14930
14931   if (!found)
14932     AllPlayersGone = TRUE;
14933
14934   ExitX = ZX = jx;
14935   ExitY = ZY = jy;
14936 }
14937
14938 #if USE_NEW_SNAP_DELAY
14939 static void setFieldForSnapping(int x, int y, int element, int direction)
14940 {
14941   struct ElementInfo *ei = &element_info[element];
14942   int direction_bit = MV_DIR_TO_BIT(direction);
14943   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14944   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14945                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14946
14947   Feld[x][y] = EL_ELEMENT_SNAPPING;
14948   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14949
14950   ResetGfxAnimation(x, y);
14951
14952   GfxElement[x][y] = element;
14953   GfxAction[x][y] = action;
14954   GfxDir[x][y] = direction;
14955   GfxFrame[x][y] = -1;
14956 }
14957 #endif
14958
14959 /*
14960   =============================================================================
14961   checkDiagonalPushing()
14962   -----------------------------------------------------------------------------
14963   check if diagonal input device direction results in pushing of object
14964   (by checking if the alternative direction is walkable, diggable, ...)
14965   =============================================================================
14966 */
14967
14968 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14969                                     int x, int y, int real_dx, int real_dy)
14970 {
14971   int jx, jy, dx, dy, xx, yy;
14972
14973   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14974     return TRUE;
14975
14976   /* diagonal direction: check alternative direction */
14977   jx = player->jx;
14978   jy = player->jy;
14979   dx = x - jx;
14980   dy = y - jy;
14981   xx = jx + (dx == 0 ? real_dx : 0);
14982   yy = jy + (dy == 0 ? real_dy : 0);
14983
14984   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14985 }
14986
14987 /*
14988   =============================================================================
14989   DigField()
14990   -----------------------------------------------------------------------------
14991   x, y:                 field next to player (non-diagonal) to try to dig to
14992   real_dx, real_dy:     direction as read from input device (can be diagonal)
14993   =============================================================================
14994 */
14995
14996 static int DigField(struct PlayerInfo *player,
14997                     int oldx, int oldy, int x, int y,
14998                     int real_dx, int real_dy, int mode)
14999 {
15000   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15001   boolean player_was_pushing = player->is_pushing;
15002   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15003   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15004   int jx = oldx, jy = oldy;
15005   int dx = x - jx, dy = y - jy;
15006   int nextx = x + dx, nexty = y + dy;
15007   int move_direction = (dx == -1 ? MV_LEFT  :
15008                         dx == +1 ? MV_RIGHT :
15009                         dy == -1 ? MV_UP    :
15010                         dy == +1 ? MV_DOWN  : MV_NONE);
15011   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15012   int dig_side = MV_DIR_OPPOSITE(move_direction);
15013   int old_element = Feld[jx][jy];
15014 #if USE_FIXED_DONT_RUN_INTO
15015   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15016 #else
15017   int element;
15018 #endif
15019   int collect_count;
15020
15021   if (is_player)                /* function can also be called by EL_PENGUIN */
15022   {
15023     if (player->MovPos == 0)
15024     {
15025       player->is_digging = FALSE;
15026       player->is_collecting = FALSE;
15027     }
15028
15029     if (player->MovPos == 0)    /* last pushing move finished */
15030       player->is_pushing = FALSE;
15031
15032     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
15033     {
15034       player->is_switching = FALSE;
15035       player->push_delay = -1;
15036
15037       return MP_NO_ACTION;
15038     }
15039   }
15040
15041 #if !USE_FIXED_DONT_RUN_INTO
15042   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15043     return MP_NO_ACTION;
15044 #endif
15045
15046   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15047     old_element = Back[jx][jy];
15048
15049   /* in case of element dropped at player position, check background */
15050   else if (Back[jx][jy] != EL_EMPTY &&
15051            game.engine_version >= VERSION_IDENT(2,2,0,0))
15052     old_element = Back[jx][jy];
15053
15054   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15055     return MP_NO_ACTION;        /* field has no opening in this direction */
15056
15057   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15058     return MP_NO_ACTION;        /* field has no opening in this direction */
15059
15060 #if USE_FIXED_DONT_RUN_INTO
15061   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15062   {
15063     SplashAcid(x, y);
15064
15065     Feld[jx][jy] = player->artwork_element;
15066     InitMovingField(jx, jy, MV_DOWN);
15067     Store[jx][jy] = EL_ACID;
15068     ContinueMoving(jx, jy);
15069     BuryPlayer(player);
15070
15071     return MP_DONT_RUN_INTO;
15072   }
15073 #endif
15074
15075 #if USE_FIXED_DONT_RUN_INTO
15076   if (player_can_move && DONT_RUN_INTO(element))
15077   {
15078     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15079
15080     return MP_DONT_RUN_INTO;
15081   }
15082 #endif
15083
15084 #if USE_FIXED_DONT_RUN_INTO
15085   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15086     return MP_NO_ACTION;
15087 #endif
15088
15089 #if !USE_FIXED_DONT_RUN_INTO
15090   element = Feld[x][y];
15091 #endif
15092
15093   collect_count = element_info[element].collect_count_initial;
15094
15095   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
15096     return MP_NO_ACTION;
15097
15098   if (game.engine_version < VERSION_IDENT(2,2,0,0))
15099     player_can_move = player_can_move_or_snap;
15100
15101   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15102       game.engine_version >= VERSION_IDENT(2,2,0,0))
15103   {
15104     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15105                                player->index_bit, dig_side);
15106     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15107                                         player->index_bit, dig_side);
15108
15109     if (element == EL_DC_LANDMINE)
15110       Bang(x, y);
15111
15112     if (Feld[x][y] != element)          /* field changed by snapping */
15113       return MP_ACTION;
15114
15115     return MP_NO_ACTION;
15116   }
15117
15118 #if USE_PLAYER_GRAVITY
15119   if (player->gravity && is_player && !player->is_auto_moving &&
15120       canFallDown(player) && move_direction != MV_DOWN &&
15121       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15122     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15123 #else
15124   if (game.gravity && is_player && !player->is_auto_moving &&
15125       canFallDown(player) && move_direction != MV_DOWN &&
15126       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15127     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
15128 #endif
15129
15130   if (player_can_move &&
15131       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15132   {
15133     int sound_element = SND_ELEMENT(element);
15134     int sound_action = ACTION_WALKING;
15135
15136     if (IS_RND_GATE(element))
15137     {
15138       if (!player->key[RND_GATE_NR(element)])
15139         return MP_NO_ACTION;
15140     }
15141     else if (IS_RND_GATE_GRAY(element))
15142     {
15143       if (!player->key[RND_GATE_GRAY_NR(element)])
15144         return MP_NO_ACTION;
15145     }
15146     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15147     {
15148       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15149         return MP_NO_ACTION;
15150     }
15151     else if (element == EL_EXIT_OPEN ||
15152              element == EL_EM_EXIT_OPEN ||
15153 #if 1
15154              element == EL_EM_EXIT_OPENING ||
15155 #endif
15156              element == EL_STEEL_EXIT_OPEN ||
15157              element == EL_EM_STEEL_EXIT_OPEN ||
15158 #if 1
15159              element == EL_EM_STEEL_EXIT_OPENING ||
15160 #endif
15161              element == EL_SP_EXIT_OPEN ||
15162              element == EL_SP_EXIT_OPENING)
15163     {
15164       sound_action = ACTION_PASSING;    /* player is passing exit */
15165     }
15166     else if (element == EL_EMPTY)
15167     {
15168       sound_action = ACTION_MOVING;             /* nothing to walk on */
15169     }
15170
15171     /* play sound from background or player, whatever is available */
15172     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15173       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15174     else
15175       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15176   }
15177   else if (player_can_move &&
15178            IS_PASSABLE(element) && canPassField(x, y, move_direction))
15179   {
15180     if (!ACCESS_FROM(element, opposite_direction))
15181       return MP_NO_ACTION;      /* field not accessible from this direction */
15182
15183     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
15184       return MP_NO_ACTION;
15185
15186     if (IS_EM_GATE(element))
15187     {
15188       if (!player->key[EM_GATE_NR(element)])
15189         return MP_NO_ACTION;
15190     }
15191     else if (IS_EM_GATE_GRAY(element))
15192     {
15193       if (!player->key[EM_GATE_GRAY_NR(element)])
15194         return MP_NO_ACTION;
15195     }
15196     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15197     {
15198       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15199         return MP_NO_ACTION;
15200     }
15201     else if (IS_EMC_GATE(element))
15202     {
15203       if (!player->key[EMC_GATE_NR(element)])
15204         return MP_NO_ACTION;
15205     }
15206     else if (IS_EMC_GATE_GRAY(element))
15207     {
15208       if (!player->key[EMC_GATE_GRAY_NR(element)])
15209         return MP_NO_ACTION;
15210     }
15211     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15212     {
15213       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15214         return MP_NO_ACTION;
15215     }
15216     else if (element == EL_DC_GATE_WHITE ||
15217              element == EL_DC_GATE_WHITE_GRAY ||
15218              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15219     {
15220       if (player->num_white_keys == 0)
15221         return MP_NO_ACTION;
15222
15223       player->num_white_keys--;
15224     }
15225     else if (IS_SP_PORT(element))
15226     {
15227       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15228           element == EL_SP_GRAVITY_PORT_RIGHT ||
15229           element == EL_SP_GRAVITY_PORT_UP ||
15230           element == EL_SP_GRAVITY_PORT_DOWN)
15231 #if USE_PLAYER_GRAVITY
15232         player->gravity = !player->gravity;
15233 #else
15234         game.gravity = !game.gravity;
15235 #endif
15236       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15237                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15238                element == EL_SP_GRAVITY_ON_PORT_UP ||
15239                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15240 #if USE_PLAYER_GRAVITY
15241         player->gravity = TRUE;
15242 #else
15243         game.gravity = TRUE;
15244 #endif
15245       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15246                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15247                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15248                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15249 #if USE_PLAYER_GRAVITY
15250         player->gravity = FALSE;
15251 #else
15252         game.gravity = FALSE;
15253 #endif
15254     }
15255
15256     /* automatically move to the next field with double speed */
15257     player->programmed_action = move_direction;
15258
15259     if (player->move_delay_reset_counter == 0)
15260     {
15261       player->move_delay_reset_counter = 2;     /* two double speed steps */
15262
15263       DOUBLE_PLAYER_SPEED(player);
15264     }
15265
15266     PlayLevelSoundAction(x, y, ACTION_PASSING);
15267   }
15268   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15269   {
15270     RemoveField(x, y);
15271
15272     if (mode != DF_SNAP)
15273     {
15274       GfxElement[x][y] = GFX_ELEMENT(element);
15275       player->is_digging = TRUE;
15276     }
15277
15278     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15279
15280     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15281                                         player->index_bit, dig_side);
15282
15283     if (mode == DF_SNAP)
15284     {
15285 #if USE_NEW_SNAP_DELAY
15286       if (level.block_snap_field)
15287         setFieldForSnapping(x, y, element, move_direction);
15288       else
15289         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15290 #else
15291       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15292 #endif
15293
15294       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15295                                           player->index_bit, dig_side);
15296     }
15297   }
15298   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15299   {
15300     RemoveField(x, y);
15301
15302     if (is_player && mode != DF_SNAP)
15303     {
15304       GfxElement[x][y] = element;
15305       player->is_collecting = TRUE;
15306     }
15307
15308     if (element == EL_SPEED_PILL)
15309     {
15310       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15311     }
15312     else if (element == EL_EXTRA_TIME && level.time > 0)
15313     {
15314       TimeLeft += level.extra_time;
15315
15316 #if 1
15317       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15318
15319       DisplayGameControlValues();
15320 #else
15321       DrawGameValue_Time(TimeLeft);
15322 #endif
15323     }
15324     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15325     {
15326       player->shield_normal_time_left += level.shield_normal_time;
15327       if (element == EL_SHIELD_DEADLY)
15328         player->shield_deadly_time_left += level.shield_deadly_time;
15329     }
15330     else if (element == EL_DYNAMITE ||
15331              element == EL_EM_DYNAMITE ||
15332              element == EL_SP_DISK_RED)
15333     {
15334       if (player->inventory_size < MAX_INVENTORY_SIZE)
15335         player->inventory_element[player->inventory_size++] = element;
15336
15337       DrawGameDoorValues();
15338     }
15339     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15340     {
15341       player->dynabomb_count++;
15342       player->dynabombs_left++;
15343     }
15344     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15345     {
15346       player->dynabomb_size++;
15347     }
15348     else if (element == EL_DYNABOMB_INCREASE_POWER)
15349     {
15350       player->dynabomb_xl = TRUE;
15351     }
15352     else if (IS_KEY(element))
15353     {
15354       player->key[KEY_NR(element)] = TRUE;
15355
15356       DrawGameDoorValues();
15357     }
15358     else if (element == EL_DC_KEY_WHITE)
15359     {
15360       player->num_white_keys++;
15361
15362       /* display white keys? */
15363       /* DrawGameDoorValues(); */
15364     }
15365     else if (IS_ENVELOPE(element))
15366     {
15367       player->show_envelope = element;
15368     }
15369     else if (element == EL_EMC_LENSES)
15370     {
15371       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15372
15373       RedrawAllInvisibleElementsForLenses();
15374     }
15375     else if (element == EL_EMC_MAGNIFIER)
15376     {
15377       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15378
15379       RedrawAllInvisibleElementsForMagnifier();
15380     }
15381     else if (IS_DROPPABLE(element) ||
15382              IS_THROWABLE(element))     /* can be collected and dropped */
15383     {
15384       int i;
15385
15386       if (collect_count == 0)
15387         player->inventory_infinite_element = element;
15388       else
15389         for (i = 0; i < collect_count; i++)
15390           if (player->inventory_size < MAX_INVENTORY_SIZE)
15391             player->inventory_element[player->inventory_size++] = element;
15392
15393       DrawGameDoorValues();
15394     }
15395     else if (collect_count > 0)
15396     {
15397       local_player->gems_still_needed -= collect_count;
15398       if (local_player->gems_still_needed < 0)
15399         local_player->gems_still_needed = 0;
15400
15401 #if 1
15402       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15403
15404       DisplayGameControlValues();
15405 #else
15406       DrawGameValue_Emeralds(local_player->gems_still_needed);
15407 #endif
15408     }
15409
15410     RaiseScoreElement(element);
15411     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15412
15413     if (is_player)
15414       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15415                                           player->index_bit, dig_side);
15416
15417     if (mode == DF_SNAP)
15418     {
15419 #if USE_NEW_SNAP_DELAY
15420       if (level.block_snap_field)
15421         setFieldForSnapping(x, y, element, move_direction);
15422       else
15423         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15424 #else
15425       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15426 #endif
15427
15428       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15429                                           player->index_bit, dig_side);
15430     }
15431   }
15432   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15433   {
15434     if (mode == DF_SNAP && element != EL_BD_ROCK)
15435       return MP_NO_ACTION;
15436
15437     if (CAN_FALL(element) && dy)
15438       return MP_NO_ACTION;
15439
15440     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15441         !(element == EL_SPRING && level.use_spring_bug))
15442       return MP_NO_ACTION;
15443
15444     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15445         ((move_direction & MV_VERTICAL &&
15446           ((element_info[element].move_pattern & MV_LEFT &&
15447             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15448            (element_info[element].move_pattern & MV_RIGHT &&
15449             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15450          (move_direction & MV_HORIZONTAL &&
15451           ((element_info[element].move_pattern & MV_UP &&
15452             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15453            (element_info[element].move_pattern & MV_DOWN &&
15454             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15455       return MP_NO_ACTION;
15456
15457     /* do not push elements already moving away faster than player */
15458     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15459         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15460       return MP_NO_ACTION;
15461
15462     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15463     {
15464       if (player->push_delay_value == -1 || !player_was_pushing)
15465         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15466     }
15467     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15468     {
15469       if (player->push_delay_value == -1)
15470         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15471     }
15472     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15473     {
15474       if (!player->is_pushing)
15475         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15476     }
15477
15478     player->is_pushing = TRUE;
15479     player->is_active = TRUE;
15480
15481     if (!(IN_LEV_FIELD(nextx, nexty) &&
15482           (IS_FREE(nextx, nexty) ||
15483            (IS_SB_ELEMENT(element) &&
15484             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15485            (IS_CUSTOM_ELEMENT(element) &&
15486             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15487       return MP_NO_ACTION;
15488
15489     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15490       return MP_NO_ACTION;
15491
15492     if (player->push_delay == -1)       /* new pushing; restart delay */
15493       player->push_delay = 0;
15494
15495     if (player->push_delay < player->push_delay_value &&
15496         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15497         element != EL_SPRING && element != EL_BALLOON)
15498     {
15499       /* make sure that there is no move delay before next try to push */
15500       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15501         player->move_delay = 0;
15502
15503       return MP_NO_ACTION;
15504     }
15505
15506     if (IS_CUSTOM_ELEMENT(element) &&
15507         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15508     {
15509       if (!DigFieldByCE(nextx, nexty, element))
15510         return MP_NO_ACTION;
15511     }
15512
15513     if (IS_SB_ELEMENT(element))
15514     {
15515       if (element == EL_SOKOBAN_FIELD_FULL)
15516       {
15517         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15518         local_player->sokobanfields_still_needed++;
15519       }
15520
15521       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15522       {
15523         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15524         local_player->sokobanfields_still_needed--;
15525       }
15526
15527       Feld[x][y] = EL_SOKOBAN_OBJECT;
15528
15529       if (Back[x][y] == Back[nextx][nexty])
15530         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15531       else if (Back[x][y] != 0)
15532         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15533                                     ACTION_EMPTYING);
15534       else
15535         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15536                                     ACTION_FILLING);
15537
15538 #if 1
15539       if (local_player->sokobanfields_still_needed == 0 &&
15540           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15541 #else
15542       if (local_player->sokobanfields_still_needed == 0 &&
15543           game.emulation == EMU_SOKOBAN)
15544 #endif
15545       {
15546         PlayerWins(player);
15547
15548         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15549       }
15550     }
15551     else
15552       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15553
15554     InitMovingField(x, y, move_direction);
15555     GfxAction[x][y] = ACTION_PUSHING;
15556
15557     if (mode == DF_SNAP)
15558       ContinueMoving(x, y);
15559     else
15560       MovPos[x][y] = (dx != 0 ? dx : dy);
15561
15562     Pushed[x][y] = TRUE;
15563     Pushed[nextx][nexty] = TRUE;
15564
15565     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15566       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15567     else
15568       player->push_delay_value = -1;    /* get new value later */
15569
15570     /* check for element change _after_ element has been pushed */
15571     if (game.use_change_when_pushing_bug)
15572     {
15573       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15574                                  player->index_bit, dig_side);
15575       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15576                                           player->index_bit, dig_side);
15577     }
15578   }
15579   else if (IS_SWITCHABLE(element))
15580   {
15581     if (PLAYER_SWITCHING(player, x, y))
15582     {
15583       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15584                                           player->index_bit, dig_side);
15585
15586       return MP_ACTION;
15587     }
15588
15589     player->is_switching = TRUE;
15590     player->switch_x = x;
15591     player->switch_y = y;
15592
15593     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15594
15595     if (element == EL_ROBOT_WHEEL)
15596     {
15597       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15598       ZX = x;
15599       ZY = y;
15600
15601       game.robot_wheel_active = TRUE;
15602
15603       TEST_DrawLevelField(x, y);
15604     }
15605     else if (element == EL_SP_TERMINAL)
15606     {
15607       int xx, yy;
15608
15609       SCAN_PLAYFIELD(xx, yy)
15610       {
15611         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15612           Bang(xx, yy);
15613         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15614           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15615       }
15616     }
15617     else if (IS_BELT_SWITCH(element))
15618     {
15619       ToggleBeltSwitch(x, y);
15620     }
15621     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15622              element == EL_SWITCHGATE_SWITCH_DOWN ||
15623              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15624              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15625     {
15626       ToggleSwitchgateSwitch(x, y);
15627     }
15628     else if (element == EL_LIGHT_SWITCH ||
15629              element == EL_LIGHT_SWITCH_ACTIVE)
15630     {
15631       ToggleLightSwitch(x, y);
15632     }
15633     else if (element == EL_TIMEGATE_SWITCH ||
15634              element == EL_DC_TIMEGATE_SWITCH)
15635     {
15636       ActivateTimegateSwitch(x, y);
15637     }
15638     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15639              element == EL_BALLOON_SWITCH_RIGHT ||
15640              element == EL_BALLOON_SWITCH_UP    ||
15641              element == EL_BALLOON_SWITCH_DOWN  ||
15642              element == EL_BALLOON_SWITCH_NONE  ||
15643              element == EL_BALLOON_SWITCH_ANY)
15644     {
15645       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15646                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15647                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15648                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15649                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15650                              move_direction);
15651     }
15652     else if (element == EL_LAMP)
15653     {
15654       Feld[x][y] = EL_LAMP_ACTIVE;
15655       local_player->lights_still_needed--;
15656
15657       ResetGfxAnimation(x, y);
15658       TEST_DrawLevelField(x, y);
15659     }
15660     else if (element == EL_TIME_ORB_FULL)
15661     {
15662       Feld[x][y] = EL_TIME_ORB_EMPTY;
15663
15664       if (level.time > 0 || level.use_time_orb_bug)
15665       {
15666         TimeLeft += level.time_orb_time;
15667         game.no_time_limit = FALSE;
15668
15669 #if 1
15670         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15671
15672         DisplayGameControlValues();
15673 #else
15674         DrawGameValue_Time(TimeLeft);
15675 #endif
15676       }
15677
15678       ResetGfxAnimation(x, y);
15679       TEST_DrawLevelField(x, y);
15680     }
15681     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15682              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15683     {
15684       int xx, yy;
15685
15686       game.ball_state = !game.ball_state;
15687
15688       SCAN_PLAYFIELD(xx, yy)
15689       {
15690         int e = Feld[xx][yy];
15691
15692         if (game.ball_state)
15693         {
15694           if (e == EL_EMC_MAGIC_BALL)
15695             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15696           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15697             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15698         }
15699         else
15700         {
15701           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15702             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15703           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15704             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15705         }
15706       }
15707     }
15708
15709     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15710                                         player->index_bit, dig_side);
15711
15712     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15713                                         player->index_bit, dig_side);
15714
15715     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15716                                         player->index_bit, dig_side);
15717
15718     return MP_ACTION;
15719   }
15720   else
15721   {
15722     if (!PLAYER_SWITCHING(player, x, y))
15723     {
15724       player->is_switching = TRUE;
15725       player->switch_x = x;
15726       player->switch_y = y;
15727
15728       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15729                                  player->index_bit, dig_side);
15730       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15731                                           player->index_bit, dig_side);
15732
15733       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15734                                  player->index_bit, dig_side);
15735       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15736                                           player->index_bit, dig_side);
15737     }
15738
15739     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15740                                player->index_bit, dig_side);
15741     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15742                                         player->index_bit, dig_side);
15743
15744     return MP_NO_ACTION;
15745   }
15746
15747   player->push_delay = -1;
15748
15749   if (is_player)                /* function can also be called by EL_PENGUIN */
15750   {
15751     if (Feld[x][y] != element)          /* really digged/collected something */
15752     {
15753       player->is_collecting = !player->is_digging;
15754       player->is_active = TRUE;
15755     }
15756   }
15757
15758   return MP_MOVING;
15759 }
15760
15761 static boolean DigFieldByCE(int x, int y, int digging_element)
15762 {
15763   int element = Feld[x][y];
15764
15765   if (!IS_FREE(x, y))
15766   {
15767     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15768                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15769                   ACTION_BREAKING);
15770
15771     /* no element can dig solid indestructible elements */
15772     if (IS_INDESTRUCTIBLE(element) &&
15773         !IS_DIGGABLE(element) &&
15774         !IS_COLLECTIBLE(element))
15775       return FALSE;
15776
15777     if (AmoebaNr[x][y] &&
15778         (element == EL_AMOEBA_FULL ||
15779          element == EL_BD_AMOEBA ||
15780          element == EL_AMOEBA_GROWING))
15781     {
15782       AmoebaCnt[AmoebaNr[x][y]]--;
15783       AmoebaCnt2[AmoebaNr[x][y]]--;
15784     }
15785
15786     if (IS_MOVING(x, y))
15787       RemoveMovingField(x, y);
15788     else
15789     {
15790       RemoveField(x, y);
15791       TEST_DrawLevelField(x, y);
15792     }
15793
15794     /* if digged element was about to explode, prevent the explosion */
15795     ExplodeField[x][y] = EX_TYPE_NONE;
15796
15797     PlayLevelSoundAction(x, y, action);
15798   }
15799
15800   Store[x][y] = EL_EMPTY;
15801
15802 #if 1
15803   /* this makes it possible to leave the removed element again */
15804   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15805     Store[x][y] = element;
15806 #else
15807   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15808   {
15809     int move_leave_element = element_info[digging_element].move_leave_element;
15810
15811     /* this makes it possible to leave the removed element again */
15812     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15813                    element : move_leave_element);
15814   }
15815 #endif
15816
15817   return TRUE;
15818 }
15819
15820 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15821 {
15822   int jx = player->jx, jy = player->jy;
15823   int x = jx + dx, y = jy + dy;
15824   int snap_direction = (dx == -1 ? MV_LEFT  :
15825                         dx == +1 ? MV_RIGHT :
15826                         dy == -1 ? MV_UP    :
15827                         dy == +1 ? MV_DOWN  : MV_NONE);
15828   boolean can_continue_snapping = (level.continuous_snapping &&
15829                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15830
15831   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15832     return FALSE;
15833
15834   if (!player->active || !IN_LEV_FIELD(x, y))
15835     return FALSE;
15836
15837   if (dx && dy)
15838     return FALSE;
15839
15840   if (!dx && !dy)
15841   {
15842     if (player->MovPos == 0)
15843       player->is_pushing = FALSE;
15844
15845     player->is_snapping = FALSE;
15846
15847     if (player->MovPos == 0)
15848     {
15849       player->is_moving = FALSE;
15850       player->is_digging = FALSE;
15851       player->is_collecting = FALSE;
15852     }
15853
15854     return FALSE;
15855   }
15856
15857 #if USE_NEW_CONTINUOUS_SNAPPING
15858   /* prevent snapping with already pressed snap key when not allowed */
15859   if (player->is_snapping && !can_continue_snapping)
15860     return FALSE;
15861 #else
15862   if (player->is_snapping)
15863     return FALSE;
15864 #endif
15865
15866   player->MovDir = snap_direction;
15867
15868   if (player->MovPos == 0)
15869   {
15870     player->is_moving = FALSE;
15871     player->is_digging = FALSE;
15872     player->is_collecting = FALSE;
15873   }
15874
15875   player->is_dropping = FALSE;
15876   player->is_dropping_pressed = FALSE;
15877   player->drop_pressed_delay = 0;
15878
15879   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15880     return FALSE;
15881
15882   player->is_snapping = TRUE;
15883   player->is_active = TRUE;
15884
15885   if (player->MovPos == 0)
15886   {
15887     player->is_moving = FALSE;
15888     player->is_digging = FALSE;
15889     player->is_collecting = FALSE;
15890   }
15891
15892   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15893     TEST_DrawLevelField(player->last_jx, player->last_jy);
15894
15895   TEST_DrawLevelField(x, y);
15896
15897   return TRUE;
15898 }
15899
15900 static boolean DropElement(struct PlayerInfo *player)
15901 {
15902   int old_element, new_element;
15903   int dropx = player->jx, dropy = player->jy;
15904   int drop_direction = player->MovDir;
15905   int drop_side = drop_direction;
15906 #if 1
15907   int drop_element = get_next_dropped_element(player);
15908 #else
15909   int drop_element = (player->inventory_size > 0 ?
15910                       player->inventory_element[player->inventory_size - 1] :
15911                       player->inventory_infinite_element != EL_UNDEFINED ?
15912                       player->inventory_infinite_element :
15913                       player->dynabombs_left > 0 ?
15914                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15915                       EL_UNDEFINED);
15916 #endif
15917
15918   player->is_dropping_pressed = TRUE;
15919
15920   /* do not drop an element on top of another element; when holding drop key
15921      pressed without moving, dropped element must move away before the next
15922      element can be dropped (this is especially important if the next element
15923      is dynamite, which can be placed on background for historical reasons) */
15924   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15925     return MP_ACTION;
15926
15927   if (IS_THROWABLE(drop_element))
15928   {
15929     dropx += GET_DX_FROM_DIR(drop_direction);
15930     dropy += GET_DY_FROM_DIR(drop_direction);
15931
15932     if (!IN_LEV_FIELD(dropx, dropy))
15933       return FALSE;
15934   }
15935
15936   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15937   new_element = drop_element;           /* default: no change when dropping */
15938
15939   /* check if player is active, not moving and ready to drop */
15940   if (!player->active || player->MovPos || player->drop_delay > 0)
15941     return FALSE;
15942
15943   /* check if player has anything that can be dropped */
15944   if (new_element == EL_UNDEFINED)
15945     return FALSE;
15946
15947   /* check if drop key was pressed long enough for EM style dynamite */
15948   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15949     return FALSE;
15950
15951   /* check if anything can be dropped at the current position */
15952   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15953     return FALSE;
15954
15955   /* collected custom elements can only be dropped on empty fields */
15956   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15957     return FALSE;
15958
15959   if (old_element != EL_EMPTY)
15960     Back[dropx][dropy] = old_element;   /* store old element on this field */
15961
15962   ResetGfxAnimation(dropx, dropy);
15963   ResetRandomAnimationValue(dropx, dropy);
15964
15965   if (player->inventory_size > 0 ||
15966       player->inventory_infinite_element != EL_UNDEFINED)
15967   {
15968     if (player->inventory_size > 0)
15969     {
15970       player->inventory_size--;
15971
15972       DrawGameDoorValues();
15973
15974       if (new_element == EL_DYNAMITE)
15975         new_element = EL_DYNAMITE_ACTIVE;
15976       else if (new_element == EL_EM_DYNAMITE)
15977         new_element = EL_EM_DYNAMITE_ACTIVE;
15978       else if (new_element == EL_SP_DISK_RED)
15979         new_element = EL_SP_DISK_RED_ACTIVE;
15980     }
15981
15982     Feld[dropx][dropy] = new_element;
15983
15984     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15985       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15986                           el2img(Feld[dropx][dropy]), 0);
15987
15988     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15989
15990     /* needed if previous element just changed to "empty" in the last frame */
15991     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15992
15993     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15994                                player->index_bit, drop_side);
15995     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15996                                         CE_PLAYER_DROPS_X,
15997                                         player->index_bit, drop_side);
15998
15999     TestIfElementTouchesCustomElement(dropx, dropy);
16000   }
16001   else          /* player is dropping a dyna bomb */
16002   {
16003     player->dynabombs_left--;
16004
16005     Feld[dropx][dropy] = new_element;
16006
16007     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16008       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16009                           el2img(Feld[dropx][dropy]), 0);
16010
16011     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16012   }
16013
16014   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16015     InitField_WithBug1(dropx, dropy, FALSE);
16016
16017   new_element = Feld[dropx][dropy];     /* element might have changed */
16018
16019   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16020       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16021   {
16022 #if 0
16023     int move_direction;
16024     int nextx, nexty;
16025 #endif
16026
16027     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16028       MovDir[dropx][dropy] = drop_direction;
16029
16030 #if 0
16031     move_direction = MovDir[dropx][dropy];
16032     nextx = dropx + GET_DX_FROM_DIR(move_direction);
16033     nexty = dropy + GET_DY_FROM_DIR(move_direction);
16034 #endif
16035
16036     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
16037
16038 #if USE_FIX_IMPACT_COLLISION
16039     /* do not cause impact style collision by dropping elements that can fall */
16040     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16041 #else
16042     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16043 #endif
16044   }
16045
16046   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16047   player->is_dropping = TRUE;
16048
16049   player->drop_pressed_delay = 0;
16050   player->is_dropping_pressed = FALSE;
16051
16052   player->drop_x = dropx;
16053   player->drop_y = dropy;
16054
16055   return TRUE;
16056 }
16057
16058 /* ------------------------------------------------------------------------- */
16059 /* game sound playing functions                                              */
16060 /* ------------------------------------------------------------------------- */
16061
16062 static int *loop_sound_frame = NULL;
16063 static int *loop_sound_volume = NULL;
16064
16065 void InitPlayLevelSound()
16066 {
16067   int num_sounds = getSoundListSize();
16068
16069   checked_free(loop_sound_frame);
16070   checked_free(loop_sound_volume);
16071
16072   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
16073   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16074 }
16075
16076 static void PlayLevelSound(int x, int y, int nr)
16077 {
16078   int sx = SCREENX(x), sy = SCREENY(y);
16079   int volume, stereo_position;
16080   int max_distance = 8;
16081   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16082
16083   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16084       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16085     return;
16086
16087   if (!IN_LEV_FIELD(x, y) ||
16088       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16089       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16090     return;
16091
16092   volume = SOUND_MAX_VOLUME;
16093
16094   if (!IN_SCR_FIELD(sx, sy))
16095   {
16096     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16097     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16098
16099     volume -= volume * (dx > dy ? dx : dy) / max_distance;
16100   }
16101
16102   stereo_position = (SOUND_MAX_LEFT +
16103                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16104                      (SCR_FIELDX + 2 * max_distance));
16105
16106   if (IS_LOOP_SOUND(nr))
16107   {
16108     /* This assures that quieter loop sounds do not overwrite louder ones,
16109        while restarting sound volume comparison with each new game frame. */
16110
16111     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16112       return;
16113
16114     loop_sound_volume[nr] = volume;
16115     loop_sound_frame[nr] = FrameCounter;
16116   }
16117
16118   PlaySoundExt(nr, volume, stereo_position, type);
16119 }
16120
16121 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16122 {
16123   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16124                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
16125                  y < LEVELY(BY1) ? LEVELY(BY1) :
16126                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
16127                  sound_action);
16128 }
16129
16130 static void PlayLevelSoundAction(int x, int y, int action)
16131 {
16132   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16133 }
16134
16135 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16136 {
16137   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16138
16139   if (sound_effect != SND_UNDEFINED)
16140     PlayLevelSound(x, y, sound_effect);
16141 }
16142
16143 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16144                                               int action)
16145 {
16146   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16147
16148   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16149     PlayLevelSound(x, y, sound_effect);
16150 }
16151
16152 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16153 {
16154   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16155
16156   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16157     PlayLevelSound(x, y, sound_effect);
16158 }
16159
16160 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16161 {
16162   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16163
16164   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16165     StopSound(sound_effect);
16166 }
16167
16168 static void PlayLevelMusic()
16169 {
16170   if (levelset.music[level_nr] != MUS_UNDEFINED)
16171     PlayMusic(levelset.music[level_nr]);        /* from config file */
16172   else
16173     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
16174 }
16175
16176 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16177 {
16178   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16179   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16180   int x = xx - 1 - offset;
16181   int y = yy - 1 - offset;
16182
16183   switch (sample)
16184   {
16185     case SAMPLE_blank:
16186       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16187       break;
16188
16189     case SAMPLE_roll:
16190       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16191       break;
16192
16193     case SAMPLE_stone:
16194       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16195       break;
16196
16197     case SAMPLE_nut:
16198       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16199       break;
16200
16201     case SAMPLE_crack:
16202       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16203       break;
16204
16205     case SAMPLE_bug:
16206       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16207       break;
16208
16209     case SAMPLE_tank:
16210       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16211       break;
16212
16213     case SAMPLE_android_clone:
16214       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16215       break;
16216
16217     case SAMPLE_android_move:
16218       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16219       break;
16220
16221     case SAMPLE_spring:
16222       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16223       break;
16224
16225     case SAMPLE_slurp:
16226       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16227       break;
16228
16229     case SAMPLE_eater:
16230       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16231       break;
16232
16233     case SAMPLE_eater_eat:
16234       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16235       break;
16236
16237     case SAMPLE_alien:
16238       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16239       break;
16240
16241     case SAMPLE_collect:
16242       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16243       break;
16244
16245     case SAMPLE_diamond:
16246       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16247       break;
16248
16249     case SAMPLE_squash:
16250       /* !!! CHECK THIS !!! */
16251 #if 1
16252       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16253 #else
16254       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16255 #endif
16256       break;
16257
16258     case SAMPLE_wonderfall:
16259       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16260       break;
16261
16262     case SAMPLE_drip:
16263       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16264       break;
16265
16266     case SAMPLE_push:
16267       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16268       break;
16269
16270     case SAMPLE_dirt:
16271       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16272       break;
16273
16274     case SAMPLE_acid:
16275       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16276       break;
16277
16278     case SAMPLE_ball:
16279       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16280       break;
16281
16282     case SAMPLE_grow:
16283       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16284       break;
16285
16286     case SAMPLE_wonder:
16287       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16288       break;
16289
16290     case SAMPLE_door:
16291       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16292       break;
16293
16294     case SAMPLE_exit_open:
16295       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16296       break;
16297
16298     case SAMPLE_exit_leave:
16299       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16300       break;
16301
16302     case SAMPLE_dynamite:
16303       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16304       break;
16305
16306     case SAMPLE_tick:
16307       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16308       break;
16309
16310     case SAMPLE_press:
16311       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16312       break;
16313
16314     case SAMPLE_wheel:
16315       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16316       break;
16317
16318     case SAMPLE_boom:
16319       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16320       break;
16321
16322     case SAMPLE_die:
16323       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16324       break;
16325
16326     case SAMPLE_time:
16327       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16328       break;
16329
16330     default:
16331       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16332       break;
16333   }
16334 }
16335
16336 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16337 {
16338   int element = map_element_SP_to_RND(element_sp);
16339   int action = map_action_SP_to_RND(action_sp);
16340   int offset = (setup.sp_show_border_elements ? 0 : 1);
16341   int x = xx - offset;
16342   int y = yy - offset;
16343
16344 #if 0
16345   printf("::: %d -> %d\n", element_sp, action_sp);
16346 #endif
16347
16348   PlayLevelSoundElementAction(x, y, element, action);
16349 }
16350
16351 void RaiseScore(int value)
16352 {
16353   local_player->score += value;
16354
16355 #if 1
16356   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16357
16358   DisplayGameControlValues();
16359 #else
16360   DrawGameValue_Score(local_player->score);
16361 #endif
16362 }
16363
16364 void RaiseScoreElement(int element)
16365 {
16366   switch (element)
16367   {
16368     case EL_EMERALD:
16369     case EL_BD_DIAMOND:
16370     case EL_EMERALD_YELLOW:
16371     case EL_EMERALD_RED:
16372     case EL_EMERALD_PURPLE:
16373     case EL_SP_INFOTRON:
16374       RaiseScore(level.score[SC_EMERALD]);
16375       break;
16376     case EL_DIAMOND:
16377       RaiseScore(level.score[SC_DIAMOND]);
16378       break;
16379     case EL_CRYSTAL:
16380       RaiseScore(level.score[SC_CRYSTAL]);
16381       break;
16382     case EL_PEARL:
16383       RaiseScore(level.score[SC_PEARL]);
16384       break;
16385     case EL_BUG:
16386     case EL_BD_BUTTERFLY:
16387     case EL_SP_ELECTRON:
16388       RaiseScore(level.score[SC_BUG]);
16389       break;
16390     case EL_SPACESHIP:
16391     case EL_BD_FIREFLY:
16392     case EL_SP_SNIKSNAK:
16393       RaiseScore(level.score[SC_SPACESHIP]);
16394       break;
16395     case EL_YAMYAM:
16396     case EL_DARK_YAMYAM:
16397       RaiseScore(level.score[SC_YAMYAM]);
16398       break;
16399     case EL_ROBOT:
16400       RaiseScore(level.score[SC_ROBOT]);
16401       break;
16402     case EL_PACMAN:
16403       RaiseScore(level.score[SC_PACMAN]);
16404       break;
16405     case EL_NUT:
16406       RaiseScore(level.score[SC_NUT]);
16407       break;
16408     case EL_DYNAMITE:
16409     case EL_EM_DYNAMITE:
16410     case EL_SP_DISK_RED:
16411     case EL_DYNABOMB_INCREASE_NUMBER:
16412     case EL_DYNABOMB_INCREASE_SIZE:
16413     case EL_DYNABOMB_INCREASE_POWER:
16414       RaiseScore(level.score[SC_DYNAMITE]);
16415       break;
16416     case EL_SHIELD_NORMAL:
16417     case EL_SHIELD_DEADLY:
16418       RaiseScore(level.score[SC_SHIELD]);
16419       break;
16420     case EL_EXTRA_TIME:
16421       RaiseScore(level.extra_time_score);
16422       break;
16423     case EL_KEY_1:
16424     case EL_KEY_2:
16425     case EL_KEY_3:
16426     case EL_KEY_4:
16427     case EL_EM_KEY_1:
16428     case EL_EM_KEY_2:
16429     case EL_EM_KEY_3:
16430     case EL_EM_KEY_4:
16431     case EL_EMC_KEY_5:
16432     case EL_EMC_KEY_6:
16433     case EL_EMC_KEY_7:
16434     case EL_EMC_KEY_8:
16435     case EL_DC_KEY_WHITE:
16436       RaiseScore(level.score[SC_KEY]);
16437       break;
16438     default:
16439       RaiseScore(element_info[element].collect_score);
16440       break;
16441   }
16442 }
16443
16444 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16445 {
16446   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16447   {
16448 #if defined(NETWORK_AVALIABLE)
16449     if (options.network)
16450       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16451     else
16452 #endif
16453     {
16454       if (quick_quit)
16455       {
16456 #if 1
16457
16458 #if 1
16459         FadeSkipNextFadeIn();
16460 #else
16461         fading = fading_none;
16462 #endif
16463
16464 #else
16465         OpenDoor(DOOR_CLOSE_1);
16466 #endif
16467
16468         game_status = GAME_MODE_MAIN;
16469
16470 #if 1
16471         DrawAndFadeInMainMenu(REDRAW_FIELD);
16472 #else
16473         DrawMainMenu();
16474 #endif
16475       }
16476       else
16477       {
16478 #if 0
16479         FadeOut(REDRAW_FIELD);
16480 #endif
16481
16482         game_status = GAME_MODE_MAIN;
16483
16484         DrawAndFadeInMainMenu(REDRAW_FIELD);
16485       }
16486     }
16487   }
16488   else          /* continue playing the game */
16489   {
16490     if (tape.playing && tape.deactivate_display)
16491       TapeDeactivateDisplayOff(TRUE);
16492
16493     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16494
16495     if (tape.playing && tape.deactivate_display)
16496       TapeDeactivateDisplayOn();
16497   }
16498 }
16499
16500 void RequestQuitGame(boolean ask_if_really_quit)
16501 {
16502   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16503   boolean skip_request = AllPlayersGone || quick_quit;
16504
16505   RequestQuitGameExt(skip_request, quick_quit,
16506                      "Do you really want to quit the game?");
16507 }
16508
16509
16510 /* ------------------------------------------------------------------------- */
16511 /* random generator functions                                                */
16512 /* ------------------------------------------------------------------------- */
16513
16514 unsigned int InitEngineRandom_RND(int seed)
16515 {
16516   game.num_random_calls = 0;
16517
16518 #if 0
16519   unsigned int rnd_seed = InitEngineRandom(seed);
16520
16521   printf("::: START RND: %d\n", rnd_seed);
16522
16523   return rnd_seed;
16524 #else
16525
16526   return InitEngineRandom(seed);
16527
16528 #endif
16529
16530 }
16531
16532 unsigned int RND(int max)
16533 {
16534   if (max > 0)
16535   {
16536     game.num_random_calls++;
16537
16538     return GetEngineRandom(max);
16539   }
16540
16541   return 0;
16542 }
16543
16544
16545 /* ------------------------------------------------------------------------- */
16546 /* game engine snapshot handling functions                                   */
16547 /* ------------------------------------------------------------------------- */
16548
16549 struct EngineSnapshotInfo
16550 {
16551   /* runtime values for custom element collect score */
16552   int collect_score[NUM_CUSTOM_ELEMENTS];
16553
16554   /* runtime values for group element choice position */
16555   int choice_pos[NUM_GROUP_ELEMENTS];
16556
16557   /* runtime values for belt position animations */
16558   int belt_graphic[4][NUM_BELT_PARTS];
16559   int belt_anim_mode[4][NUM_BELT_PARTS];
16560 };
16561
16562 static struct EngineSnapshotInfo engine_snapshot_rnd;
16563 static char *snapshot_level_identifier = NULL;
16564 static int snapshot_level_nr = -1;
16565
16566 static void SaveEngineSnapshotValues_RND()
16567 {
16568   static int belt_base_active_element[4] =
16569   {
16570     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16571     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16572     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16573     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16574   };
16575   int i, j;
16576
16577   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16578   {
16579     int element = EL_CUSTOM_START + i;
16580
16581     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16582   }
16583
16584   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16585   {
16586     int element = EL_GROUP_START + i;
16587
16588     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16589   }
16590
16591   for (i = 0; i < 4; i++)
16592   {
16593     for (j = 0; j < NUM_BELT_PARTS; j++)
16594     {
16595       int element = belt_base_active_element[i] + j;
16596       int graphic = el2img(element);
16597       int anim_mode = graphic_info[graphic].anim_mode;
16598
16599       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16600       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16601     }
16602   }
16603 }
16604
16605 static void LoadEngineSnapshotValues_RND()
16606 {
16607   unsigned int num_random_calls = game.num_random_calls;
16608   int i, j;
16609
16610   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16611   {
16612     int element = EL_CUSTOM_START + i;
16613
16614     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16615   }
16616
16617   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16618   {
16619     int element = EL_GROUP_START + i;
16620
16621     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16622   }
16623
16624   for (i = 0; i < 4; i++)
16625   {
16626     for (j = 0; j < NUM_BELT_PARTS; j++)
16627     {
16628       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16629       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16630
16631       graphic_info[graphic].anim_mode = anim_mode;
16632     }
16633   }
16634
16635   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16636   {
16637     InitRND(tape.random_seed);
16638     for (i = 0; i < num_random_calls; i++)
16639       RND(1);
16640   }
16641
16642   if (game.num_random_calls != num_random_calls)
16643   {
16644     Error(ERR_INFO, "number of random calls out of sync");
16645     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16646     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16647     Error(ERR_EXIT, "this should not happen -- please debug");
16648   }
16649 }
16650
16651 void SaveEngineSnapshot()
16652 {
16653   /* do not save snapshots from editor */
16654   if (level_editor_test_game)
16655     return;
16656
16657   /* free previous snapshot buffers, if needed */
16658   FreeEngineSnapshotBuffers();
16659
16660   /* copy some special values to a structure better suited for the snapshot */
16661
16662   SaveEngineSnapshotValues_RND();
16663   SaveEngineSnapshotValues_EM();
16664   SaveEngineSnapshotValues_SP();
16665
16666   /* save values stored in special snapshot structure */
16667
16668   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16669   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16670   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16671
16672   /* save further RND engine values */
16673
16674   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16675   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16676   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16677
16678   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16679   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16680   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16681   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16682
16683   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16684   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16685   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16686   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16687   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16688
16689   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16690   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16691   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16692
16693   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16694
16695   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16696
16697   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16698   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16699
16700   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16701   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16702   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16703   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16704   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16705   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16706   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16707   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16708   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16709   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16710   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16711   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16712   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16713   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16714   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16715   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16716   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16717   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16718
16719   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16720   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16721
16722   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16723   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16724   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16725
16726   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16727   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16728
16729   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16730   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16731   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16732   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16733   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16734
16735   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16736   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16737
16738   /* save level identification information */
16739
16740   setString(&snapshot_level_identifier, leveldir_current->identifier);
16741   snapshot_level_nr = level_nr;
16742
16743 #if 0
16744   ListNode *node = engine_snapshot_list_rnd;
16745   int num_bytes = 0;
16746
16747   while (node != NULL)
16748   {
16749     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16750
16751     node = node->next;
16752   }
16753
16754   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16755 #endif
16756 }
16757
16758 void LoadEngineSnapshot()
16759 {
16760   /* restore generically stored snapshot buffers */
16761
16762   LoadEngineSnapshotBuffers();
16763
16764   /* restore special values from snapshot structure */
16765
16766   LoadEngineSnapshotValues_RND();
16767   LoadEngineSnapshotValues_EM();
16768   LoadEngineSnapshotValues_SP();
16769 }
16770
16771 boolean CheckEngineSnapshot()
16772 {
16773   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16774           snapshot_level_nr == level_nr);
16775 }
16776
16777
16778 /* ---------- new game button stuff ---------------------------------------- */
16779
16780 static struct
16781 {
16782   int graphic;
16783   struct Rect *pos;
16784   int gadget_id;
16785   char *infotext;
16786 } gamebutton_info[NUM_GAME_BUTTONS] =
16787 {
16788   {
16789     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
16790     GAME_CTRL_ID_STOP,                  "stop game"
16791   },
16792   {
16793     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
16794     GAME_CTRL_ID_PAUSE,                 "pause game"
16795   },
16796   {
16797     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
16798     GAME_CTRL_ID_PLAY,                  "play game"
16799   },
16800   {
16801     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
16802     SOUND_CTRL_ID_MUSIC,                "background music on/off"
16803   },
16804   {
16805     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
16806     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
16807   },
16808   {
16809     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
16810     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
16811   },
16812   {
16813     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
16814     GAME_CTRL_ID_SAVE,                  "save game"
16815   },
16816   {
16817     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
16818     GAME_CTRL_ID_LOAD,                  "load game"
16819   }
16820 };
16821
16822 void CreateGameButtons()
16823 {
16824   int i;
16825
16826   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16827   {
16828     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16829     struct Rect *pos = gamebutton_info[i].pos;
16830     struct GadgetInfo *gi;
16831     int button_type;
16832     boolean checked;
16833     unsigned int event_mask;
16834     int gd_x   = gfx->src_x;
16835     int gd_y   = gfx->src_y;
16836     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16837     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16838     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16839     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16840     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16841     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16842     int id = i;
16843
16844     if (gfx->bitmap == NULL)
16845     {
16846       game_gadget[id] = NULL;
16847
16848       continue;
16849     }
16850
16851     if (id == GAME_CTRL_ID_STOP ||
16852         id == GAME_CTRL_ID_PAUSE ||
16853         id == GAME_CTRL_ID_PLAY ||
16854         id == GAME_CTRL_ID_SAVE ||
16855         id == GAME_CTRL_ID_LOAD)
16856     {
16857       button_type = GD_TYPE_NORMAL_BUTTON;
16858       checked = FALSE;
16859       event_mask = GD_EVENT_RELEASED;
16860     }
16861     else
16862     {
16863       button_type = GD_TYPE_CHECK_BUTTON;
16864       checked =
16865         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16866          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16867          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16868       event_mask = GD_EVENT_PRESSED;
16869     }
16870
16871     gi = CreateGadget(GDI_CUSTOM_ID, id,
16872                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16873                       GDI_X, DX + GDI_ACTIVE_POS(pos->x),
16874                       GDI_Y, DY + GDI_ACTIVE_POS(pos->y),
16875                       GDI_WIDTH, gfx->width,
16876                       GDI_HEIGHT, gfx->height,
16877                       GDI_TYPE, button_type,
16878                       GDI_STATE, GD_BUTTON_UNPRESSED,
16879                       GDI_CHECKED, checked,
16880                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16881                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16882                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16883                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16884                       GDI_DIRECT_DRAW, FALSE,
16885                       GDI_EVENT_MASK, event_mask,
16886                       GDI_CALLBACK_ACTION, HandleGameButtons,
16887                       GDI_END);
16888
16889     if (gi == NULL)
16890       Error(ERR_EXIT, "cannot create gadget");
16891
16892     game_gadget[id] = gi;
16893   }
16894 }
16895
16896 void FreeGameButtons()
16897 {
16898   int i;
16899
16900   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16901     FreeGadget(game_gadget[i]);
16902 }
16903
16904 void MapGameButtons()
16905 {
16906   int i;
16907
16908   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16909     MapGadget(game_gadget[i]);
16910 }
16911
16912 void UnmapGameButtons()
16913 {
16914   int i;
16915
16916   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16917     UnmapGadget(game_gadget[i]);
16918 }
16919
16920 void RedrawGameButtons()
16921 {
16922   int i;
16923
16924   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16925     RedrawGadget(game_gadget[i]);
16926 }
16927
16928 static void HandleGameButtonsExt(int id)
16929 {
16930   if (game_status != GAME_MODE_PLAYING)
16931     return;
16932
16933   switch (id)
16934   {
16935     case GAME_CTRL_ID_STOP:
16936       if (tape.playing)
16937         TapeStop();
16938       else
16939         RequestQuitGame(TRUE);
16940       break;
16941
16942     case GAME_CTRL_ID_PAUSE:
16943       if (options.network)
16944       {
16945 #if defined(NETWORK_AVALIABLE)
16946         if (tape.pausing)
16947           SendToServer_ContinuePlaying();
16948         else
16949           SendToServer_PausePlaying();
16950 #endif
16951       }
16952       else
16953         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16954       break;
16955
16956     case GAME_CTRL_ID_PLAY:
16957       if (tape.pausing)
16958       {
16959 #if defined(NETWORK_AVALIABLE)
16960         if (options.network)
16961           SendToServer_ContinuePlaying();
16962         else
16963 #endif
16964         {
16965           tape.pausing = FALSE;
16966           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16967         }
16968       }
16969       break;
16970
16971     case SOUND_CTRL_ID_MUSIC:
16972       if (setup.sound_music)
16973       { 
16974         setup.sound_music = FALSE;
16975
16976         FadeMusic();
16977       }
16978       else if (audio.music_available)
16979       { 
16980         setup.sound = setup.sound_music = TRUE;
16981
16982         SetAudioMode(setup.sound);
16983
16984         PlayLevelMusic();
16985       }
16986       break;
16987
16988     case SOUND_CTRL_ID_LOOPS:
16989       if (setup.sound_loops)
16990         setup.sound_loops = FALSE;
16991       else if (audio.loops_available)
16992       {
16993         setup.sound = setup.sound_loops = TRUE;
16994
16995         SetAudioMode(setup.sound);
16996       }
16997       break;
16998
16999     case SOUND_CTRL_ID_SIMPLE:
17000       if (setup.sound_simple)
17001         setup.sound_simple = FALSE;
17002       else if (audio.sound_available)
17003       {
17004         setup.sound = setup.sound_simple = TRUE;
17005
17006         SetAudioMode(setup.sound);
17007       }
17008       break;
17009
17010     case GAME_CTRL_ID_SAVE:
17011       TapeQuickSave();
17012       break;
17013
17014     case GAME_CTRL_ID_LOAD:
17015       TapeQuickLoad();
17016       break;
17017
17018     default:
17019       break;
17020   }
17021 }
17022
17023 static void HandleGameButtons(struct GadgetInfo *gi)
17024 {
17025   HandleGameButtonsExt(gi->custom_id);
17026 }
17027
17028 void HandleSoundButtonKeys(Key key)
17029 {
17030 #if 1
17031   if (key == setup.shortcut.sound_simple)
17032     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17033   else if (key == setup.shortcut.sound_loops)
17034     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17035   else if (key == setup.shortcut.sound_music)
17036     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17037 #else
17038   if (key == setup.shortcut.sound_simple)
17039     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17040   else if (key == setup.shortcut.sound_loops)
17041     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17042   else if (key == setup.shortcut.sound_music)
17043     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17044 #endif
17045 }