rnd-20071029-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_PANEL_LEVEL_NUMBER                 0
135 #define GAME_PANEL_GEMS                         1
136 #define GAME_PANEL_INVENTORY_COUNT              2
137 #define GAME_PANEL_INVENTORY_FIRST_1            3
138 #define GAME_PANEL_INVENTORY_FIRST_2            4
139 #define GAME_PANEL_INVENTORY_FIRST_3            5
140 #define GAME_PANEL_INVENTORY_FIRST_4            6
141 #define GAME_PANEL_INVENTORY_FIRST_5            7
142 #define GAME_PANEL_INVENTORY_FIRST_6            8
143 #define GAME_PANEL_INVENTORY_FIRST_7            9
144 #define GAME_PANEL_INVENTORY_FIRST_8            10
145 #define GAME_PANEL_INVENTORY_LAST_1             11
146 #define GAME_PANEL_INVENTORY_LAST_2             12
147 #define GAME_PANEL_INVENTORY_LAST_3             13
148 #define GAME_PANEL_INVENTORY_LAST_4             14
149 #define GAME_PANEL_INVENTORY_LAST_5             15
150 #define GAME_PANEL_INVENTORY_LAST_6             16
151 #define GAME_PANEL_INVENTORY_LAST_7             17
152 #define GAME_PANEL_INVENTORY_LAST_8             18
153 #define GAME_PANEL_KEY_1                        19
154 #define GAME_PANEL_KEY_2                        20
155 #define GAME_PANEL_KEY_3                        21
156 #define GAME_PANEL_KEY_4                        22
157 #define GAME_PANEL_KEY_5                        23
158 #define GAME_PANEL_KEY_6                        24
159 #define GAME_PANEL_KEY_7                        25
160 #define GAME_PANEL_KEY_8                        26
161 #define GAME_PANEL_KEY_WHITE                    27
162 #define GAME_PANEL_KEY_WHITE_COUNT              28
163 #define GAME_PANEL_SCORE                        29
164 #define GAME_PANEL_HIGHSCORE                    30
165 #define GAME_PANEL_TIME                         31
166 #define GAME_PANEL_TIME_HH                      32
167 #define GAME_PANEL_TIME_MM                      33
168 #define GAME_PANEL_TIME_SS                      34
169 #define GAME_PANEL_SHIELD_NORMAL                35
170 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
171 #define GAME_PANEL_SHIELD_DEADLY                37
172 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
173 #define GAME_PANEL_EXIT                         39
174 #define GAME_PANEL_EMC_MAGIC_BALL               40
175 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
176 #define GAME_PANEL_LIGHT_SWITCH                 42
177 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
178 #define GAME_PANEL_TIMEGATE_SWITCH              44
179 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
180 #define GAME_PANEL_SWITCHGATE_SWITCH            46
181 #define GAME_PANEL_EMC_LENSES                   47
182 #define GAME_PANEL_EMC_LENSES_TIME              48
183 #define GAME_PANEL_EMC_MAGNIFIER                49
184 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
185 #define GAME_PANEL_BALLOON_SWITCH               51
186 #define GAME_PANEL_DYNABOMB_NUMBER              52
187 #define GAME_PANEL_DYNABOMB_SIZE                53
188 #define GAME_PANEL_DYNABOMB_POWER               54
189 #define GAME_PANEL_PENGUINS                     55
190 #define GAME_PANEL_SOKOBAN_OBJECTS              56
191 #define GAME_PANEL_SOKOBAN_FIELDS               57
192 #define GAME_PANEL_ROBOT_WHEEL                  58
193 #define GAME_PANEL_CONVEYOR_BELT_1              59
194 #define GAME_PANEL_CONVEYOR_BELT_2              60
195 #define GAME_PANEL_CONVEYOR_BELT_3              61
196 #define GAME_PANEL_CONVEYOR_BELT_4              62
197 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
198 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
199 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
200 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
201 #define GAME_PANEL_MAGIC_WALL                   67
202 #define GAME_PANEL_MAGIC_WALL_TIME              68
203 #define GAME_PANEL_GRAVITY_STATE                69
204 #define GAME_PANEL_GRAPHIC_1                    70
205 #define GAME_PANEL_GRAPHIC_2                    71
206 #define GAME_PANEL_GRAPHIC_3                    72
207 #define GAME_PANEL_GRAPHIC_4                    73
208 #define GAME_PANEL_GRAPHIC_5                    74
209 #define GAME_PANEL_GRAPHIC_6                    75
210 #define GAME_PANEL_GRAPHIC_7                    76
211 #define GAME_PANEL_GRAPHIC_8                    77
212 #define GAME_PANEL_ELEMENT_1                    78
213 #define GAME_PANEL_ELEMENT_2                    79
214 #define GAME_PANEL_ELEMENT_3                    80
215 #define GAME_PANEL_ELEMENT_4                    81
216 #define GAME_PANEL_ELEMENT_5                    82
217 #define GAME_PANEL_ELEMENT_6                    83
218 #define GAME_PANEL_ELEMENT_7                    84
219 #define GAME_PANEL_ELEMENT_8                    85
220 #define GAME_PANEL_ELEMENT_COUNT_1              86
221 #define GAME_PANEL_ELEMENT_COUNT_2              87
222 #define GAME_PANEL_ELEMENT_COUNT_3              88
223 #define GAME_PANEL_ELEMENT_COUNT_4              89
224 #define GAME_PANEL_ELEMENT_COUNT_5              90
225 #define GAME_PANEL_ELEMENT_COUNT_6              91
226 #define GAME_PANEL_ELEMENT_COUNT_7              92
227 #define GAME_PANEL_ELEMENT_COUNT_8              93
228 #define GAME_PANEL_CE_SCORE_1                   94
229 #define GAME_PANEL_CE_SCORE_2                   95
230 #define GAME_PANEL_CE_SCORE_3                   96
231 #define GAME_PANEL_CE_SCORE_4                   97
232 #define GAME_PANEL_CE_SCORE_5                   98
233 #define GAME_PANEL_CE_SCORE_6                   99
234 #define GAME_PANEL_CE_SCORE_7                   100
235 #define GAME_PANEL_CE_SCORE_8                   101
236 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
237 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
238 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
239 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
240 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
241 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
242 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
243 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
244 #define GAME_PANEL_PLAYER_NAME                  110
245 #define GAME_PANEL_LEVEL_NAME                   111
246 #define GAME_PANEL_LEVEL_AUTHOR                 112
247
248 #define NUM_GAME_PANEL_CONTROLS                 113
249
250 struct GamePanelOrderInfo
251 {
252   int nr;
253   int sort_priority;
254 };
255
256 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
257
258 struct GamePanelControlInfo
259 {
260   int nr;
261
262   struct TextPosInfo *pos;
263   int type;
264
265   int value, last_value;
266   int frame, last_frame;
267   int gfx_frame;
268   int gfx_random;
269 };
270
271 static struct GamePanelControlInfo game_panel_controls[] =
272 {
273   {
274     GAME_PANEL_LEVEL_NUMBER,
275     &game.panel.level_number,
276     TYPE_INTEGER,
277   },
278   {
279     GAME_PANEL_GEMS,
280     &game.panel.gems,
281     TYPE_INTEGER,
282   },
283   {
284     GAME_PANEL_INVENTORY_COUNT,
285     &game.panel.inventory_count,
286     TYPE_INTEGER,
287   },
288   {
289     GAME_PANEL_INVENTORY_FIRST_1,
290     &game.panel.inventory_first[0],
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_FIRST_2,
295     &game.panel.inventory_first[1],
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_FIRST_3,
300     &game.panel.inventory_first[2],
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_FIRST_4,
305     &game.panel.inventory_first[3],
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_FIRST_5,
310     &game.panel.inventory_first[4],
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_FIRST_6,
315     &game.panel.inventory_first[5],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_FIRST_7,
320     &game.panel.inventory_first[6],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_INVENTORY_FIRST_8,
325     &game.panel.inventory_first[7],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_INVENTORY_LAST_1,
330     &game.panel.inventory_last[0],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_INVENTORY_LAST_2,
335     &game.panel.inventory_last[1],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_INVENTORY_LAST_3,
340     &game.panel.inventory_last[2],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_INVENTORY_LAST_4,
345     &game.panel.inventory_last[3],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_INVENTORY_LAST_5,
350     &game.panel.inventory_last[4],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_INVENTORY_LAST_6,
355     &game.panel.inventory_last[5],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_INVENTORY_LAST_7,
360     &game.panel.inventory_last[6],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_INVENTORY_LAST_8,
365     &game.panel.inventory_last[7],
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_KEY_1,
370     &game.panel.key[0],
371     TYPE_ELEMENT,
372   },
373   {
374     GAME_PANEL_KEY_2,
375     &game.panel.key[1],
376     TYPE_ELEMENT,
377   },
378   {
379     GAME_PANEL_KEY_3,
380     &game.panel.key[2],
381     TYPE_ELEMENT,
382   },
383   {
384     GAME_PANEL_KEY_4,
385     &game.panel.key[3],
386     TYPE_ELEMENT,
387   },
388   {
389     GAME_PANEL_KEY_5,
390     &game.panel.key[4],
391     TYPE_ELEMENT,
392   },
393   {
394     GAME_PANEL_KEY_6,
395     &game.panel.key[5],
396     TYPE_ELEMENT,
397   },
398   {
399     GAME_PANEL_KEY_7,
400     &game.panel.key[6],
401     TYPE_ELEMENT,
402   },
403   {
404     GAME_PANEL_KEY_8,
405     &game.panel.key[7],
406     TYPE_ELEMENT,
407   },
408   {
409     GAME_PANEL_KEY_WHITE,
410     &game.panel.key_white,
411     TYPE_ELEMENT,
412   },
413   {
414     GAME_PANEL_KEY_WHITE_COUNT,
415     &game.panel.key_white_count,
416     TYPE_INTEGER,
417   },
418   {
419     GAME_PANEL_SCORE,
420     &game.panel.score,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HIGHSCORE,
425     &game.panel.highscore,
426     TYPE_INTEGER,
427   },
428   {
429     GAME_PANEL_TIME,
430     &game.panel.time,
431     TYPE_INTEGER,
432   },
433   {
434     GAME_PANEL_TIME_HH,
435     &game.panel.time_hh,
436     TYPE_INTEGER,
437   },
438   {
439     GAME_PANEL_TIME_MM,
440     &game.panel.time_mm,
441     TYPE_INTEGER,
442   },
443   {
444     GAME_PANEL_TIME_SS,
445     &game.panel.time_ss,
446     TYPE_INTEGER,
447   },
448   {
449     GAME_PANEL_SHIELD_NORMAL,
450     &game.panel.shield_normal,
451     TYPE_ELEMENT,
452   },
453   {
454     GAME_PANEL_SHIELD_NORMAL_TIME,
455     &game.panel.shield_normal_time,
456     TYPE_INTEGER,
457   },
458   {
459     GAME_PANEL_SHIELD_DEADLY,
460     &game.panel.shield_deadly,
461     TYPE_ELEMENT,
462   },
463   {
464     GAME_PANEL_SHIELD_DEADLY_TIME,
465     &game.panel.shield_deadly_time,
466     TYPE_INTEGER,
467   },
468   {
469     GAME_PANEL_EXIT,
470     &game.panel.exit,
471     TYPE_ELEMENT,
472   },
473   {
474     GAME_PANEL_EMC_MAGIC_BALL,
475     &game.panel.emc_magic_ball,
476     TYPE_ELEMENT,
477   },
478   {
479     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
480     &game.panel.emc_magic_ball_switch,
481     TYPE_ELEMENT,
482   },
483   {
484     GAME_PANEL_LIGHT_SWITCH,
485     &game.panel.light_switch,
486     TYPE_ELEMENT,
487   },
488   {
489     GAME_PANEL_LIGHT_SWITCH_TIME,
490     &game.panel.light_switch_time,
491     TYPE_INTEGER,
492   },
493   {
494     GAME_PANEL_TIMEGATE_SWITCH,
495     &game.panel.timegate_switch,
496     TYPE_ELEMENT,
497   },
498   {
499     GAME_PANEL_TIMEGATE_SWITCH_TIME,
500     &game.panel.timegate_switch_time,
501     TYPE_INTEGER,
502   },
503   {
504     GAME_PANEL_SWITCHGATE_SWITCH,
505     &game.panel.switchgate_switch,
506     TYPE_ELEMENT,
507   },
508   {
509     GAME_PANEL_EMC_LENSES,
510     &game.panel.emc_lenses,
511     TYPE_ELEMENT,
512   },
513   {
514     GAME_PANEL_EMC_LENSES_TIME,
515     &game.panel.emc_lenses_time,
516     TYPE_INTEGER,
517   },
518   {
519     GAME_PANEL_EMC_MAGNIFIER,
520     &game.panel.emc_magnifier,
521     TYPE_ELEMENT,
522   },
523   {
524     GAME_PANEL_EMC_MAGNIFIER_TIME,
525     &game.panel.emc_magnifier_time,
526     TYPE_INTEGER,
527   },
528   {
529     GAME_PANEL_BALLOON_SWITCH,
530     &game.panel.balloon_switch,
531     TYPE_ELEMENT,
532   },
533   {
534     GAME_PANEL_DYNABOMB_NUMBER,
535     &game.panel.dynabomb_number,
536     TYPE_INTEGER,
537   },
538   {
539     GAME_PANEL_DYNABOMB_SIZE,
540     &game.panel.dynabomb_size,
541     TYPE_INTEGER,
542   },
543   {
544     GAME_PANEL_DYNABOMB_POWER,
545     &game.panel.dynabomb_power,
546     TYPE_ELEMENT,
547   },
548   {
549     GAME_PANEL_PENGUINS,
550     &game.panel.penguins,
551     TYPE_INTEGER,
552   },
553   {
554     GAME_PANEL_SOKOBAN_OBJECTS,
555     &game.panel.sokoban_objects,
556     TYPE_INTEGER,
557   },
558   {
559     GAME_PANEL_SOKOBAN_FIELDS,
560     &game.panel.sokoban_fields,
561     TYPE_INTEGER,
562   },
563   {
564     GAME_PANEL_ROBOT_WHEEL,
565     &game.panel.robot_wheel,
566     TYPE_ELEMENT,
567   },
568   {
569     GAME_PANEL_CONVEYOR_BELT_1,
570     &game.panel.conveyor_belt[0],
571     TYPE_ELEMENT,
572   },
573   {
574     GAME_PANEL_CONVEYOR_BELT_2,
575     &game.panel.conveyor_belt[1],
576     TYPE_ELEMENT,
577   },
578   {
579     GAME_PANEL_CONVEYOR_BELT_3,
580     &game.panel.conveyor_belt[2],
581     TYPE_ELEMENT,
582   },
583   {
584     GAME_PANEL_CONVEYOR_BELT_4,
585     &game.panel.conveyor_belt[3],
586     TYPE_ELEMENT,
587   },
588   {
589     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
590     &game.panel.conveyor_belt_switch[0],
591     TYPE_ELEMENT,
592   },
593   {
594     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
595     &game.panel.conveyor_belt_switch[1],
596     TYPE_ELEMENT,
597   },
598   {
599     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
600     &game.panel.conveyor_belt_switch[2],
601     TYPE_ELEMENT,
602   },
603   {
604     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
605     &game.panel.conveyor_belt_switch[3],
606     TYPE_ELEMENT,
607   },
608   {
609     GAME_PANEL_MAGIC_WALL,
610     &game.panel.magic_wall,
611     TYPE_ELEMENT,
612   },
613   {
614     GAME_PANEL_MAGIC_WALL_TIME,
615     &game.panel.magic_wall_time,
616     TYPE_INTEGER,
617   },
618   {
619     GAME_PANEL_GRAVITY_STATE,
620     &game.panel.gravity_state,
621     TYPE_STRING,
622   },
623   {
624     GAME_PANEL_GRAPHIC_1,
625     &game.panel.graphic[0],
626     TYPE_ELEMENT,
627   },
628   {
629     GAME_PANEL_GRAPHIC_2,
630     &game.panel.graphic[1],
631     TYPE_ELEMENT,
632   },
633   {
634     GAME_PANEL_GRAPHIC_3,
635     &game.panel.graphic[2],
636     TYPE_ELEMENT,
637   },
638   {
639     GAME_PANEL_GRAPHIC_4,
640     &game.panel.graphic[3],
641     TYPE_ELEMENT,
642   },
643   {
644     GAME_PANEL_GRAPHIC_5,
645     &game.panel.graphic[4],
646     TYPE_ELEMENT,
647   },
648   {
649     GAME_PANEL_GRAPHIC_6,
650     &game.panel.graphic[5],
651     TYPE_ELEMENT,
652   },
653   {
654     GAME_PANEL_GRAPHIC_7,
655     &game.panel.graphic[6],
656     TYPE_ELEMENT,
657   },
658   {
659     GAME_PANEL_GRAPHIC_8,
660     &game.panel.graphic[7],
661     TYPE_ELEMENT,
662   },
663   {
664     GAME_PANEL_ELEMENT_1,
665     &game.panel.element[0],
666     TYPE_ELEMENT,
667   },
668   {
669     GAME_PANEL_ELEMENT_2,
670     &game.panel.element[1],
671     TYPE_ELEMENT,
672   },
673   {
674     GAME_PANEL_ELEMENT_3,
675     &game.panel.element[2],
676     TYPE_ELEMENT,
677   },
678   {
679     GAME_PANEL_ELEMENT_4,
680     &game.panel.element[3],
681     TYPE_ELEMENT,
682   },
683   {
684     GAME_PANEL_ELEMENT_5,
685     &game.panel.element[4],
686     TYPE_ELEMENT,
687   },
688   {
689     GAME_PANEL_ELEMENT_6,
690     &game.panel.element[5],
691     TYPE_ELEMENT,
692   },
693   {
694     GAME_PANEL_ELEMENT_7,
695     &game.panel.element[6],
696     TYPE_ELEMENT,
697   },
698   {
699     GAME_PANEL_ELEMENT_8,
700     &game.panel.element[7],
701     TYPE_ELEMENT,
702   },
703   {
704     GAME_PANEL_ELEMENT_COUNT_1,
705     &game.panel.element_count[0],
706     TYPE_INTEGER,
707   },
708   {
709     GAME_PANEL_ELEMENT_COUNT_2,
710     &game.panel.element_count[1],
711     TYPE_INTEGER,
712   },
713   {
714     GAME_PANEL_ELEMENT_COUNT_3,
715     &game.panel.element_count[2],
716     TYPE_INTEGER,
717   },
718   {
719     GAME_PANEL_ELEMENT_COUNT_4,
720     &game.panel.element_count[3],
721     TYPE_INTEGER,
722   },
723   {
724     GAME_PANEL_ELEMENT_COUNT_5,
725     &game.panel.element_count[4],
726     TYPE_INTEGER,
727   },
728   {
729     GAME_PANEL_ELEMENT_COUNT_6,
730     &game.panel.element_count[5],
731     TYPE_INTEGER,
732   },
733   {
734     GAME_PANEL_ELEMENT_COUNT_7,
735     &game.panel.element_count[6],
736     TYPE_INTEGER,
737   },
738   {
739     GAME_PANEL_ELEMENT_COUNT_8,
740     &game.panel.element_count[7],
741     TYPE_INTEGER,
742   },
743   {
744     GAME_PANEL_CE_SCORE_1,
745     &game.panel.ce_score[0],
746     TYPE_INTEGER,
747   },
748   {
749     GAME_PANEL_CE_SCORE_2,
750     &game.panel.ce_score[1],
751     TYPE_INTEGER,
752   },
753   {
754     GAME_PANEL_CE_SCORE_3,
755     &game.panel.ce_score[2],
756     TYPE_INTEGER,
757   },
758   {
759     GAME_PANEL_CE_SCORE_4,
760     &game.panel.ce_score[3],
761     TYPE_INTEGER,
762   },
763   {
764     GAME_PANEL_CE_SCORE_5,
765     &game.panel.ce_score[4],
766     TYPE_INTEGER,
767   },
768   {
769     GAME_PANEL_CE_SCORE_6,
770     &game.panel.ce_score[5],
771     TYPE_INTEGER,
772   },
773   {
774     GAME_PANEL_CE_SCORE_7,
775     &game.panel.ce_score[6],
776     TYPE_INTEGER,
777   },
778   {
779     GAME_PANEL_CE_SCORE_8,
780     &game.panel.ce_score[7],
781     TYPE_INTEGER,
782   },
783   {
784     GAME_PANEL_CE_SCORE_1_ELEMENT,
785     &game.panel.ce_score_element[0],
786     TYPE_ELEMENT,
787   },
788   {
789     GAME_PANEL_CE_SCORE_2_ELEMENT,
790     &game.panel.ce_score_element[1],
791     TYPE_ELEMENT,
792   },
793   {
794     GAME_PANEL_CE_SCORE_3_ELEMENT,
795     &game.panel.ce_score_element[2],
796     TYPE_ELEMENT,
797   },
798   {
799     GAME_PANEL_CE_SCORE_4_ELEMENT,
800     &game.panel.ce_score_element[3],
801     TYPE_ELEMENT,
802   },
803   {
804     GAME_PANEL_CE_SCORE_5_ELEMENT,
805     &game.panel.ce_score_element[4],
806     TYPE_ELEMENT,
807   },
808   {
809     GAME_PANEL_CE_SCORE_6_ELEMENT,
810     &game.panel.ce_score_element[5],
811     TYPE_ELEMENT,
812   },
813   {
814     GAME_PANEL_CE_SCORE_7_ELEMENT,
815     &game.panel.ce_score_element[6],
816     TYPE_ELEMENT,
817   },
818   {
819     GAME_PANEL_CE_SCORE_8_ELEMENT,
820     &game.panel.ce_score_element[7],
821     TYPE_ELEMENT,
822   },
823   {
824     GAME_PANEL_PLAYER_NAME,
825     &game.panel.player_name,
826     TYPE_STRING,
827   },
828   {
829     GAME_PANEL_LEVEL_NAME,
830     &game.panel.level_name,
831     TYPE_STRING,
832   },
833   {
834     GAME_PANEL_LEVEL_AUTHOR,
835     &game.panel.level_author,
836     TYPE_STRING,
837   },
838
839   {
840     -1,
841     NULL,
842     -1,
843   }
844 };
845 #endif
846
847
848 /* values for delayed check of falling and moving elements and for collision */
849 #define CHECK_DELAY_MOVING      3
850 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
851 #define CHECK_DELAY_COLLISION   2
852 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
853
854 /* values for initial player move delay (initial delay counter value) */
855 #define INITIAL_MOVE_DELAY_OFF  -1
856 #define INITIAL_MOVE_DELAY_ON   0
857
858 /* values for player movement speed (which is in fact a delay value) */
859 #define MOVE_DELAY_MIN_SPEED    32
860 #define MOVE_DELAY_NORMAL_SPEED 8
861 #define MOVE_DELAY_HIGH_SPEED   4
862 #define MOVE_DELAY_MAX_SPEED    1
863
864 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
865 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
866
867 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
868 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
869
870 /* values for other actions */
871 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
872 #define MOVE_STEPSIZE_MIN       (1)
873 #define MOVE_STEPSIZE_MAX       (TILEX)
874
875 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
876 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
877
878 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
879
880 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
881                                  RND(element_info[e].push_delay_random))
882 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
883                                  RND(element_info[e].drop_delay_random))
884 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
885                                  RND(element_info[e].move_delay_random))
886 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
887                                     (element_info[e].move_delay_random))
888 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
889                                  RND(element_info[e].ce_value_random_initial))
890 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
891 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
892                                  RND((c)->delay_random * (c)->delay_frames))
893 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
894                                  RND((c)->delay_random))
895
896
897 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
898          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
899
900 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
901         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
902          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
903          (be) + (e) - EL_SELF)
904
905 #define GET_PLAYER_FROM_BITS(p)                                         \
906         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
907
908 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
909         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
910          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
911          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
912          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
913          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
914          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
915          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
916          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
917          (e))
918
919 #define CAN_GROW_INTO(e)                                                \
920         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
921
922 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
923                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
934                                         (CAN_MOVE_INTO_ACID(e) &&       \
935                                          Feld[x][y] == EL_ACID) ||      \
936                                         (condition)))
937
938 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
939                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
940                                         (condition) ||                  \
941                                         (CAN_MOVE_INTO_ACID(e) &&       \
942                                          Feld[x][y] == EL_ACID) ||      \
943                                         (DONT_COLLIDE_WITH(e) &&        \
944                                          IS_PLAYER(x, y) &&             \
945                                          !PLAYER_ENEMY_PROTECTED(x, y))))
946
947 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
949
950 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
951         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
952
953 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
954         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
955
956 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
957         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
958                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
959
960 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
961         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
962
963 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
965
966 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
967         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
968
969 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
970         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
971
972 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
973         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
974
975 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
977                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
978                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
979                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
980                                                  IS_FOOD_PENGUIN(Feld[x][y])))
981 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
985         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
986
987 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
988         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
989
990 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
991         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
992                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
993
994 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
995
996 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
997                 (!IS_PLAYER(x, y) &&                                    \
998                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
999
1000 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1002
1003 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1004 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1005
1006 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1007 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1008 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1009 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1010
1011 /* game button identifiers */
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define SOUND_CTRL_ID_MUSIC             3
1016 #define SOUND_CTRL_ID_LOOPS             4
1017 #define SOUND_CTRL_ID_SIMPLE            5
1018
1019 #define NUM_GAME_BUTTONS                6
1020
1021
1022 /* forward declaration for internal use */
1023
1024 static void CreateField(int, int, int);
1025
1026 static void ResetGfxAnimation(int, int);
1027
1028 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1029 static void AdvanceFrameAndPlayerCounters(int);
1030
1031 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1032 static boolean MovePlayer(struct PlayerInfo *, int, int);
1033 static void ScrollPlayer(struct PlayerInfo *, int);
1034 static void ScrollScreen(struct PlayerInfo *, int);
1035
1036 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1037
1038 static void InitBeltMovement(void);
1039 static void CloseAllOpenTimegates(void);
1040 static void CheckGravityMovement(struct PlayerInfo *);
1041 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1042 static void KillPlayerUnlessEnemyProtected(int, int);
1043 static void KillPlayerUnlessExplosionProtected(int, int);
1044
1045 static void TestIfPlayerTouchesCustomElement(int, int);
1046 static void TestIfElementTouchesCustomElement(int, int);
1047 static void TestIfElementHitsCustomElement(int, int, int);
1048 #if 0
1049 static void TestIfElementSmashesCustomElement(int, int, int);
1050 #endif
1051
1052 static void HandleElementChange(int, int, int);
1053 static void ExecuteCustomElementAction(int, int, int, int);
1054 static boolean ChangeElement(int, int, int, int);
1055
1056 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1057 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1058         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1059 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1060         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1061 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1062         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1063 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1064         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1065
1066 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1067 #define CheckElementChange(x, y, e, te, ev)                             \
1068         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1069 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1070         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1071 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1072         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1073
1074 static void PlayLevelSound(int, int, int);
1075 static void PlayLevelSoundNearest(int, int, int);
1076 static void PlayLevelSoundAction(int, int, int);
1077 static void PlayLevelSoundElementAction(int, int, int, int);
1078 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1079 static void PlayLevelSoundActionIfLoop(int, int, int);
1080 static void StopLevelSoundActionIfLoop(int, int, int);
1081 static void PlayLevelMusic();
1082
1083 static void MapGameButtons();
1084 static void HandleGameButtons(struct GadgetInfo *);
1085
1086 int AmoebeNachbarNr(int, int);
1087 void AmoebeUmwandeln(int, int);
1088 void ContinueMoving(int, int);
1089 void Bang(int, int);
1090 void InitMovDir(int, int);
1091 void InitAmoebaNr(int, int);
1092 int NewHiScore(void);
1093
1094 void TestIfGoodThingHitsBadThing(int, int, int);
1095 void TestIfBadThingHitsGoodThing(int, int, int);
1096 void TestIfPlayerTouchesBadThing(int, int);
1097 void TestIfPlayerRunsIntoBadThing(int, int, int);
1098 void TestIfBadThingTouchesPlayer(int, int);
1099 void TestIfBadThingRunsIntoPlayer(int, int, int);
1100 void TestIfFriendTouchesBadThing(int, int);
1101 void TestIfBadThingTouchesFriend(int, int);
1102 void TestIfBadThingTouchesOtherBadThing(int, int);
1103
1104 void KillPlayer(struct PlayerInfo *);
1105 void BuryPlayer(struct PlayerInfo *);
1106 void RemovePlayer(struct PlayerInfo *);
1107
1108 boolean SnapField(struct PlayerInfo *, int, int);
1109 boolean DropElement(struct PlayerInfo *);
1110
1111 static int getInvisibleActiveFromInvisibleElement(int);
1112 static int getInvisibleFromInvisibleActiveElement(int);
1113
1114 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1115
1116 /* for detection of endless loops, caused by custom element programming */
1117 /* (using maximal playfield width x 10 is just a rough approximation) */
1118 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1119
1120 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1121 {                                                                       \
1122   if (recursion_loop_detected)                                          \
1123     return (rc);                                                        \
1124                                                                         \
1125   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1126   {                                                                     \
1127     recursion_loop_detected = TRUE;                                     \
1128     recursion_loop_element = (e);                                       \
1129   }                                                                     \
1130                                                                         \
1131   recursion_loop_depth++;                                               \
1132 }
1133
1134 #define RECURSION_LOOP_DETECTION_END()                                  \
1135 {                                                                       \
1136   recursion_loop_depth--;                                               \
1137 }
1138
1139 static int recursion_loop_depth;
1140 static boolean recursion_loop_detected;
1141 static boolean recursion_loop_element;
1142
1143
1144 /* ------------------------------------------------------------------------- */
1145 /* definition of elements that automatically change to other elements after  */
1146 /* a specified time, eventually calling a function when changing             */
1147 /* ------------------------------------------------------------------------- */
1148
1149 /* forward declaration for changer functions */
1150 static void InitBuggyBase(int, int);
1151 static void WarnBuggyBase(int, int);
1152
1153 static void InitTrap(int, int);
1154 static void ActivateTrap(int, int);
1155 static void ChangeActiveTrap(int, int);
1156
1157 static void InitRobotWheel(int, int);
1158 static void RunRobotWheel(int, int);
1159 static void StopRobotWheel(int, int);
1160
1161 static void InitTimegateWheel(int, int);
1162 static void RunTimegateWheel(int, int);
1163
1164 static void InitMagicBallDelay(int, int);
1165 static void ActivateMagicBall(int, int);
1166
1167 struct ChangingElementInfo
1168 {
1169   int element;
1170   int target_element;
1171   int change_delay;
1172   void (*pre_change_function)(int x, int y);
1173   void (*change_function)(int x, int y);
1174   void (*post_change_function)(int x, int y);
1175 };
1176
1177 static struct ChangingElementInfo change_delay_list[] =
1178 {
1179   {
1180     EL_NUT_BREAKING,
1181     EL_EMERALD,
1182     6,
1183     NULL,
1184     NULL,
1185     NULL
1186   },
1187   {
1188     EL_PEARL_BREAKING,
1189     EL_EMPTY,
1190     8,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_EXIT_OPENING,
1197     EL_EXIT_OPEN,
1198     29,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_CLOSING,
1205     EL_EXIT_CLOSED,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_STEEL_EXIT_OPENING,
1213     EL_STEEL_EXIT_OPEN,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_CLOSING,
1221     EL_STEEL_EXIT_CLOSED,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_EM_EXIT_OPENING,
1229     EL_EM_EXIT_OPEN,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_CLOSING,
1237 #if 1
1238     EL_EMPTY,
1239 #else
1240     EL_EM_EXIT_CLOSED,
1241 #endif
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_STEEL_EXIT_OPENING,
1249     EL_EM_STEEL_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_STEEL_EXIT_CLOSING,
1257 #if 1
1258     EL_STEELWALL,
1259 #else
1260     EL_EM_STEEL_EXIT_CLOSED,
1261 #endif
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite()
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars()
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   /* make sure that stepsize value is always a power of 2 */
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   /* do no immediately change move delay -- the player might just be moving */
1623   player->move_delay_value_next = move_delay;
1624
1625   /* information if player can move must be set separately */
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig()
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658   InitJoysticks();
1659 }
1660
1661 int GetElementFromGroupElement(int element)
1662 {
1663   if (IS_GROUP_ELEMENT(element))
1664   {
1665     struct ElementGroupInfo *group = element_info[element].group;
1666     int last_anim_random_frame = gfx.anim_random_frame;
1667     int element_pos;
1668
1669     if (group->choice_mode == ANIM_RANDOM)
1670       gfx.anim_random_frame = RND(group->num_elements_resolved);
1671
1672     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1673                                     group->choice_mode, 0,
1674                                     group->choice_pos);
1675
1676     if (group->choice_mode == ANIM_RANDOM)
1677       gfx.anim_random_frame = last_anim_random_frame;
1678
1679     group->choice_pos++;
1680
1681     element = group->element_resolved[element_pos];
1682   }
1683
1684   return element;
1685 }
1686
1687 static void InitPlayerField(int x, int y, int element, boolean init_game)
1688 {
1689   if (element == EL_SP_MURPHY)
1690   {
1691     if (init_game)
1692     {
1693       if (stored_player[0].present)
1694       {
1695         Feld[x][y] = EL_SP_MURPHY_CLONE;
1696
1697         return;
1698       }
1699       else
1700       {
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     /* ---------- initialize player's last field block delay --------------- */
1723
1724     /* always start with reliable default value (no adjustment needed) */
1725     player->block_delay_adjustment = 0;
1726
1727     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     /* special case 2: in game engines before 3.1.1, blocking was different */
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!options.network || player->connected)
1736     {
1737       player->active = TRUE;
1738
1739       /* remove potentially duplicate players */
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745       if (options.debug)
1746       {
1747         printf("Player %d activated.\n", player->element_nr);
1748         printf("[Local player is %d and currently %s.]\n",
1749                local_player->element_nr,
1750                local_player->active ? "active" : "not active");
1751       }
1752     }
1753
1754     Feld[x][y] = EL_EMPTY;
1755
1756     player->jx = player->last_jx = x;
1757     player->jy = player->last_jy = y;
1758   }
1759 }
1760
1761 static void InitField(int x, int y, boolean init_game)
1762 {
1763   int element = Feld[x][y];
1764
1765   switch (element)
1766   {
1767     case EL_SP_MURPHY:
1768     case EL_PLAYER_1:
1769     case EL_PLAYER_2:
1770     case EL_PLAYER_3:
1771     case EL_PLAYER_4:
1772       InitPlayerField(x, y, element, init_game);
1773       break;
1774
1775     case EL_SOKOBAN_FIELD_PLAYER:
1776       element = Feld[x][y] = EL_PLAYER_1;
1777       InitField(x, y, init_game);
1778
1779       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1780       InitField(x, y, init_game);
1781       break;
1782
1783     case EL_SOKOBAN_FIELD_EMPTY:
1784       local_player->sokobanfields_still_needed++;
1785       break;
1786
1787     case EL_STONEBLOCK:
1788       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1789         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1790       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1791         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1792       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1793         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1794       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1795         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1796       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1797         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1798       break;
1799
1800     case EL_BUG:
1801     case EL_BUG_RIGHT:
1802     case EL_BUG_UP:
1803     case EL_BUG_LEFT:
1804     case EL_BUG_DOWN:
1805     case EL_SPACESHIP:
1806     case EL_SPACESHIP_RIGHT:
1807     case EL_SPACESHIP_UP:
1808     case EL_SPACESHIP_LEFT:
1809     case EL_SPACESHIP_DOWN:
1810     case EL_BD_BUTTERFLY:
1811     case EL_BD_BUTTERFLY_RIGHT:
1812     case EL_BD_BUTTERFLY_UP:
1813     case EL_BD_BUTTERFLY_LEFT:
1814     case EL_BD_BUTTERFLY_DOWN:
1815     case EL_BD_FIREFLY:
1816     case EL_BD_FIREFLY_RIGHT:
1817     case EL_BD_FIREFLY_UP:
1818     case EL_BD_FIREFLY_LEFT:
1819     case EL_BD_FIREFLY_DOWN:
1820     case EL_PACMAN_RIGHT:
1821     case EL_PACMAN_UP:
1822     case EL_PACMAN_LEFT:
1823     case EL_PACMAN_DOWN:
1824     case EL_YAMYAM:
1825     case EL_YAMYAM_LEFT:
1826     case EL_YAMYAM_RIGHT:
1827     case EL_YAMYAM_UP:
1828     case EL_YAMYAM_DOWN:
1829     case EL_DARK_YAMYAM:
1830     case EL_ROBOT:
1831     case EL_PACMAN:
1832     case EL_SP_SNIKSNAK:
1833     case EL_SP_ELECTRON:
1834     case EL_MOLE:
1835     case EL_MOLE_LEFT:
1836     case EL_MOLE_RIGHT:
1837     case EL_MOLE_UP:
1838     case EL_MOLE_DOWN:
1839       InitMovDir(x, y);
1840       break;
1841
1842     case EL_AMOEBA_FULL:
1843     case EL_BD_AMOEBA:
1844       InitAmoebaNr(x, y);
1845       break;
1846
1847     case EL_AMOEBA_DROP:
1848       if (y == lev_fieldy - 1)
1849       {
1850         Feld[x][y] = EL_AMOEBA_GROWING;
1851         Store[x][y] = EL_AMOEBA_WET;
1852       }
1853       break;
1854
1855     case EL_DYNAMITE_ACTIVE:
1856     case EL_SP_DISK_RED_ACTIVE:
1857     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1858     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1859     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1860     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1861       MovDelay[x][y] = 96;
1862       break;
1863
1864     case EL_EM_DYNAMITE_ACTIVE:
1865       MovDelay[x][y] = 32;
1866       break;
1867
1868     case EL_LAMP:
1869       local_player->lights_still_needed++;
1870       break;
1871
1872     case EL_PENGUIN:
1873       local_player->friends_still_needed++;
1874       break;
1875
1876     case EL_PIG:
1877     case EL_DRAGON:
1878       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1879       break;
1880
1881     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1882     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1883     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1884     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1885     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1886     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1887     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1888     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1889     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1890     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1891     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1892     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1893       if (init_game)
1894       {
1895         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1896         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1897         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1898
1899         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1900         {
1901           game.belt_dir[belt_nr] = belt_dir;
1902           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1903         }
1904         else    /* more than one switch -- set it like the first switch */
1905         {
1906           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1907         }
1908       }
1909       break;
1910
1911 #if !USE_BOTH_SWITCHGATE_SWITCHES
1912     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1913       if (init_game)
1914         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1915       break;
1916
1917     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1918       if (init_game)
1919         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1920       break;
1921 #endif
1922
1923     case EL_LIGHT_SWITCH_ACTIVE:
1924       if (init_game)
1925         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1926       break;
1927
1928     case EL_INVISIBLE_STEELWALL:
1929     case EL_INVISIBLE_WALL:
1930     case EL_INVISIBLE_SAND:
1931       if (game.light_time_left > 0 ||
1932           game.lenses_time_left > 0)
1933         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1934       break;
1935
1936     case EL_EMC_MAGIC_BALL:
1937       if (game.ball_state)
1938         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1939       break;
1940
1941     case EL_EMC_MAGIC_BALL_SWITCH:
1942       if (game.ball_state)
1943         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1944       break;
1945
1946     case EL_TRIGGER_PLAYER:
1947     case EL_TRIGGER_ELEMENT:
1948     case EL_TRIGGER_CE_VALUE:
1949     case EL_TRIGGER_CE_SCORE:
1950     case EL_SELF:
1951     case EL_ANY_ELEMENT:
1952     case EL_CURRENT_CE_VALUE:
1953     case EL_CURRENT_CE_SCORE:
1954     case EL_PREV_CE_1:
1955     case EL_PREV_CE_2:
1956     case EL_PREV_CE_3:
1957     case EL_PREV_CE_4:
1958     case EL_PREV_CE_5:
1959     case EL_PREV_CE_6:
1960     case EL_PREV_CE_7:
1961     case EL_PREV_CE_8:
1962     case EL_NEXT_CE_1:
1963     case EL_NEXT_CE_2:
1964     case EL_NEXT_CE_3:
1965     case EL_NEXT_CE_4:
1966     case EL_NEXT_CE_5:
1967     case EL_NEXT_CE_6:
1968     case EL_NEXT_CE_7:
1969     case EL_NEXT_CE_8:
1970       /* reference elements should not be used on the playfield */
1971       Feld[x][y] = EL_EMPTY;
1972       break;
1973
1974     default:
1975       if (IS_CUSTOM_ELEMENT(element))
1976       {
1977         if (CAN_MOVE(element))
1978           InitMovDir(x, y);
1979
1980 #if USE_NEW_CUSTOM_VALUE
1981         if (!element_info[element].use_last_ce_value || init_game)
1982           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1983 #endif
1984       }
1985       else if (IS_GROUP_ELEMENT(element))
1986       {
1987         Feld[x][y] = GetElementFromGroupElement(element);
1988
1989         InitField(x, y, init_game);
1990       }
1991
1992       break;
1993   }
1994
1995   if (!init_game)
1996     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1997 }
1998
1999 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2000 {
2001   InitField(x, y, init_game);
2002
2003   /* not needed to call InitMovDir() -- already done by InitField()! */
2004   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2005       CAN_MOVE(Feld[x][y]))
2006     InitMovDir(x, y);
2007 }
2008
2009 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2010 {
2011   int old_element = Feld[x][y];
2012
2013   InitField(x, y, init_game);
2014
2015   /* not needed to call InitMovDir() -- already done by InitField()! */
2016   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2017       CAN_MOVE(old_element) &&
2018       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2019     InitMovDir(x, y);
2020
2021   /* this case is in fact a combination of not less than three bugs:
2022      first, it calls InitMovDir() for elements that can move, although this is
2023      already done by InitField(); then, it checks the element that was at this
2024      field _before_ the call to InitField() (which can change it); lastly, it
2025      was not called for "mole with direction" elements, which were treated as
2026      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2027   */
2028 }
2029
2030 #if 1
2031
2032 static int get_key_element_from_nr(int key_nr)
2033 {
2034   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2035                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2036                           EL_EM_KEY_1 : EL_KEY_1);
2037
2038   return key_base_element + key_nr;
2039 }
2040
2041 static int get_next_dropped_element(struct PlayerInfo *player)
2042 {
2043   return (player->inventory_size > 0 ?
2044           player->inventory_element[player->inventory_size - 1] :
2045           player->inventory_infinite_element != EL_UNDEFINED ?
2046           player->inventory_infinite_element :
2047           player->dynabombs_left > 0 ?
2048           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2049           EL_UNDEFINED);
2050 }
2051
2052 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2053 {
2054   /* pos >= 0: get element from bottom of the stack;
2055      pos <  0: get element from top of the stack */
2056
2057   if (pos < 0)
2058   {
2059     int min_inventory_size = -pos;
2060     int inventory_pos = player->inventory_size - min_inventory_size;
2061     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2062
2063     return (player->inventory_size >= min_inventory_size ?
2064             player->inventory_element[inventory_pos] :
2065             player->inventory_infinite_element != EL_UNDEFINED ?
2066             player->inventory_infinite_element :
2067             player->dynabombs_left >= min_dynabombs_left ?
2068             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2069             EL_UNDEFINED);
2070   }
2071   else
2072   {
2073     int min_dynabombs_left = pos + 1;
2074     int min_inventory_size = pos + 1 - player->dynabombs_left;
2075     int inventory_pos = pos - player->dynabombs_left;
2076
2077     return (player->inventory_infinite_element != EL_UNDEFINED ?
2078             player->inventory_infinite_element :
2079             player->dynabombs_left >= min_dynabombs_left ?
2080             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2081             player->inventory_size >= min_inventory_size ?
2082             player->inventory_element[inventory_pos] :
2083             EL_UNDEFINED);
2084   }
2085 }
2086
2087 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2088 {
2089   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2090   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2091   int compare_result;
2092
2093   if (gpo1->sort_priority != gpo2->sort_priority)
2094     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2095   else
2096     compare_result = gpo1->nr - gpo2->nr;
2097
2098   return compare_result;
2099 }
2100
2101 void InitGameControlValues()
2102 {
2103   int i;
2104
2105   for (i = 0; game_panel_controls[i].nr != -1; i++)
2106   {
2107     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2108     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2109     struct TextPosInfo *pos = gpc->pos;
2110     int nr = gpc->nr;
2111     int type = gpc->type;
2112
2113     if (nr != i)
2114     {
2115       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2116       Error(ERR_EXIT, "this should not happen -- please debug");
2117     }
2118
2119     /* force update of game controls after initialization */
2120     gpc->value = gpc->last_value = -1;
2121     gpc->frame = gpc->last_frame = -1;
2122     gpc->gfx_frame = -1;
2123
2124     /* determine panel value width for later calculation of alignment */
2125     if (type == TYPE_INTEGER || type == TYPE_STRING)
2126     {
2127       pos->width = pos->size * getFontWidth(pos->font);
2128       pos->height = getFontHeight(pos->font);
2129     }
2130     else if (type == TYPE_ELEMENT)
2131     {
2132       pos->width = pos->size;
2133       pos->height = pos->size;
2134     }
2135
2136     /* fill structure for game panel draw order */
2137     gpo->nr = gpc->nr;
2138     gpo->sort_priority = pos->sort_priority;
2139   }
2140
2141   /* sort game panel controls according to sort_priority and control number */
2142   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2143         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2144 }
2145
2146 void UpdatePlayfieldElementCount()
2147 {
2148   boolean use_element_count = FALSE;
2149   int i, j, x, y;
2150
2151   /* first check if it is needed at all to calculate playfield element count */
2152   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2153     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2154       use_element_count = TRUE;
2155
2156   if (!use_element_count)
2157     return;
2158
2159   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2160     element_info[i].element_count = 0;
2161
2162   SCAN_PLAYFIELD(x, y)
2163   {
2164     element_info[Feld[x][y]].element_count++;
2165   }
2166
2167   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2168     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2169       if (IS_IN_GROUP(j, i))
2170         element_info[EL_GROUP_START + i].element_count +=
2171           element_info[j].element_count;
2172 }
2173
2174 void UpdateGameControlValues()
2175 {
2176   int i, k;
2177   int time = (local_player->LevelSolved ?
2178               local_player->LevelSolved_CountingTime :
2179               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2180               level.native_em_level->lev->time :
2181               level.time == 0 ? TimePlayed : TimeLeft);
2182   int score = (local_player->LevelSolved ?
2183                local_player->LevelSolved_CountingScore :
2184                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185                level.native_em_level->lev->score :
2186                local_player->score);
2187   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2188               level.native_em_level->lev->required :
2189               local_player->gems_still_needed);
2190   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2191                      level.native_em_level->lev->required > 0 :
2192                      local_player->gems_still_needed > 0 ||
2193                      local_player->sokobanfields_still_needed > 0 ||
2194                      local_player->lights_still_needed > 0);
2195
2196   UpdatePlayfieldElementCount();
2197
2198   /* update game panel control values */
2199
2200   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2201   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2202
2203   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2204   for (i = 0; i < MAX_NUM_KEYS; i++)
2205     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2206   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2207   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2208
2209   if (game.centered_player_nr == -1)
2210   {
2211     for (i = 0; i < MAX_PLAYERS; i++)
2212     {
2213       for (k = 0; k < MAX_NUM_KEYS; k++)
2214       {
2215         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2216         {
2217           if (level.native_em_level->ply[i]->keys & (1 << k))
2218             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2219               get_key_element_from_nr(k);
2220         }
2221         else if (stored_player[i].key[k])
2222           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2223             get_key_element_from_nr(k);
2224       }
2225
2226       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2227         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2228           level.native_em_level->ply[i]->dynamite;
2229       else
2230         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2231           stored_player[i].inventory_size;
2232
2233       if (stored_player[i].num_white_keys > 0)
2234         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2235           EL_DC_KEY_WHITE;
2236
2237       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2238         stored_player[i].num_white_keys;
2239     }
2240   }
2241   else
2242   {
2243     int player_nr = game.centered_player_nr;
2244
2245     for (k = 0; k < MAX_NUM_KEYS; k++)
2246     {
2247       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2248       {
2249         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2250           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2251             get_key_element_from_nr(k);
2252       }
2253       else if (stored_player[player_nr].key[k])
2254         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2255           get_key_element_from_nr(k);
2256     }
2257
2258     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2259       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2260         level.native_em_level->ply[player_nr]->dynamite;
2261     else
2262       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2263         stored_player[player_nr].inventory_size;
2264
2265     if (stored_player[player_nr].num_white_keys > 0)
2266       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2267
2268     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2269       stored_player[player_nr].num_white_keys;
2270   }
2271
2272   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2273   {
2274     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2275       get_inventory_element_from_pos(local_player, i);
2276     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2277       get_inventory_element_from_pos(local_player, -i - 1);
2278   }
2279
2280   game_panel_controls[GAME_PANEL_SCORE].value = score;
2281   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2282
2283   game_panel_controls[GAME_PANEL_TIME].value = time;
2284
2285   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2286   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2287   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2288
2289   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2290     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2291      EL_EMPTY);
2292   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2293     local_player->shield_normal_time_left;
2294   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2295     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2296      EL_EMPTY);
2297   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2298     local_player->shield_deadly_time_left;
2299
2300   game_panel_controls[GAME_PANEL_EXIT].value =
2301     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2302
2303   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2304     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2305   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2306     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2307      EL_EMC_MAGIC_BALL_SWITCH);
2308
2309   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2310     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2311   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2312     game.light_time_left;
2313
2314   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2315     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2316   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2317     game.timegate_time_left;
2318
2319   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2320     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2321
2322   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2323     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2324   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2325     game.lenses_time_left;
2326
2327   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2328     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2329   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2330     game.magnify_time_left;
2331
2332   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2333     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2334      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2335      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2336      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2337      EL_BALLOON_SWITCH_NONE);
2338
2339   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2340     local_player->dynabomb_count;
2341   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2342     local_player->dynabomb_size;
2343   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2344     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2345
2346   game_panel_controls[GAME_PANEL_PENGUINS].value =
2347     local_player->friends_still_needed;
2348
2349   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2350     local_player->sokobanfields_still_needed;
2351   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2352     local_player->sokobanfields_still_needed;
2353
2354   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2355     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2356
2357   for (i = 0; i < NUM_BELTS; i++)
2358   {
2359     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2360       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2361        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2362     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2363       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2364   }
2365
2366   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2367     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2368   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2369     game.magic_wall_time_left;
2370
2371 #if USE_PLAYER_GRAVITY
2372   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2373     local_player->gravity;
2374 #else
2375   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2376 #endif
2377
2378   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2379     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2380
2381   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2382     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2383       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2384        game.panel.element[i].id : EL_UNDEFINED);
2385
2386   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2387     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2388       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2389        element_info[game.panel.element_count[i].id].element_count : 0);
2390
2391   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2392     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2393       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2394        element_info[game.panel.ce_score[i].id].collect_score : 0);
2395
2396   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2397     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2398       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2399        element_info[game.panel.ce_score_element[i].id].collect_score :
2400        EL_UNDEFINED);
2401
2402   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2403   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2404   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2405
2406   /* update game panel control frames */
2407
2408   for (i = 0; game_panel_controls[i].nr != -1; i++)
2409   {
2410     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2411
2412     if (gpc->type == TYPE_ELEMENT)
2413     {
2414       int last_anim_random_frame = gfx.anim_random_frame;
2415       int element = gpc->value;
2416       int graphic = el2panelimg(element);
2417
2418       if (gpc->value != gpc->last_value)
2419       {
2420         gpc->gfx_frame = 0;
2421         gpc->gfx_random = INIT_GFX_RANDOM();
2422       }
2423       else
2424       {
2425         gpc->gfx_frame++;
2426
2427         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2428             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2429           gpc->gfx_random = INIT_GFX_RANDOM();
2430       }
2431
2432       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2433         gfx.anim_random_frame = gpc->gfx_random;
2434
2435       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2436         gpc->gfx_frame = element_info[element].collect_score;
2437
2438       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2439                                             gpc->gfx_frame);
2440
2441       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2442         gfx.anim_random_frame = last_anim_random_frame;
2443     }
2444   }
2445 }
2446
2447 void DisplayGameControlValues()
2448 {
2449   boolean redraw_panel = FALSE;
2450   int i;
2451
2452   for (i = 0; game_panel_controls[i].nr != -1; i++)
2453   {
2454     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2455
2456     if (PANEL_DEACTIVATED(gpc->pos))
2457       continue;
2458
2459     if (gpc->value == gpc->last_value &&
2460         gpc->frame == gpc->last_frame)
2461       continue;
2462
2463     redraw_panel = TRUE;
2464   }
2465
2466   if (!redraw_panel)
2467     return;
2468
2469   /* copy default game door content to main double buffer */
2470   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2471              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2472
2473   /* redraw game control buttons */
2474 #if 1
2475   RedrawGameButtons();
2476 #else
2477   UnmapGameButtons();
2478   MapGameButtons();
2479 #endif
2480
2481   game_status = GAME_MODE_PSEUDO_PANEL;
2482
2483 #if 1
2484   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2485 #else
2486   for (i = 0; game_panel_controls[i].nr != -1; i++)
2487 #endif
2488   {
2489 #if 1
2490     int nr = game_panel_order[i].nr;
2491     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2492 #else
2493     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2494     int nr = gpc->nr;
2495 #endif
2496     struct TextPosInfo *pos = gpc->pos;
2497     int type = gpc->type;
2498     int value = gpc->value;
2499     int frame = gpc->frame;
2500 #if 0
2501     int last_value = gpc->last_value;
2502     int last_frame = gpc->last_frame;
2503 #endif
2504     int size = pos->size;
2505     int font = pos->font;
2506     boolean draw_masked = pos->draw_masked;
2507     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2508
2509     if (PANEL_DEACTIVATED(pos))
2510       continue;
2511
2512 #if 0
2513     if (value == last_value && frame == last_frame)
2514       continue;
2515 #endif
2516
2517     gpc->last_value = value;
2518     gpc->last_frame = frame;
2519
2520 #if 0
2521     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2522 #endif
2523
2524     if (type == TYPE_INTEGER)
2525     {
2526       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2527           nr == GAME_PANEL_TIME)
2528       {
2529         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2530
2531         if (use_dynamic_size)           /* use dynamic number of digits */
2532         {
2533           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2534           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2535           int size2 = size1 + 1;
2536           int font1 = pos->font;
2537           int font2 = pos->font_alt;
2538
2539           size = (value < value_change ? size1 : size2);
2540           font = (value < value_change ? font1 : font2);
2541
2542 #if 0
2543           /* clear background if value just changed its size (dynamic digits) */
2544           if ((last_value < value_change) != (value < value_change))
2545           {
2546             int width1 = size1 * getFontWidth(font1);
2547             int width2 = size2 * getFontWidth(font2);
2548             int max_width = MAX(width1, width2);
2549             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2550
2551             pos->width = max_width;
2552
2553             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2554                                        max_width, max_height);
2555           }
2556 #endif
2557         }
2558       }
2559
2560 #if 1
2561       /* correct text size if "digits" is zero or less */
2562       if (size <= 0)
2563         size = strlen(int2str(value, size));
2564
2565       /* dynamically correct text alignment */
2566       pos->width = size * getFontWidth(font);
2567 #endif
2568
2569       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2570                   int2str(value, size), font, mask_mode);
2571     }
2572     else if (type == TYPE_ELEMENT)
2573     {
2574       int element, graphic;
2575       Bitmap *src_bitmap;
2576       int src_x, src_y;
2577       int width, height;
2578       int dst_x = PANEL_XPOS(pos);
2579       int dst_y = PANEL_YPOS(pos);
2580
2581 #if 1
2582       if (value != EL_UNDEFINED && value != EL_EMPTY)
2583       {
2584         element = value;
2585         graphic = el2panelimg(value);
2586
2587         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2588
2589 #if 1
2590         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2591           size = TILESIZE;
2592 #endif
2593
2594         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2595                               &src_x, &src_y);
2596
2597         width  = graphic_info[graphic].width  * size / TILESIZE;
2598         height = graphic_info[graphic].height * size / TILESIZE;
2599
2600         if (draw_masked)
2601         {
2602           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2603                         dst_x - src_x, dst_y - src_y);
2604           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2605                            dst_x, dst_y);
2606         }
2607         else
2608         {
2609           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2610                      dst_x, dst_y);
2611         }
2612       }
2613 #else
2614       if (value == EL_UNDEFINED || value == EL_EMPTY)
2615       {
2616         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2617         graphic = el2panelimg(element);
2618
2619         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2620         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2621         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2622       }
2623       else
2624       {
2625         element = value;
2626         graphic = el2panelimg(value);
2627
2628         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2629       }
2630
2631       width  = graphic_info[graphic].width  * size / TILESIZE;
2632       height = graphic_info[graphic].height * size / TILESIZE;
2633
2634       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2635 #endif
2636     }
2637     else if (type == TYPE_STRING)
2638     {
2639       boolean active = (value != 0);
2640       char *state_normal = "off";
2641       char *state_active = "on";
2642       char *state = (active ? state_active : state_normal);
2643       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2644                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2645                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2646                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2647
2648       if (nr == GAME_PANEL_GRAVITY_STATE)
2649       {
2650         int font1 = pos->font;          /* (used for normal state) */
2651         int font2 = pos->font_alt;      /* (used for active state) */
2652 #if 0
2653         int size1 = strlen(state_normal);
2654         int size2 = strlen(state_active);
2655         int width1 = size1 * getFontWidth(font1);
2656         int width2 = size2 * getFontWidth(font2);
2657         int max_width = MAX(width1, width2);
2658         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2659
2660         pos->width = max_width;
2661
2662         /* clear background for values that may have changed its size */
2663         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2664                                    max_width, max_height);
2665 #endif
2666
2667         font = (active ? font2 : font1);
2668       }
2669
2670       if (s != NULL)
2671       {
2672         char *s_cut;
2673
2674 #if 1
2675         if (size <= 0)
2676         {
2677           /* don't truncate output if "chars" is zero or less */
2678           size = strlen(s);
2679
2680           /* dynamically correct text alignment */
2681           pos->width = size * getFontWidth(font);
2682         }
2683 #endif
2684
2685         s_cut = getStringCopyN(s, size);
2686
2687         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2688                     s_cut, font, mask_mode);
2689
2690         free(s_cut);
2691       }
2692     }
2693
2694     redraw_mask |= REDRAW_DOOR_1;
2695   }
2696
2697   game_status = GAME_MODE_PLAYING;
2698 }
2699
2700 void UpdateAndDisplayGameControlValues()
2701 {
2702   if (tape.warp_forward)
2703     return;
2704
2705   UpdateGameControlValues();
2706   DisplayGameControlValues();
2707 }
2708
2709 void DrawGameValue_Emeralds(int value)
2710 {
2711   struct TextPosInfo *pos = &game.panel.gems;
2712 #if 1
2713   int font_nr = pos->font;
2714 #else
2715   int font_nr = FONT_TEXT_2;
2716 #endif
2717   int font_width = getFontWidth(font_nr);
2718   int chars = pos->size;
2719
2720 #if 1
2721   return;       /* !!! USE NEW STUFF !!! */
2722 #endif
2723
2724   if (PANEL_DEACTIVATED(pos))
2725     return;
2726
2727   pos->width = chars * font_width;
2728
2729   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2730 }
2731
2732 void DrawGameValue_Dynamite(int value)
2733 {
2734   struct TextPosInfo *pos = &game.panel.inventory_count;
2735 #if 1
2736   int font_nr = pos->font;
2737 #else
2738   int font_nr = FONT_TEXT_2;
2739 #endif
2740   int font_width = getFontWidth(font_nr);
2741   int chars = pos->size;
2742
2743 #if 1
2744   return;       /* !!! USE NEW STUFF !!! */
2745 #endif
2746
2747   if (PANEL_DEACTIVATED(pos))
2748     return;
2749
2750   pos->width = chars * font_width;
2751
2752   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2753 }
2754
2755 void DrawGameValue_Score(int value)
2756 {
2757   struct TextPosInfo *pos = &game.panel.score;
2758 #if 1
2759   int font_nr = pos->font;
2760 #else
2761   int font_nr = FONT_TEXT_2;
2762 #endif
2763   int font_width = getFontWidth(font_nr);
2764   int chars = pos->size;
2765
2766 #if 1
2767   return;       /* !!! USE NEW STUFF !!! */
2768 #endif
2769
2770   if (PANEL_DEACTIVATED(pos))
2771     return;
2772
2773   pos->width = chars * font_width;
2774
2775   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2776 }
2777
2778 void DrawGameValue_Time(int value)
2779 {
2780   struct TextPosInfo *pos = &game.panel.time;
2781   static int last_value = -1;
2782   int chars1 = 3;
2783   int chars2 = 4;
2784   int chars = pos->size;
2785 #if 1
2786   int font1_nr = pos->font;
2787   int font2_nr = pos->font_alt;
2788 #else
2789   int font1_nr = FONT_TEXT_2;
2790   int font2_nr = FONT_TEXT_1;
2791 #endif
2792   int font_nr = font1_nr;
2793   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2794
2795 #if 1
2796   return;       /* !!! USE NEW STUFF !!! */
2797 #endif
2798
2799   if (PANEL_DEACTIVATED(pos))
2800     return;
2801
2802   if (use_dynamic_chars)                /* use dynamic number of chars */
2803   {
2804     chars   = (value < 1000 ? chars1   : chars2);
2805     font_nr = (value < 1000 ? font1_nr : font2_nr);
2806   }
2807
2808   /* clear background if value just changed its size (dynamic chars only) */
2809   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2810   {
2811     int width1 = chars1 * getFontWidth(font1_nr);
2812     int width2 = chars2 * getFontWidth(font2_nr);
2813     int max_width = MAX(width1, width2);
2814     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2815
2816     pos->width = max_width;
2817
2818     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2819                                max_width, max_height);
2820   }
2821
2822   pos->width = chars * getFontWidth(font_nr);
2823
2824   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2825
2826   last_value = value;
2827 }
2828
2829 void DrawGameValue_Level(int value)
2830 {
2831   struct TextPosInfo *pos = &game.panel.level_number;
2832   int chars1 = 2;
2833   int chars2 = 3;
2834   int chars = pos->size;
2835 #if 1
2836   int font1_nr = pos->font;
2837   int font2_nr = pos->font_alt;
2838 #else
2839   int font1_nr = FONT_TEXT_2;
2840   int font2_nr = FONT_TEXT_1;
2841 #endif
2842   int font_nr = font1_nr;
2843   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2844
2845 #if 1
2846   return;       /* !!! USE NEW STUFF !!! */
2847 #endif
2848
2849   if (PANEL_DEACTIVATED(pos))
2850     return;
2851
2852   if (use_dynamic_chars)                /* use dynamic number of chars */
2853   {
2854     chars   = (level_nr < 100 ? chars1   : chars2);
2855     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2856   }
2857
2858   pos->width = chars * getFontWidth(font_nr);
2859
2860   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2861 }
2862
2863 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2864 {
2865 #if 0
2866   struct TextPosInfo *pos = &game.panel.keys;
2867 #endif
2868 #if 0
2869   int base_key_graphic = EL_KEY_1;
2870 #endif
2871   int i;
2872
2873 #if 1
2874   return;       /* !!! USE NEW STUFF !!! */
2875 #endif
2876
2877 #if 0
2878   if (PANEL_DEACTIVATED(pos))
2879     return;
2880 #endif
2881
2882 #if 0
2883   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2884     base_key_graphic = EL_EM_KEY_1;
2885 #endif
2886
2887 #if 0
2888   pos->width = 4 * MINI_TILEX;
2889 #endif
2890
2891 #if 1
2892   for (i = 0; i < MAX_NUM_KEYS; i++)
2893 #else
2894   /* currently only 4 of 8 possible keys are displayed */
2895   for (i = 0; i < STD_NUM_KEYS; i++)
2896 #endif
2897   {
2898 #if 1
2899     struct TextPosInfo *pos = &game.panel.key[i];
2900 #endif
2901     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2902     int src_y = DOOR_GFX_PAGEY1 + 123;
2903 #if 1
2904     int dst_x = PANEL_XPOS(pos);
2905     int dst_y = PANEL_YPOS(pos);
2906 #else
2907     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2908     int dst_y = PANEL_YPOS(pos);
2909 #endif
2910
2911 #if 1
2912     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2913                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2914                    EL_KEY_1) + i;
2915     int graphic = el2edimg(element);
2916 #endif
2917
2918 #if 1
2919     if (PANEL_DEACTIVATED(pos))
2920       continue;
2921 #endif
2922
2923 #if 0
2924     /* masked blit with tiles from half-size scaled bitmap does not work yet
2925        (no mask bitmap created for these sizes after loading and scaling) --
2926        solution: load without creating mask, scale, then create final mask */
2927
2928     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2929                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2930
2931     if (key[i])
2932     {
2933 #if 0
2934       int graphic = el2edimg(base_key_graphic + i);
2935 #endif
2936       Bitmap *src_bitmap;
2937       int src_x, src_y;
2938
2939       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2940
2941       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2942                     dst_x - src_x, dst_y - src_y);
2943       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2944                        dst_x, dst_y);
2945     }
2946 #else
2947 #if 1
2948     if (key[i])
2949       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2950     else
2951       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2952                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2953 #else
2954     if (key[i])
2955       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2956     else
2957       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2958                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2959 #endif
2960 #endif
2961   }
2962 }
2963
2964 #else
2965
2966 void DrawGameValue_Emeralds(int value)
2967 {
2968   int font_nr = FONT_TEXT_2;
2969   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2970
2971   if (PANEL_DEACTIVATED(game.panel.gems))
2972     return;
2973
2974   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2975 }
2976
2977 void DrawGameValue_Dynamite(int value)
2978 {
2979   int font_nr = FONT_TEXT_2;
2980   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2981
2982   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2983     return;
2984
2985   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2986 }
2987
2988 void DrawGameValue_Score(int value)
2989 {
2990   int font_nr = FONT_TEXT_2;
2991   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2992
2993   if (PANEL_DEACTIVATED(game.panel.score))
2994     return;
2995
2996   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2997 }
2998
2999 void DrawGameValue_Time(int value)
3000 {
3001   int font1_nr = FONT_TEXT_2;
3002 #if 1
3003   int font2_nr = FONT_TEXT_1;
3004 #else
3005   int font2_nr = FONT_LEVEL_NUMBER;
3006 #endif
3007   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3008   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3009
3010   if (PANEL_DEACTIVATED(game.panel.time))
3011     return;
3012
3013   /* clear background if value just changed its size */
3014   if (value == 999 || value == 1000)
3015     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3016
3017   if (value < 1000)
3018     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3019   else
3020     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3021 }
3022
3023 void DrawGameValue_Level(int value)
3024 {
3025   int font1_nr = FONT_TEXT_2;
3026 #if 1
3027   int font2_nr = FONT_TEXT_1;
3028 #else
3029   int font2_nr = FONT_LEVEL_NUMBER;
3030 #endif
3031
3032   if (PANEL_DEACTIVATED(game.panel.level))
3033     return;
3034
3035   if (level_nr < 100)
3036     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3037   else
3038     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3039 }
3040
3041 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3042 {
3043   int base_key_graphic = EL_KEY_1;
3044   int i;
3045
3046   if (PANEL_DEACTIVATED(game.panel.keys))
3047     return;
3048
3049   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3050     base_key_graphic = EL_EM_KEY_1;
3051
3052   /* currently only 4 of 8 possible keys are displayed */
3053   for (i = 0; i < STD_NUM_KEYS; i++)
3054   {
3055     int x = XX_KEYS + i * MINI_TILEX;
3056     int y = YY_KEYS;
3057
3058     if (key[i])
3059       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3060     else
3061       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3062                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3063   }
3064 }
3065
3066 #endif
3067
3068 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3069                        int key_bits)
3070 {
3071   int key[MAX_NUM_KEYS];
3072   int i;
3073
3074   /* prevent EM engine from updating time/score values parallel to GameWon() */
3075   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3076       local_player->LevelSolved)
3077     return;
3078
3079   for (i = 0; i < MAX_NUM_KEYS; i++)
3080     key[i] = key_bits & (1 << i);
3081
3082   DrawGameValue_Level(level_nr);
3083
3084   DrawGameValue_Emeralds(emeralds);
3085   DrawGameValue_Dynamite(dynamite);
3086   DrawGameValue_Score(score);
3087   DrawGameValue_Time(time);
3088
3089   DrawGameValue_Keys(key);
3090 }
3091
3092 void UpdateGameDoorValues()
3093 {
3094   UpdateGameControlValues();
3095 }
3096
3097 void DrawGameDoorValues()
3098 {
3099   DisplayGameControlValues();
3100 }
3101
3102 void DrawGameDoorValues_OLD()
3103 {
3104   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3105   int dynamite_value = 0;
3106   int score_value = (local_player->LevelSolved ? local_player->score_final :
3107                      local_player->score);
3108   int gems_value = local_player->gems_still_needed;
3109   int key_bits = 0;
3110   int i, j;
3111
3112   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3113   {
3114     DrawGameDoorValues_EM();
3115
3116     return;
3117   }
3118
3119   if (game.centered_player_nr == -1)
3120   {
3121     for (i = 0; i < MAX_PLAYERS; i++)
3122     {
3123       for (j = 0; j < MAX_NUM_KEYS; j++)
3124         if (stored_player[i].key[j])
3125           key_bits |= (1 << j);
3126
3127       dynamite_value += stored_player[i].inventory_size;
3128     }
3129   }
3130   else
3131   {
3132     int player_nr = game.centered_player_nr;
3133
3134     for (i = 0; i < MAX_NUM_KEYS; i++)
3135       if (stored_player[player_nr].key[i])
3136         key_bits |= (1 << i);
3137
3138     dynamite_value = stored_player[player_nr].inventory_size;
3139   }
3140
3141   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3142                     key_bits);
3143 }
3144
3145
3146 /*
3147   =============================================================================
3148   InitGameEngine()
3149   -----------------------------------------------------------------------------
3150   initialize game engine due to level / tape version number
3151   =============================================================================
3152 */
3153
3154 static void InitGameEngine()
3155 {
3156   int i, j, k, l, x, y;
3157
3158   /* set game engine from tape file when re-playing, else from level file */
3159   game.engine_version = (tape.playing ? tape.engine_version :
3160                          level.game_version);
3161
3162   /* ---------------------------------------------------------------------- */
3163   /* set flags for bugs and changes according to active game engine version */
3164   /* ---------------------------------------------------------------------- */
3165
3166   /*
3167     Summary of bugfix/change:
3168     Fixed handling for custom elements that change when pushed by the player.
3169
3170     Fixed/changed in version:
3171     3.1.0
3172
3173     Description:
3174     Before 3.1.0, custom elements that "change when pushing" changed directly
3175     after the player started pushing them (until then handled in "DigField()").
3176     Since 3.1.0, these custom elements are not changed until the "pushing"
3177     move of the element is finished (now handled in "ContinueMoving()").
3178
3179     Affected levels/tapes:
3180     The first condition is generally needed for all levels/tapes before version
3181     3.1.0, which might use the old behaviour before it was changed; known tapes
3182     that are affected are some tapes from the level set "Walpurgis Gardens" by
3183     Jamie Cullen.
3184     The second condition is an exception from the above case and is needed for
3185     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3186     above (including some development versions of 3.1.0), but before it was
3187     known that this change would break tapes like the above and was fixed in
3188     3.1.1, so that the changed behaviour was active although the engine version
3189     while recording maybe was before 3.1.0. There is at least one tape that is
3190     affected by this exception, which is the tape for the one-level set "Bug
3191     Machine" by Juergen Bonhagen.
3192   */
3193
3194   game.use_change_when_pushing_bug =
3195     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3196      !(tape.playing &&
3197        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3198        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3199
3200   /*
3201     Summary of bugfix/change:
3202     Fixed handling for blocking the field the player leaves when moving.
3203
3204     Fixed/changed in version:
3205     3.1.1
3206
3207     Description:
3208     Before 3.1.1, when "block last field when moving" was enabled, the field
3209     the player is leaving when moving was blocked for the time of the move,
3210     and was directly unblocked afterwards. This resulted in the last field
3211     being blocked for exactly one less than the number of frames of one player
3212     move. Additionally, even when blocking was disabled, the last field was
3213     blocked for exactly one frame.
3214     Since 3.1.1, due to changes in player movement handling, the last field
3215     is not blocked at all when blocking is disabled. When blocking is enabled,
3216     the last field is blocked for exactly the number of frames of one player
3217     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3218     last field is blocked for exactly one more than the number of frames of
3219     one player move.
3220
3221     Affected levels/tapes:
3222     (!!! yet to be determined -- probably many !!!)
3223   */
3224
3225   game.use_block_last_field_bug =
3226     (game.engine_version < VERSION_IDENT(3,1,1,0));
3227
3228   /*
3229     Summary of bugfix/change:
3230     Changed behaviour of CE changes with multiple changes per single frame.
3231
3232     Fixed/changed in version:
3233     3.2.0-6
3234
3235     Description:
3236     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3237     This resulted in race conditions where CEs seem to behave strange in some
3238     situations (where triggered CE changes were just skipped because there was
3239     already a CE change on that tile in the playfield in that engine frame).
3240     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3241     (The number of changes per frame must be limited in any case, because else
3242     it is easily possible to define CE changes that would result in an infinite
3243     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3244     should be set large enough so that it would only be reached in cases where
3245     the corresponding CE change conditions run into a loop. Therefore, it seems
3246     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3247     maximal number of change pages for custom elements.)
3248
3249     Affected levels/tapes:
3250     Probably many.
3251   */
3252
3253 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3254   game.max_num_changes_per_frame = 1;
3255 #else
3256   game.max_num_changes_per_frame =
3257     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3258 #endif
3259
3260   /* ---------------------------------------------------------------------- */
3261
3262   /* default scan direction: scan playfield from top/left to bottom/right */
3263   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3264
3265   /* dynamically adjust element properties according to game engine version */
3266   InitElementPropertiesEngine(game.engine_version);
3267
3268 #if 0
3269   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3270   printf("          tape version == %06d [%s] [file: %06d]\n",
3271          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3272          tape.file_version);
3273   printf("       => game.engine_version == %06d\n", game.engine_version);
3274 #endif
3275
3276   /* ---------- initialize player's initial move delay --------------------- */
3277
3278   /* dynamically adjust player properties according to level information */
3279   for (i = 0; i < MAX_PLAYERS; i++)
3280     game.initial_move_delay_value[i] =
3281       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3282
3283   /* dynamically adjust player properties according to game engine version */
3284   for (i = 0; i < MAX_PLAYERS; i++)
3285     game.initial_move_delay[i] =
3286       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3287        game.initial_move_delay_value[i] : 0);
3288
3289   /* ---------- initialize player's initial push delay --------------------- */
3290
3291   /* dynamically adjust player properties according to game engine version */
3292   game.initial_push_delay_value =
3293     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3294
3295   /* ---------- initialize changing elements ------------------------------- */
3296
3297   /* initialize changing elements information */
3298   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3299   {
3300     struct ElementInfo *ei = &element_info[i];
3301
3302     /* this pointer might have been changed in the level editor */
3303     ei->change = &ei->change_page[0];
3304
3305     if (!IS_CUSTOM_ELEMENT(i))
3306     {
3307       ei->change->target_element = EL_EMPTY_SPACE;
3308       ei->change->delay_fixed = 0;
3309       ei->change->delay_random = 0;
3310       ei->change->delay_frames = 1;
3311     }
3312
3313     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3314     {
3315       ei->has_change_event[j] = FALSE;
3316
3317       ei->event_page_nr[j] = 0;
3318       ei->event_page[j] = &ei->change_page[0];
3319     }
3320   }
3321
3322   /* add changing elements from pre-defined list */
3323   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3324   {
3325     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3326     struct ElementInfo *ei = &element_info[ch_delay->element];
3327
3328     ei->change->target_element       = ch_delay->target_element;
3329     ei->change->delay_fixed          = ch_delay->change_delay;
3330
3331     ei->change->pre_change_function  = ch_delay->pre_change_function;
3332     ei->change->change_function      = ch_delay->change_function;
3333     ei->change->post_change_function = ch_delay->post_change_function;
3334
3335     ei->change->can_change = TRUE;
3336     ei->change->can_change_or_has_action = TRUE;
3337
3338     ei->has_change_event[CE_DELAY] = TRUE;
3339
3340     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3341     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3342   }
3343
3344   /* ---------- initialize internal run-time variables --------------------- */
3345
3346   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3347   {
3348     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3349
3350     for (j = 0; j < ei->num_change_pages; j++)
3351     {
3352       ei->change_page[j].can_change_or_has_action =
3353         (ei->change_page[j].can_change |
3354          ei->change_page[j].has_action);
3355     }
3356   }
3357
3358   /* add change events from custom element configuration */
3359   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3360   {
3361     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3362
3363     for (j = 0; j < ei->num_change_pages; j++)
3364     {
3365       if (!ei->change_page[j].can_change_or_has_action)
3366         continue;
3367
3368       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3369       {
3370         /* only add event page for the first page found with this event */
3371         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3372         {
3373           ei->has_change_event[k] = TRUE;
3374
3375           ei->event_page_nr[k] = j;
3376           ei->event_page[k] = &ei->change_page[j];
3377         }
3378       }
3379     }
3380   }
3381
3382 #if 1
3383   /* ---------- initialize reference elements in change conditions --------- */
3384
3385   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3386   {
3387     int element = EL_CUSTOM_START + i;
3388     struct ElementInfo *ei = &element_info[element];
3389
3390     for (j = 0; j < ei->num_change_pages; j++)
3391     {
3392       int trigger_element = ei->change_page[j].initial_trigger_element;
3393
3394       if (trigger_element >= EL_PREV_CE_8 &&
3395           trigger_element <= EL_NEXT_CE_8)
3396         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3397
3398       ei->change_page[j].trigger_element = trigger_element;
3399     }
3400   }
3401 #endif
3402
3403   /* ---------- initialize run-time trigger player and element ------------- */
3404
3405   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3406   {
3407     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3408
3409     for (j = 0; j < ei->num_change_pages; j++)
3410     {
3411       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3412       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3413       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3414       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3415       ei->change_page[j].actual_trigger_ce_value = 0;
3416       ei->change_page[j].actual_trigger_ce_score = 0;
3417     }
3418   }
3419
3420   /* ---------- initialize trigger events ---------------------------------- */
3421
3422   /* initialize trigger events information */
3423   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3424     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3425       trigger_events[i][j] = FALSE;
3426
3427   /* add trigger events from element change event properties */
3428   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3429   {
3430     struct ElementInfo *ei = &element_info[i];
3431
3432     for (j = 0; j < ei->num_change_pages; j++)
3433     {
3434       if (!ei->change_page[j].can_change_or_has_action)
3435         continue;
3436
3437       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3438       {
3439         int trigger_element = ei->change_page[j].trigger_element;
3440
3441         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3442         {
3443           if (ei->change_page[j].has_event[k])
3444           {
3445             if (IS_GROUP_ELEMENT(trigger_element))
3446             {
3447               struct ElementGroupInfo *group =
3448                 element_info[trigger_element].group;
3449
3450               for (l = 0; l < group->num_elements_resolved; l++)
3451                 trigger_events[group->element_resolved[l]][k] = TRUE;
3452             }
3453             else if (trigger_element == EL_ANY_ELEMENT)
3454               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3455                 trigger_events[l][k] = TRUE;
3456             else
3457               trigger_events[trigger_element][k] = TRUE;
3458           }
3459         }
3460       }
3461     }
3462   }
3463
3464   /* ---------- initialize push delay -------------------------------------- */
3465
3466   /* initialize push delay values to default */
3467   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3468   {
3469     if (!IS_CUSTOM_ELEMENT(i))
3470     {
3471       /* set default push delay values (corrected since version 3.0.7-1) */
3472       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3473       {
3474         element_info[i].push_delay_fixed = 2;
3475         element_info[i].push_delay_random = 8;
3476       }
3477       else
3478       {
3479         element_info[i].push_delay_fixed = 8;
3480         element_info[i].push_delay_random = 8;
3481       }
3482     }
3483   }
3484
3485   /* set push delay value for certain elements from pre-defined list */
3486   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3487   {
3488     int e = push_delay_list[i].element;
3489
3490     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3491     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3492   }
3493
3494   /* set push delay value for Supaplex elements for newer engine versions */
3495   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3496   {
3497     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3498     {
3499       if (IS_SP_ELEMENT(i))
3500       {
3501         /* set SP push delay to just enough to push under a falling zonk */
3502         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3503
3504         element_info[i].push_delay_fixed  = delay;
3505         element_info[i].push_delay_random = 0;
3506       }
3507     }
3508   }
3509
3510   /* ---------- initialize move stepsize ----------------------------------- */
3511
3512   /* initialize move stepsize values to default */
3513   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3514     if (!IS_CUSTOM_ELEMENT(i))
3515       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3516
3517   /* set move stepsize value for certain elements from pre-defined list */
3518   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3519   {
3520     int e = move_stepsize_list[i].element;
3521
3522     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3523   }
3524
3525   /* ---------- initialize collect score ----------------------------------- */
3526
3527   /* initialize collect score values for custom elements from initial value */
3528   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3529     if (IS_CUSTOM_ELEMENT(i))
3530       element_info[i].collect_score = element_info[i].collect_score_initial;
3531
3532   /* ---------- initialize collect count ----------------------------------- */
3533
3534   /* initialize collect count values for non-custom elements */
3535   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3536     if (!IS_CUSTOM_ELEMENT(i))
3537       element_info[i].collect_count_initial = 0;
3538
3539   /* add collect count values for all elements from pre-defined list */
3540   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3541     element_info[collect_count_list[i].element].collect_count_initial =
3542       collect_count_list[i].count;
3543
3544   /* ---------- initialize access direction -------------------------------- */
3545
3546   /* initialize access direction values to default (access from every side) */
3547   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3548     if (!IS_CUSTOM_ELEMENT(i))
3549       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3550
3551   /* set access direction value for certain elements from pre-defined list */
3552   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3553     element_info[access_direction_list[i].element].access_direction =
3554       access_direction_list[i].direction;
3555
3556   /* ---------- initialize explosion content ------------------------------- */
3557   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3558   {
3559     if (IS_CUSTOM_ELEMENT(i))
3560       continue;
3561
3562     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3563     {
3564       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3565
3566       element_info[i].content.e[x][y] =
3567         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3568          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3569          i == EL_PLAYER_3 ? EL_EMERALD :
3570          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3571          i == EL_MOLE ? EL_EMERALD_RED :
3572          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3573          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3574          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3575          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3576          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3577          i == EL_WALL_EMERALD ? EL_EMERALD :
3578          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3579          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3580          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3581          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3582          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3583          i == EL_WALL_PEARL ? EL_PEARL :
3584          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3585          EL_EMPTY);
3586     }
3587   }
3588
3589   /* ---------- initialize recursion detection ------------------------------ */
3590   recursion_loop_depth = 0;
3591   recursion_loop_detected = FALSE;
3592   recursion_loop_element = EL_UNDEFINED;
3593
3594   /* ---------- initialize graphics engine ---------------------------------- */
3595   game.scroll_delay_value =
3596     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3597      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3598   game.scroll_delay_value =
3599     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3600 }
3601
3602 int get_num_special_action(int element, int action_first, int action_last)
3603 {
3604   int num_special_action = 0;
3605   int i, j;
3606
3607   for (i = action_first; i <= action_last; i++)
3608   {
3609     boolean found = FALSE;
3610
3611     for (j = 0; j < NUM_DIRECTIONS; j++)
3612       if (el_act_dir2img(element, i, j) !=
3613           el_act_dir2img(element, ACTION_DEFAULT, j))
3614         found = TRUE;
3615
3616     if (found)
3617       num_special_action++;
3618     else
3619       break;
3620   }
3621
3622   return num_special_action;
3623 }
3624
3625
3626 /*
3627   =============================================================================
3628   InitGame()
3629   -----------------------------------------------------------------------------
3630   initialize and start new game
3631   =============================================================================
3632 */
3633
3634 void InitGame()
3635 {
3636   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3637   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3638   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3639 #if 0
3640   boolean do_fading = (game_status == GAME_MODE_MAIN);
3641 #endif
3642   int i, j, x, y;
3643
3644   game_status = GAME_MODE_PLAYING;
3645
3646   InitGameEngine();
3647   InitGameControlValues();
3648
3649   /* don't play tapes over network */
3650   network_playing = (options.network && !tape.playing);
3651
3652   for (i = 0; i < MAX_PLAYERS; i++)
3653   {
3654     struct PlayerInfo *player = &stored_player[i];
3655
3656     player->index_nr = i;
3657     player->index_bit = (1 << i);
3658     player->element_nr = EL_PLAYER_1 + i;
3659
3660     player->present = FALSE;
3661     player->active = FALSE;
3662     player->killed = FALSE;
3663
3664     player->action = 0;
3665     player->effective_action = 0;
3666     player->programmed_action = 0;
3667
3668     player->score = 0;
3669     player->score_final = 0;
3670
3671     player->gems_still_needed = level.gems_needed;
3672     player->sokobanfields_still_needed = 0;
3673     player->lights_still_needed = 0;
3674     player->friends_still_needed = 0;
3675
3676     for (j = 0; j < MAX_NUM_KEYS; j++)
3677       player->key[j] = FALSE;
3678
3679     player->num_white_keys = 0;
3680
3681     player->dynabomb_count = 0;
3682     player->dynabomb_size = 1;
3683     player->dynabombs_left = 0;
3684     player->dynabomb_xl = FALSE;
3685
3686     player->MovDir = MV_NONE;
3687     player->MovPos = 0;
3688     player->GfxPos = 0;
3689     player->GfxDir = MV_NONE;
3690     player->GfxAction = ACTION_DEFAULT;
3691     player->Frame = 0;
3692     player->StepFrame = 0;
3693
3694     player->use_murphy = FALSE;
3695     player->artwork_element =
3696       (level.use_artwork_element[i] ? level.artwork_element[i] :
3697        player->element_nr);
3698
3699     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3700     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3701
3702     player->gravity = level.initial_player_gravity[i];
3703
3704     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3705
3706     player->actual_frame_counter = 0;
3707
3708     player->step_counter = 0;
3709
3710     player->last_move_dir = MV_NONE;
3711
3712     player->is_active = FALSE;
3713
3714     player->is_waiting = FALSE;
3715     player->is_moving = FALSE;
3716     player->is_auto_moving = FALSE;
3717     player->is_digging = FALSE;
3718     player->is_snapping = FALSE;
3719     player->is_collecting = FALSE;
3720     player->is_pushing = FALSE;
3721     player->is_switching = FALSE;
3722     player->is_dropping = FALSE;
3723     player->is_dropping_pressed = FALSE;
3724
3725     player->is_bored = FALSE;
3726     player->is_sleeping = FALSE;
3727
3728     player->frame_counter_bored = -1;
3729     player->frame_counter_sleeping = -1;
3730
3731     player->anim_delay_counter = 0;
3732     player->post_delay_counter = 0;
3733
3734     player->dir_waiting = MV_NONE;
3735     player->action_waiting = ACTION_DEFAULT;
3736     player->last_action_waiting = ACTION_DEFAULT;
3737     player->special_action_bored = ACTION_DEFAULT;
3738     player->special_action_sleeping = ACTION_DEFAULT;
3739
3740     player->switch_x = -1;
3741     player->switch_y = -1;
3742
3743     player->drop_x = -1;
3744     player->drop_y = -1;
3745
3746     player->show_envelope = 0;
3747
3748     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3749
3750     player->push_delay       = -1;      /* initialized when pushing starts */
3751     player->push_delay_value = game.initial_push_delay_value;
3752
3753     player->drop_delay = 0;
3754     player->drop_pressed_delay = 0;
3755
3756     player->last_jx = -1;
3757     player->last_jy = -1;
3758     player->jx = -1;
3759     player->jy = -1;
3760
3761     player->shield_normal_time_left = 0;
3762     player->shield_deadly_time_left = 0;
3763
3764     player->inventory_infinite_element = EL_UNDEFINED;
3765     player->inventory_size = 0;
3766
3767     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3768     SnapField(player, 0, 0);
3769
3770     player->LevelSolved = FALSE;
3771     player->GameOver = FALSE;
3772
3773     player->LevelSolved_GameWon = FALSE;
3774     player->LevelSolved_GameEnd = FALSE;
3775     player->LevelSolved_PanelOff = FALSE;
3776     player->LevelSolved_SaveTape = FALSE;
3777     player->LevelSolved_SaveScore = FALSE;
3778     player->LevelSolved_CountingTime = 0;
3779     player->LevelSolved_CountingScore = 0;
3780   }
3781
3782   network_player_action_received = FALSE;
3783
3784 #if defined(NETWORK_AVALIABLE)
3785   /* initial null action */
3786   if (network_playing)
3787     SendToServer_MovePlayer(MV_NONE);
3788 #endif
3789
3790   ZX = ZY = -1;
3791   ExitX = ExitY = -1;
3792
3793   FrameCounter = 0;
3794   TimeFrames = 0;
3795   TimePlayed = 0;
3796   TimeLeft = level.time;
3797   TapeTime = 0;
3798
3799   ScreenMovDir = MV_NONE;
3800   ScreenMovPos = 0;
3801   ScreenGfxPos = 0;
3802
3803   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3804
3805   AllPlayersGone = FALSE;
3806
3807   game.yamyam_content_nr = 0;
3808   game.robot_wheel_active = FALSE;
3809   game.magic_wall_active = FALSE;
3810   game.magic_wall_time_left = 0;
3811   game.light_time_left = 0;
3812   game.timegate_time_left = 0;
3813   game.switchgate_pos = 0;
3814   game.wind_direction = level.wind_direction_initial;
3815
3816 #if !USE_PLAYER_GRAVITY
3817   game.gravity = FALSE;
3818   game.explosions_delayed = TRUE;
3819 #endif
3820
3821   game.lenses_time_left = 0;
3822   game.magnify_time_left = 0;
3823
3824   game.ball_state = level.ball_state_initial;
3825   game.ball_content_nr = 0;
3826
3827   game.envelope_active = FALSE;
3828
3829   /* set focus to local player for network games, else to all players */
3830   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3831   game.centered_player_nr_next = game.centered_player_nr;
3832   game.set_centered_player = FALSE;
3833
3834   if (network_playing && tape.recording)
3835   {
3836     /* store client dependent player focus when recording network games */
3837     tape.centered_player_nr_next = game.centered_player_nr_next;
3838     tape.set_centered_player = TRUE;
3839   }
3840
3841   for (i = 0; i < NUM_BELTS; i++)
3842   {
3843     game.belt_dir[i] = MV_NONE;
3844     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3845   }
3846
3847   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3848     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3849
3850   SCAN_PLAYFIELD(x, y)
3851   {
3852     Feld[x][y] = level.field[x][y];
3853     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3854     ChangeDelay[x][y] = 0;
3855     ChangePage[x][y] = -1;
3856 #if USE_NEW_CUSTOM_VALUE
3857     CustomValue[x][y] = 0;              /* initialized in InitField() */
3858 #endif
3859     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3860     AmoebaNr[x][y] = 0;
3861     WasJustMoving[x][y] = 0;
3862     WasJustFalling[x][y] = 0;
3863     CheckCollision[x][y] = 0;
3864     CheckImpact[x][y] = 0;
3865     Stop[x][y] = FALSE;
3866     Pushed[x][y] = FALSE;
3867
3868     ChangeCount[x][y] = 0;
3869     ChangeEvent[x][y] = -1;
3870
3871     ExplodePhase[x][y] = 0;
3872     ExplodeDelay[x][y] = 0;
3873     ExplodeField[x][y] = EX_TYPE_NONE;
3874
3875     RunnerVisit[x][y] = 0;
3876     PlayerVisit[x][y] = 0;
3877
3878     GfxFrame[x][y] = 0;
3879     GfxRandom[x][y] = INIT_GFX_RANDOM();
3880     GfxElement[x][y] = EL_UNDEFINED;
3881     GfxAction[x][y] = ACTION_DEFAULT;
3882     GfxDir[x][y] = MV_NONE;
3883   }
3884
3885   SCAN_PLAYFIELD(x, y)
3886   {
3887     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3888       emulate_bd = FALSE;
3889     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3890       emulate_sb = FALSE;
3891     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3892       emulate_sp = FALSE;
3893
3894     InitField(x, y, TRUE);
3895
3896     ResetGfxAnimation(x, y);
3897   }
3898
3899   InitBeltMovement();
3900
3901   for (i = 0; i < MAX_PLAYERS; i++)
3902   {
3903     struct PlayerInfo *player = &stored_player[i];
3904
3905     /* set number of special actions for bored and sleeping animation */
3906     player->num_special_action_bored =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_BORING_1, ACTION_BORING_LAST);
3909     player->num_special_action_sleeping =
3910       get_num_special_action(player->artwork_element,
3911                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3912   }
3913
3914   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3915                     emulate_sb ? EMU_SOKOBAN :
3916                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3917
3918 #if USE_NEW_ALL_SLIPPERY
3919   /* initialize type of slippery elements */
3920   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3921   {
3922     if (!IS_CUSTOM_ELEMENT(i))
3923     {
3924       /* default: elements slip down either to the left or right randomly */
3925       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3926
3927       /* SP style elements prefer to slip down on the left side */
3928       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3929         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3930
3931       /* BD style elements prefer to slip down on the left side */
3932       if (game.emulation == EMU_BOULDERDASH)
3933         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3934     }
3935   }
3936 #endif
3937
3938   /* initialize explosion and ignition delay */
3939   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3940   {
3941     if (!IS_CUSTOM_ELEMENT(i))
3942     {
3943       int num_phase = 8;
3944       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3945                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3946                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3947       int last_phase = (num_phase + 1) * delay;
3948       int half_phase = (num_phase / 2) * delay;
3949
3950       element_info[i].explosion_delay = last_phase - 1;
3951       element_info[i].ignition_delay = half_phase;
3952
3953       if (i == EL_BLACK_ORB)
3954         element_info[i].ignition_delay = 1;
3955     }
3956
3957 #if 0
3958     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3959       element_info[i].explosion_delay = 1;
3960
3961     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3962       element_info[i].ignition_delay = 1;
3963 #endif
3964   }
3965
3966   /* correct non-moving belts to start moving left */
3967   for (i = 0; i < NUM_BELTS; i++)
3968     if (game.belt_dir[i] == MV_NONE)
3969       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3970
3971   /* check if any connected player was not found in playfield */
3972   for (i = 0; i < MAX_PLAYERS; i++)
3973   {
3974     struct PlayerInfo *player = &stored_player[i];
3975
3976     if (player->connected && !player->present)
3977     {
3978       for (j = 0; j < MAX_PLAYERS; j++)
3979       {
3980         struct PlayerInfo *some_player = &stored_player[j];
3981         int jx = some_player->jx, jy = some_player->jy;
3982
3983         /* assign first free player found that is present in the playfield */
3984         if (some_player->present && !some_player->connected)
3985         {
3986           player->present = TRUE;
3987           player->active = TRUE;
3988
3989           some_player->present = FALSE;
3990           some_player->active = FALSE;
3991
3992           player->artwork_element = some_player->artwork_element;
3993
3994           player->block_last_field       = some_player->block_last_field;
3995           player->block_delay_adjustment = some_player->block_delay_adjustment;
3996
3997           StorePlayer[jx][jy] = player->element_nr;
3998           player->jx = player->last_jx = jx;
3999           player->jy = player->last_jy = jy;
4000
4001           break;
4002         }
4003       }
4004     }
4005   }
4006
4007   if (tape.playing)
4008   {
4009     /* when playing a tape, eliminate all players who do not participate */
4010
4011     for (i = 0; i < MAX_PLAYERS; i++)
4012     {
4013       if (stored_player[i].active && !tape.player_participates[i])
4014       {
4015         struct PlayerInfo *player = &stored_player[i];
4016         int jx = player->jx, jy = player->jy;
4017
4018         player->active = FALSE;
4019         StorePlayer[jx][jy] = 0;
4020         Feld[jx][jy] = EL_EMPTY;
4021       }
4022     }
4023   }
4024   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4025   {
4026     /* when in single player mode, eliminate all but the first active player */
4027
4028     for (i = 0; i < MAX_PLAYERS; i++)
4029     {
4030       if (stored_player[i].active)
4031       {
4032         for (j = i + 1; j < MAX_PLAYERS; j++)
4033         {
4034           if (stored_player[j].active)
4035           {
4036             struct PlayerInfo *player = &stored_player[j];
4037             int jx = player->jx, jy = player->jy;
4038
4039             player->active = FALSE;
4040             player->present = FALSE;
4041
4042             StorePlayer[jx][jy] = 0;
4043             Feld[jx][jy] = EL_EMPTY;
4044           }
4045         }
4046       }
4047     }
4048   }
4049
4050   /* when recording the game, store which players take part in the game */
4051   if (tape.recording)
4052   {
4053     for (i = 0; i < MAX_PLAYERS; i++)
4054       if (stored_player[i].active)
4055         tape.player_participates[i] = TRUE;
4056   }
4057
4058   if (options.debug)
4059   {
4060     for (i = 0; i < MAX_PLAYERS; i++)
4061     {
4062       struct PlayerInfo *player = &stored_player[i];
4063
4064       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4065              i+1,
4066              player->present,
4067              player->connected,
4068              player->active);
4069       if (local_player == player)
4070         printf("Player  %d is local player.\n", i+1);
4071     }
4072   }
4073
4074   if (BorderElement == EL_EMPTY)
4075   {
4076     SBX_Left = 0;
4077     SBX_Right = lev_fieldx - SCR_FIELDX;
4078     SBY_Upper = 0;
4079     SBY_Lower = lev_fieldy - SCR_FIELDY;
4080   }
4081   else
4082   {
4083     SBX_Left = -1;
4084     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4085     SBY_Upper = -1;
4086     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4087   }
4088
4089   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4090     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4091
4092   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4093     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4094
4095   /* if local player not found, look for custom element that might create
4096      the player (make some assumptions about the right custom element) */
4097   if (!local_player->present)
4098   {
4099     int start_x = 0, start_y = 0;
4100     int found_rating = 0;
4101     int found_element = EL_UNDEFINED;
4102     int player_nr = local_player->index_nr;
4103
4104     SCAN_PLAYFIELD(x, y)
4105     {
4106       int element = Feld[x][y];
4107       int content;
4108       int xx, yy;
4109       boolean is_player;
4110
4111       if (level.use_start_element[player_nr] &&
4112           level.start_element[player_nr] == element &&
4113           found_rating < 4)
4114       {
4115         start_x = x;
4116         start_y = y;
4117
4118         found_rating = 4;
4119         found_element = element;
4120       }
4121
4122       if (!IS_CUSTOM_ELEMENT(element))
4123         continue;
4124
4125       if (CAN_CHANGE(element))
4126       {
4127         for (i = 0; i < element_info[element].num_change_pages; i++)
4128         {
4129           /* check for player created from custom element as single target */
4130           content = element_info[element].change_page[i].target_element;
4131           is_player = ELEM_IS_PLAYER(content);
4132
4133           if (is_player && (found_rating < 3 ||
4134                             (found_rating == 3 && element < found_element)))
4135           {
4136             start_x = x;
4137             start_y = y;
4138
4139             found_rating = 3;
4140             found_element = element;
4141           }
4142         }
4143       }
4144
4145       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4146       {
4147         /* check for player created from custom element as explosion content */
4148         content = element_info[element].content.e[xx][yy];
4149         is_player = ELEM_IS_PLAYER(content);
4150
4151         if (is_player && (found_rating < 2 ||
4152                           (found_rating == 2 && element < found_element)))
4153         {
4154           start_x = x + xx - 1;
4155           start_y = y + yy - 1;
4156
4157           found_rating = 2;
4158           found_element = element;
4159         }
4160
4161         if (!CAN_CHANGE(element))
4162           continue;
4163
4164         for (i = 0; i < element_info[element].num_change_pages; i++)
4165         {
4166           /* check for player created from custom element as extended target */
4167           content =
4168             element_info[element].change_page[i].target_content.e[xx][yy];
4169
4170           is_player = ELEM_IS_PLAYER(content);
4171
4172           if (is_player && (found_rating < 1 ||
4173                             (found_rating == 1 && element < found_element)))
4174           {
4175             start_x = x + xx - 1;
4176             start_y = y + yy - 1;
4177
4178             found_rating = 1;
4179             found_element = element;
4180           }
4181         }
4182       }
4183     }
4184
4185     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4186                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4187                 start_x - MIDPOSX);
4188
4189     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4190                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4191                 start_y - MIDPOSY);
4192   }
4193   else
4194   {
4195     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4196                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4197                 local_player->jx - MIDPOSX);
4198
4199     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4200                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4201                 local_player->jy - MIDPOSY);
4202   }
4203
4204 #if 0
4205   /* do not use PLAYING mask for fading out from main screen */
4206   game_status = GAME_MODE_MAIN;
4207 #endif
4208
4209   StopAnimation();
4210
4211   if (!game.restart_level)
4212     CloseDoor(DOOR_CLOSE_1);
4213
4214 #if 1
4215   if (level_editor_test_game)
4216     FadeSkipNextFadeIn();
4217   else
4218     FadeSetEnterScreen();
4219 #else
4220   if (level_editor_test_game)
4221     fading = fading_none;
4222   else
4223     fading = menu.destination;
4224 #endif
4225
4226 #if 1
4227   FadeOut(REDRAW_FIELD);
4228 #else
4229   if (do_fading)
4230     FadeOut(REDRAW_FIELD);
4231 #endif
4232
4233 #if 0
4234   game_status = GAME_MODE_PLAYING;
4235 #endif
4236
4237   /* !!! FIX THIS (START) !!! */
4238   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4239   {
4240     InitGameEngine_EM();
4241
4242     /* blit playfield from scroll buffer to normal back buffer for fading in */
4243     BlitScreenToBitmap_EM(backbuffer);
4244   }
4245   else
4246   {
4247     DrawLevel();
4248     DrawAllPlayers();
4249
4250     /* after drawing the level, correct some elements */
4251     if (game.timegate_time_left == 0)
4252       CloseAllOpenTimegates();
4253
4254     /* blit playfield from scroll buffer to normal back buffer for fading in */
4255     if (setup.soft_scrolling)
4256       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4257
4258     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4259   }
4260   /* !!! FIX THIS (END) !!! */
4261
4262 #if 1
4263   FadeIn(REDRAW_FIELD);
4264 #else
4265   if (do_fading)
4266     FadeIn(REDRAW_FIELD);
4267
4268   BackToFront();
4269 #endif
4270
4271   if (!game.restart_level)
4272   {
4273     /* copy default game door content to main double buffer */
4274     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4275                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4276   }
4277
4278   SetPanelBackground();
4279   SetDrawBackgroundMask(REDRAW_DOOR_1);
4280
4281 #if 1
4282   UpdateAndDisplayGameControlValues();
4283 #else
4284   UpdateGameDoorValues();
4285   DrawGameDoorValues();
4286 #endif
4287
4288   if (!game.restart_level)
4289   {
4290     UnmapGameButtons();
4291     UnmapTapeButtons();
4292     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4293     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4294     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4295     MapGameButtons();
4296     MapTapeButtons();
4297
4298     /* copy actual game door content to door double buffer for OpenDoor() */
4299     BlitBitmap(drawto, bitmap_db_door,
4300                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4301
4302     OpenDoor(DOOR_OPEN_ALL);
4303
4304     PlaySound(SND_GAME_STARTING);
4305
4306     if (setup.sound_music)
4307       PlayLevelMusic();
4308
4309     KeyboardAutoRepeatOffUnlessAutoplay();
4310
4311     if (options.debug)
4312     {
4313       for (i = 0; i < MAX_PLAYERS; i++)
4314         printf("Player %d %sactive.\n",
4315                i + 1, (stored_player[i].active ? "" : "not "));
4316     }
4317   }
4318
4319 #if 1
4320   UnmapAllGadgets();
4321
4322   MapGameButtons();
4323   MapTapeButtons();
4324 #endif
4325
4326   game.restart_level = FALSE;
4327 }
4328
4329 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4330 {
4331   /* this is used for non-R'n'D game engines to update certain engine values */
4332
4333   /* needed to determine if sounds are played within the visible screen area */
4334   scroll_x = actual_scroll_x;
4335   scroll_y = actual_scroll_y;
4336 }
4337
4338 void InitMovDir(int x, int y)
4339 {
4340   int i, element = Feld[x][y];
4341   static int xy[4][2] =
4342   {
4343     {  0, +1 },
4344     { +1,  0 },
4345     {  0, -1 },
4346     { -1,  0 }
4347   };
4348   static int direction[3][4] =
4349   {
4350     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4351     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4352     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4353   };
4354
4355   switch (element)
4356   {
4357     case EL_BUG_RIGHT:
4358     case EL_BUG_UP:
4359     case EL_BUG_LEFT:
4360     case EL_BUG_DOWN:
4361       Feld[x][y] = EL_BUG;
4362       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4363       break;
4364
4365     case EL_SPACESHIP_RIGHT:
4366     case EL_SPACESHIP_UP:
4367     case EL_SPACESHIP_LEFT:
4368     case EL_SPACESHIP_DOWN:
4369       Feld[x][y] = EL_SPACESHIP;
4370       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4371       break;
4372
4373     case EL_BD_BUTTERFLY_RIGHT:
4374     case EL_BD_BUTTERFLY_UP:
4375     case EL_BD_BUTTERFLY_LEFT:
4376     case EL_BD_BUTTERFLY_DOWN:
4377       Feld[x][y] = EL_BD_BUTTERFLY;
4378       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4379       break;
4380
4381     case EL_BD_FIREFLY_RIGHT:
4382     case EL_BD_FIREFLY_UP:
4383     case EL_BD_FIREFLY_LEFT:
4384     case EL_BD_FIREFLY_DOWN:
4385       Feld[x][y] = EL_BD_FIREFLY;
4386       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4387       break;
4388
4389     case EL_PACMAN_RIGHT:
4390     case EL_PACMAN_UP:
4391     case EL_PACMAN_LEFT:
4392     case EL_PACMAN_DOWN:
4393       Feld[x][y] = EL_PACMAN;
4394       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4395       break;
4396
4397     case EL_YAMYAM_LEFT:
4398     case EL_YAMYAM_RIGHT:
4399     case EL_YAMYAM_UP:
4400     case EL_YAMYAM_DOWN:
4401       Feld[x][y] = EL_YAMYAM;
4402       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4403       break;
4404
4405     case EL_SP_SNIKSNAK:
4406       MovDir[x][y] = MV_UP;
4407       break;
4408
4409     case EL_SP_ELECTRON:
4410       MovDir[x][y] = MV_LEFT;
4411       break;
4412
4413     case EL_MOLE_LEFT:
4414     case EL_MOLE_RIGHT:
4415     case EL_MOLE_UP:
4416     case EL_MOLE_DOWN:
4417       Feld[x][y] = EL_MOLE;
4418       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4419       break;
4420
4421     default:
4422       if (IS_CUSTOM_ELEMENT(element))
4423       {
4424         struct ElementInfo *ei = &element_info[element];
4425         int move_direction_initial = ei->move_direction_initial;
4426         int move_pattern = ei->move_pattern;
4427
4428         if (move_direction_initial == MV_START_PREVIOUS)
4429         {
4430           if (MovDir[x][y] != MV_NONE)
4431             return;
4432
4433           move_direction_initial = MV_START_AUTOMATIC;
4434         }
4435
4436         if (move_direction_initial == MV_START_RANDOM)
4437           MovDir[x][y] = 1 << RND(4);
4438         else if (move_direction_initial & MV_ANY_DIRECTION)
4439           MovDir[x][y] = move_direction_initial;
4440         else if (move_pattern == MV_ALL_DIRECTIONS ||
4441                  move_pattern == MV_TURNING_LEFT ||
4442                  move_pattern == MV_TURNING_RIGHT ||
4443                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4444                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4445                  move_pattern == MV_TURNING_RANDOM)
4446           MovDir[x][y] = 1 << RND(4);
4447         else if (move_pattern == MV_HORIZONTAL)
4448           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4449         else if (move_pattern == MV_VERTICAL)
4450           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4451         else if (move_pattern & MV_ANY_DIRECTION)
4452           MovDir[x][y] = element_info[element].move_pattern;
4453         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4454                  move_pattern == MV_ALONG_RIGHT_SIDE)
4455         {
4456           /* use random direction as default start direction */
4457           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4458             MovDir[x][y] = 1 << RND(4);
4459
4460           for (i = 0; i < NUM_DIRECTIONS; i++)
4461           {
4462             int x1 = x + xy[i][0];
4463             int y1 = y + xy[i][1];
4464
4465             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4466             {
4467               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4468                 MovDir[x][y] = direction[0][i];
4469               else
4470                 MovDir[x][y] = direction[1][i];
4471
4472               break;
4473             }
4474           }
4475         }                
4476       }
4477       else
4478       {
4479         MovDir[x][y] = 1 << RND(4);
4480
4481         if (element != EL_BUG &&
4482             element != EL_SPACESHIP &&
4483             element != EL_BD_BUTTERFLY &&
4484             element != EL_BD_FIREFLY)
4485           break;
4486
4487         for (i = 0; i < NUM_DIRECTIONS; i++)
4488         {
4489           int x1 = x + xy[i][0];
4490           int y1 = y + xy[i][1];
4491
4492           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4493           {
4494             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4495             {
4496               MovDir[x][y] = direction[0][i];
4497               break;
4498             }
4499             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4500                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4501             {
4502               MovDir[x][y] = direction[1][i];
4503               break;
4504             }
4505           }
4506         }
4507       }
4508       break;
4509   }
4510
4511   GfxDir[x][y] = MovDir[x][y];
4512 }
4513
4514 void InitAmoebaNr(int x, int y)
4515 {
4516   int i;
4517   int group_nr = AmoebeNachbarNr(x, y);
4518
4519   if (group_nr == 0)
4520   {
4521     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4522     {
4523       if (AmoebaCnt[i] == 0)
4524       {
4525         group_nr = i;
4526         break;
4527       }
4528     }
4529   }
4530
4531   AmoebaNr[x][y] = group_nr;
4532   AmoebaCnt[group_nr]++;
4533   AmoebaCnt2[group_nr]++;
4534 }
4535
4536 static void PlayerWins(struct PlayerInfo *player)
4537 {
4538   player->LevelSolved = TRUE;
4539   player->GameOver = TRUE;
4540
4541   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4542                          level.native_em_level->lev->score : player->score);
4543
4544   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4545   player->LevelSolved_CountingScore = player->score_final;
4546 }
4547
4548 void GameWon()
4549 {
4550   static int time, time_final;
4551   static int score, score_final;
4552   static int game_over_delay_1 = 0;
4553   static int game_over_delay_2 = 0;
4554   int game_over_delay_value_1 = 50;
4555   int game_over_delay_value_2 = 50;
4556
4557   if (!local_player->LevelSolved_GameWon)
4558   {
4559     int i;
4560
4561     /* do not start end game actions before the player stops moving (to exit) */
4562     if (local_player->MovPos)
4563       return;
4564
4565     local_player->LevelSolved_GameWon = TRUE;
4566     local_player->LevelSolved_SaveTape = tape.recording;
4567     local_player->LevelSolved_SaveScore = !tape.playing;
4568
4569     if (tape.auto_play)         /* tape might already be stopped here */
4570       tape.auto_play_level_solved = TRUE;
4571
4572 #if 1
4573     TapeStop();
4574 #endif
4575
4576     game_over_delay_1 = game_over_delay_value_1;
4577     game_over_delay_2 = game_over_delay_value_2;
4578
4579     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4580     score = score_final = local_player->score_final;
4581
4582     if (TimeLeft > 0)
4583     {
4584       time_final = 0;
4585       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4586     }
4587     else if (level.time == 0 && TimePlayed < 999)
4588     {
4589       time_final = 999;
4590       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4591     }
4592
4593     local_player->score_final = score_final;
4594
4595     if (level_editor_test_game)
4596     {
4597       time = time_final;
4598       score = score_final;
4599
4600 #if 1
4601       local_player->LevelSolved_CountingTime = time;
4602       local_player->LevelSolved_CountingScore = score;
4603
4604       game_panel_controls[GAME_PANEL_TIME].value = time;
4605       game_panel_controls[GAME_PANEL_SCORE].value = score;
4606
4607       DisplayGameControlValues();
4608 #else
4609       DrawGameValue_Time(time);
4610       DrawGameValue_Score(score);
4611 #endif
4612     }
4613
4614     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4615     {
4616       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4617       {
4618         /* close exit door after last player */
4619         if ((AllPlayersGone &&
4620              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4621               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4622               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4623             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4624             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4625         {
4626           int element = Feld[ExitX][ExitY];
4627
4628 #if 0
4629           if (element == EL_EM_EXIT_OPEN ||
4630               element == EL_EM_STEEL_EXIT_OPEN)
4631           {
4632             Bang(ExitX, ExitY);
4633           }
4634           else
4635 #endif
4636           {
4637             Feld[ExitX][ExitY] =
4638               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4639                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4640                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4641                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4642                EL_EM_STEEL_EXIT_CLOSING);
4643
4644             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4645           }
4646         }
4647
4648         /* player disappears */
4649         DrawLevelField(ExitX, ExitY);
4650       }
4651
4652       for (i = 0; i < MAX_PLAYERS; i++)
4653       {
4654         struct PlayerInfo *player = &stored_player[i];
4655
4656         if (player->present)
4657         {
4658           RemovePlayer(player);
4659
4660           /* player disappears */
4661           DrawLevelField(player->jx, player->jy);
4662         }
4663       }
4664     }
4665
4666     PlaySound(SND_GAME_WINNING);
4667   }
4668
4669   if (game_over_delay_1 > 0)
4670   {
4671     game_over_delay_1--;
4672
4673     return;
4674   }
4675
4676   if (time != time_final)
4677   {
4678     int time_to_go = ABS(time_final - time);
4679     int time_count_dir = (time < time_final ? +1 : -1);
4680     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4681
4682     time  += time_count_steps * time_count_dir;
4683     score += time_count_steps * level.score[SC_TIME_BONUS];
4684
4685 #if 1
4686     local_player->LevelSolved_CountingTime = time;
4687     local_player->LevelSolved_CountingScore = score;
4688
4689     game_panel_controls[GAME_PANEL_TIME].value = time;
4690     game_panel_controls[GAME_PANEL_SCORE].value = score;
4691
4692     DisplayGameControlValues();
4693 #else
4694     DrawGameValue_Time(time);
4695     DrawGameValue_Score(score);
4696 #endif
4697
4698     if (time == time_final)
4699       StopSound(SND_GAME_LEVELTIME_BONUS);
4700     else if (setup.sound_loops)
4701       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4702     else
4703       PlaySound(SND_GAME_LEVELTIME_BONUS);
4704
4705     return;
4706   }
4707
4708   local_player->LevelSolved_PanelOff = TRUE;
4709
4710   if (game_over_delay_2 > 0)
4711   {
4712     game_over_delay_2--;
4713
4714     return;
4715   }
4716
4717 #if 1
4718   GameEnd();
4719 #endif
4720 }
4721
4722 void GameEnd()
4723 {
4724   int hi_pos;
4725   boolean raise_level = FALSE;
4726
4727   local_player->LevelSolved_GameEnd = TRUE;
4728
4729   CloseDoor(DOOR_CLOSE_1);
4730
4731   if (local_player->LevelSolved_SaveTape)
4732   {
4733 #if 0
4734     TapeStop();
4735 #endif
4736
4737 #if 1
4738     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4739 #else
4740     SaveTape(tape.level_nr);            /* ask to save tape */
4741 #endif
4742   }
4743
4744   if (level_editor_test_game)
4745   {
4746     game_status = GAME_MODE_MAIN;
4747
4748 #if 1
4749     DrawAndFadeInMainMenu(REDRAW_FIELD);
4750 #else
4751     DrawMainMenu();
4752 #endif
4753
4754     return;
4755   }
4756
4757   if (!local_player->LevelSolved_SaveScore)
4758   {
4759 #if 1
4760     FadeOut(REDRAW_FIELD);
4761 #endif
4762
4763     game_status = GAME_MODE_MAIN;
4764
4765     DrawAndFadeInMainMenu(REDRAW_FIELD);
4766
4767     return;
4768   }
4769
4770   if (level_nr == leveldir_current->handicap_level)
4771   {
4772     leveldir_current->handicap_level++;
4773     SaveLevelSetup_SeriesInfo();
4774   }
4775
4776   if (level_nr < leveldir_current->last_level)
4777     raise_level = TRUE;                 /* advance to next level */
4778
4779   if ((hi_pos = NewHiScore()) >= 0) 
4780   {
4781     game_status = GAME_MODE_SCORES;
4782
4783     DrawHallOfFame(hi_pos);
4784
4785     if (raise_level)
4786     {
4787       level_nr++;
4788       TapeErase();
4789     }
4790   }
4791   else
4792   {
4793 #if 1
4794     FadeOut(REDRAW_FIELD);
4795 #endif
4796
4797     game_status = GAME_MODE_MAIN;
4798
4799     if (raise_level)
4800     {
4801       level_nr++;
4802       TapeErase();
4803     }
4804
4805     DrawAndFadeInMainMenu(REDRAW_FIELD);
4806   }
4807 }
4808
4809 int NewHiScore()
4810 {
4811   int k, l;
4812   int position = -1;
4813
4814   LoadScore(level_nr);
4815
4816   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4817       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4818     return -1;
4819
4820   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4821   {
4822     if (local_player->score_final > highscore[k].Score)
4823     {
4824       /* player has made it to the hall of fame */
4825
4826       if (k < MAX_SCORE_ENTRIES - 1)
4827       {
4828         int m = MAX_SCORE_ENTRIES - 1;
4829
4830 #ifdef ONE_PER_NAME
4831         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4832           if (strEqual(setup.player_name, highscore[l].Name))
4833             m = l;
4834         if (m == k)     /* player's new highscore overwrites his old one */
4835           goto put_into_list;
4836 #endif
4837
4838         for (l = m; l > k; l--)
4839         {
4840           strcpy(highscore[l].Name, highscore[l - 1].Name);
4841           highscore[l].Score = highscore[l - 1].Score;
4842         }
4843       }
4844
4845 #ifdef ONE_PER_NAME
4846       put_into_list:
4847 #endif
4848       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4849       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4850       highscore[k].Score = local_player->score_final; 
4851       position = k;
4852       break;
4853     }
4854
4855 #ifdef ONE_PER_NAME
4856     else if (!strncmp(setup.player_name, highscore[k].Name,
4857                       MAX_PLAYER_NAME_LEN))
4858       break;    /* player already there with a higher score */
4859 #endif
4860
4861   }
4862
4863   if (position >= 0) 
4864     SaveScore(level_nr);
4865
4866   return position;
4867 }
4868
4869 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4870 {
4871   int element = Feld[x][y];
4872   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4873   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4874   int horiz_move = (dx != 0);
4875   int sign = (horiz_move ? dx : dy);
4876   int step = sign * element_info[element].move_stepsize;
4877
4878   /* special values for move stepsize for spring and things on conveyor belt */
4879   if (horiz_move)
4880   {
4881     if (CAN_FALL(element) &&
4882         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4883       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4884     else if (element == EL_SPRING)
4885       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4886   }
4887
4888   return step;
4889 }
4890
4891 inline static int getElementMoveStepsize(int x, int y)
4892 {
4893   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4894 }
4895
4896 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4897 {
4898   if (player->GfxAction != action || player->GfxDir != dir)
4899   {
4900 #if 0
4901     printf("Player frame reset! (%d => %d, %d => %d)\n",
4902            player->GfxAction, action, player->GfxDir, dir);
4903 #endif
4904
4905     player->GfxAction = action;
4906     player->GfxDir = dir;
4907     player->Frame = 0;
4908     player->StepFrame = 0;
4909   }
4910 }
4911
4912 #if USE_GFX_RESET_GFX_ANIMATION
4913 static void ResetGfxFrame(int x, int y, boolean redraw)
4914 {
4915   int element = Feld[x][y];
4916   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4917   int last_gfx_frame = GfxFrame[x][y];
4918
4919   if (graphic_info[graphic].anim_global_sync)
4920     GfxFrame[x][y] = FrameCounter;
4921   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4922     GfxFrame[x][y] = CustomValue[x][y];
4923   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4924     GfxFrame[x][y] = element_info[element].collect_score;
4925   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4926     GfxFrame[x][y] = ChangeDelay[x][y];
4927
4928   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4929     DrawLevelGraphicAnimation(x, y, graphic);
4930 }
4931 #endif
4932
4933 static void ResetGfxAnimation(int x, int y)
4934 {
4935   GfxAction[x][y] = ACTION_DEFAULT;
4936   GfxDir[x][y] = MovDir[x][y];
4937   GfxFrame[x][y] = 0;
4938
4939 #if USE_GFX_RESET_GFX_ANIMATION
4940   ResetGfxFrame(x, y, FALSE);
4941 #endif
4942 }
4943
4944 static void ResetRandomAnimationValue(int x, int y)
4945 {
4946   GfxRandom[x][y] = INIT_GFX_RANDOM();
4947 }
4948
4949 void InitMovingField(int x, int y, int direction)
4950 {
4951   int element = Feld[x][y];
4952   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4953   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4954   int newx = x + dx;
4955   int newy = y + dy;
4956   boolean is_moving_before, is_moving_after;
4957 #if 0
4958   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4959 #endif
4960
4961   /* check if element was/is moving or being moved before/after mode change */
4962 #if 1
4963 #if 1
4964   is_moving_before = (WasJustMoving[x][y] != 0);
4965 #else
4966   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4967   is_moving_before = WasJustMoving[x][y];
4968 #endif
4969 #else
4970   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4971 #endif
4972   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4973
4974   /* reset animation only for moving elements which change direction of moving
4975      or which just started or stopped moving
4976      (else CEs with property "can move" / "not moving" are reset each frame) */
4977 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4978 #if 1
4979   if (is_moving_before != is_moving_after ||
4980       direction != MovDir[x][y])
4981     ResetGfxAnimation(x, y);
4982 #else
4983   if ((is_moving_before || is_moving_after) && !continues_moving)
4984     ResetGfxAnimation(x, y);
4985 #endif
4986 #else
4987   if (!continues_moving)
4988     ResetGfxAnimation(x, y);
4989 #endif
4990
4991   MovDir[x][y] = direction;
4992   GfxDir[x][y] = direction;
4993
4994 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4995   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4996                      direction == MV_DOWN && CAN_FALL(element) ?
4997                      ACTION_FALLING : ACTION_MOVING);
4998 #else
4999   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5000                      ACTION_FALLING : ACTION_MOVING);
5001 #endif
5002
5003   /* this is needed for CEs with property "can move" / "not moving" */
5004
5005   if (is_moving_after)
5006   {
5007     if (Feld[newx][newy] == EL_EMPTY)
5008       Feld[newx][newy] = EL_BLOCKED;
5009
5010     MovDir[newx][newy] = MovDir[x][y];
5011
5012 #if USE_NEW_CUSTOM_VALUE
5013     CustomValue[newx][newy] = CustomValue[x][y];
5014 #endif
5015
5016     GfxFrame[newx][newy] = GfxFrame[x][y];
5017     GfxRandom[newx][newy] = GfxRandom[x][y];
5018     GfxAction[newx][newy] = GfxAction[x][y];
5019     GfxDir[newx][newy] = GfxDir[x][y];
5020   }
5021 }
5022
5023 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5024 {
5025   int direction = MovDir[x][y];
5026   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5027   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5028
5029   *goes_to_x = newx;
5030   *goes_to_y = newy;
5031 }
5032
5033 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5034 {
5035   int oldx = x, oldy = y;
5036   int direction = MovDir[x][y];
5037
5038   if (direction == MV_LEFT)
5039     oldx++;
5040   else if (direction == MV_RIGHT)
5041     oldx--;
5042   else if (direction == MV_UP)
5043     oldy++;
5044   else if (direction == MV_DOWN)
5045     oldy--;
5046
5047   *comes_from_x = oldx;
5048   *comes_from_y = oldy;
5049 }
5050
5051 int MovingOrBlocked2Element(int x, int y)
5052 {
5053   int element = Feld[x][y];
5054
5055   if (element == EL_BLOCKED)
5056   {
5057     int oldx, oldy;
5058
5059     Blocked2Moving(x, y, &oldx, &oldy);
5060     return Feld[oldx][oldy];
5061   }
5062   else
5063     return element;
5064 }
5065
5066 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5067 {
5068   /* like MovingOrBlocked2Element(), but if element is moving
5069      and (x,y) is the field the moving element is just leaving,
5070      return EL_BLOCKED instead of the element value */
5071   int element = Feld[x][y];
5072
5073   if (IS_MOVING(x, y))
5074   {
5075     if (element == EL_BLOCKED)
5076     {
5077       int oldx, oldy;
5078
5079       Blocked2Moving(x, y, &oldx, &oldy);
5080       return Feld[oldx][oldy];
5081     }
5082     else
5083       return EL_BLOCKED;
5084   }
5085   else
5086     return element;
5087 }
5088
5089 static void RemoveField(int x, int y)
5090 {
5091   Feld[x][y] = EL_EMPTY;
5092
5093   MovPos[x][y] = 0;
5094   MovDir[x][y] = 0;
5095   MovDelay[x][y] = 0;
5096
5097 #if USE_NEW_CUSTOM_VALUE
5098   CustomValue[x][y] = 0;
5099 #endif
5100
5101   AmoebaNr[x][y] = 0;
5102   ChangeDelay[x][y] = 0;
5103   ChangePage[x][y] = -1;
5104   Pushed[x][y] = FALSE;
5105
5106 #if 0
5107   ExplodeField[x][y] = EX_TYPE_NONE;
5108 #endif
5109
5110   GfxElement[x][y] = EL_UNDEFINED;
5111   GfxAction[x][y] = ACTION_DEFAULT;
5112   GfxDir[x][y] = MV_NONE;
5113 }
5114
5115 void RemoveMovingField(int x, int y)
5116 {
5117   int oldx = x, oldy = y, newx = x, newy = y;
5118   int element = Feld[x][y];
5119   int next_element = EL_UNDEFINED;
5120
5121   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5122     return;
5123
5124   if (IS_MOVING(x, y))
5125   {
5126     Moving2Blocked(x, y, &newx, &newy);
5127
5128     if (Feld[newx][newy] != EL_BLOCKED)
5129     {
5130       /* element is moving, but target field is not free (blocked), but
5131          already occupied by something different (example: acid pool);
5132          in this case, only remove the moving field, but not the target */
5133
5134       RemoveField(oldx, oldy);
5135
5136       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5137
5138       DrawLevelField(oldx, oldy);
5139
5140       return;
5141     }
5142   }
5143   else if (element == EL_BLOCKED)
5144   {
5145     Blocked2Moving(x, y, &oldx, &oldy);
5146     if (!IS_MOVING(oldx, oldy))
5147       return;
5148   }
5149
5150   if (element == EL_BLOCKED &&
5151       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5152        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5153        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5154        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5155        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5156        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5157     next_element = get_next_element(Feld[oldx][oldy]);
5158
5159   RemoveField(oldx, oldy);
5160   RemoveField(newx, newy);
5161
5162   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5163
5164   if (next_element != EL_UNDEFINED)
5165     Feld[oldx][oldy] = next_element;
5166
5167   DrawLevelField(oldx, oldy);
5168   DrawLevelField(newx, newy);
5169 }
5170
5171 void DrawDynamite(int x, int y)
5172 {
5173   int sx = SCREENX(x), sy = SCREENY(y);
5174   int graphic = el2img(Feld[x][y]);
5175   int frame;
5176
5177   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5178     return;
5179
5180   if (IS_WALKABLE_INSIDE(Back[x][y]))
5181     return;
5182
5183   if (Back[x][y])
5184     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5185   else if (Store[x][y])
5186     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5187
5188   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5189
5190   if (Back[x][y] || Store[x][y])
5191     DrawGraphicThruMask(sx, sy, graphic, frame);
5192   else
5193     DrawGraphic(sx, sy, graphic, frame);
5194 }
5195
5196 void CheckDynamite(int x, int y)
5197 {
5198   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5199   {
5200     MovDelay[x][y]--;
5201
5202     if (MovDelay[x][y] != 0)
5203     {
5204       DrawDynamite(x, y);
5205       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5206
5207       return;
5208     }
5209   }
5210
5211   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5212
5213   Bang(x, y);
5214 }
5215
5216 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5217 {
5218   boolean num_checked_players = 0;
5219   int i;
5220
5221   for (i = 0; i < MAX_PLAYERS; i++)
5222   {
5223     if (stored_player[i].active)
5224     {
5225       int sx = stored_player[i].jx;
5226       int sy = stored_player[i].jy;
5227
5228       if (num_checked_players == 0)
5229       {
5230         *sx1 = *sx2 = sx;
5231         *sy1 = *sy2 = sy;
5232       }
5233       else
5234       {
5235         *sx1 = MIN(*sx1, sx);
5236         *sy1 = MIN(*sy1, sy);
5237         *sx2 = MAX(*sx2, sx);
5238         *sy2 = MAX(*sy2, sy);
5239       }
5240
5241       num_checked_players++;
5242     }
5243   }
5244 }
5245
5246 static boolean checkIfAllPlayersFitToScreen_RND()
5247 {
5248   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5249
5250   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5251
5252   return (sx2 - sx1 < SCR_FIELDX &&
5253           sy2 - sy1 < SCR_FIELDY);
5254 }
5255
5256 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5257 {
5258   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5259
5260   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5261
5262   *sx = (sx1 + sx2) / 2;
5263   *sy = (sy1 + sy2) / 2;
5264 }
5265
5266 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5267                         boolean center_screen, boolean quick_relocation)
5268 {
5269   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5270   boolean no_delay = (tape.warp_forward);
5271   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5272   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5273
5274   if (quick_relocation)
5275   {
5276     int offset = game.scroll_delay_value;
5277
5278     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5279     {
5280       if (!level.shifted_relocation || center_screen)
5281       {
5282         /* quick relocation (without scrolling), with centering of screen */
5283
5284         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5285                     x > SBX_Right + MIDPOSX ? SBX_Right :
5286                     x - MIDPOSX);
5287
5288         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5289                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5290                     y - MIDPOSY);
5291       }
5292       else
5293       {
5294         /* quick relocation (without scrolling), but do not center screen */
5295
5296         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5297                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5298                                old_x - MIDPOSX);
5299
5300         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5301                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5302                                old_y - MIDPOSY);
5303
5304         int offset_x = x + (scroll_x - center_scroll_x);
5305         int offset_y = y + (scroll_y - center_scroll_y);
5306
5307         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5308                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5309                     offset_x - MIDPOSX);
5310
5311         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5312                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5313                     offset_y - MIDPOSY);
5314       }
5315     }
5316     else
5317     {
5318       /* quick relocation (without scrolling), inside visible screen area */
5319
5320       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5321           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5322         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5323
5324       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5325           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5326         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5327
5328       /* don't scroll over playfield boundaries */
5329       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5330         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5331
5332       /* don't scroll over playfield boundaries */
5333       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5334         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5335     }
5336
5337     RedrawPlayfield(TRUE, 0,0,0,0);
5338   }
5339   else
5340   {
5341 #if 1
5342     int scroll_xx, scroll_yy;
5343
5344     if (!level.shifted_relocation || center_screen)
5345     {
5346       /* visible relocation (with scrolling), with centering of screen */
5347
5348       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5349                    x > SBX_Right + MIDPOSX ? SBX_Right :
5350                    x - MIDPOSX);
5351
5352       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5353                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5354                    y - MIDPOSY);
5355     }
5356     else
5357     {
5358       /* visible relocation (with scrolling), but do not center screen */
5359
5360       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5361                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5362                              old_x - MIDPOSX);
5363
5364       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5365                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5366                              old_y - MIDPOSY);
5367
5368       int offset_x = x + (scroll_x - center_scroll_x);
5369       int offset_y = y + (scroll_y - center_scroll_y);
5370
5371       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5372                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5373                    offset_x - MIDPOSX);
5374
5375       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5376                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5377                    offset_y - MIDPOSY);
5378     }
5379
5380 #else
5381
5382     /* visible relocation (with scrolling), with centering of screen */
5383
5384     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5385                      x > SBX_Right + MIDPOSX ? SBX_Right :
5386                      x - MIDPOSX);
5387
5388     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5389                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5390                      y - MIDPOSY);
5391 #endif
5392
5393     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5394
5395     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5396     {
5397       int dx = 0, dy = 0;
5398       int fx = FX, fy = FY;
5399
5400       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5401       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5402
5403       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5404         break;
5405
5406       scroll_x -= dx;
5407       scroll_y -= dy;
5408
5409       fx += dx * TILEX / 2;
5410       fy += dy * TILEY / 2;
5411
5412       ScrollLevel(dx, dy);
5413       DrawAllPlayers();
5414
5415       /* scroll in two steps of half tile size to make things smoother */
5416       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5417       FlushDisplay();
5418       Delay(wait_delay_value);
5419
5420       /* scroll second step to align at full tile size */
5421       BackToFront();
5422       Delay(wait_delay_value);
5423     }
5424
5425     DrawAllPlayers();
5426     BackToFront();
5427     Delay(wait_delay_value);
5428   }
5429 }
5430
5431 void RelocatePlayer(int jx, int jy, int el_player_raw)
5432 {
5433   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5434   int player_nr = GET_PLAYER_NR(el_player);
5435   struct PlayerInfo *player = &stored_player[player_nr];
5436   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5437   boolean no_delay = (tape.warp_forward);
5438   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5439   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5440   int old_jx = player->jx;
5441   int old_jy = player->jy;
5442   int old_element = Feld[old_jx][old_jy];
5443   int element = Feld[jx][jy];
5444   boolean player_relocated = (old_jx != jx || old_jy != jy);
5445
5446   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5447   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5448   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5449   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5450   int leave_side_horiz = move_dir_horiz;
5451   int leave_side_vert  = move_dir_vert;
5452   int enter_side = enter_side_horiz | enter_side_vert;
5453   int leave_side = leave_side_horiz | leave_side_vert;
5454
5455   if (player->GameOver)         /* do not reanimate dead player */
5456     return;
5457
5458   if (!player_relocated)        /* no need to relocate the player */
5459     return;
5460
5461   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5462   {
5463     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5464     DrawLevelField(jx, jy);
5465   }
5466
5467   if (player->present)
5468   {
5469     while (player->MovPos)
5470     {
5471       ScrollPlayer(player, SCROLL_GO_ON);
5472       ScrollScreen(NULL, SCROLL_GO_ON);
5473
5474       AdvanceFrameAndPlayerCounters(player->index_nr);
5475
5476       DrawPlayer(player);
5477
5478       BackToFront();
5479       Delay(wait_delay_value);
5480     }
5481
5482     DrawPlayer(player);         /* needed here only to cleanup last field */
5483     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5484
5485     player->is_moving = FALSE;
5486   }
5487
5488   if (IS_CUSTOM_ELEMENT(old_element))
5489     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5490                                CE_LEFT_BY_PLAYER,
5491                                player->index_bit, leave_side);
5492
5493   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5494                                       CE_PLAYER_LEAVES_X,
5495                                       player->index_bit, leave_side);
5496
5497   Feld[jx][jy] = el_player;
5498   InitPlayerField(jx, jy, el_player, TRUE);
5499
5500   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5501   {
5502     Feld[jx][jy] = element;
5503     InitField(jx, jy, FALSE);
5504   }
5505
5506   /* only visually relocate centered player */
5507   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5508                      FALSE, level.instant_relocation);
5509
5510   TestIfPlayerTouchesBadThing(jx, jy);
5511   TestIfPlayerTouchesCustomElement(jx, jy);
5512
5513   if (IS_CUSTOM_ELEMENT(element))
5514     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5515                                player->index_bit, enter_side);
5516
5517   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5518                                       player->index_bit, enter_side);
5519 }
5520
5521 void Explode(int ex, int ey, int phase, int mode)
5522 {
5523   int x, y;
5524   int last_phase;
5525   int border_element;
5526
5527   /* !!! eliminate this variable !!! */
5528   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5529
5530   if (game.explosions_delayed)
5531   {
5532     ExplodeField[ex][ey] = mode;
5533     return;
5534   }
5535
5536   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5537   {
5538     int center_element = Feld[ex][ey];
5539     int artwork_element, explosion_element;     /* set these values later */
5540
5541 #if 0
5542     /* --- This is only really needed (and now handled) in "Impact()". --- */
5543     /* do not explode moving elements that left the explode field in time */
5544     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5545         center_element == EL_EMPTY &&
5546         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5547       return;
5548 #endif
5549
5550 #if 0
5551     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5552     if (mode == EX_TYPE_NORMAL ||
5553         mode == EX_TYPE_CENTER ||
5554         mode == EX_TYPE_CROSS)
5555       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5556 #endif
5557
5558     /* remove things displayed in background while burning dynamite */
5559     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5560       Back[ex][ey] = 0;
5561
5562     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5563     {
5564       /* put moving element to center field (and let it explode there) */
5565       center_element = MovingOrBlocked2Element(ex, ey);
5566       RemoveMovingField(ex, ey);
5567       Feld[ex][ey] = center_element;
5568     }
5569
5570     /* now "center_element" is finally determined -- set related values now */
5571     artwork_element = center_element;           /* for custom player artwork */
5572     explosion_element = center_element;         /* for custom player artwork */
5573
5574     if (IS_PLAYER(ex, ey))
5575     {
5576       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5577
5578       artwork_element = stored_player[player_nr].artwork_element;
5579
5580       if (level.use_explosion_element[player_nr])
5581       {
5582         explosion_element = level.explosion_element[player_nr];
5583         artwork_element = explosion_element;
5584       }
5585     }
5586
5587 #if 1
5588     if (mode == EX_TYPE_NORMAL ||
5589         mode == EX_TYPE_CENTER ||
5590         mode == EX_TYPE_CROSS)
5591       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5592 #endif
5593
5594     last_phase = element_info[explosion_element].explosion_delay + 1;
5595
5596     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5597     {
5598       int xx = x - ex + 1;
5599       int yy = y - ey + 1;
5600       int element;
5601
5602       if (!IN_LEV_FIELD(x, y) ||
5603           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5604           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5605         continue;
5606
5607       element = Feld[x][y];
5608
5609       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5610       {
5611         element = MovingOrBlocked2Element(x, y);
5612
5613         if (!IS_EXPLOSION_PROOF(element))
5614           RemoveMovingField(x, y);
5615       }
5616
5617       /* indestructible elements can only explode in center (but not flames) */
5618       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5619                                            mode == EX_TYPE_BORDER)) ||
5620           element == EL_FLAMES)
5621         continue;
5622
5623       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5624          behaviour, for example when touching a yamyam that explodes to rocks
5625          with active deadly shield, a rock is created under the player !!! */
5626       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5627 #if 0
5628       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5629           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5630            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5631 #else
5632       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5633 #endif
5634       {
5635         if (IS_ACTIVE_BOMB(element))
5636         {
5637           /* re-activate things under the bomb like gate or penguin */
5638           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5639           Back[x][y] = 0;
5640         }
5641
5642         continue;
5643       }
5644
5645       /* save walkable background elements while explosion on same tile */
5646       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5647           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5648         Back[x][y] = element;
5649
5650       /* ignite explodable elements reached by other explosion */
5651       if (element == EL_EXPLOSION)
5652         element = Store2[x][y];
5653
5654       if (AmoebaNr[x][y] &&
5655           (element == EL_AMOEBA_FULL ||
5656            element == EL_BD_AMOEBA ||
5657            element == EL_AMOEBA_GROWING))
5658       {
5659         AmoebaCnt[AmoebaNr[x][y]]--;
5660         AmoebaCnt2[AmoebaNr[x][y]]--;
5661       }
5662
5663       RemoveField(x, y);
5664
5665       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5666       {
5667         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5668
5669         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5670
5671         if (PLAYERINFO(ex, ey)->use_murphy)
5672           Store[x][y] = EL_EMPTY;
5673       }
5674
5675       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5676          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5677       else if (ELEM_IS_PLAYER(center_element))
5678         Store[x][y] = EL_EMPTY;
5679       else if (center_element == EL_YAMYAM)
5680         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5681       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5682         Store[x][y] = element_info[center_element].content.e[xx][yy];
5683 #if 1
5684       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5685          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5686          otherwise) -- FIX THIS !!! */
5687       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5688         Store[x][y] = element_info[element].content.e[1][1];
5689 #else
5690       else if (!CAN_EXPLODE(element))
5691         Store[x][y] = element_info[element].content.e[1][1];
5692 #endif
5693       else
5694         Store[x][y] = EL_EMPTY;
5695
5696       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5697           center_element == EL_AMOEBA_TO_DIAMOND)
5698         Store2[x][y] = element;
5699
5700       Feld[x][y] = EL_EXPLOSION;
5701       GfxElement[x][y] = artwork_element;
5702
5703       ExplodePhase[x][y] = 1;
5704       ExplodeDelay[x][y] = last_phase;
5705
5706       Stop[x][y] = TRUE;
5707     }
5708
5709     if (center_element == EL_YAMYAM)
5710       game.yamyam_content_nr =
5711         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5712
5713     return;
5714   }
5715
5716   if (Stop[ex][ey])
5717     return;
5718
5719   x = ex;
5720   y = ey;
5721
5722   if (phase == 1)
5723     GfxFrame[x][y] = 0;         /* restart explosion animation */
5724
5725   last_phase = ExplodeDelay[x][y];
5726
5727   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5728
5729 #ifdef DEBUG
5730
5731   /* activate this even in non-DEBUG version until cause for crash in
5732      getGraphicAnimationFrame() (see below) is found and eliminated */
5733
5734 #endif
5735 #if 1
5736
5737 #if 1
5738   /* this can happen if the player leaves an explosion just in time */
5739   if (GfxElement[x][y] == EL_UNDEFINED)
5740     GfxElement[x][y] = EL_EMPTY;
5741 #else
5742   if (GfxElement[x][y] == EL_UNDEFINED)
5743   {
5744     printf("\n\n");
5745     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5746     printf("Explode(): This should never happen!\n");
5747     printf("\n\n");
5748
5749     GfxElement[x][y] = EL_EMPTY;
5750   }
5751 #endif
5752
5753 #endif
5754
5755   border_element = Store2[x][y];
5756   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5757     border_element = StorePlayer[x][y];
5758
5759   if (phase == element_info[border_element].ignition_delay ||
5760       phase == last_phase)
5761   {
5762     boolean border_explosion = FALSE;
5763
5764     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5765         !PLAYER_EXPLOSION_PROTECTED(x, y))
5766     {
5767       KillPlayerUnlessExplosionProtected(x, y);
5768       border_explosion = TRUE;
5769     }
5770     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5771     {
5772       Feld[x][y] = Store2[x][y];
5773       Store2[x][y] = 0;
5774       Bang(x, y);
5775       border_explosion = TRUE;
5776     }
5777     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5778     {
5779       AmoebeUmwandeln(x, y);
5780       Store2[x][y] = 0;
5781       border_explosion = TRUE;
5782     }
5783
5784     /* if an element just explodes due to another explosion (chain-reaction),
5785        do not immediately end the new explosion when it was the last frame of
5786        the explosion (as it would be done in the following "if"-statement!) */
5787     if (border_explosion && phase == last_phase)
5788       return;
5789   }
5790
5791   if (phase == last_phase)
5792   {
5793     int element;
5794
5795     element = Feld[x][y] = Store[x][y];
5796     Store[x][y] = Store2[x][y] = 0;
5797     GfxElement[x][y] = EL_UNDEFINED;
5798
5799     /* player can escape from explosions and might therefore be still alive */
5800     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5801         element <= EL_PLAYER_IS_EXPLODING_4)
5802     {
5803       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5804       int explosion_element = EL_PLAYER_1 + player_nr;
5805       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5806       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5807
5808       if (level.use_explosion_element[player_nr])
5809         explosion_element = level.explosion_element[player_nr];
5810
5811       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5812                     element_info[explosion_element].content.e[xx][yy]);
5813     }
5814
5815     /* restore probably existing indestructible background element */
5816     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5817       element = Feld[x][y] = Back[x][y];
5818     Back[x][y] = 0;
5819
5820     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5821     GfxDir[x][y] = MV_NONE;
5822     ChangeDelay[x][y] = 0;
5823     ChangePage[x][y] = -1;
5824
5825 #if USE_NEW_CUSTOM_VALUE
5826     CustomValue[x][y] = 0;
5827 #endif
5828
5829     InitField_WithBug2(x, y, FALSE);
5830
5831     DrawLevelField(x, y);
5832
5833     TestIfElementTouchesCustomElement(x, y);
5834
5835     if (GFX_CRUMBLED(element))
5836       DrawLevelFieldCrumbledSandNeighbours(x, y);
5837
5838     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5839       StorePlayer[x][y] = 0;
5840
5841     if (ELEM_IS_PLAYER(element))
5842       RelocatePlayer(x, y, element);
5843   }
5844   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5845   {
5846     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5847     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5848
5849     if (phase == delay)
5850       DrawLevelFieldCrumbledSand(x, y);
5851
5852     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5853     {
5854       DrawLevelElement(x, y, Back[x][y]);
5855       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5856     }
5857     else if (IS_WALKABLE_UNDER(Back[x][y]))
5858     {
5859       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5860       DrawLevelElementThruMask(x, y, Back[x][y]);
5861     }
5862     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5863       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5864   }
5865 }
5866
5867 void DynaExplode(int ex, int ey)
5868 {
5869   int i, j;
5870   int dynabomb_element = Feld[ex][ey];
5871   int dynabomb_size = 1;
5872   boolean dynabomb_xl = FALSE;
5873   struct PlayerInfo *player;
5874   static int xy[4][2] =
5875   {
5876     { 0, -1 },
5877     { -1, 0 },
5878     { +1, 0 },
5879     { 0, +1 }
5880   };
5881
5882   if (IS_ACTIVE_BOMB(dynabomb_element))
5883   {
5884     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5885     dynabomb_size = player->dynabomb_size;
5886     dynabomb_xl = player->dynabomb_xl;
5887     player->dynabombs_left++;
5888   }
5889
5890   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5891
5892   for (i = 0; i < NUM_DIRECTIONS; i++)
5893   {
5894     for (j = 1; j <= dynabomb_size; j++)
5895     {
5896       int x = ex + j * xy[i][0];
5897       int y = ey + j * xy[i][1];
5898       int element;
5899
5900       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5901         break;
5902
5903       element = Feld[x][y];
5904
5905       /* do not restart explosions of fields with active bombs */
5906       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5907         continue;
5908
5909       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5910
5911       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5912           !IS_DIGGABLE(element) && !dynabomb_xl)
5913         break;
5914     }
5915   }
5916 }
5917
5918 void Bang(int x, int y)
5919 {
5920   int element = MovingOrBlocked2Element(x, y);
5921   int explosion_type = EX_TYPE_NORMAL;
5922
5923   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5924   {
5925     struct PlayerInfo *player = PLAYERINFO(x, y);
5926
5927     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5928                             player->element_nr);
5929
5930     if (level.use_explosion_element[player->index_nr])
5931     {
5932       int explosion_element = level.explosion_element[player->index_nr];
5933
5934       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5935         explosion_type = EX_TYPE_CROSS;
5936       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5937         explosion_type = EX_TYPE_CENTER;
5938     }
5939   }
5940
5941   switch (element)
5942   {
5943     case EL_BUG:
5944     case EL_SPACESHIP:
5945     case EL_BD_BUTTERFLY:
5946     case EL_BD_FIREFLY:
5947     case EL_YAMYAM:
5948     case EL_DARK_YAMYAM:
5949     case EL_ROBOT:
5950     case EL_PACMAN:
5951     case EL_MOLE:
5952       RaiseScoreElement(element);
5953       break;
5954
5955     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5956     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5957     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5958     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5959     case EL_DYNABOMB_INCREASE_NUMBER:
5960     case EL_DYNABOMB_INCREASE_SIZE:
5961     case EL_DYNABOMB_INCREASE_POWER:
5962       explosion_type = EX_TYPE_DYNA;
5963       break;
5964
5965     case EL_DC_LANDMINE:
5966 #if 0
5967     case EL_EM_EXIT_OPEN:
5968     case EL_EM_STEEL_EXIT_OPEN:
5969 #endif
5970       explosion_type = EX_TYPE_CENTER;
5971       break;
5972
5973     case EL_PENGUIN:
5974     case EL_LAMP:
5975     case EL_LAMP_ACTIVE:
5976     case EL_AMOEBA_TO_DIAMOND:
5977       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5978         explosion_type = EX_TYPE_CENTER;
5979       break;
5980
5981     default:
5982       if (element_info[element].explosion_type == EXPLODES_CROSS)
5983         explosion_type = EX_TYPE_CROSS;
5984       else if (element_info[element].explosion_type == EXPLODES_1X1)
5985         explosion_type = EX_TYPE_CENTER;
5986       break;
5987   }
5988
5989   if (explosion_type == EX_TYPE_DYNA)
5990     DynaExplode(x, y);
5991   else
5992     Explode(x, y, EX_PHASE_START, explosion_type);
5993
5994   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5995 }
5996
5997 void SplashAcid(int x, int y)
5998 {
5999   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6000       (!IN_LEV_FIELD(x - 1, y - 2) ||
6001        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6002     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6003
6004   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6005       (!IN_LEV_FIELD(x + 1, y - 2) ||
6006        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6007     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6008
6009   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6010 }
6011
6012 static void InitBeltMovement()
6013 {
6014   static int belt_base_element[4] =
6015   {
6016     EL_CONVEYOR_BELT_1_LEFT,
6017     EL_CONVEYOR_BELT_2_LEFT,
6018     EL_CONVEYOR_BELT_3_LEFT,
6019     EL_CONVEYOR_BELT_4_LEFT
6020   };
6021   static int belt_base_active_element[4] =
6022   {
6023     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6024     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6025     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6026     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6027   };
6028
6029   int x, y, i, j;
6030
6031   /* set frame order for belt animation graphic according to belt direction */
6032   for (i = 0; i < NUM_BELTS; i++)
6033   {
6034     int belt_nr = i;
6035
6036     for (j = 0; j < NUM_BELT_PARTS; j++)
6037     {
6038       int element = belt_base_active_element[belt_nr] + j;
6039       int graphic_1 = el2img(element);
6040       int graphic_2 = el2panelimg(element);
6041
6042       if (game.belt_dir[i] == MV_LEFT)
6043       {
6044         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6045         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6046       }
6047       else
6048       {
6049         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6050         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6051       }
6052     }
6053   }
6054
6055   SCAN_PLAYFIELD(x, y)
6056   {
6057     int element = Feld[x][y];
6058
6059     for (i = 0; i < NUM_BELTS; i++)
6060     {
6061       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6062       {
6063         int e_belt_nr = getBeltNrFromBeltElement(element);
6064         int belt_nr = i;
6065
6066         if (e_belt_nr == belt_nr)
6067         {
6068           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6069
6070           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6071         }
6072       }
6073     }
6074   }
6075 }
6076
6077 static void ToggleBeltSwitch(int x, int y)
6078 {
6079   static int belt_base_element[4] =
6080   {
6081     EL_CONVEYOR_BELT_1_LEFT,
6082     EL_CONVEYOR_BELT_2_LEFT,
6083     EL_CONVEYOR_BELT_3_LEFT,
6084     EL_CONVEYOR_BELT_4_LEFT
6085   };
6086   static int belt_base_active_element[4] =
6087   {
6088     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6089     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6090     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6091     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6092   };
6093   static int belt_base_switch_element[4] =
6094   {
6095     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6096     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6097     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6098     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6099   };
6100   static int belt_move_dir[4] =
6101   {
6102     MV_LEFT,
6103     MV_NONE,
6104     MV_RIGHT,
6105     MV_NONE,
6106   };
6107
6108   int element = Feld[x][y];
6109   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6110   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6111   int belt_dir = belt_move_dir[belt_dir_nr];
6112   int xx, yy, i;
6113
6114   if (!IS_BELT_SWITCH(element))
6115     return;
6116
6117   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6118   game.belt_dir[belt_nr] = belt_dir;
6119
6120   if (belt_dir_nr == 3)
6121     belt_dir_nr = 1;
6122
6123   /* set frame order for belt animation graphic according to belt direction */
6124   for (i = 0; i < NUM_BELT_PARTS; i++)
6125   {
6126     int element = belt_base_active_element[belt_nr] + i;
6127     int graphic_1 = el2img(element);
6128     int graphic_2 = el2panelimg(element);
6129
6130     if (belt_dir == MV_LEFT)
6131     {
6132       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6133       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6134     }
6135     else
6136     {
6137       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6138       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6139     }
6140   }
6141
6142   SCAN_PLAYFIELD(xx, yy)
6143   {
6144     int element = Feld[xx][yy];
6145
6146     if (IS_BELT_SWITCH(element))
6147     {
6148       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6149
6150       if (e_belt_nr == belt_nr)
6151       {
6152         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6153         DrawLevelField(xx, yy);
6154       }
6155     }
6156     else if (IS_BELT(element) && belt_dir != MV_NONE)
6157     {
6158       int e_belt_nr = getBeltNrFromBeltElement(element);
6159
6160       if (e_belt_nr == belt_nr)
6161       {
6162         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6163
6164         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6165         DrawLevelField(xx, yy);
6166       }
6167     }
6168     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6169     {
6170       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6171
6172       if (e_belt_nr == belt_nr)
6173       {
6174         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6175
6176         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6177         DrawLevelField(xx, yy);
6178       }
6179     }
6180   }
6181 }
6182
6183 static void ToggleSwitchgateSwitch(int x, int y)
6184 {
6185   int xx, yy;
6186
6187   game.switchgate_pos = !game.switchgate_pos;
6188
6189   SCAN_PLAYFIELD(xx, yy)
6190   {
6191     int element = Feld[xx][yy];
6192
6193 #if !USE_BOTH_SWITCHGATE_SWITCHES
6194     if (element == EL_SWITCHGATE_SWITCH_UP ||
6195         element == EL_SWITCHGATE_SWITCH_DOWN)
6196     {
6197       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6198       DrawLevelField(xx, yy);
6199     }
6200     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6201              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6202     {
6203       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6204       DrawLevelField(xx, yy);
6205     }
6206 #else
6207     if (element == EL_SWITCHGATE_SWITCH_UP)
6208     {
6209       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6210       DrawLevelField(xx, yy);
6211     }
6212     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6213     {
6214       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6215       DrawLevelField(xx, yy);
6216     }
6217     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6218     {
6219       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6220       DrawLevelField(xx, yy);
6221     }
6222     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6223     {
6224       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6225       DrawLevelField(xx, yy);
6226     }
6227 #endif
6228     else if (element == EL_SWITCHGATE_OPEN ||
6229              element == EL_SWITCHGATE_OPENING)
6230     {
6231       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6232
6233       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6234     }
6235     else if (element == EL_SWITCHGATE_CLOSED ||
6236              element == EL_SWITCHGATE_CLOSING)
6237     {
6238       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6239
6240       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6241     }
6242   }
6243 }
6244
6245 static int getInvisibleActiveFromInvisibleElement(int element)
6246 {
6247   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6248           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6249           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6250           element);
6251 }
6252
6253 static int getInvisibleFromInvisibleActiveElement(int element)
6254 {
6255   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6256           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6257           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6258           element);
6259 }
6260
6261 static void RedrawAllLightSwitchesAndInvisibleElements()
6262 {
6263   int x, y;
6264
6265   SCAN_PLAYFIELD(x, y)
6266   {
6267     int element = Feld[x][y];
6268
6269     if (element == EL_LIGHT_SWITCH &&
6270         game.light_time_left > 0)
6271     {
6272       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6273       DrawLevelField(x, y);
6274     }
6275     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6276              game.light_time_left == 0)
6277     {
6278       Feld[x][y] = EL_LIGHT_SWITCH;
6279       DrawLevelField(x, y);
6280     }
6281     else if (element == EL_EMC_DRIPPER &&
6282              game.light_time_left > 0)
6283     {
6284       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6285       DrawLevelField(x, y);
6286     }
6287     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6288              game.light_time_left == 0)
6289     {
6290       Feld[x][y] = EL_EMC_DRIPPER;
6291       DrawLevelField(x, y);
6292     }
6293     else if (element == EL_INVISIBLE_STEELWALL ||
6294              element == EL_INVISIBLE_WALL ||
6295              element == EL_INVISIBLE_SAND)
6296     {
6297       if (game.light_time_left > 0)
6298         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6299
6300       DrawLevelField(x, y);
6301
6302       /* uncrumble neighbour fields, if needed */
6303       if (element == EL_INVISIBLE_SAND)
6304         DrawLevelFieldCrumbledSandNeighbours(x, y);
6305     }
6306     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6307              element == EL_INVISIBLE_WALL_ACTIVE ||
6308              element == EL_INVISIBLE_SAND_ACTIVE)
6309     {
6310       if (game.light_time_left == 0)
6311         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6312
6313       DrawLevelField(x, y);
6314
6315       /* re-crumble neighbour fields, if needed */
6316       if (element == EL_INVISIBLE_SAND)
6317         DrawLevelFieldCrumbledSandNeighbours(x, y);
6318     }
6319   }
6320 }
6321
6322 static void RedrawAllInvisibleElementsForLenses()
6323 {
6324   int x, y;
6325
6326   SCAN_PLAYFIELD(x, y)
6327   {
6328     int element = Feld[x][y];
6329
6330     if (element == EL_EMC_DRIPPER &&
6331         game.lenses_time_left > 0)
6332     {
6333       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6334       DrawLevelField(x, y);
6335     }
6336     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6337              game.lenses_time_left == 0)
6338     {
6339       Feld[x][y] = EL_EMC_DRIPPER;
6340       DrawLevelField(x, y);
6341     }
6342     else if (element == EL_INVISIBLE_STEELWALL ||
6343              element == EL_INVISIBLE_WALL ||
6344              element == EL_INVISIBLE_SAND)
6345     {
6346       if (game.lenses_time_left > 0)
6347         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6348
6349       DrawLevelField(x, y);
6350
6351       /* uncrumble neighbour fields, if needed */
6352       if (element == EL_INVISIBLE_SAND)
6353         DrawLevelFieldCrumbledSandNeighbours(x, y);
6354     }
6355     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6356              element == EL_INVISIBLE_WALL_ACTIVE ||
6357              element == EL_INVISIBLE_SAND_ACTIVE)
6358     {
6359       if (game.lenses_time_left == 0)
6360         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6361
6362       DrawLevelField(x, y);
6363
6364       /* re-crumble neighbour fields, if needed */
6365       if (element == EL_INVISIBLE_SAND)
6366         DrawLevelFieldCrumbledSandNeighbours(x, y);
6367     }
6368   }
6369 }
6370
6371 static void RedrawAllInvisibleElementsForMagnifier()
6372 {
6373   int x, y;
6374
6375   SCAN_PLAYFIELD(x, y)
6376   {
6377     int element = Feld[x][y];
6378
6379     if (element == EL_EMC_FAKE_GRASS &&
6380         game.magnify_time_left > 0)
6381     {
6382       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6383       DrawLevelField(x, y);
6384     }
6385     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6386              game.magnify_time_left == 0)
6387     {
6388       Feld[x][y] = EL_EMC_FAKE_GRASS;
6389       DrawLevelField(x, y);
6390     }
6391     else if (IS_GATE_GRAY(element) &&
6392              game.magnify_time_left > 0)
6393     {
6394       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6395                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6396                     IS_EM_GATE_GRAY(element) ?
6397                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6398                     IS_EMC_GATE_GRAY(element) ?
6399                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6400                     element);
6401       DrawLevelField(x, y);
6402     }
6403     else if (IS_GATE_GRAY_ACTIVE(element) &&
6404              game.magnify_time_left == 0)
6405     {
6406       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6407                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6408                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6409                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6410                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6411                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6412                     element);
6413       DrawLevelField(x, y);
6414     }
6415   }
6416 }
6417
6418 static void ToggleLightSwitch(int x, int y)
6419 {
6420   int element = Feld[x][y];
6421
6422   game.light_time_left =
6423     (element == EL_LIGHT_SWITCH ?
6424      level.time_light * FRAMES_PER_SECOND : 0);
6425
6426   RedrawAllLightSwitchesAndInvisibleElements();
6427 }
6428
6429 static void ActivateTimegateSwitch(int x, int y)
6430 {
6431   int xx, yy;
6432
6433   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6434
6435   SCAN_PLAYFIELD(xx, yy)
6436   {
6437     int element = Feld[xx][yy];
6438
6439     if (element == EL_TIMEGATE_CLOSED ||
6440         element == EL_TIMEGATE_CLOSING)
6441     {
6442       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6443       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6444     }
6445
6446     /*
6447     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6448     {
6449       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6450       DrawLevelField(xx, yy);
6451     }
6452     */
6453
6454   }
6455
6456 #if 1
6457   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6458                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6459 #else
6460   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6461 #endif
6462 }
6463
6464 void Impact(int x, int y)
6465 {
6466   boolean last_line = (y == lev_fieldy - 1);
6467   boolean object_hit = FALSE;
6468   boolean impact = (last_line || object_hit);
6469   int element = Feld[x][y];
6470   int smashed = EL_STEELWALL;
6471
6472   if (!last_line)       /* check if element below was hit */
6473   {
6474     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6475       return;
6476
6477     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6478                                          MovDir[x][y + 1] != MV_DOWN ||
6479                                          MovPos[x][y + 1] <= TILEY / 2));
6480
6481     /* do not smash moving elements that left the smashed field in time */
6482     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6483         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6484       object_hit = FALSE;
6485
6486 #if USE_QUICKSAND_IMPACT_BUGFIX
6487     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6488     {
6489       RemoveMovingField(x, y + 1);
6490       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6491       Feld[x][y + 2] = EL_ROCK;
6492       DrawLevelField(x, y + 2);
6493
6494       object_hit = TRUE;
6495     }
6496
6497     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6498     {
6499       RemoveMovingField(x, y + 1);
6500       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6501       Feld[x][y + 2] = EL_ROCK;
6502       DrawLevelField(x, y + 2);
6503
6504       object_hit = TRUE;
6505     }
6506 #endif
6507
6508     if (object_hit)
6509       smashed = MovingOrBlocked2Element(x, y + 1);
6510
6511     impact = (last_line || object_hit);
6512   }
6513
6514   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6515   {
6516     SplashAcid(x, y + 1);
6517     return;
6518   }
6519
6520   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6521   /* only reset graphic animation if graphic really changes after impact */
6522   if (impact &&
6523       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6524   {
6525     ResetGfxAnimation(x, y);
6526     DrawLevelField(x, y);
6527   }
6528
6529   if (impact && CAN_EXPLODE_IMPACT(element))
6530   {
6531     Bang(x, y);
6532     return;
6533   }
6534   else if (impact && element == EL_PEARL &&
6535            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6536   {
6537     ResetGfxAnimation(x, y);
6538
6539     Feld[x][y] = EL_PEARL_BREAKING;
6540     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6541     return;
6542   }
6543   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6544   {
6545     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6546
6547     return;
6548   }
6549
6550   if (impact && element == EL_AMOEBA_DROP)
6551   {
6552     if (object_hit && IS_PLAYER(x, y + 1))
6553       KillPlayerUnlessEnemyProtected(x, y + 1);
6554     else if (object_hit && smashed == EL_PENGUIN)
6555       Bang(x, y + 1);
6556     else
6557     {
6558       Feld[x][y] = EL_AMOEBA_GROWING;
6559       Store[x][y] = EL_AMOEBA_WET;
6560
6561       ResetRandomAnimationValue(x, y);
6562     }
6563     return;
6564   }
6565
6566   if (object_hit)               /* check which object was hit */
6567   {
6568     if ((CAN_PASS_MAGIC_WALL(element) && 
6569          (smashed == EL_MAGIC_WALL ||
6570           smashed == EL_BD_MAGIC_WALL)) ||
6571         (CAN_PASS_DC_MAGIC_WALL(element) &&
6572          smashed == EL_DC_MAGIC_WALL))
6573     {
6574       int xx, yy;
6575       int activated_magic_wall =
6576         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6577          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6578          EL_DC_MAGIC_WALL_ACTIVE);
6579
6580       /* activate magic wall / mill */
6581       SCAN_PLAYFIELD(xx, yy)
6582       {
6583         if (Feld[xx][yy] == smashed)
6584           Feld[xx][yy] = activated_magic_wall;
6585       }
6586
6587       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6588       game.magic_wall_active = TRUE;
6589
6590       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6591                             SND_MAGIC_WALL_ACTIVATING :
6592                             smashed == EL_BD_MAGIC_WALL ?
6593                             SND_BD_MAGIC_WALL_ACTIVATING :
6594                             SND_DC_MAGIC_WALL_ACTIVATING));
6595     }
6596
6597     if (IS_PLAYER(x, y + 1))
6598     {
6599       if (CAN_SMASH_PLAYER(element))
6600       {
6601         KillPlayerUnlessEnemyProtected(x, y + 1);
6602         return;
6603       }
6604     }
6605     else if (smashed == EL_PENGUIN)
6606     {
6607       if (CAN_SMASH_PLAYER(element))
6608       {
6609         Bang(x, y + 1);
6610         return;
6611       }
6612     }
6613     else if (element == EL_BD_DIAMOND)
6614     {
6615       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6616       {
6617         Bang(x, y + 1);
6618         return;
6619       }
6620     }
6621     else if (((element == EL_SP_INFOTRON ||
6622                element == EL_SP_ZONK) &&
6623               (smashed == EL_SP_SNIKSNAK ||
6624                smashed == EL_SP_ELECTRON ||
6625                smashed == EL_SP_DISK_ORANGE)) ||
6626              (element == EL_SP_INFOTRON &&
6627               smashed == EL_SP_DISK_YELLOW))
6628     {
6629       Bang(x, y + 1);
6630       return;
6631     }
6632     else if (CAN_SMASH_EVERYTHING(element))
6633     {
6634       if (IS_CLASSIC_ENEMY(smashed) ||
6635           CAN_EXPLODE_SMASHED(smashed))
6636       {
6637         Bang(x, y + 1);
6638         return;
6639       }
6640       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6641       {
6642         if (smashed == EL_LAMP ||
6643             smashed == EL_LAMP_ACTIVE)
6644         {
6645           Bang(x, y + 1);
6646           return;
6647         }
6648         else if (smashed == EL_NUT)
6649         {
6650           Feld[x][y + 1] = EL_NUT_BREAKING;
6651           PlayLevelSound(x, y, SND_NUT_BREAKING);
6652           RaiseScoreElement(EL_NUT);
6653           return;
6654         }
6655         else if (smashed == EL_PEARL)
6656         {
6657           ResetGfxAnimation(x, y);
6658
6659           Feld[x][y + 1] = EL_PEARL_BREAKING;
6660           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6661           return;
6662         }
6663         else if (smashed == EL_DIAMOND)
6664         {
6665           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6666           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6667           return;
6668         }
6669         else if (IS_BELT_SWITCH(smashed))
6670         {
6671           ToggleBeltSwitch(x, y + 1);
6672         }
6673         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6674                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6675                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6676                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6677         {
6678           ToggleSwitchgateSwitch(x, y + 1);
6679         }
6680         else if (smashed == EL_LIGHT_SWITCH ||
6681                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6682         {
6683           ToggleLightSwitch(x, y + 1);
6684         }
6685         else
6686         {
6687 #if 0
6688           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6689 #endif
6690
6691           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6692
6693           CheckElementChangeBySide(x, y + 1, smashed, element,
6694                                    CE_SWITCHED, CH_SIDE_TOP);
6695           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6696                                             CH_SIDE_TOP);
6697         }
6698       }
6699       else
6700       {
6701         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6702       }
6703     }
6704   }
6705
6706   /* play sound of magic wall / mill */
6707   if (!last_line &&
6708       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6709        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6710        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6711   {
6712     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6713       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6714     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6715       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6716     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6717       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6718
6719     return;
6720   }
6721
6722   /* play sound of object that hits the ground */
6723   if (last_line || object_hit)
6724     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6725 }
6726
6727 inline static void TurnRoundExt(int x, int y)
6728 {
6729   static struct
6730   {
6731     int dx, dy;
6732   } move_xy[] =
6733   {
6734     {  0,  0 },
6735     { -1,  0 },
6736     { +1,  0 },
6737     {  0,  0 },
6738     {  0, -1 },
6739     {  0,  0 }, { 0, 0 }, { 0, 0 },
6740     {  0, +1 }
6741   };
6742   static struct
6743   {
6744     int left, right, back;
6745   } turn[] =
6746   {
6747     { 0,        0,              0        },
6748     { MV_DOWN,  MV_UP,          MV_RIGHT },
6749     { MV_UP,    MV_DOWN,        MV_LEFT  },
6750     { 0,        0,              0        },
6751     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6752     { 0,        0,              0        },
6753     { 0,        0,              0        },
6754     { 0,        0,              0        },
6755     { MV_RIGHT, MV_LEFT,        MV_UP    }
6756   };
6757
6758   int element = Feld[x][y];
6759   int move_pattern = element_info[element].move_pattern;
6760
6761   int old_move_dir = MovDir[x][y];
6762   int left_dir  = turn[old_move_dir].left;
6763   int right_dir = turn[old_move_dir].right;
6764   int back_dir  = turn[old_move_dir].back;
6765
6766   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6767   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6768   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6769   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6770
6771   int left_x  = x + left_dx,  left_y  = y + left_dy;
6772   int right_x = x + right_dx, right_y = y + right_dy;
6773   int move_x  = x + move_dx,  move_y  = y + move_dy;
6774
6775   int xx, yy;
6776
6777   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6778   {
6779     TestIfBadThingTouchesOtherBadThing(x, y);
6780
6781     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6782       MovDir[x][y] = right_dir;
6783     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6784       MovDir[x][y] = left_dir;
6785
6786     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6787       MovDelay[x][y] = 9;
6788     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6789       MovDelay[x][y] = 1;
6790   }
6791   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6792   {
6793     TestIfBadThingTouchesOtherBadThing(x, y);
6794
6795     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6796       MovDir[x][y] = left_dir;
6797     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6798       MovDir[x][y] = right_dir;
6799
6800     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6801       MovDelay[x][y] = 9;
6802     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6803       MovDelay[x][y] = 1;
6804   }
6805   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6806   {
6807     TestIfBadThingTouchesOtherBadThing(x, y);
6808
6809     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6810       MovDir[x][y] = left_dir;
6811     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6812       MovDir[x][y] = right_dir;
6813
6814     if (MovDir[x][y] != old_move_dir)
6815       MovDelay[x][y] = 9;
6816   }
6817   else if (element == EL_YAMYAM)
6818   {
6819     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6820     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6821
6822     if (can_turn_left && can_turn_right)
6823       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6824     else if (can_turn_left)
6825       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6826     else if (can_turn_right)
6827       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6828     else
6829       MovDir[x][y] = back_dir;
6830
6831     MovDelay[x][y] = 16 + 16 * RND(3);
6832   }
6833   else if (element == EL_DARK_YAMYAM)
6834   {
6835     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6836                                                          left_x, left_y);
6837     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6838                                                          right_x, right_y);
6839
6840     if (can_turn_left && can_turn_right)
6841       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6842     else if (can_turn_left)
6843       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6844     else if (can_turn_right)
6845       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6846     else
6847       MovDir[x][y] = back_dir;
6848
6849     MovDelay[x][y] = 16 + 16 * RND(3);
6850   }
6851   else if (element == EL_PACMAN)
6852   {
6853     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6854     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6855
6856     if (can_turn_left && can_turn_right)
6857       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6858     else if (can_turn_left)
6859       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6860     else if (can_turn_right)
6861       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6862     else
6863       MovDir[x][y] = back_dir;
6864
6865     MovDelay[x][y] = 6 + RND(40);
6866   }
6867   else if (element == EL_PIG)
6868   {
6869     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6870     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6871     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6872     boolean should_turn_left, should_turn_right, should_move_on;
6873     int rnd_value = 24;
6874     int rnd = RND(rnd_value);
6875
6876     should_turn_left = (can_turn_left &&
6877                         (!can_move_on ||
6878                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6879                                                    y + back_dy + left_dy)));
6880     should_turn_right = (can_turn_right &&
6881                          (!can_move_on ||
6882                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6883                                                     y + back_dy + right_dy)));
6884     should_move_on = (can_move_on &&
6885                       (!can_turn_left ||
6886                        !can_turn_right ||
6887                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6888                                                  y + move_dy + left_dy) ||
6889                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6890                                                  y + move_dy + right_dy)));
6891
6892     if (should_turn_left || should_turn_right || should_move_on)
6893     {
6894       if (should_turn_left && should_turn_right && should_move_on)
6895         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6896                         rnd < 2 * rnd_value / 3 ? right_dir :
6897                         old_move_dir);
6898       else if (should_turn_left && should_turn_right)
6899         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6900       else if (should_turn_left && should_move_on)
6901         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6902       else if (should_turn_right && should_move_on)
6903         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6904       else if (should_turn_left)
6905         MovDir[x][y] = left_dir;
6906       else if (should_turn_right)
6907         MovDir[x][y] = right_dir;
6908       else if (should_move_on)
6909         MovDir[x][y] = old_move_dir;
6910     }
6911     else if (can_move_on && rnd > rnd_value / 8)
6912       MovDir[x][y] = old_move_dir;
6913     else if (can_turn_left && can_turn_right)
6914       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6915     else if (can_turn_left && rnd > rnd_value / 8)
6916       MovDir[x][y] = left_dir;
6917     else if (can_turn_right && rnd > rnd_value/8)
6918       MovDir[x][y] = right_dir;
6919     else
6920       MovDir[x][y] = back_dir;
6921
6922     xx = x + move_xy[MovDir[x][y]].dx;
6923     yy = y + move_xy[MovDir[x][y]].dy;
6924
6925     if (!IN_LEV_FIELD(xx, yy) ||
6926         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6927       MovDir[x][y] = old_move_dir;
6928
6929     MovDelay[x][y] = 0;
6930   }
6931   else if (element == EL_DRAGON)
6932   {
6933     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6934     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6935     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6936     int rnd_value = 24;
6937     int rnd = RND(rnd_value);
6938
6939     if (can_move_on && rnd > rnd_value / 8)
6940       MovDir[x][y] = old_move_dir;
6941     else if (can_turn_left && can_turn_right)
6942       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6943     else if (can_turn_left && rnd > rnd_value / 8)
6944       MovDir[x][y] = left_dir;
6945     else if (can_turn_right && rnd > rnd_value / 8)
6946       MovDir[x][y] = right_dir;
6947     else
6948       MovDir[x][y] = back_dir;
6949
6950     xx = x + move_xy[MovDir[x][y]].dx;
6951     yy = y + move_xy[MovDir[x][y]].dy;
6952
6953     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6954       MovDir[x][y] = old_move_dir;
6955
6956     MovDelay[x][y] = 0;
6957   }
6958   else if (element == EL_MOLE)
6959   {
6960     boolean can_move_on =
6961       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6962                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6963                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6964     if (!can_move_on)
6965     {
6966       boolean can_turn_left =
6967         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6968                               IS_AMOEBOID(Feld[left_x][left_y])));
6969
6970       boolean can_turn_right =
6971         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6972                               IS_AMOEBOID(Feld[right_x][right_y])));
6973
6974       if (can_turn_left && can_turn_right)
6975         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6976       else if (can_turn_left)
6977         MovDir[x][y] = left_dir;
6978       else
6979         MovDir[x][y] = right_dir;
6980     }
6981
6982     if (MovDir[x][y] != old_move_dir)
6983       MovDelay[x][y] = 9;
6984   }
6985   else if (element == EL_BALLOON)
6986   {
6987     MovDir[x][y] = game.wind_direction;
6988     MovDelay[x][y] = 0;
6989   }
6990   else if (element == EL_SPRING)
6991   {
6992 #if USE_NEW_SPRING_BUMPER
6993     if (MovDir[x][y] & MV_HORIZONTAL)
6994     {
6995       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6996           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6997       {
6998         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6999         ResetGfxAnimation(move_x, move_y);
7000         DrawLevelField(move_x, move_y);
7001
7002         MovDir[x][y] = back_dir;
7003       }
7004       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7005                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7006         MovDir[x][y] = MV_NONE;
7007     }
7008 #else
7009     if (MovDir[x][y] & MV_HORIZONTAL &&
7010         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7011          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7012       MovDir[x][y] = MV_NONE;
7013 #endif
7014
7015     MovDelay[x][y] = 0;
7016   }
7017   else if (element == EL_ROBOT ||
7018            element == EL_SATELLITE ||
7019            element == EL_PENGUIN ||
7020            element == EL_EMC_ANDROID)
7021   {
7022     int attr_x = -1, attr_y = -1;
7023
7024     if (AllPlayersGone)
7025     {
7026       attr_x = ExitX;
7027       attr_y = ExitY;
7028     }
7029     else
7030     {
7031       int i;
7032
7033       for (i = 0; i < MAX_PLAYERS; i++)
7034       {
7035         struct PlayerInfo *player = &stored_player[i];
7036         int jx = player->jx, jy = player->jy;
7037
7038         if (!player->active)
7039           continue;
7040
7041         if (attr_x == -1 ||
7042             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7043         {
7044           attr_x = jx;
7045           attr_y = jy;
7046         }
7047       }
7048     }
7049
7050     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7051         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7052          game.engine_version < VERSION_IDENT(3,1,0,0)))
7053     {
7054       attr_x = ZX;
7055       attr_y = ZY;
7056     }
7057
7058     if (element == EL_PENGUIN)
7059     {
7060       int i;
7061       static int xy[4][2] =
7062       {
7063         { 0, -1 },
7064         { -1, 0 },
7065         { +1, 0 },
7066         { 0, +1 }
7067       };
7068
7069       for (i = 0; i < NUM_DIRECTIONS; i++)
7070       {
7071         int ex = x + xy[i][0];
7072         int ey = y + xy[i][1];
7073
7074         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7075                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7076                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7077                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7078         {
7079           attr_x = ex;
7080           attr_y = ey;
7081           break;
7082         }
7083       }
7084     }
7085
7086     MovDir[x][y] = MV_NONE;
7087     if (attr_x < x)
7088       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7089     else if (attr_x > x)
7090       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7091     if (attr_y < y)
7092       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7093     else if (attr_y > y)
7094       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7095
7096     if (element == EL_ROBOT)
7097     {
7098       int newx, newy;
7099
7100       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7101         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7102       Moving2Blocked(x, y, &newx, &newy);
7103
7104       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7105         MovDelay[x][y] = 8 + 8 * !RND(3);
7106       else
7107         MovDelay[x][y] = 16;
7108     }
7109     else if (element == EL_PENGUIN)
7110     {
7111       int newx, newy;
7112
7113       MovDelay[x][y] = 1;
7114
7115       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7116       {
7117         boolean first_horiz = RND(2);
7118         int new_move_dir = MovDir[x][y];
7119
7120         MovDir[x][y] =
7121           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7122         Moving2Blocked(x, y, &newx, &newy);
7123
7124         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7125           return;
7126
7127         MovDir[x][y] =
7128           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7129         Moving2Blocked(x, y, &newx, &newy);
7130
7131         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7132           return;
7133
7134         MovDir[x][y] = old_move_dir;
7135         return;
7136       }
7137     }
7138     else if (element == EL_SATELLITE)
7139     {
7140       int newx, newy;
7141
7142       MovDelay[x][y] = 1;
7143
7144       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7145       {
7146         boolean first_horiz = RND(2);
7147         int new_move_dir = MovDir[x][y];
7148
7149         MovDir[x][y] =
7150           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7151         Moving2Blocked(x, y, &newx, &newy);
7152
7153         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7154           return;
7155
7156         MovDir[x][y] =
7157           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7158         Moving2Blocked(x, y, &newx, &newy);
7159
7160         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7161           return;
7162
7163         MovDir[x][y] = old_move_dir;
7164         return;
7165       }
7166     }
7167     else if (element == EL_EMC_ANDROID)
7168     {
7169       static int check_pos[16] =
7170       {
7171         -1,             /*  0 => (invalid)          */
7172         7,              /*  1 => MV_LEFT            */
7173         3,              /*  2 => MV_RIGHT           */
7174         -1,             /*  3 => (invalid)          */
7175         1,              /*  4 =>            MV_UP   */
7176         0,              /*  5 => MV_LEFT  | MV_UP   */
7177         2,              /*  6 => MV_RIGHT | MV_UP   */
7178         -1,             /*  7 => (invalid)          */
7179         5,              /*  8 =>            MV_DOWN */
7180         6,              /*  9 => MV_LEFT  | MV_DOWN */
7181         4,              /* 10 => MV_RIGHT | MV_DOWN */
7182         -1,             /* 11 => (invalid)          */
7183         -1,             /* 12 => (invalid)          */
7184         -1,             /* 13 => (invalid)          */
7185         -1,             /* 14 => (invalid)          */
7186         -1,             /* 15 => (invalid)          */
7187       };
7188       static struct
7189       {
7190         int dx, dy;
7191         int dir;
7192       } check_xy[8] =
7193       {
7194         { -1, -1,       MV_LEFT  | MV_UP   },
7195         {  0, -1,                  MV_UP   },
7196         { +1, -1,       MV_RIGHT | MV_UP   },
7197         { +1,  0,       MV_RIGHT           },
7198         { +1, +1,       MV_RIGHT | MV_DOWN },
7199         {  0, +1,                  MV_DOWN },
7200         { -1, +1,       MV_LEFT  | MV_DOWN },
7201         { -1,  0,       MV_LEFT            },
7202       };
7203       int start_pos, check_order;
7204       boolean can_clone = FALSE;
7205       int i;
7206
7207       /* check if there is any free field around current position */
7208       for (i = 0; i < 8; i++)
7209       {
7210         int newx = x + check_xy[i].dx;
7211         int newy = y + check_xy[i].dy;
7212
7213         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7214         {
7215           can_clone = TRUE;
7216
7217           break;
7218         }
7219       }
7220
7221       if (can_clone)            /* randomly find an element to clone */
7222       {
7223         can_clone = FALSE;
7224
7225         start_pos = check_pos[RND(8)];
7226         check_order = (RND(2) ? -1 : +1);
7227
7228         for (i = 0; i < 8; i++)
7229         {
7230           int pos_raw = start_pos + i * check_order;
7231           int pos = (pos_raw + 8) % 8;
7232           int newx = x + check_xy[pos].dx;
7233           int newy = y + check_xy[pos].dy;
7234
7235           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7236           {
7237             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7238             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7239
7240             Store[x][y] = Feld[newx][newy];
7241
7242             can_clone = TRUE;
7243
7244             break;
7245           }
7246         }
7247       }
7248
7249       if (can_clone)            /* randomly find a direction to move */
7250       {
7251         can_clone = FALSE;
7252
7253         start_pos = check_pos[RND(8)];
7254         check_order = (RND(2) ? -1 : +1);
7255
7256         for (i = 0; i < 8; i++)
7257         {
7258           int pos_raw = start_pos + i * check_order;
7259           int pos = (pos_raw + 8) % 8;
7260           int newx = x + check_xy[pos].dx;
7261           int newy = y + check_xy[pos].dy;
7262           int new_move_dir = check_xy[pos].dir;
7263
7264           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7265           {
7266             MovDir[x][y] = new_move_dir;
7267             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7268
7269             can_clone = TRUE;
7270
7271             break;
7272           }
7273         }
7274       }
7275
7276       if (can_clone)            /* cloning and moving successful */
7277         return;
7278
7279       /* cannot clone -- try to move towards player */
7280
7281       start_pos = check_pos[MovDir[x][y] & 0x0f];
7282       check_order = (RND(2) ? -1 : +1);
7283
7284       for (i = 0; i < 3; i++)
7285       {
7286         /* first check start_pos, then previous/next or (next/previous) pos */
7287         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7288         int pos = (pos_raw + 8) % 8;
7289         int newx = x + check_xy[pos].dx;
7290         int newy = y + check_xy[pos].dy;
7291         int new_move_dir = check_xy[pos].dir;
7292
7293         if (IS_PLAYER(newx, newy))
7294           break;
7295
7296         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7297         {
7298           MovDir[x][y] = new_move_dir;
7299           MovDelay[x][y] = level.android_move_time * 8 + 1;
7300
7301           break;
7302         }
7303       }
7304     }
7305   }
7306   else if (move_pattern == MV_TURNING_LEFT ||
7307            move_pattern == MV_TURNING_RIGHT ||
7308            move_pattern == MV_TURNING_LEFT_RIGHT ||
7309            move_pattern == MV_TURNING_RIGHT_LEFT ||
7310            move_pattern == MV_TURNING_RANDOM ||
7311            move_pattern == MV_ALL_DIRECTIONS)
7312   {
7313     boolean can_turn_left =
7314       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7315     boolean can_turn_right =
7316       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7317
7318     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7319       return;
7320
7321     if (move_pattern == MV_TURNING_LEFT)
7322       MovDir[x][y] = left_dir;
7323     else if (move_pattern == MV_TURNING_RIGHT)
7324       MovDir[x][y] = right_dir;
7325     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7326       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7327     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7328       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7329     else if (move_pattern == MV_TURNING_RANDOM)
7330       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7331                       can_turn_right && !can_turn_left ? right_dir :
7332                       RND(2) ? left_dir : right_dir);
7333     else if (can_turn_left && can_turn_right)
7334       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7335     else if (can_turn_left)
7336       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7337     else if (can_turn_right)
7338       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7339     else
7340       MovDir[x][y] = back_dir;
7341
7342     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7343   }
7344   else if (move_pattern == MV_HORIZONTAL ||
7345            move_pattern == MV_VERTICAL)
7346   {
7347     if (move_pattern & old_move_dir)
7348       MovDir[x][y] = back_dir;
7349     else if (move_pattern == MV_HORIZONTAL)
7350       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7351     else if (move_pattern == MV_VERTICAL)
7352       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7353
7354     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7355   }
7356   else if (move_pattern & MV_ANY_DIRECTION)
7357   {
7358     MovDir[x][y] = move_pattern;
7359     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7360   }
7361   else if (move_pattern & MV_WIND_DIRECTION)
7362   {
7363     MovDir[x][y] = game.wind_direction;
7364     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7365   }
7366   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7367   {
7368     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7369       MovDir[x][y] = left_dir;
7370     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7371       MovDir[x][y] = right_dir;
7372
7373     if (MovDir[x][y] != old_move_dir)
7374       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7375   }
7376   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7377   {
7378     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7379       MovDir[x][y] = right_dir;
7380     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7381       MovDir[x][y] = left_dir;
7382
7383     if (MovDir[x][y] != old_move_dir)
7384       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7385   }
7386   else if (move_pattern == MV_TOWARDS_PLAYER ||
7387            move_pattern == MV_AWAY_FROM_PLAYER)
7388   {
7389     int attr_x = -1, attr_y = -1;
7390     int newx, newy;
7391     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7392
7393     if (AllPlayersGone)
7394     {
7395       attr_x = ExitX;
7396       attr_y = ExitY;
7397     }
7398     else
7399     {
7400       int i;
7401
7402       for (i = 0; i < MAX_PLAYERS; i++)
7403       {
7404         struct PlayerInfo *player = &stored_player[i];
7405         int jx = player->jx, jy = player->jy;
7406
7407         if (!player->active)
7408           continue;
7409
7410         if (attr_x == -1 ||
7411             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7412         {
7413           attr_x = jx;
7414           attr_y = jy;
7415         }
7416       }
7417     }
7418
7419     MovDir[x][y] = MV_NONE;
7420     if (attr_x < x)
7421       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7422     else if (attr_x > x)
7423       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7424     if (attr_y < y)
7425       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7426     else if (attr_y > y)
7427       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7428
7429     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7430
7431     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7432     {
7433       boolean first_horiz = RND(2);
7434       int new_move_dir = MovDir[x][y];
7435
7436       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7437       {
7438         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7439         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7440
7441         return;
7442       }
7443
7444       MovDir[x][y] =
7445         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7446       Moving2Blocked(x, y, &newx, &newy);
7447
7448       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7449         return;
7450
7451       MovDir[x][y] =
7452         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7453       Moving2Blocked(x, y, &newx, &newy);
7454
7455       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7456         return;
7457
7458       MovDir[x][y] = old_move_dir;
7459     }
7460   }
7461   else if (move_pattern == MV_WHEN_PUSHED ||
7462            move_pattern == MV_WHEN_DROPPED)
7463   {
7464     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7465       MovDir[x][y] = MV_NONE;
7466
7467     MovDelay[x][y] = 0;
7468   }
7469   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7470   {
7471     static int test_xy[7][2] =
7472     {
7473       { 0, -1 },
7474       { -1, 0 },
7475       { +1, 0 },
7476       { 0, +1 },
7477       { 0, -1 },
7478       { -1, 0 },
7479       { +1, 0 },
7480     };
7481     static int test_dir[7] =
7482     {
7483       MV_UP,
7484       MV_LEFT,
7485       MV_RIGHT,
7486       MV_DOWN,
7487       MV_UP,
7488       MV_LEFT,
7489       MV_RIGHT,
7490     };
7491     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7492     int move_preference = -1000000;     /* start with very low preference */
7493     int new_move_dir = MV_NONE;
7494     int start_test = RND(4);
7495     int i;
7496
7497     for (i = 0; i < NUM_DIRECTIONS; i++)
7498     {
7499       int move_dir = test_dir[start_test + i];
7500       int move_dir_preference;
7501
7502       xx = x + test_xy[start_test + i][0];
7503       yy = y + test_xy[start_test + i][1];
7504
7505       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7506           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7507       {
7508         new_move_dir = move_dir;
7509
7510         break;
7511       }
7512
7513       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7514         continue;
7515
7516       move_dir_preference = -1 * RunnerVisit[xx][yy];
7517       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7518         move_dir_preference = PlayerVisit[xx][yy];
7519
7520       if (move_dir_preference > move_preference)
7521       {
7522         /* prefer field that has not been visited for the longest time */
7523         move_preference = move_dir_preference;
7524         new_move_dir = move_dir;
7525       }
7526       else if (move_dir_preference == move_preference &&
7527                move_dir == old_move_dir)
7528       {
7529         /* prefer last direction when all directions are preferred equally */
7530         move_preference = move_dir_preference;
7531         new_move_dir = move_dir;
7532       }
7533     }
7534
7535     MovDir[x][y] = new_move_dir;
7536     if (old_move_dir != new_move_dir)
7537       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7538   }
7539 }
7540
7541 static void TurnRound(int x, int y)
7542 {
7543   int direction = MovDir[x][y];
7544
7545   TurnRoundExt(x, y);
7546
7547   GfxDir[x][y] = MovDir[x][y];
7548
7549   if (direction != MovDir[x][y])
7550     GfxFrame[x][y] = 0;
7551
7552   if (MovDelay[x][y])
7553     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7554
7555   ResetGfxFrame(x, y, FALSE);
7556 }
7557
7558 static boolean JustBeingPushed(int x, int y)
7559 {
7560   int i;
7561
7562   for (i = 0; i < MAX_PLAYERS; i++)
7563   {
7564     struct PlayerInfo *player = &stored_player[i];
7565
7566     if (player->active && player->is_pushing && player->MovPos)
7567     {
7568       int next_jx = player->jx + (player->jx - player->last_jx);
7569       int next_jy = player->jy + (player->jy - player->last_jy);
7570
7571       if (x == next_jx && y == next_jy)
7572         return TRUE;
7573     }
7574   }
7575
7576   return FALSE;
7577 }
7578
7579 void StartMoving(int x, int y)
7580 {
7581   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7582   int element = Feld[x][y];
7583
7584   if (Stop[x][y])
7585     return;
7586
7587   if (MovDelay[x][y] == 0)
7588     GfxAction[x][y] = ACTION_DEFAULT;
7589
7590   if (CAN_FALL(element) && y < lev_fieldy - 1)
7591   {
7592     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7593         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7594       if (JustBeingPushed(x, y))
7595         return;
7596
7597     if (element == EL_QUICKSAND_FULL)
7598     {
7599       if (IS_FREE(x, y + 1))
7600       {
7601         InitMovingField(x, y, MV_DOWN);
7602         started_moving = TRUE;
7603
7604         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7605 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7606         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7607           Store[x][y] = EL_ROCK;
7608 #else
7609         Store[x][y] = EL_ROCK;
7610 #endif
7611
7612         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7613       }
7614       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7615       {
7616         if (!MovDelay[x][y])
7617         {
7618           MovDelay[x][y] = TILEY + 1;
7619
7620           ResetGfxAnimation(x, y);
7621           ResetGfxAnimation(x, y + 1);
7622         }
7623
7624         if (MovDelay[x][y])
7625         {
7626           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7627           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7628
7629           MovDelay[x][y]--;
7630           if (MovDelay[x][y])
7631             return;
7632         }
7633
7634         Feld[x][y] = EL_QUICKSAND_EMPTY;
7635         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7636         Store[x][y + 1] = Store[x][y];
7637         Store[x][y] = 0;
7638
7639         PlayLevelSoundAction(x, y, ACTION_FILLING);
7640       }
7641       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7642       {
7643         if (!MovDelay[x][y])
7644         {
7645           MovDelay[x][y] = TILEY + 1;
7646
7647           ResetGfxAnimation(x, y);
7648           ResetGfxAnimation(x, y + 1);
7649         }
7650
7651         if (MovDelay[x][y])
7652         {
7653           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7654           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7655
7656           MovDelay[x][y]--;
7657           if (MovDelay[x][y])
7658             return;
7659         }
7660
7661         Feld[x][y] = EL_QUICKSAND_EMPTY;
7662         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7663         Store[x][y + 1] = Store[x][y];
7664         Store[x][y] = 0;
7665
7666         PlayLevelSoundAction(x, y, ACTION_FILLING);
7667       }
7668     }
7669     else if (element == EL_QUICKSAND_FAST_FULL)
7670     {
7671       if (IS_FREE(x, y + 1))
7672       {
7673         InitMovingField(x, y, MV_DOWN);
7674         started_moving = TRUE;
7675
7676         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7677 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7678         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7679           Store[x][y] = EL_ROCK;
7680 #else
7681         Store[x][y] = EL_ROCK;
7682 #endif
7683
7684         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7685       }
7686       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7687       {
7688         if (!MovDelay[x][y])
7689         {
7690           MovDelay[x][y] = TILEY + 1;
7691
7692           ResetGfxAnimation(x, y);
7693           ResetGfxAnimation(x, y + 1);
7694         }
7695
7696         if (MovDelay[x][y])
7697         {
7698           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7699           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7700
7701           MovDelay[x][y]--;
7702           if (MovDelay[x][y])
7703             return;
7704         }
7705
7706         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7707         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7708         Store[x][y + 1] = Store[x][y];
7709         Store[x][y] = 0;
7710
7711         PlayLevelSoundAction(x, y, ACTION_FILLING);
7712       }
7713       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7714       {
7715         if (!MovDelay[x][y])
7716         {
7717           MovDelay[x][y] = TILEY + 1;
7718
7719           ResetGfxAnimation(x, y);
7720           ResetGfxAnimation(x, y + 1);
7721         }
7722
7723         if (MovDelay[x][y])
7724         {
7725           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7726           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7727
7728           MovDelay[x][y]--;
7729           if (MovDelay[x][y])
7730             return;
7731         }
7732
7733         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7734         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7735         Store[x][y + 1] = Store[x][y];
7736         Store[x][y] = 0;
7737
7738         PlayLevelSoundAction(x, y, ACTION_FILLING);
7739       }
7740     }
7741     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7742              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7743     {
7744       InitMovingField(x, y, MV_DOWN);
7745       started_moving = TRUE;
7746
7747       Feld[x][y] = EL_QUICKSAND_FILLING;
7748       Store[x][y] = element;
7749
7750       PlayLevelSoundAction(x, y, ACTION_FILLING);
7751     }
7752     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7753              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7754     {
7755       InitMovingField(x, y, MV_DOWN);
7756       started_moving = TRUE;
7757
7758       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7759       Store[x][y] = element;
7760
7761       PlayLevelSoundAction(x, y, ACTION_FILLING);
7762     }
7763     else if (element == EL_MAGIC_WALL_FULL)
7764     {
7765       if (IS_FREE(x, y + 1))
7766       {
7767         InitMovingField(x, y, MV_DOWN);
7768         started_moving = TRUE;
7769
7770         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7771         Store[x][y] = EL_CHANGED(Store[x][y]);
7772       }
7773       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7774       {
7775         if (!MovDelay[x][y])
7776           MovDelay[x][y] = TILEY/4 + 1;
7777
7778         if (MovDelay[x][y])
7779         {
7780           MovDelay[x][y]--;
7781           if (MovDelay[x][y])
7782             return;
7783         }
7784
7785         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7786         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7787         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7788         Store[x][y] = 0;
7789       }
7790     }
7791     else if (element == EL_BD_MAGIC_WALL_FULL)
7792     {
7793       if (IS_FREE(x, y + 1))
7794       {
7795         InitMovingField(x, y, MV_DOWN);
7796         started_moving = TRUE;
7797
7798         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7799         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7800       }
7801       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7802       {
7803         if (!MovDelay[x][y])
7804           MovDelay[x][y] = TILEY/4 + 1;
7805
7806         if (MovDelay[x][y])
7807         {
7808           MovDelay[x][y]--;
7809           if (MovDelay[x][y])
7810             return;
7811         }
7812
7813         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7814         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7815         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7816         Store[x][y] = 0;
7817       }
7818     }
7819     else if (element == EL_DC_MAGIC_WALL_FULL)
7820     {
7821       if (IS_FREE(x, y + 1))
7822       {
7823         InitMovingField(x, y, MV_DOWN);
7824         started_moving = TRUE;
7825
7826         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7827         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7828       }
7829       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7830       {
7831         if (!MovDelay[x][y])
7832           MovDelay[x][y] = TILEY/4 + 1;
7833
7834         if (MovDelay[x][y])
7835         {
7836           MovDelay[x][y]--;
7837           if (MovDelay[x][y])
7838             return;
7839         }
7840
7841         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7842         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7843         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7844         Store[x][y] = 0;
7845       }
7846     }
7847     else if ((CAN_PASS_MAGIC_WALL(element) &&
7848               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7849                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7850              (CAN_PASS_DC_MAGIC_WALL(element) &&
7851               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7852
7853     {
7854       InitMovingField(x, y, MV_DOWN);
7855       started_moving = TRUE;
7856
7857       Feld[x][y] =
7858         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7859          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7860          EL_DC_MAGIC_WALL_FILLING);
7861       Store[x][y] = element;
7862     }
7863     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7864     {
7865       SplashAcid(x, y + 1);
7866
7867       InitMovingField(x, y, MV_DOWN);
7868       started_moving = TRUE;
7869
7870       Store[x][y] = EL_ACID;
7871     }
7872     else if (
7873 #if USE_FIX_IMPACT_COLLISION
7874              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7875               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7876 #else
7877              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7878               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7879 #endif
7880              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7881               CAN_FALL(element) && WasJustFalling[x][y] &&
7882               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7883
7884              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7885               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7886               (Feld[x][y + 1] == EL_BLOCKED)))
7887     {
7888       /* this is needed for a special case not covered by calling "Impact()"
7889          from "ContinueMoving()": if an element moves to a tile directly below
7890          another element which was just falling on that tile (which was empty
7891          in the previous frame), the falling element above would just stop
7892          instead of smashing the element below (in previous version, the above
7893          element was just checked for "moving" instead of "falling", resulting
7894          in incorrect smashes caused by horizontal movement of the above
7895          element; also, the case of the player being the element to smash was
7896          simply not covered here... :-/ ) */
7897
7898       CheckCollision[x][y] = 0;
7899       CheckImpact[x][y] = 0;
7900
7901       Impact(x, y);
7902     }
7903     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7904     {
7905       if (MovDir[x][y] == MV_NONE)
7906       {
7907         InitMovingField(x, y, MV_DOWN);
7908         started_moving = TRUE;
7909       }
7910     }
7911     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7912     {
7913       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7914         MovDir[x][y] = MV_DOWN;
7915
7916       InitMovingField(x, y, MV_DOWN);
7917       started_moving = TRUE;
7918     }
7919     else if (element == EL_AMOEBA_DROP)
7920     {
7921       Feld[x][y] = EL_AMOEBA_GROWING;
7922       Store[x][y] = EL_AMOEBA_WET;
7923     }
7924     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7925               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7926              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7927              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7928     {
7929       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7930                                 (IS_FREE(x - 1, y + 1) ||
7931                                  Feld[x - 1][y + 1] == EL_ACID));
7932       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7933                                 (IS_FREE(x + 1, y + 1) ||
7934                                  Feld[x + 1][y + 1] == EL_ACID));
7935       boolean can_fall_any  = (can_fall_left || can_fall_right);
7936       boolean can_fall_both = (can_fall_left && can_fall_right);
7937       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7938
7939 #if USE_NEW_ALL_SLIPPERY
7940       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7941       {
7942         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7943           can_fall_right = FALSE;
7944         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7945           can_fall_left = FALSE;
7946         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7947           can_fall_right = FALSE;
7948         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7949           can_fall_left = FALSE;
7950
7951         can_fall_any  = (can_fall_left || can_fall_right);
7952         can_fall_both = FALSE;
7953       }
7954 #else
7955       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7956       {
7957         if (slippery_type == SLIPPERY_ONLY_LEFT)
7958           can_fall_right = FALSE;
7959         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7960           can_fall_left = FALSE;
7961         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7962           can_fall_right = FALSE;
7963         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7964           can_fall_left = FALSE;
7965
7966         can_fall_any  = (can_fall_left || can_fall_right);
7967         can_fall_both = (can_fall_left && can_fall_right);
7968       }
7969 #endif
7970
7971 #if USE_NEW_ALL_SLIPPERY
7972 #else
7973 #if USE_NEW_SP_SLIPPERY
7974       /* !!! better use the same properties as for custom elements here !!! */
7975       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7976                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7977       {
7978         can_fall_right = FALSE;         /* slip down on left side */
7979         can_fall_both = FALSE;
7980       }
7981 #endif
7982 #endif
7983
7984 #if USE_NEW_ALL_SLIPPERY
7985       if (can_fall_both)
7986       {
7987         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7988           can_fall_right = FALSE;       /* slip down on left side */
7989         else
7990           can_fall_left = !(can_fall_right = RND(2));
7991
7992         can_fall_both = FALSE;
7993       }
7994 #else
7995       if (can_fall_both)
7996       {
7997         if (game.emulation == EMU_BOULDERDASH ||
7998             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7999           can_fall_right = FALSE;       /* slip down on left side */
8000         else
8001           can_fall_left = !(can_fall_right = RND(2));
8002
8003         can_fall_both = FALSE;
8004       }
8005 #endif
8006
8007       if (can_fall_any)
8008       {
8009         /* if not determined otherwise, prefer left side for slipping down */
8010         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8011         started_moving = TRUE;
8012       }
8013     }
8014 #if 0
8015     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8016 #else
8017     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8018 #endif
8019     {
8020       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8021       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8022       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8023       int belt_dir = game.belt_dir[belt_nr];
8024
8025       if ((belt_dir == MV_LEFT  && left_is_free) ||
8026           (belt_dir == MV_RIGHT && right_is_free))
8027       {
8028         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8029
8030         InitMovingField(x, y, belt_dir);
8031         started_moving = TRUE;
8032
8033         Pushed[x][y] = TRUE;
8034         Pushed[nextx][y] = TRUE;
8035
8036         GfxAction[x][y] = ACTION_DEFAULT;
8037       }
8038       else
8039       {
8040         MovDir[x][y] = 0;       /* if element was moving, stop it */
8041       }
8042     }
8043   }
8044
8045   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8046 #if 0
8047   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8048 #else
8049   if (CAN_MOVE(element) && !started_moving)
8050 #endif
8051   {
8052     int move_pattern = element_info[element].move_pattern;
8053     int newx, newy;
8054
8055 #if 0
8056 #if DEBUG
8057     if (MovDir[x][y] == MV_NONE)
8058     {
8059       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8060              x, y, element, element_info[element].token_name);
8061       printf("StartMoving(): This should never happen!\n");
8062     }
8063 #endif
8064 #endif
8065
8066     Moving2Blocked(x, y, &newx, &newy);
8067
8068     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8069       return;
8070
8071     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8072         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8073     {
8074       WasJustMoving[x][y] = 0;
8075       CheckCollision[x][y] = 0;
8076
8077       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8078
8079       if (Feld[x][y] != element)        /* element has changed */
8080         return;
8081     }
8082
8083     if (!MovDelay[x][y])        /* start new movement phase */
8084     {
8085       /* all objects that can change their move direction after each step
8086          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8087
8088       if (element != EL_YAMYAM &&
8089           element != EL_DARK_YAMYAM &&
8090           element != EL_PACMAN &&
8091           !(move_pattern & MV_ANY_DIRECTION) &&
8092           move_pattern != MV_TURNING_LEFT &&
8093           move_pattern != MV_TURNING_RIGHT &&
8094           move_pattern != MV_TURNING_LEFT_RIGHT &&
8095           move_pattern != MV_TURNING_RIGHT_LEFT &&
8096           move_pattern != MV_TURNING_RANDOM)
8097       {
8098         TurnRound(x, y);
8099
8100         if (MovDelay[x][y] && (element == EL_BUG ||
8101                                element == EL_SPACESHIP ||
8102                                element == EL_SP_SNIKSNAK ||
8103                                element == EL_SP_ELECTRON ||
8104                                element == EL_MOLE))
8105           DrawLevelField(x, y);
8106       }
8107     }
8108
8109     if (MovDelay[x][y])         /* wait some time before next movement */
8110     {
8111       MovDelay[x][y]--;
8112
8113       if (element == EL_ROBOT ||
8114           element == EL_YAMYAM ||
8115           element == EL_DARK_YAMYAM)
8116       {
8117         DrawLevelElementAnimationIfNeeded(x, y, element);
8118         PlayLevelSoundAction(x, y, ACTION_WAITING);
8119       }
8120       else if (element == EL_SP_ELECTRON)
8121         DrawLevelElementAnimationIfNeeded(x, y, element);
8122       else if (element == EL_DRAGON)
8123       {
8124         int i;
8125         int dir = MovDir[x][y];
8126         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8127         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8128         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8129                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8130                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8131                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8132         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8133
8134         GfxAction[x][y] = ACTION_ATTACKING;
8135
8136         if (IS_PLAYER(x, y))
8137           DrawPlayerField(x, y);
8138         else
8139           DrawLevelField(x, y);
8140
8141         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8142
8143         for (i = 1; i <= 3; i++)
8144         {
8145           int xx = x + i * dx;
8146           int yy = y + i * dy;
8147           int sx = SCREENX(xx);
8148           int sy = SCREENY(yy);
8149           int flame_graphic = graphic + (i - 1);
8150
8151           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8152             break;
8153
8154           if (MovDelay[x][y])
8155           {
8156             int flamed = MovingOrBlocked2Element(xx, yy);
8157
8158             /* !!! */
8159 #if 0
8160             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8161               Bang(xx, yy);
8162             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8163               RemoveMovingField(xx, yy);
8164             else
8165               RemoveField(xx, yy);
8166 #else
8167             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8168               Bang(xx, yy);
8169             else
8170               RemoveMovingField(xx, yy);
8171 #endif
8172
8173             ChangeDelay[xx][yy] = 0;
8174
8175             Feld[xx][yy] = EL_FLAMES;
8176
8177             if (IN_SCR_FIELD(sx, sy))
8178             {
8179               DrawLevelFieldCrumbledSand(xx, yy);
8180               DrawGraphic(sx, sy, flame_graphic, frame);
8181             }
8182           }
8183           else
8184           {
8185             if (Feld[xx][yy] == EL_FLAMES)
8186               Feld[xx][yy] = EL_EMPTY;
8187             DrawLevelField(xx, yy);
8188           }
8189         }
8190       }
8191
8192       if (MovDelay[x][y])       /* element still has to wait some time */
8193       {
8194         PlayLevelSoundAction(x, y, ACTION_WAITING);
8195
8196         return;
8197       }
8198     }
8199
8200     /* now make next step */
8201
8202     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8203
8204     if (DONT_COLLIDE_WITH(element) &&
8205         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8206         !PLAYER_ENEMY_PROTECTED(newx, newy))
8207     {
8208       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8209
8210       return;
8211     }
8212
8213     else if (CAN_MOVE_INTO_ACID(element) &&
8214              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8215              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8216              (MovDir[x][y] == MV_DOWN ||
8217               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8218     {
8219       SplashAcid(newx, newy);
8220       Store[x][y] = EL_ACID;
8221     }
8222     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8223     {
8224       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8225           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8226           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8227           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8228       {
8229         RemoveField(x, y);
8230         DrawLevelField(x, y);
8231
8232         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8233         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8234           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8235
8236         local_player->friends_still_needed--;
8237         if (!local_player->friends_still_needed &&
8238             !local_player->GameOver && AllPlayersGone)
8239           PlayerWins(local_player);
8240
8241         return;
8242       }
8243       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8244       {
8245         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8246           DrawLevelField(newx, newy);
8247         else
8248           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8249       }
8250       else if (!IS_FREE(newx, newy))
8251       {
8252         GfxAction[x][y] = ACTION_WAITING;
8253
8254         if (IS_PLAYER(x, y))
8255           DrawPlayerField(x, y);
8256         else
8257           DrawLevelField(x, y);
8258
8259         return;
8260       }
8261     }
8262     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8263     {
8264       if (IS_FOOD_PIG(Feld[newx][newy]))
8265       {
8266         if (IS_MOVING(newx, newy))
8267           RemoveMovingField(newx, newy);
8268         else
8269         {
8270           Feld[newx][newy] = EL_EMPTY;
8271           DrawLevelField(newx, newy);
8272         }
8273
8274         PlayLevelSound(x, y, SND_PIG_DIGGING);
8275       }
8276       else if (!IS_FREE(newx, newy))
8277       {
8278         if (IS_PLAYER(x, y))
8279           DrawPlayerField(x, y);
8280         else
8281           DrawLevelField(x, y);
8282
8283         return;
8284       }
8285     }
8286     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8287     {
8288       if (Store[x][y] != EL_EMPTY)
8289       {
8290         boolean can_clone = FALSE;
8291         int xx, yy;
8292
8293         /* check if element to clone is still there */
8294         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8295         {
8296           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8297           {
8298             can_clone = TRUE;
8299
8300             break;
8301           }
8302         }
8303
8304         /* cannot clone or target field not free anymore -- do not clone */
8305         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8306           Store[x][y] = EL_EMPTY;
8307       }
8308
8309       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8310       {
8311         if (IS_MV_DIAGONAL(MovDir[x][y]))
8312         {
8313           int diagonal_move_dir = MovDir[x][y];
8314           int stored = Store[x][y];
8315           int change_delay = 8;
8316           int graphic;
8317
8318           /* android is moving diagonally */
8319
8320           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8321
8322           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8323           GfxElement[x][y] = EL_EMC_ANDROID;
8324           GfxAction[x][y] = ACTION_SHRINKING;
8325           GfxDir[x][y] = diagonal_move_dir;
8326           ChangeDelay[x][y] = change_delay;
8327
8328           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8329                                    GfxDir[x][y]);
8330
8331           DrawLevelGraphicAnimation(x, y, graphic);
8332           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8333
8334           if (Feld[newx][newy] == EL_ACID)
8335           {
8336             SplashAcid(newx, newy);
8337
8338             return;
8339           }
8340
8341           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8342
8343           Store[newx][newy] = EL_EMC_ANDROID;
8344           GfxElement[newx][newy] = EL_EMC_ANDROID;
8345           GfxAction[newx][newy] = ACTION_GROWING;
8346           GfxDir[newx][newy] = diagonal_move_dir;
8347           ChangeDelay[newx][newy] = change_delay;
8348
8349           graphic = el_act_dir2img(GfxElement[newx][newy],
8350                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8351
8352           DrawLevelGraphicAnimation(newx, newy, graphic);
8353           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8354
8355           return;
8356         }
8357         else
8358         {
8359           Feld[newx][newy] = EL_EMPTY;
8360           DrawLevelField(newx, newy);
8361
8362           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8363         }
8364       }
8365       else if (!IS_FREE(newx, newy))
8366       {
8367 #if 0
8368         if (IS_PLAYER(x, y))
8369           DrawPlayerField(x, y);
8370         else
8371           DrawLevelField(x, y);
8372 #endif
8373
8374         return;
8375       }
8376     }
8377     else if (IS_CUSTOM_ELEMENT(element) &&
8378              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8379     {
8380       int new_element = Feld[newx][newy];
8381
8382       if (!IS_FREE(newx, newy))
8383       {
8384         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8385                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8386                       ACTION_BREAKING);
8387
8388         /* no element can dig solid indestructible elements */
8389         if (IS_INDESTRUCTIBLE(new_element) &&
8390             !IS_DIGGABLE(new_element) &&
8391             !IS_COLLECTIBLE(new_element))
8392           return;
8393
8394         if (AmoebaNr[newx][newy] &&
8395             (new_element == EL_AMOEBA_FULL ||
8396              new_element == EL_BD_AMOEBA ||
8397              new_element == EL_AMOEBA_GROWING))
8398         {
8399           AmoebaCnt[AmoebaNr[newx][newy]]--;
8400           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8401         }
8402
8403         if (IS_MOVING(newx, newy))
8404           RemoveMovingField(newx, newy);
8405         else
8406         {
8407           RemoveField(newx, newy);
8408           DrawLevelField(newx, newy);
8409         }
8410
8411         /* if digged element was about to explode, prevent the explosion */
8412         ExplodeField[newx][newy] = EX_TYPE_NONE;
8413
8414         PlayLevelSoundAction(x, y, action);
8415       }
8416
8417       Store[newx][newy] = EL_EMPTY;
8418 #if 1
8419       /* this makes it possible to leave the removed element again */
8420       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8421         Store[newx][newy] = new_element;
8422 #else
8423       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8424       {
8425         int move_leave_element = element_info[element].move_leave_element;
8426
8427         /* this makes it possible to leave the removed element again */
8428         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8429                              new_element : move_leave_element);
8430       }
8431 #endif
8432
8433       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8434       {
8435         RunnerVisit[x][y] = FrameCounter;
8436         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8437       }
8438     }
8439     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8440     {
8441       if (!IS_FREE(newx, newy))
8442       {
8443         if (IS_PLAYER(x, y))
8444           DrawPlayerField(x, y);
8445         else
8446           DrawLevelField(x, y);
8447
8448         return;
8449       }
8450       else
8451       {
8452         boolean wanna_flame = !RND(10);
8453         int dx = newx - x, dy = newy - y;
8454         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8455         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8456         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8457                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8458         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8459                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8460
8461         if ((wanna_flame ||
8462              IS_CLASSIC_ENEMY(element1) ||
8463              IS_CLASSIC_ENEMY(element2)) &&
8464             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8465             element1 != EL_FLAMES && element2 != EL_FLAMES)
8466         {
8467           ResetGfxAnimation(x, y);
8468           GfxAction[x][y] = ACTION_ATTACKING;
8469
8470           if (IS_PLAYER(x, y))
8471             DrawPlayerField(x, y);
8472           else
8473             DrawLevelField(x, y);
8474
8475           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8476
8477           MovDelay[x][y] = 50;
8478
8479           /* !!! */
8480 #if 0
8481           RemoveField(newx, newy);
8482 #endif
8483           Feld[newx][newy] = EL_FLAMES;
8484           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8485           {
8486 #if 0
8487             RemoveField(newx1, newy1);
8488 #endif
8489             Feld[newx1][newy1] = EL_FLAMES;
8490           }
8491           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8492           {
8493 #if 0
8494             RemoveField(newx2, newy2);
8495 #endif
8496             Feld[newx2][newy2] = EL_FLAMES;
8497           }
8498
8499           return;
8500         }
8501       }
8502     }
8503     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8504              Feld[newx][newy] == EL_DIAMOND)
8505     {
8506       if (IS_MOVING(newx, newy))
8507         RemoveMovingField(newx, newy);
8508       else
8509       {
8510         Feld[newx][newy] = EL_EMPTY;
8511         DrawLevelField(newx, newy);
8512       }
8513
8514       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8515     }
8516     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8517              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8518     {
8519       if (AmoebaNr[newx][newy])
8520       {
8521         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8522         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8523             Feld[newx][newy] == EL_BD_AMOEBA)
8524           AmoebaCnt[AmoebaNr[newx][newy]]--;
8525       }
8526
8527 #if 0
8528       /* !!! test !!! */
8529       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8530       {
8531         RemoveMovingField(newx, newy);
8532       }
8533 #else
8534       if (IS_MOVING(newx, newy))
8535       {
8536         RemoveMovingField(newx, newy);
8537       }
8538 #endif
8539       else
8540       {
8541         Feld[newx][newy] = EL_EMPTY;
8542         DrawLevelField(newx, newy);
8543       }
8544
8545       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8546     }
8547     else if ((element == EL_PACMAN || element == EL_MOLE)
8548              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8549     {
8550       if (AmoebaNr[newx][newy])
8551       {
8552         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8553         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8554             Feld[newx][newy] == EL_BD_AMOEBA)
8555           AmoebaCnt[AmoebaNr[newx][newy]]--;
8556       }
8557
8558       if (element == EL_MOLE)
8559       {
8560         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8561         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8562
8563         ResetGfxAnimation(x, y);
8564         GfxAction[x][y] = ACTION_DIGGING;
8565         DrawLevelField(x, y);
8566
8567         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8568
8569         return;                         /* wait for shrinking amoeba */
8570       }
8571       else      /* element == EL_PACMAN */
8572       {
8573         Feld[newx][newy] = EL_EMPTY;
8574         DrawLevelField(newx, newy);
8575         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8576       }
8577     }
8578     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8579              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8580               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8581     {
8582       /* wait for shrinking amoeba to completely disappear */
8583       return;
8584     }
8585     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8586     {
8587       /* object was running against a wall */
8588
8589       TurnRound(x, y);
8590
8591 #if 0
8592       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8593       if (move_pattern & MV_ANY_DIRECTION &&
8594           move_pattern == MovDir[x][y])
8595       {
8596         int blocking_element =
8597           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8598
8599         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8600                                  MovDir[x][y]);
8601
8602         element = Feld[x][y];   /* element might have changed */
8603       }
8604 #endif
8605
8606       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8607         DrawLevelElementAnimation(x, y, element);
8608
8609       if (DONT_TOUCH(element))
8610         TestIfBadThingTouchesPlayer(x, y);
8611
8612       return;
8613     }
8614
8615     InitMovingField(x, y, MovDir[x][y]);
8616
8617     PlayLevelSoundAction(x, y, ACTION_MOVING);
8618   }
8619
8620   if (MovDir[x][y])
8621     ContinueMoving(x, y);
8622 }
8623
8624 void ContinueMoving(int x, int y)
8625 {
8626   int element = Feld[x][y];
8627   struct ElementInfo *ei = &element_info[element];
8628   int direction = MovDir[x][y];
8629   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8630   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8631   int newx = x + dx, newy = y + dy;
8632   int stored = Store[x][y];
8633   int stored_new = Store[newx][newy];
8634   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8635   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8636   boolean last_line = (newy == lev_fieldy - 1);
8637
8638   MovPos[x][y] += getElementMoveStepsize(x, y);
8639
8640   if (pushed_by_player) /* special case: moving object pushed by player */
8641     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8642
8643   if (ABS(MovPos[x][y]) < TILEX)
8644   {
8645 #if 0
8646     int ee = Feld[x][y];
8647     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8648     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8649
8650     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8651            x, y, ABS(MovPos[x][y]),
8652            ee, gg, ff,
8653            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8654 #endif
8655
8656     DrawLevelField(x, y);
8657
8658     return;     /* element is still moving */
8659   }
8660
8661   /* element reached destination field */
8662
8663   Feld[x][y] = EL_EMPTY;
8664   Feld[newx][newy] = element;
8665   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8666
8667   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8668   {
8669     element = Feld[newx][newy] = EL_ACID;
8670   }
8671   else if (element == EL_MOLE)
8672   {
8673     Feld[x][y] = EL_SAND;
8674
8675     DrawLevelFieldCrumbledSandNeighbours(x, y);
8676   }
8677   else if (element == EL_QUICKSAND_FILLING)
8678   {
8679     element = Feld[newx][newy] = get_next_element(element);
8680     Store[newx][newy] = Store[x][y];
8681   }
8682   else if (element == EL_QUICKSAND_EMPTYING)
8683   {
8684     Feld[x][y] = get_next_element(element);
8685     element = Feld[newx][newy] = Store[x][y];
8686   }
8687   else if (element == EL_QUICKSAND_FAST_FILLING)
8688   {
8689     element = Feld[newx][newy] = get_next_element(element);
8690     Store[newx][newy] = Store[x][y];
8691   }
8692   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8693   {
8694     Feld[x][y] = get_next_element(element);
8695     element = Feld[newx][newy] = Store[x][y];
8696   }
8697   else if (element == EL_MAGIC_WALL_FILLING)
8698   {
8699     element = Feld[newx][newy] = get_next_element(element);
8700     if (!game.magic_wall_active)
8701       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8702     Store[newx][newy] = Store[x][y];
8703   }
8704   else if (element == EL_MAGIC_WALL_EMPTYING)
8705   {
8706     Feld[x][y] = get_next_element(element);
8707     if (!game.magic_wall_active)
8708       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8709     element = Feld[newx][newy] = Store[x][y];
8710
8711 #if USE_NEW_CUSTOM_VALUE
8712     InitField(newx, newy, FALSE);
8713 #endif
8714   }
8715   else if (element == EL_BD_MAGIC_WALL_FILLING)
8716   {
8717     element = Feld[newx][newy] = get_next_element(element);
8718     if (!game.magic_wall_active)
8719       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8720     Store[newx][newy] = Store[x][y];
8721   }
8722   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8723   {
8724     Feld[x][y] = get_next_element(element);
8725     if (!game.magic_wall_active)
8726       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8727     element = Feld[newx][newy] = Store[x][y];
8728
8729 #if USE_NEW_CUSTOM_VALUE
8730     InitField(newx, newy, FALSE);
8731 #endif
8732   }
8733   else if (element == EL_DC_MAGIC_WALL_FILLING)
8734   {
8735     element = Feld[newx][newy] = get_next_element(element);
8736     if (!game.magic_wall_active)
8737       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8738     Store[newx][newy] = Store[x][y];
8739   }
8740   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8741   {
8742     Feld[x][y] = get_next_element(element);
8743     if (!game.magic_wall_active)
8744       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8745     element = Feld[newx][newy] = Store[x][y];
8746
8747 #if USE_NEW_CUSTOM_VALUE
8748     InitField(newx, newy, FALSE);
8749 #endif
8750   }
8751   else if (element == EL_AMOEBA_DROPPING)
8752   {
8753     Feld[x][y] = get_next_element(element);
8754     element = Feld[newx][newy] = Store[x][y];
8755   }
8756   else if (element == EL_SOKOBAN_OBJECT)
8757   {
8758     if (Back[x][y])
8759       Feld[x][y] = Back[x][y];
8760
8761     if (Back[newx][newy])
8762       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8763
8764     Back[x][y] = Back[newx][newy] = 0;
8765   }
8766
8767   Store[x][y] = EL_EMPTY;
8768   MovPos[x][y] = 0;
8769   MovDir[x][y] = 0;
8770   MovDelay[x][y] = 0;
8771
8772   MovDelay[newx][newy] = 0;
8773
8774   if (CAN_CHANGE_OR_HAS_ACTION(element))
8775   {
8776     /* copy element change control values to new field */
8777     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8778     ChangePage[newx][newy]  = ChangePage[x][y];
8779     ChangeCount[newx][newy] = ChangeCount[x][y];
8780     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8781   }
8782
8783 #if USE_NEW_CUSTOM_VALUE
8784   CustomValue[newx][newy] = CustomValue[x][y];
8785 #endif
8786
8787   ChangeDelay[x][y] = 0;
8788   ChangePage[x][y] = -1;
8789   ChangeCount[x][y] = 0;
8790   ChangeEvent[x][y] = -1;
8791
8792 #if USE_NEW_CUSTOM_VALUE
8793   CustomValue[x][y] = 0;
8794 #endif
8795
8796   /* copy animation control values to new field */
8797   GfxFrame[newx][newy]  = GfxFrame[x][y];
8798   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8799   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8800   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8801
8802   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8803
8804   /* some elements can leave other elements behind after moving */
8805 #if 1
8806   if (ei->move_leave_element != EL_EMPTY &&
8807       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8808       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8809 #else
8810   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8811       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8812       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8813 #endif
8814   {
8815     int move_leave_element = ei->move_leave_element;
8816
8817 #if 1
8818 #if 1
8819     /* this makes it possible to leave the removed element again */
8820     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8821       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8822 #else
8823     /* this makes it possible to leave the removed element again */
8824     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8825       move_leave_element = stored;
8826 #endif
8827 #else
8828     /* this makes it possible to leave the removed element again */
8829     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8830         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8831       move_leave_element = stored;
8832 #endif
8833
8834     Feld[x][y] = move_leave_element;
8835
8836     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8837       MovDir[x][y] = direction;
8838
8839     InitField(x, y, FALSE);
8840
8841     if (GFX_CRUMBLED(Feld[x][y]))
8842       DrawLevelFieldCrumbledSandNeighbours(x, y);
8843
8844     if (ELEM_IS_PLAYER(move_leave_element))
8845       RelocatePlayer(x, y, move_leave_element);
8846   }
8847
8848   /* do this after checking for left-behind element */
8849   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8850
8851   if (!CAN_MOVE(element) ||
8852       (CAN_FALL(element) && direction == MV_DOWN &&
8853        (element == EL_SPRING ||
8854         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8855         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8856     GfxDir[x][y] = MovDir[newx][newy] = 0;
8857
8858   DrawLevelField(x, y);
8859   DrawLevelField(newx, newy);
8860
8861   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8862
8863   /* prevent pushed element from moving on in pushed direction */
8864   if (pushed_by_player && CAN_MOVE(element) &&
8865       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8866       !(element_info[element].move_pattern & direction))
8867     TurnRound(newx, newy);
8868
8869   /* prevent elements on conveyor belt from moving on in last direction */
8870   if (pushed_by_conveyor && CAN_FALL(element) &&
8871       direction & MV_HORIZONTAL)
8872     MovDir[newx][newy] = 0;
8873
8874   if (!pushed_by_player)
8875   {
8876     int nextx = newx + dx, nexty = newy + dy;
8877     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8878
8879     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8880
8881     if (CAN_FALL(element) && direction == MV_DOWN)
8882       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8883
8884     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8885       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8886
8887 #if USE_FIX_IMPACT_COLLISION
8888     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8889       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8890 #endif
8891   }
8892
8893   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8894   {
8895     TestIfBadThingTouchesPlayer(newx, newy);
8896     TestIfBadThingTouchesFriend(newx, newy);
8897
8898     if (!IS_CUSTOM_ELEMENT(element))
8899       TestIfBadThingTouchesOtherBadThing(newx, newy);
8900   }
8901   else if (element == EL_PENGUIN)
8902     TestIfFriendTouchesBadThing(newx, newy);
8903
8904   /* give the player one last chance (one more frame) to move away */
8905   if (CAN_FALL(element) && direction == MV_DOWN &&
8906       (last_line || (!IS_FREE(x, newy + 1) &&
8907                      (!IS_PLAYER(x, newy + 1) ||
8908                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8909     Impact(x, newy);
8910
8911   if (pushed_by_player && !game.use_change_when_pushing_bug)
8912   {
8913     int push_side = MV_DIR_OPPOSITE(direction);
8914     struct PlayerInfo *player = PLAYERINFO(x, y);
8915
8916     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8917                                player->index_bit, push_side);
8918     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8919                                         player->index_bit, push_side);
8920   }
8921
8922   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8923     MovDelay[newx][newy] = 1;
8924
8925   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8926
8927   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8928
8929 #if 0
8930   if (ChangePage[newx][newy] != -1)             /* delayed change */
8931   {
8932     int page = ChangePage[newx][newy];
8933     struct ElementChangeInfo *change = &ei->change_page[page];
8934
8935     ChangePage[newx][newy] = -1;
8936
8937     if (change->can_change)
8938     {
8939       if (ChangeElement(newx, newy, element, page))
8940       {
8941         if (change->post_change_function)
8942           change->post_change_function(newx, newy);
8943       }
8944     }
8945
8946     if (change->has_action)
8947       ExecuteCustomElementAction(newx, newy, element, page);
8948   }
8949 #endif
8950
8951   TestIfElementHitsCustomElement(newx, newy, direction);
8952   TestIfPlayerTouchesCustomElement(newx, newy);
8953   TestIfElementTouchesCustomElement(newx, newy);
8954
8955   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8956       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8957     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8958                              MV_DIR_OPPOSITE(direction));
8959 }
8960
8961 int AmoebeNachbarNr(int ax, int ay)
8962 {
8963   int i;
8964   int element = Feld[ax][ay];
8965   int group_nr = 0;
8966   static int xy[4][2] =
8967   {
8968     { 0, -1 },
8969     { -1, 0 },
8970     { +1, 0 },
8971     { 0, +1 }
8972   };
8973
8974   for (i = 0; i < NUM_DIRECTIONS; i++)
8975   {
8976     int x = ax + xy[i][0];
8977     int y = ay + xy[i][1];
8978
8979     if (!IN_LEV_FIELD(x, y))
8980       continue;
8981
8982     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8983       group_nr = AmoebaNr[x][y];
8984   }
8985
8986   return group_nr;
8987 }
8988
8989 void AmoebenVereinigen(int ax, int ay)
8990 {
8991   int i, x, y, xx, yy;
8992   int new_group_nr = AmoebaNr[ax][ay];
8993   static int xy[4][2] =
8994   {
8995     { 0, -1 },
8996     { -1, 0 },
8997     { +1, 0 },
8998     { 0, +1 }
8999   };
9000
9001   if (new_group_nr == 0)
9002     return;
9003
9004   for (i = 0; i < NUM_DIRECTIONS; i++)
9005   {
9006     x = ax + xy[i][0];
9007     y = ay + xy[i][1];
9008
9009     if (!IN_LEV_FIELD(x, y))
9010       continue;
9011
9012     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9013          Feld[x][y] == EL_BD_AMOEBA ||
9014          Feld[x][y] == EL_AMOEBA_DEAD) &&
9015         AmoebaNr[x][y] != new_group_nr)
9016     {
9017       int old_group_nr = AmoebaNr[x][y];
9018
9019       if (old_group_nr == 0)
9020         return;
9021
9022       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9023       AmoebaCnt[old_group_nr] = 0;
9024       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9025       AmoebaCnt2[old_group_nr] = 0;
9026
9027       SCAN_PLAYFIELD(xx, yy)
9028       {
9029         if (AmoebaNr[xx][yy] == old_group_nr)
9030           AmoebaNr[xx][yy] = new_group_nr;
9031       }
9032     }
9033   }
9034 }
9035
9036 void AmoebeUmwandeln(int ax, int ay)
9037 {
9038   int i, x, y;
9039
9040   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9041   {
9042     int group_nr = AmoebaNr[ax][ay];
9043
9044 #ifdef DEBUG
9045     if (group_nr == 0)
9046     {
9047       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9048       printf("AmoebeUmwandeln(): This should never happen!\n");
9049       return;
9050     }
9051 #endif
9052
9053     SCAN_PLAYFIELD(x, y)
9054     {
9055       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9056       {
9057         AmoebaNr[x][y] = 0;
9058         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9059       }
9060     }
9061
9062     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9063                             SND_AMOEBA_TURNING_TO_GEM :
9064                             SND_AMOEBA_TURNING_TO_ROCK));
9065     Bang(ax, ay);
9066   }
9067   else
9068   {
9069     static int xy[4][2] =
9070     {
9071       { 0, -1 },
9072       { -1, 0 },
9073       { +1, 0 },
9074       { 0, +1 }
9075     };
9076
9077     for (i = 0; i < NUM_DIRECTIONS; i++)
9078     {
9079       x = ax + xy[i][0];
9080       y = ay + xy[i][1];
9081
9082       if (!IN_LEV_FIELD(x, y))
9083         continue;
9084
9085       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9086       {
9087         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9088                               SND_AMOEBA_TURNING_TO_GEM :
9089                               SND_AMOEBA_TURNING_TO_ROCK));
9090         Bang(x, y);
9091       }
9092     }
9093   }
9094 }
9095
9096 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9097 {
9098   int x, y;
9099   int group_nr = AmoebaNr[ax][ay];
9100   boolean done = FALSE;
9101
9102 #ifdef DEBUG
9103   if (group_nr == 0)
9104   {
9105     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9106     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9107     return;
9108   }
9109 #endif
9110
9111   SCAN_PLAYFIELD(x, y)
9112   {
9113     if (AmoebaNr[x][y] == group_nr &&
9114         (Feld[x][y] == EL_AMOEBA_DEAD ||
9115          Feld[x][y] == EL_BD_AMOEBA ||
9116          Feld[x][y] == EL_AMOEBA_GROWING))
9117     {
9118       AmoebaNr[x][y] = 0;
9119       Feld[x][y] = new_element;
9120       InitField(x, y, FALSE);
9121       DrawLevelField(x, y);
9122       done = TRUE;
9123     }
9124   }
9125
9126   if (done)
9127     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9128                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9129                             SND_BD_AMOEBA_TURNING_TO_GEM));
9130 }
9131
9132 void AmoebeWaechst(int x, int y)
9133 {
9134   static unsigned long sound_delay = 0;
9135   static unsigned long sound_delay_value = 0;
9136
9137   if (!MovDelay[x][y])          /* start new growing cycle */
9138   {
9139     MovDelay[x][y] = 7;
9140
9141     if (DelayReached(&sound_delay, sound_delay_value))
9142     {
9143       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9144       sound_delay_value = 30;
9145     }
9146   }
9147
9148   if (MovDelay[x][y])           /* wait some time before growing bigger */
9149   {
9150     MovDelay[x][y]--;
9151     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9152     {
9153       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9154                                            6 - MovDelay[x][y]);
9155
9156       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9157     }
9158
9159     if (!MovDelay[x][y])
9160     {
9161       Feld[x][y] = Store[x][y];
9162       Store[x][y] = 0;
9163       DrawLevelField(x, y);
9164     }
9165   }
9166 }
9167
9168 void AmoebaDisappearing(int x, int y)
9169 {
9170   static unsigned long sound_delay = 0;
9171   static unsigned long sound_delay_value = 0;
9172
9173   if (!MovDelay[x][y])          /* start new shrinking cycle */
9174   {
9175     MovDelay[x][y] = 7;
9176
9177     if (DelayReached(&sound_delay, sound_delay_value))
9178       sound_delay_value = 30;
9179   }
9180
9181   if (MovDelay[x][y])           /* wait some time before shrinking */
9182   {
9183     MovDelay[x][y]--;
9184     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9185     {
9186       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9187                                            6 - MovDelay[x][y]);
9188
9189       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9190     }
9191
9192     if (!MovDelay[x][y])
9193     {
9194       Feld[x][y] = EL_EMPTY;
9195       DrawLevelField(x, y);
9196
9197       /* don't let mole enter this field in this cycle;
9198          (give priority to objects falling to this field from above) */
9199       Stop[x][y] = TRUE;
9200     }
9201   }
9202 }
9203
9204 void AmoebeAbleger(int ax, int ay)
9205 {
9206   int i;
9207   int element = Feld[ax][ay];
9208   int graphic = el2img(element);
9209   int newax = ax, neway = ay;
9210   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9211   static int xy[4][2] =
9212   {
9213     { 0, -1 },
9214     { -1, 0 },
9215     { +1, 0 },
9216     { 0, +1 }
9217   };
9218
9219   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9220   {
9221     Feld[ax][ay] = EL_AMOEBA_DEAD;
9222     DrawLevelField(ax, ay);
9223     return;
9224   }
9225
9226   if (IS_ANIMATED(graphic))
9227     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9228
9229   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9230     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9231
9232   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9233   {
9234     MovDelay[ax][ay]--;
9235     if (MovDelay[ax][ay])
9236       return;
9237   }
9238
9239   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9240   {
9241     int start = RND(4);
9242     int x = ax + xy[start][0];
9243     int y = ay + xy[start][1];
9244
9245     if (!IN_LEV_FIELD(x, y))
9246       return;
9247
9248     if (IS_FREE(x, y) ||
9249         CAN_GROW_INTO(Feld[x][y]) ||
9250         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9251         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9252     {
9253       newax = x;
9254       neway = y;
9255     }
9256
9257     if (newax == ax && neway == ay)
9258       return;
9259   }
9260   else                          /* normal or "filled" (BD style) amoeba */
9261   {
9262     int start = RND(4);
9263     boolean waiting_for_player = FALSE;
9264
9265     for (i = 0; i < NUM_DIRECTIONS; i++)
9266     {
9267       int j = (start + i) % 4;
9268       int x = ax + xy[j][0];
9269       int y = ay + xy[j][1];
9270
9271       if (!IN_LEV_FIELD(x, y))
9272         continue;
9273
9274       if (IS_FREE(x, y) ||
9275           CAN_GROW_INTO(Feld[x][y]) ||
9276           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9277           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9278       {
9279         newax = x;
9280         neway = y;
9281         break;
9282       }
9283       else if (IS_PLAYER(x, y))
9284         waiting_for_player = TRUE;
9285     }
9286
9287     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9288     {
9289       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9290       {
9291         Feld[ax][ay] = EL_AMOEBA_DEAD;
9292         DrawLevelField(ax, ay);
9293         AmoebaCnt[AmoebaNr[ax][ay]]--;
9294
9295         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9296         {
9297           if (element == EL_AMOEBA_FULL)
9298             AmoebeUmwandeln(ax, ay);
9299           else if (element == EL_BD_AMOEBA)
9300             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9301         }
9302       }
9303       return;
9304     }
9305     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9306     {
9307       /* amoeba gets larger by growing in some direction */
9308
9309       int new_group_nr = AmoebaNr[ax][ay];
9310
9311 #ifdef DEBUG
9312   if (new_group_nr == 0)
9313   {
9314     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9315     printf("AmoebeAbleger(): This should never happen!\n");
9316     return;
9317   }
9318 #endif
9319
9320       AmoebaNr[newax][neway] = new_group_nr;
9321       AmoebaCnt[new_group_nr]++;
9322       AmoebaCnt2[new_group_nr]++;
9323
9324       /* if amoeba touches other amoeba(s) after growing, unify them */
9325       AmoebenVereinigen(newax, neway);
9326
9327       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9328       {
9329         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9330         return;
9331       }
9332     }
9333   }
9334
9335   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9336       (neway == lev_fieldy - 1 && newax != ax))
9337   {
9338     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9339     Store[newax][neway] = element;
9340   }
9341   else if (neway == ay || element == EL_EMC_DRIPPER)
9342   {
9343     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9344
9345     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9346   }
9347   else
9348   {
9349     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9350     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9351     Store[ax][ay] = EL_AMOEBA_DROP;
9352     ContinueMoving(ax, ay);
9353     return;
9354   }
9355
9356   DrawLevelField(newax, neway);
9357 }
9358
9359 void Life(int ax, int ay)
9360 {
9361   int x1, y1, x2, y2;
9362   int life_time = 40;
9363   int element = Feld[ax][ay];
9364   int graphic = el2img(element);
9365   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9366                          level.biomaze);
9367   boolean changed = FALSE;
9368
9369   if (IS_ANIMATED(graphic))
9370     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9371
9372   if (Stop[ax][ay])
9373     return;
9374
9375   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9376     MovDelay[ax][ay] = life_time;
9377
9378   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9379   {
9380     MovDelay[ax][ay]--;
9381     if (MovDelay[ax][ay])
9382       return;
9383   }
9384
9385   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9386   {
9387     int xx = ax+x1, yy = ay+y1;
9388     int nachbarn = 0;
9389
9390     if (!IN_LEV_FIELD(xx, yy))
9391       continue;
9392
9393     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9394     {
9395       int x = xx+x2, y = yy+y2;
9396
9397       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9398         continue;
9399
9400       if (((Feld[x][y] == element ||
9401             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9402            !Stop[x][y]) ||
9403           (IS_FREE(x, y) && Stop[x][y]))
9404         nachbarn++;
9405     }
9406
9407     if (xx == ax && yy == ay)           /* field in the middle */
9408     {
9409       if (nachbarn < life_parameter[0] ||
9410           nachbarn > life_parameter[1])
9411       {
9412         Feld[xx][yy] = EL_EMPTY;
9413         if (!Stop[xx][yy])
9414           DrawLevelField(xx, yy);
9415         Stop[xx][yy] = TRUE;
9416         changed = TRUE;
9417       }
9418     }
9419     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9420     {                                   /* free border field */
9421       if (nachbarn >= life_parameter[2] &&
9422           nachbarn <= life_parameter[3])
9423       {
9424         Feld[xx][yy] = element;
9425         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9426         if (!Stop[xx][yy])
9427           DrawLevelField(xx, yy);
9428         Stop[xx][yy] = TRUE;
9429         changed = TRUE;
9430       }
9431     }
9432   }
9433
9434   if (changed)
9435     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9436                    SND_GAME_OF_LIFE_GROWING);
9437 }
9438
9439 static void InitRobotWheel(int x, int y)
9440 {
9441   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9442 }
9443
9444 static void RunRobotWheel(int x, int y)
9445 {
9446   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9447 }
9448
9449 static void StopRobotWheel(int x, int y)
9450 {
9451   if (ZX == x && ZY == y)
9452   {
9453     ZX = ZY = -1;
9454
9455     game.robot_wheel_active = FALSE;
9456   }
9457 }
9458
9459 static void InitTimegateWheel(int x, int y)
9460 {
9461   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9462 }
9463
9464 static void RunTimegateWheel(int x, int y)
9465 {
9466   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9467 }
9468
9469 static void InitMagicBallDelay(int x, int y)
9470 {
9471 #if 1
9472   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9473 #else
9474   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9475 #endif
9476 }
9477
9478 static void ActivateMagicBall(int bx, int by)
9479 {
9480   int x, y;
9481
9482   if (level.ball_random)
9483   {
9484     int pos_border = RND(8);    /* select one of the eight border elements */
9485     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9486     int xx = pos_content % 3;
9487     int yy = pos_content / 3;
9488
9489     x = bx - 1 + xx;
9490     y = by - 1 + yy;
9491
9492     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9493       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9494   }
9495   else
9496   {
9497     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9498     {
9499       int xx = x - bx + 1;
9500       int yy = y - by + 1;
9501
9502       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9503         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9504     }
9505   }
9506
9507   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9508 }
9509
9510 void CheckExit(int x, int y)
9511 {
9512   if (local_player->gems_still_needed > 0 ||
9513       local_player->sokobanfields_still_needed > 0 ||
9514       local_player->lights_still_needed > 0)
9515   {
9516     int element = Feld[x][y];
9517     int graphic = el2img(element);
9518
9519     if (IS_ANIMATED(graphic))
9520       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9521
9522     return;
9523   }
9524
9525   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9526     return;
9527
9528   Feld[x][y] = EL_EXIT_OPENING;
9529
9530   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9531 }
9532
9533 void CheckExitEM(int x, int y)
9534 {
9535   if (local_player->gems_still_needed > 0 ||
9536       local_player->sokobanfields_still_needed > 0 ||
9537       local_player->lights_still_needed > 0)
9538   {
9539     int element = Feld[x][y];
9540     int graphic = el2img(element);
9541
9542     if (IS_ANIMATED(graphic))
9543       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9544
9545     return;
9546   }
9547
9548   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9549     return;
9550
9551   Feld[x][y] = EL_EM_EXIT_OPENING;
9552
9553   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9554 }
9555
9556 void CheckExitSteel(int x, int y)
9557 {
9558   if (local_player->gems_still_needed > 0 ||
9559       local_player->sokobanfields_still_needed > 0 ||
9560       local_player->lights_still_needed > 0)
9561   {
9562     int element = Feld[x][y];
9563     int graphic = el2img(element);
9564
9565     if (IS_ANIMATED(graphic))
9566       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9567
9568     return;
9569   }
9570
9571   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9572     return;
9573
9574   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9575
9576   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9577 }
9578
9579 void CheckExitSteelEM(int x, int y)
9580 {
9581   if (local_player->gems_still_needed > 0 ||
9582       local_player->sokobanfields_still_needed > 0 ||
9583       local_player->lights_still_needed > 0)
9584   {
9585     int element = Feld[x][y];
9586     int graphic = el2img(element);
9587
9588     if (IS_ANIMATED(graphic))
9589       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9590
9591     return;
9592   }
9593
9594   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9595     return;
9596
9597   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9598
9599   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9600 }
9601
9602 void CheckExitSP(int x, int y)
9603 {
9604   if (local_player->gems_still_needed > 0)
9605   {
9606     int element = Feld[x][y];
9607     int graphic = el2img(element);
9608
9609     if (IS_ANIMATED(graphic))
9610       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9611
9612     return;
9613   }
9614
9615   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9616     return;
9617
9618   Feld[x][y] = EL_SP_EXIT_OPENING;
9619
9620   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9621 }
9622
9623 static void CloseAllOpenTimegates()
9624 {
9625   int x, y;
9626
9627   SCAN_PLAYFIELD(x, y)
9628   {
9629     int element = Feld[x][y];
9630
9631     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9632     {
9633       Feld[x][y] = EL_TIMEGATE_CLOSING;
9634
9635       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9636     }
9637   }
9638 }
9639
9640 void DrawTwinkleOnField(int x, int y)
9641 {
9642   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9643     return;
9644
9645   if (Feld[x][y] == EL_BD_DIAMOND)
9646     return;
9647
9648   if (MovDelay[x][y] == 0)      /* next animation frame */
9649     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9650
9651   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9652   {
9653     MovDelay[x][y]--;
9654
9655     DrawLevelElementAnimation(x, y, Feld[x][y]);
9656
9657     if (MovDelay[x][y] != 0)
9658     {
9659       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9660                                            10 - MovDelay[x][y]);
9661
9662       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9663     }
9664   }
9665 }
9666
9667 void MauerWaechst(int x, int y)
9668 {
9669   int delay = 6;
9670
9671   if (!MovDelay[x][y])          /* next animation frame */
9672     MovDelay[x][y] = 3 * delay;
9673
9674   if (MovDelay[x][y])           /* wait some time before next frame */
9675   {
9676     MovDelay[x][y]--;
9677
9678     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9679     {
9680       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9681       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9682
9683       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9684     }
9685
9686     if (!MovDelay[x][y])
9687     {
9688       if (MovDir[x][y] == MV_LEFT)
9689       {
9690         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9691           DrawLevelField(x - 1, y);
9692       }
9693       else if (MovDir[x][y] == MV_RIGHT)
9694       {
9695         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9696           DrawLevelField(x + 1, y);
9697       }
9698       else if (MovDir[x][y] == MV_UP)
9699       {
9700         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9701           DrawLevelField(x, y - 1);
9702       }
9703       else
9704       {
9705         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9706           DrawLevelField(x, y + 1);
9707       }
9708
9709       Feld[x][y] = Store[x][y];
9710       Store[x][y] = 0;
9711       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9712       DrawLevelField(x, y);
9713     }
9714   }
9715 }
9716
9717 void MauerAbleger(int ax, int ay)
9718 {
9719   int element = Feld[ax][ay];
9720   int graphic = el2img(element);
9721   boolean oben_frei = FALSE, unten_frei = FALSE;
9722   boolean links_frei = FALSE, rechts_frei = FALSE;
9723   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9724   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9725   boolean new_wall = FALSE;
9726
9727   if (IS_ANIMATED(graphic))
9728     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9729
9730   if (!MovDelay[ax][ay])        /* start building new wall */
9731     MovDelay[ax][ay] = 6;
9732
9733   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9734   {
9735     MovDelay[ax][ay]--;
9736     if (MovDelay[ax][ay])
9737       return;
9738   }
9739
9740   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9741     oben_frei = TRUE;
9742   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9743     unten_frei = TRUE;
9744   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9745     links_frei = TRUE;
9746   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9747     rechts_frei = TRUE;
9748
9749   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9750       element == EL_EXPANDABLE_WALL_ANY)
9751   {
9752     if (oben_frei)
9753     {
9754       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9755       Store[ax][ay-1] = element;
9756       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9757       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9758         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9759                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9760       new_wall = TRUE;
9761     }
9762     if (unten_frei)
9763     {
9764       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9765       Store[ax][ay+1] = element;
9766       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9767       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9768         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9769                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9770       new_wall = TRUE;
9771     }
9772   }
9773
9774   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9775       element == EL_EXPANDABLE_WALL_ANY ||
9776       element == EL_EXPANDABLE_WALL ||
9777       element == EL_BD_EXPANDABLE_WALL)
9778   {
9779     if (links_frei)
9780     {
9781       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9782       Store[ax-1][ay] = element;
9783       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9784       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9785         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9786                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9787       new_wall = TRUE;
9788     }
9789
9790     if (rechts_frei)
9791     {
9792       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9793       Store[ax+1][ay] = element;
9794       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9795       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9796         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9797                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9798       new_wall = TRUE;
9799     }
9800   }
9801
9802   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9803     DrawLevelField(ax, ay);
9804
9805   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9806     oben_massiv = TRUE;
9807   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9808     unten_massiv = TRUE;
9809   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9810     links_massiv = TRUE;
9811   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9812     rechts_massiv = TRUE;
9813
9814   if (((oben_massiv && unten_massiv) ||
9815        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9816        element == EL_EXPANDABLE_WALL) &&
9817       ((links_massiv && rechts_massiv) ||
9818        element == EL_EXPANDABLE_WALL_VERTICAL))
9819     Feld[ax][ay] = EL_WALL;
9820
9821   if (new_wall)
9822     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9823 }
9824
9825 void MauerAblegerStahl(int ax, int ay)
9826 {
9827   int element = Feld[ax][ay];
9828   int graphic = el2img(element);
9829   boolean oben_frei = FALSE, unten_frei = FALSE;
9830   boolean links_frei = FALSE, rechts_frei = FALSE;
9831   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9832   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9833   boolean new_wall = FALSE;
9834
9835   if (IS_ANIMATED(graphic))
9836     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9837
9838   if (!MovDelay[ax][ay])        /* start building new wall */
9839     MovDelay[ax][ay] = 6;
9840
9841   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9842   {
9843     MovDelay[ax][ay]--;
9844     if (MovDelay[ax][ay])
9845       return;
9846   }
9847
9848   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9849     oben_frei = TRUE;
9850   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9851     unten_frei = TRUE;
9852   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9853     links_frei = TRUE;
9854   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9855     rechts_frei = TRUE;
9856
9857   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9858       element == EL_EXPANDABLE_STEELWALL_ANY)
9859   {
9860     if (oben_frei)
9861     {
9862       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9863       Store[ax][ay-1] = element;
9864       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9865       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9866         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9867                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9868       new_wall = TRUE;
9869     }
9870     if (unten_frei)
9871     {
9872       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9873       Store[ax][ay+1] = element;
9874       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9875       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9876         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9877                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9878       new_wall = TRUE;
9879     }
9880   }
9881
9882   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9883       element == EL_EXPANDABLE_STEELWALL_ANY)
9884   {
9885     if (links_frei)
9886     {
9887       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9888       Store[ax-1][ay] = element;
9889       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9890       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9891         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9892                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9893       new_wall = TRUE;
9894     }
9895
9896     if (rechts_frei)
9897     {
9898       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9899       Store[ax+1][ay] = element;
9900       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9901       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9902         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9903                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9904       new_wall = TRUE;
9905     }
9906   }
9907
9908   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9909     oben_massiv = TRUE;
9910   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9911     unten_massiv = TRUE;
9912   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9913     links_massiv = TRUE;
9914   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9915     rechts_massiv = TRUE;
9916
9917   if (((oben_massiv && unten_massiv) ||
9918        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9919       ((links_massiv && rechts_massiv) ||
9920        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9921     Feld[ax][ay] = EL_STEELWALL;
9922
9923   if (new_wall)
9924     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9925 }
9926
9927 void CheckForDragon(int x, int y)
9928 {
9929   int i, j;
9930   boolean dragon_found = FALSE;
9931   static int xy[4][2] =
9932   {
9933     { 0, -1 },
9934     { -1, 0 },
9935     { +1, 0 },
9936     { 0, +1 }
9937   };
9938
9939   for (i = 0; i < NUM_DIRECTIONS; i++)
9940   {
9941     for (j = 0; j < 4; j++)
9942     {
9943       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9944
9945       if (IN_LEV_FIELD(xx, yy) &&
9946           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9947       {
9948         if (Feld[xx][yy] == EL_DRAGON)
9949           dragon_found = TRUE;
9950       }
9951       else
9952         break;
9953     }
9954   }
9955
9956   if (!dragon_found)
9957   {
9958     for (i = 0; i < NUM_DIRECTIONS; i++)
9959     {
9960       for (j = 0; j < 3; j++)
9961       {
9962         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9963   
9964         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9965         {
9966           Feld[xx][yy] = EL_EMPTY;
9967           DrawLevelField(xx, yy);
9968         }
9969         else
9970           break;
9971       }
9972     }
9973   }
9974 }
9975
9976 static void InitBuggyBase(int x, int y)
9977 {
9978   int element = Feld[x][y];
9979   int activating_delay = FRAMES_PER_SECOND / 4;
9980
9981   ChangeDelay[x][y] =
9982     (element == EL_SP_BUGGY_BASE ?
9983      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9984      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9985      activating_delay :
9986      element == EL_SP_BUGGY_BASE_ACTIVE ?
9987      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9988 }
9989
9990 static void WarnBuggyBase(int x, int y)
9991 {
9992   int i;
9993   static int xy[4][2] =
9994   {
9995     { 0, -1 },
9996     { -1, 0 },
9997     { +1, 0 },
9998     { 0, +1 }
9999   };
10000
10001   for (i = 0; i < NUM_DIRECTIONS; i++)
10002   {
10003     int xx = x + xy[i][0];
10004     int yy = y + xy[i][1];
10005
10006     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10007     {
10008       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10009
10010       break;
10011     }
10012   }
10013 }
10014
10015 static void InitTrap(int x, int y)
10016 {
10017   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10018 }
10019
10020 static void ActivateTrap(int x, int y)
10021 {
10022   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10023 }
10024
10025 static void ChangeActiveTrap(int x, int y)
10026 {
10027   int graphic = IMG_TRAP_ACTIVE;
10028
10029   /* if new animation frame was drawn, correct crumbled sand border */
10030   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10031     DrawLevelFieldCrumbledSand(x, y);
10032 }
10033
10034 static int getSpecialActionElement(int element, int number, int base_element)
10035 {
10036   return (element != EL_EMPTY ? element :
10037           number != -1 ? base_element + number - 1 :
10038           EL_EMPTY);
10039 }
10040
10041 static int getModifiedActionNumber(int value_old, int operator, int operand,
10042                                    int value_min, int value_max)
10043 {
10044   int value_new = (operator == CA_MODE_SET      ? operand :
10045                    operator == CA_MODE_ADD      ? value_old + operand :
10046                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10047                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10048                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10049                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10050                    value_old);
10051
10052   return (value_new < value_min ? value_min :
10053           value_new > value_max ? value_max :
10054           value_new);
10055 }
10056
10057 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10058 {
10059   struct ElementInfo *ei = &element_info[element];
10060   struct ElementChangeInfo *change = &ei->change_page[page];
10061   int target_element = change->target_element;
10062   int action_type = change->action_type;
10063   int action_mode = change->action_mode;
10064   int action_arg = change->action_arg;
10065   int i;
10066
10067   if (!change->has_action)
10068     return;
10069
10070   /* ---------- determine action paramater values -------------------------- */
10071
10072   int level_time_value =
10073     (level.time > 0 ? TimeLeft :
10074      TimePlayed);
10075
10076   int action_arg_element =
10077     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10078      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10079      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10080      EL_EMPTY);
10081
10082   int action_arg_direction =
10083     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10084      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10085      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10086      change->actual_trigger_side :
10087      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10088      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10089      MV_NONE);
10090
10091   int action_arg_number_min =
10092     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10093      CA_ARG_MIN);
10094
10095   int action_arg_number_max =
10096     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10097      action_type == CA_SET_LEVEL_GEMS ? 999 :
10098      action_type == CA_SET_LEVEL_TIME ? 9999 :
10099      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10100      action_type == CA_SET_CE_VALUE ? 9999 :
10101      action_type == CA_SET_CE_SCORE ? 9999 :
10102      CA_ARG_MAX);
10103
10104   int action_arg_number_reset =
10105     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10106      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10107      action_type == CA_SET_LEVEL_TIME ? level.time :
10108      action_type == CA_SET_LEVEL_SCORE ? 0 :
10109 #if USE_NEW_CUSTOM_VALUE
10110      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10111 #else
10112      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10113 #endif
10114      action_type == CA_SET_CE_SCORE ? 0 :
10115      0);
10116
10117   int action_arg_number =
10118     (action_arg <= CA_ARG_MAX ? action_arg :
10119      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10120      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10121      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10122      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10123      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10124      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10125 #if USE_NEW_CUSTOM_VALUE
10126      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10127 #else
10128      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10129 #endif
10130      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10131      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10132      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10133      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10134      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10135      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10136      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10137      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10138      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10139      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10140      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10141      -1);
10142
10143   int action_arg_number_old =
10144     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10145      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10146      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10147      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10148      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10149      0);
10150
10151   int action_arg_number_new =
10152     getModifiedActionNumber(action_arg_number_old,
10153                             action_mode, action_arg_number,
10154                             action_arg_number_min, action_arg_number_max);
10155
10156 #if 1
10157   int trigger_player_bits = change->actual_trigger_player_bits;
10158 #else
10159   int trigger_player_bits =
10160     (change->actual_trigger_player >= EL_PLAYER_1 &&
10161      change->actual_trigger_player <= EL_PLAYER_4 ?
10162      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10163      PLAYER_BITS_ANY);
10164 #endif
10165
10166   int action_arg_player_bits =
10167     (action_arg >= CA_ARG_PLAYER_1 &&
10168      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10169      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10170      PLAYER_BITS_ANY);
10171
10172   /* ---------- execute action  -------------------------------------------- */
10173
10174   switch (action_type)
10175   {
10176     case CA_NO_ACTION:
10177     {
10178       return;
10179     }
10180
10181     /* ---------- level actions  ------------------------------------------- */
10182
10183     case CA_RESTART_LEVEL:
10184     {
10185       game.restart_level = TRUE;
10186
10187       break;
10188     }
10189
10190     case CA_SHOW_ENVELOPE:
10191     {
10192       int element = getSpecialActionElement(action_arg_element,
10193                                             action_arg_number, EL_ENVELOPE_1);
10194
10195       if (IS_ENVELOPE(element))
10196         local_player->show_envelope = element;
10197
10198       break;
10199     }
10200
10201     case CA_SET_LEVEL_TIME:
10202     {
10203       if (level.time > 0)       /* only modify limited time value */
10204       {
10205         TimeLeft = action_arg_number_new;
10206
10207 #if 1
10208         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10209
10210         DisplayGameControlValues();
10211 #else
10212         DrawGameValue_Time(TimeLeft);
10213 #endif
10214
10215         if (!TimeLeft && setup.time_limit)
10216           for (i = 0; i < MAX_PLAYERS; i++)
10217             KillPlayer(&stored_player[i]);
10218       }
10219
10220       break;
10221     }
10222
10223     case CA_SET_LEVEL_SCORE:
10224     {
10225       local_player->score = action_arg_number_new;
10226
10227 #if 1
10228       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10229
10230       DisplayGameControlValues();
10231 #else
10232       DrawGameValue_Score(local_player->score);
10233 #endif
10234
10235       break;
10236     }
10237
10238     case CA_SET_LEVEL_GEMS:
10239     {
10240       local_player->gems_still_needed = action_arg_number_new;
10241
10242 #if 1
10243       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10244
10245       DisplayGameControlValues();
10246 #else
10247       DrawGameValue_Emeralds(local_player->gems_still_needed);
10248 #endif
10249
10250       break;
10251     }
10252
10253 #if !USE_PLAYER_GRAVITY
10254     case CA_SET_LEVEL_GRAVITY:
10255     {
10256       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10257                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10258                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10259                       game.gravity);
10260       break;
10261     }
10262 #endif
10263
10264     case CA_SET_LEVEL_WIND:
10265     {
10266       game.wind_direction = action_arg_direction;
10267
10268       break;
10269     }
10270
10271     /* ---------- player actions  ------------------------------------------ */
10272
10273     case CA_MOVE_PLAYER:
10274     {
10275       /* automatically move to the next field in specified direction */
10276       for (i = 0; i < MAX_PLAYERS; i++)
10277         if (trigger_player_bits & (1 << i))
10278           stored_player[i].programmed_action = action_arg_direction;
10279
10280       break;
10281     }
10282
10283     case CA_EXIT_PLAYER:
10284     {
10285       for (i = 0; i < MAX_PLAYERS; i++)
10286         if (action_arg_player_bits & (1 << i))
10287           PlayerWins(&stored_player[i]);
10288
10289       break;
10290     }
10291
10292     case CA_KILL_PLAYER:
10293     {
10294       for (i = 0; i < MAX_PLAYERS; i++)
10295         if (action_arg_player_bits & (1 << i))
10296           KillPlayer(&stored_player[i]);
10297
10298       break;
10299     }
10300
10301     case CA_SET_PLAYER_KEYS:
10302     {
10303       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10304       int element = getSpecialActionElement(action_arg_element,
10305                                             action_arg_number, EL_KEY_1);
10306
10307       if (IS_KEY(element))
10308       {
10309         for (i = 0; i < MAX_PLAYERS; i++)
10310         {
10311           if (trigger_player_bits & (1 << i))
10312           {
10313             stored_player[i].key[KEY_NR(element)] = key_state;
10314
10315             DrawGameDoorValues();
10316           }
10317         }
10318       }
10319
10320       break;
10321     }
10322
10323     case CA_SET_PLAYER_SPEED:
10324     {
10325       for (i = 0; i < MAX_PLAYERS; i++)
10326       {
10327         if (trigger_player_bits & (1 << i))
10328         {
10329           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10330
10331           if (action_arg == CA_ARG_SPEED_FASTER &&
10332               stored_player[i].cannot_move)
10333           {
10334             action_arg_number = STEPSIZE_VERY_SLOW;
10335           }
10336           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10337                    action_arg == CA_ARG_SPEED_FASTER)
10338           {
10339             action_arg_number = 2;
10340             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10341                            CA_MODE_MULTIPLY);
10342           }
10343           else if (action_arg == CA_ARG_NUMBER_RESET)
10344           {
10345             action_arg_number = level.initial_player_stepsize[i];
10346           }
10347
10348           move_stepsize =
10349             getModifiedActionNumber(move_stepsize,
10350                                     action_mode,
10351                                     action_arg_number,
10352                                     action_arg_number_min,
10353                                     action_arg_number_max);
10354
10355           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10356         }
10357       }
10358
10359       break;
10360     }
10361
10362     case CA_SET_PLAYER_SHIELD:
10363     {
10364       for (i = 0; i < MAX_PLAYERS; i++)
10365       {
10366         if (trigger_player_bits & (1 << i))
10367         {
10368           if (action_arg == CA_ARG_SHIELD_OFF)
10369           {
10370             stored_player[i].shield_normal_time_left = 0;
10371             stored_player[i].shield_deadly_time_left = 0;
10372           }
10373           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10374           {
10375             stored_player[i].shield_normal_time_left = 999999;
10376           }
10377           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10378           {
10379             stored_player[i].shield_normal_time_left = 999999;
10380             stored_player[i].shield_deadly_time_left = 999999;
10381           }
10382         }
10383       }
10384
10385       break;
10386     }
10387
10388 #if USE_PLAYER_GRAVITY
10389     case CA_SET_PLAYER_GRAVITY:
10390     {
10391       for (i = 0; i < MAX_PLAYERS; i++)
10392       {
10393         if (trigger_player_bits & (1 << i))
10394         {
10395           stored_player[i].gravity =
10396             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10397              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10398              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10399              stored_player[i].gravity);
10400         }
10401       }
10402
10403       break;
10404     }
10405 #endif
10406
10407     case CA_SET_PLAYER_ARTWORK:
10408     {
10409       for (i = 0; i < MAX_PLAYERS; i++)
10410       {
10411         if (trigger_player_bits & (1 << i))
10412         {
10413           int artwork_element = action_arg_element;
10414
10415           if (action_arg == CA_ARG_ELEMENT_RESET)
10416             artwork_element =
10417               (level.use_artwork_element[i] ? level.artwork_element[i] :
10418                stored_player[i].element_nr);
10419
10420 #if USE_GFX_RESET_PLAYER_ARTWORK
10421           if (stored_player[i].artwork_element != artwork_element)
10422             stored_player[i].Frame = 0;
10423 #endif
10424
10425           stored_player[i].artwork_element = artwork_element;
10426
10427           SetPlayerWaiting(&stored_player[i], FALSE);
10428
10429           /* set number of special actions for bored and sleeping animation */
10430           stored_player[i].num_special_action_bored =
10431             get_num_special_action(artwork_element,
10432                                    ACTION_BORING_1, ACTION_BORING_LAST);
10433           stored_player[i].num_special_action_sleeping =
10434             get_num_special_action(artwork_element,
10435                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10436         }
10437       }
10438
10439       break;
10440     }
10441
10442     /* ---------- CE actions  ---------------------------------------------- */
10443
10444     case CA_SET_CE_VALUE:
10445     {
10446 #if USE_NEW_CUSTOM_VALUE
10447       int last_ce_value = CustomValue[x][y];
10448
10449       CustomValue[x][y] = action_arg_number_new;
10450
10451       if (CustomValue[x][y] != last_ce_value)
10452       {
10453         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10454         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10455
10456         if (CustomValue[x][y] == 0)
10457         {
10458           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10459           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10460         }
10461       }
10462 #endif
10463
10464       break;
10465     }
10466
10467     case CA_SET_CE_SCORE:
10468     {
10469 #if USE_NEW_CUSTOM_VALUE
10470       int last_ce_score = ei->collect_score;
10471
10472       ei->collect_score = action_arg_number_new;
10473
10474       if (ei->collect_score != last_ce_score)
10475       {
10476         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10477         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10478
10479         if (ei->collect_score == 0)
10480         {
10481           int xx, yy;
10482
10483           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10484           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10485
10486           /*
10487             This is a very special case that seems to be a mixture between
10488             CheckElementChange() and CheckTriggeredElementChange(): while
10489             the first one only affects single elements that are triggered
10490             directly, the second one affects multiple elements in the playfield
10491             that are triggered indirectly by another element. This is a third
10492             case: Changing the CE score always affects multiple identical CEs,
10493             so every affected CE must be checked, not only the single CE for
10494             which the CE score was changed in the first place (as every instance
10495             of that CE shares the same CE score, and therefore also can change)!
10496           */
10497           SCAN_PLAYFIELD(xx, yy)
10498           {
10499             if (Feld[xx][yy] == element)
10500               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10501                                  CE_SCORE_GETS_ZERO);
10502           }
10503         }
10504       }
10505 #endif
10506
10507       break;
10508     }
10509
10510     /* ---------- engine actions  ------------------------------------------ */
10511
10512     case CA_SET_ENGINE_SCAN_MODE:
10513     {
10514       InitPlayfieldScanMode(action_arg);
10515
10516       break;
10517     }
10518
10519     default:
10520       break;
10521   }
10522 }
10523
10524 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10525 {
10526   int old_element = Feld[x][y];
10527   int new_element = GetElementFromGroupElement(element);
10528   int previous_move_direction = MovDir[x][y];
10529 #if USE_NEW_CUSTOM_VALUE
10530   int last_ce_value = CustomValue[x][y];
10531 #endif
10532   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10533   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10534   boolean add_player_onto_element = (new_element_is_player &&
10535 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10536                                      /* this breaks SnakeBite when a snake is
10537                                         halfway through a door that closes */
10538                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10539                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10540 #endif
10541                                      IS_WALKABLE(old_element));
10542
10543 #if 0
10544   /* check if element under the player changes from accessible to unaccessible
10545      (needed for special case of dropping element which then changes) */
10546   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10547       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10548   {
10549     Bang(x, y);
10550
10551     return;
10552   }
10553 #endif
10554
10555   if (!add_player_onto_element)
10556   {
10557     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10558       RemoveMovingField(x, y);
10559     else
10560       RemoveField(x, y);
10561
10562     Feld[x][y] = new_element;
10563
10564 #if !USE_GFX_RESET_GFX_ANIMATION
10565     ResetGfxAnimation(x, y);
10566     ResetRandomAnimationValue(x, y);
10567 #endif
10568
10569     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10570       MovDir[x][y] = previous_move_direction;
10571
10572 #if USE_NEW_CUSTOM_VALUE
10573     if (element_info[new_element].use_last_ce_value)
10574       CustomValue[x][y] = last_ce_value;
10575 #endif
10576
10577     InitField_WithBug1(x, y, FALSE);
10578
10579     new_element = Feld[x][y];   /* element may have changed */
10580
10581 #if USE_GFX_RESET_GFX_ANIMATION
10582     ResetGfxAnimation(x, y);
10583     ResetRandomAnimationValue(x, y);
10584 #endif
10585
10586     DrawLevelField(x, y);
10587
10588     if (GFX_CRUMBLED(new_element))
10589       DrawLevelFieldCrumbledSandNeighbours(x, y);
10590   }
10591
10592 #if 1
10593   /* check if element under the player changes from accessible to unaccessible
10594      (needed for special case of dropping element which then changes) */
10595   /* (must be checked after creating new element for walkable group elements) */
10596 #if USE_FIX_KILLED_BY_NON_WALKABLE
10597   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10598       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10599   {
10600     Bang(x, y);
10601
10602     return;
10603   }
10604 #else
10605   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10606       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10607   {
10608     Bang(x, y);
10609
10610     return;
10611   }
10612 #endif
10613 #endif
10614
10615   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10616   if (new_element_is_player)
10617     RelocatePlayer(x, y, new_element);
10618
10619   if (is_change)
10620     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10621
10622   TestIfBadThingTouchesPlayer(x, y);
10623   TestIfPlayerTouchesCustomElement(x, y);
10624   TestIfElementTouchesCustomElement(x, y);
10625 }
10626
10627 static void CreateField(int x, int y, int element)
10628 {
10629   CreateFieldExt(x, y, element, FALSE);
10630 }
10631
10632 static void CreateElementFromChange(int x, int y, int element)
10633 {
10634   element = GET_VALID_RUNTIME_ELEMENT(element);
10635
10636 #if USE_STOP_CHANGED_ELEMENTS
10637   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10638   {
10639     int old_element = Feld[x][y];
10640
10641     /* prevent changed element from moving in same engine frame
10642        unless both old and new element can either fall or move */
10643     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10644         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10645       Stop[x][y] = TRUE;
10646   }
10647 #endif
10648
10649   CreateFieldExt(x, y, element, TRUE);
10650 }
10651
10652 static boolean ChangeElement(int x, int y, int element, int page)
10653 {
10654   struct ElementInfo *ei = &element_info[element];
10655   struct ElementChangeInfo *change = &ei->change_page[page];
10656   int ce_value = CustomValue[x][y];
10657   int ce_score = ei->collect_score;
10658   int target_element;
10659   int old_element = Feld[x][y];
10660
10661   /* always use default change event to prevent running into a loop */
10662   if (ChangeEvent[x][y] == -1)
10663     ChangeEvent[x][y] = CE_DELAY;
10664
10665   if (ChangeEvent[x][y] == CE_DELAY)
10666   {
10667     /* reset actual trigger element, trigger player and action element */
10668     change->actual_trigger_element = EL_EMPTY;
10669     change->actual_trigger_player = EL_PLAYER_1;
10670     change->actual_trigger_player_bits = CH_PLAYER_1;
10671     change->actual_trigger_side = CH_SIDE_NONE;
10672     change->actual_trigger_ce_value = 0;
10673     change->actual_trigger_ce_score = 0;
10674   }
10675
10676   /* do not change elements more than a specified maximum number of changes */
10677   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10678     return FALSE;
10679
10680   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10681
10682   if (change->explode)
10683   {
10684     Bang(x, y);
10685
10686     return TRUE;
10687   }
10688
10689   if (change->use_target_content)
10690   {
10691     boolean complete_replace = TRUE;
10692     boolean can_replace[3][3];
10693     int xx, yy;
10694
10695     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10696     {
10697       boolean is_empty;
10698       boolean is_walkable;
10699       boolean is_diggable;
10700       boolean is_collectible;
10701       boolean is_removable;
10702       boolean is_destructible;
10703       int ex = x + xx - 1;
10704       int ey = y + yy - 1;
10705       int content_element = change->target_content.e[xx][yy];
10706       int e;
10707
10708       can_replace[xx][yy] = TRUE;
10709
10710       if (ex == x && ey == y)   /* do not check changing element itself */
10711         continue;
10712
10713       if (content_element == EL_EMPTY_SPACE)
10714       {
10715         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10716
10717         continue;
10718       }
10719
10720       if (!IN_LEV_FIELD(ex, ey))
10721       {
10722         can_replace[xx][yy] = FALSE;
10723         complete_replace = FALSE;
10724
10725         continue;
10726       }
10727
10728       e = Feld[ex][ey];
10729
10730       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10731         e = MovingOrBlocked2Element(ex, ey);
10732
10733       is_empty = (IS_FREE(ex, ey) ||
10734                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10735
10736       is_walkable     = (is_empty || IS_WALKABLE(e));
10737       is_diggable     = (is_empty || IS_DIGGABLE(e));
10738       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10739       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10740       is_removable    = (is_diggable || is_collectible);
10741
10742       can_replace[xx][yy] =
10743         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10744           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10745           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10746           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10747           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10748           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10749          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10750
10751       if (!can_replace[xx][yy])
10752         complete_replace = FALSE;
10753     }
10754
10755     if (!change->only_if_complete || complete_replace)
10756     {
10757       boolean something_has_changed = FALSE;
10758
10759       if (change->only_if_complete && change->use_random_replace &&
10760           RND(100) < change->random_percentage)
10761         return FALSE;
10762
10763       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10764       {
10765         int ex = x + xx - 1;
10766         int ey = y + yy - 1;
10767         int content_element;
10768
10769         if (can_replace[xx][yy] && (!change->use_random_replace ||
10770                                     RND(100) < change->random_percentage))
10771         {
10772           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10773             RemoveMovingField(ex, ey);
10774
10775           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10776
10777           content_element = change->target_content.e[xx][yy];
10778           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10779                                               ce_value, ce_score);
10780
10781           CreateElementFromChange(ex, ey, target_element);
10782
10783           something_has_changed = TRUE;
10784
10785           /* for symmetry reasons, freeze newly created border elements */
10786           if (ex != x || ey != y)
10787             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10788         }
10789       }
10790
10791       if (something_has_changed)
10792       {
10793         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10794         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10795       }
10796     }
10797   }
10798   else
10799   {
10800     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10801                                         ce_value, ce_score);
10802
10803     if (element == EL_DIAGONAL_GROWING ||
10804         element == EL_DIAGONAL_SHRINKING)
10805     {
10806       target_element = Store[x][y];
10807
10808       Store[x][y] = EL_EMPTY;
10809     }
10810
10811     CreateElementFromChange(x, y, target_element);
10812
10813     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10814     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10815   }
10816
10817   /* this uses direct change before indirect change */
10818   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10819
10820   return TRUE;
10821 }
10822
10823 #if USE_NEW_DELAYED_ACTION
10824
10825 static void HandleElementChange(int x, int y, int page)
10826 {
10827   int element = MovingOrBlocked2Element(x, y);
10828   struct ElementInfo *ei = &element_info[element];
10829   struct ElementChangeInfo *change = &ei->change_page[page];
10830
10831 #ifdef DEBUG
10832   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10833       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10834   {
10835     printf("\n\n");
10836     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10837            x, y, element, element_info[element].token_name);
10838     printf("HandleElementChange(): This should never happen!\n");
10839     printf("\n\n");
10840   }
10841 #endif
10842
10843   /* this can happen with classic bombs on walkable, changing elements */
10844   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10845   {
10846 #if 0
10847     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10848       ChangeDelay[x][y] = 0;
10849 #endif
10850
10851     return;
10852   }
10853
10854   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10855   {
10856     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10857
10858     if (change->can_change)
10859     {
10860 #if 1
10861       /* !!! not clear why graphic animation should be reset at all here !!! */
10862       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10863 #if USE_GFX_RESET_WHEN_NOT_MOVING
10864       /* when a custom element is about to change (for example by change delay),
10865          do not reset graphic animation when the custom element is moving */
10866       if (!IS_MOVING(x, y))
10867 #endif
10868       {
10869         ResetGfxAnimation(x, y);
10870         ResetRandomAnimationValue(x, y);
10871       }
10872 #endif
10873
10874       if (change->pre_change_function)
10875         change->pre_change_function(x, y);
10876     }
10877   }
10878
10879   ChangeDelay[x][y]--;
10880
10881   if (ChangeDelay[x][y] != 0)           /* continue element change */
10882   {
10883     if (change->can_change)
10884     {
10885       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10886
10887       if (IS_ANIMATED(graphic))
10888         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10889
10890       if (change->change_function)
10891         change->change_function(x, y);
10892     }
10893   }
10894   else                                  /* finish element change */
10895   {
10896     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10897     {
10898       page = ChangePage[x][y];
10899       ChangePage[x][y] = -1;
10900
10901       change = &ei->change_page[page];
10902     }
10903
10904     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10905     {
10906       ChangeDelay[x][y] = 1;            /* try change after next move step */
10907       ChangePage[x][y] = page;          /* remember page to use for change */
10908
10909       return;
10910     }
10911
10912     if (change->can_change)
10913     {
10914       if (ChangeElement(x, y, element, page))
10915       {
10916         if (change->post_change_function)
10917           change->post_change_function(x, y);
10918       }
10919     }
10920
10921     if (change->has_action)
10922       ExecuteCustomElementAction(x, y, element, page);
10923   }
10924 }
10925
10926 #else
10927
10928 static void HandleElementChange(int x, int y, int page)
10929 {
10930   int element = MovingOrBlocked2Element(x, y);
10931   struct ElementInfo *ei = &element_info[element];
10932   struct ElementChangeInfo *change = &ei->change_page[page];
10933
10934 #ifdef DEBUG
10935   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10936   {
10937     printf("\n\n");
10938     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10939            x, y, element, element_info[element].token_name);
10940     printf("HandleElementChange(): This should never happen!\n");
10941     printf("\n\n");
10942   }
10943 #endif
10944
10945   /* this can happen with classic bombs on walkable, changing elements */
10946   if (!CAN_CHANGE(element))
10947   {
10948 #if 0
10949     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10950       ChangeDelay[x][y] = 0;
10951 #endif
10952
10953     return;
10954   }
10955
10956   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10957   {
10958     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10959
10960     ResetGfxAnimation(x, y);
10961     ResetRandomAnimationValue(x, y);
10962
10963     if (change->pre_change_function)
10964       change->pre_change_function(x, y);
10965   }
10966
10967   ChangeDelay[x][y]--;
10968
10969   if (ChangeDelay[x][y] != 0)           /* continue element change */
10970   {
10971     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10972
10973     if (IS_ANIMATED(graphic))
10974       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10975
10976     if (change->change_function)
10977       change->change_function(x, y);
10978   }
10979   else                                  /* finish element change */
10980   {
10981     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10982     {
10983       page = ChangePage[x][y];
10984       ChangePage[x][y] = -1;
10985
10986       change = &ei->change_page[page];
10987     }
10988
10989     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10990     {
10991       ChangeDelay[x][y] = 1;            /* try change after next move step */
10992       ChangePage[x][y] = page;          /* remember page to use for change */
10993
10994       return;
10995     }
10996
10997     if (ChangeElement(x, y, element, page))
10998     {
10999       if (change->post_change_function)
11000         change->post_change_function(x, y);
11001     }
11002   }
11003 }
11004
11005 #endif
11006
11007 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11008                                               int trigger_element,
11009                                               int trigger_event,
11010                                               int trigger_player,
11011                                               int trigger_side,
11012                                               int trigger_page)
11013 {
11014   boolean change_done_any = FALSE;
11015   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11016   int i;
11017
11018   if (!(trigger_events[trigger_element][trigger_event]))
11019     return FALSE;
11020
11021 #if 0
11022   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11023          trigger_event, recursion_loop_depth, recursion_loop_detected,
11024          recursion_loop_element, EL_NAME(recursion_loop_element));
11025 #endif
11026
11027   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11028
11029   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11030   {
11031     int element = EL_CUSTOM_START + i;
11032     boolean change_done = FALSE;
11033     int p;
11034
11035     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11036         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11037       continue;
11038
11039     for (p = 0; p < element_info[element].num_change_pages; p++)
11040     {
11041       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11042
11043       if (change->can_change_or_has_action &&
11044           change->has_event[trigger_event] &&
11045           change->trigger_side & trigger_side &&
11046           change->trigger_player & trigger_player &&
11047           change->trigger_page & trigger_page_bits &&
11048           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11049       {
11050         change->actual_trigger_element = trigger_element;
11051         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11052         change->actual_trigger_player_bits = trigger_player;
11053         change->actual_trigger_side = trigger_side;
11054         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11055         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11056
11057         if ((change->can_change && !change_done) || change->has_action)
11058         {
11059           int x, y;
11060
11061           SCAN_PLAYFIELD(x, y)
11062           {
11063             if (Feld[x][y] == element)
11064             {
11065               if (change->can_change && !change_done)
11066               {
11067                 ChangeDelay[x][y] = 1;
11068                 ChangeEvent[x][y] = trigger_event;
11069
11070                 HandleElementChange(x, y, p);
11071               }
11072 #if USE_NEW_DELAYED_ACTION
11073               else if (change->has_action)
11074               {
11075                 ExecuteCustomElementAction(x, y, element, p);
11076                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11077               }
11078 #else
11079               if (change->has_action)
11080               {
11081                 ExecuteCustomElementAction(x, y, element, p);
11082                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11083               }
11084 #endif
11085             }
11086           }
11087
11088           if (change->can_change)
11089           {
11090             change_done = TRUE;
11091             change_done_any = TRUE;
11092           }
11093         }
11094       }
11095     }
11096   }
11097
11098   RECURSION_LOOP_DETECTION_END();
11099
11100   return change_done_any;
11101 }
11102
11103 static boolean CheckElementChangeExt(int x, int y,
11104                                      int element,
11105                                      int trigger_element,
11106                                      int trigger_event,
11107                                      int trigger_player,
11108                                      int trigger_side)
11109 {
11110   boolean change_done = FALSE;
11111   int p;
11112
11113   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11114       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11115     return FALSE;
11116
11117   if (Feld[x][y] == EL_BLOCKED)
11118   {
11119     Blocked2Moving(x, y, &x, &y);
11120     element = Feld[x][y];
11121   }
11122
11123 #if 0
11124   /* check if element has already changed */
11125   if (Feld[x][y] != element)
11126     return FALSE;
11127 #else
11128   /* check if element has already changed or is about to change after moving */
11129   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11130        Feld[x][y] != element) ||
11131
11132       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11133        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11134         ChangePage[x][y] != -1)))
11135     return FALSE;
11136 #endif
11137
11138 #if 0
11139   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11140          trigger_event, recursion_loop_depth, recursion_loop_detected,
11141          recursion_loop_element, EL_NAME(recursion_loop_element));
11142 #endif
11143
11144   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11145
11146   for (p = 0; p < element_info[element].num_change_pages; p++)
11147   {
11148     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11149
11150     /* check trigger element for all events where the element that is checked
11151        for changing interacts with a directly adjacent element -- this is
11152        different to element changes that affect other elements to change on the
11153        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11154     boolean check_trigger_element =
11155       (trigger_event == CE_TOUCHING_X ||
11156        trigger_event == CE_HITTING_X ||
11157        trigger_event == CE_HIT_BY_X ||
11158 #if 1
11159        /* this one was forgotten until 3.2.3 */
11160        trigger_event == CE_DIGGING_X);
11161 #endif
11162
11163     if (change->can_change_or_has_action &&
11164         change->has_event[trigger_event] &&
11165         change->trigger_side & trigger_side &&
11166         change->trigger_player & trigger_player &&
11167         (!check_trigger_element ||
11168          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11169     {
11170       change->actual_trigger_element = trigger_element;
11171       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11172       change->actual_trigger_player_bits = trigger_player;
11173       change->actual_trigger_side = trigger_side;
11174       change->actual_trigger_ce_value = CustomValue[x][y];
11175       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11176
11177       /* special case: trigger element not at (x,y) position for some events */
11178       if (check_trigger_element)
11179       {
11180         static struct
11181         {
11182           int dx, dy;
11183         } move_xy[] =
11184           {
11185             {  0,  0 },
11186             { -1,  0 },
11187             { +1,  0 },
11188             {  0,  0 },
11189             {  0, -1 },
11190             {  0,  0 }, { 0, 0 }, { 0, 0 },
11191             {  0, +1 }
11192           };
11193
11194         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11195         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11196
11197         change->actual_trigger_ce_value = CustomValue[xx][yy];
11198         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11199       }
11200
11201       if (change->can_change && !change_done)
11202       {
11203         ChangeDelay[x][y] = 1;
11204         ChangeEvent[x][y] = trigger_event;
11205
11206         HandleElementChange(x, y, p);
11207
11208         change_done = TRUE;
11209       }
11210 #if USE_NEW_DELAYED_ACTION
11211       else if (change->has_action)
11212       {
11213         ExecuteCustomElementAction(x, y, element, p);
11214         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11215       }
11216 #else
11217       if (change->has_action)
11218       {
11219         ExecuteCustomElementAction(x, y, element, p);
11220         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11221       }
11222 #endif
11223     }
11224   }
11225
11226   RECURSION_LOOP_DETECTION_END();
11227
11228   return change_done;
11229 }
11230
11231 static void PlayPlayerSound(struct PlayerInfo *player)
11232 {
11233   int jx = player->jx, jy = player->jy;
11234   int sound_element = player->artwork_element;
11235   int last_action = player->last_action_waiting;
11236   int action = player->action_waiting;
11237
11238   if (player->is_waiting)
11239   {
11240     if (action != last_action)
11241       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11242     else
11243       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11244   }
11245   else
11246   {
11247     if (action != last_action)
11248       StopSound(element_info[sound_element].sound[last_action]);
11249
11250     if (last_action == ACTION_SLEEPING)
11251       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11252   }
11253 }
11254
11255 static void PlayAllPlayersSound()
11256 {
11257   int i;
11258
11259   for (i = 0; i < MAX_PLAYERS; i++)
11260     if (stored_player[i].active)
11261       PlayPlayerSound(&stored_player[i]);
11262 }
11263
11264 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11265 {
11266   boolean last_waiting = player->is_waiting;
11267   int move_dir = player->MovDir;
11268
11269   player->dir_waiting = move_dir;
11270   player->last_action_waiting = player->action_waiting;
11271
11272   if (is_waiting)
11273   {
11274     if (!last_waiting)          /* not waiting -> waiting */
11275     {
11276       player->is_waiting = TRUE;
11277
11278       player->frame_counter_bored =
11279         FrameCounter +
11280         game.player_boring_delay_fixed +
11281         GetSimpleRandom(game.player_boring_delay_random);
11282       player->frame_counter_sleeping =
11283         FrameCounter +
11284         game.player_sleeping_delay_fixed +
11285         GetSimpleRandom(game.player_sleeping_delay_random);
11286
11287       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11288     }
11289
11290     if (game.player_sleeping_delay_fixed +
11291         game.player_sleeping_delay_random > 0 &&
11292         player->anim_delay_counter == 0 &&
11293         player->post_delay_counter == 0 &&
11294         FrameCounter >= player->frame_counter_sleeping)
11295       player->is_sleeping = TRUE;
11296     else if (game.player_boring_delay_fixed +
11297              game.player_boring_delay_random > 0 &&
11298              FrameCounter >= player->frame_counter_bored)
11299       player->is_bored = TRUE;
11300
11301     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11302                               player->is_bored ? ACTION_BORING :
11303                               ACTION_WAITING);
11304
11305     if (player->is_sleeping && player->use_murphy)
11306     {
11307       /* special case for sleeping Murphy when leaning against non-free tile */
11308
11309       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11310           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11311            !IS_MOVING(player->jx - 1, player->jy)))
11312         move_dir = MV_LEFT;
11313       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11314                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11315                 !IS_MOVING(player->jx + 1, player->jy)))
11316         move_dir = MV_RIGHT;
11317       else
11318         player->is_sleeping = FALSE;
11319
11320       player->dir_waiting = move_dir;
11321     }
11322
11323     if (player->is_sleeping)
11324     {
11325       if (player->num_special_action_sleeping > 0)
11326       {
11327         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11328         {
11329           int last_special_action = player->special_action_sleeping;
11330           int num_special_action = player->num_special_action_sleeping;
11331           int special_action =
11332             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11333              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11334              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11335              last_special_action + 1 : ACTION_SLEEPING);
11336           int special_graphic =
11337             el_act_dir2img(player->artwork_element, special_action, move_dir);
11338
11339           player->anim_delay_counter =
11340             graphic_info[special_graphic].anim_delay_fixed +
11341             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11342           player->post_delay_counter =
11343             graphic_info[special_graphic].post_delay_fixed +
11344             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11345
11346           player->special_action_sleeping = special_action;
11347         }
11348
11349         if (player->anim_delay_counter > 0)
11350         {
11351           player->action_waiting = player->special_action_sleeping;
11352           player->anim_delay_counter--;
11353         }
11354         else if (player->post_delay_counter > 0)
11355         {
11356           player->post_delay_counter--;
11357         }
11358       }
11359     }
11360     else if (player->is_bored)
11361     {
11362       if (player->num_special_action_bored > 0)
11363       {
11364         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11365         {
11366           int special_action =
11367             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11368           int special_graphic =
11369             el_act_dir2img(player->artwork_element, special_action, move_dir);
11370
11371           player->anim_delay_counter =
11372             graphic_info[special_graphic].anim_delay_fixed +
11373             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11374           player->post_delay_counter =
11375             graphic_info[special_graphic].post_delay_fixed +
11376             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11377
11378           player->special_action_bored = special_action;
11379         }
11380
11381         if (player->anim_delay_counter > 0)
11382         {
11383           player->action_waiting = player->special_action_bored;
11384           player->anim_delay_counter--;
11385         }
11386         else if (player->post_delay_counter > 0)
11387         {
11388           player->post_delay_counter--;
11389         }
11390       }
11391     }
11392   }
11393   else if (last_waiting)        /* waiting -> not waiting */
11394   {
11395     player->is_waiting = FALSE;
11396     player->is_bored = FALSE;
11397     player->is_sleeping = FALSE;
11398
11399     player->frame_counter_bored = -1;
11400     player->frame_counter_sleeping = -1;
11401
11402     player->anim_delay_counter = 0;
11403     player->post_delay_counter = 0;
11404
11405     player->dir_waiting = player->MovDir;
11406     player->action_waiting = ACTION_DEFAULT;
11407
11408     player->special_action_bored = ACTION_DEFAULT;
11409     player->special_action_sleeping = ACTION_DEFAULT;
11410   }
11411 }
11412
11413 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11414 {
11415   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11416   int left      = player_action & JOY_LEFT;
11417   int right     = player_action & JOY_RIGHT;
11418   int up        = player_action & JOY_UP;
11419   int down      = player_action & JOY_DOWN;
11420   int button1   = player_action & JOY_BUTTON_1;
11421   int button2   = player_action & JOY_BUTTON_2;
11422   int dx        = (left ? -1 : right ? 1 : 0);
11423   int dy        = (up   ? -1 : down  ? 1 : 0);
11424
11425   if (!player->active || tape.pausing)
11426     return 0;
11427
11428   if (player_action)
11429   {
11430     if (button1)
11431       snapped = SnapField(player, dx, dy);
11432     else
11433     {
11434       if (button2)
11435         dropped = DropElement(player);
11436
11437       moved = MovePlayer(player, dx, dy);
11438     }
11439
11440     if (tape.single_step && tape.recording && !tape.pausing)
11441     {
11442       if (button1 || (dropped && !moved))
11443       {
11444         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11445         SnapField(player, 0, 0);                /* stop snapping */
11446       }
11447     }
11448
11449     SetPlayerWaiting(player, FALSE);
11450
11451     return player_action;
11452   }
11453   else
11454   {
11455     /* no actions for this player (no input at player's configured device) */
11456
11457     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11458     SnapField(player, 0, 0);
11459     CheckGravityMovementWhenNotMoving(player);
11460
11461     if (player->MovPos == 0)
11462       SetPlayerWaiting(player, TRUE);
11463
11464     if (player->MovPos == 0)    /* needed for tape.playing */
11465       player->is_moving = FALSE;
11466
11467     player->is_dropping = FALSE;
11468     player->is_dropping_pressed = FALSE;
11469     player->drop_pressed_delay = 0;
11470
11471     return 0;
11472   }
11473 }
11474
11475 static void CheckLevelTime()
11476 {
11477   int i;
11478
11479   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11480   {
11481     if (level.native_em_level->lev->home == 0)  /* all players at home */
11482     {
11483       PlayerWins(local_player);
11484
11485       AllPlayersGone = TRUE;
11486
11487       level.native_em_level->lev->home = -1;
11488     }
11489
11490     if (level.native_em_level->ply[0]->alive == 0 &&
11491         level.native_em_level->ply[1]->alive == 0 &&
11492         level.native_em_level->ply[2]->alive == 0 &&
11493         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11494       AllPlayersGone = TRUE;
11495   }
11496
11497   if (TimeFrames >= FRAMES_PER_SECOND)
11498   {
11499     TimeFrames = 0;
11500     TapeTime++;
11501
11502     for (i = 0; i < MAX_PLAYERS; i++)
11503     {
11504       struct PlayerInfo *player = &stored_player[i];
11505
11506       if (SHIELD_ON(player))
11507       {
11508         player->shield_normal_time_left--;
11509
11510         if (player->shield_deadly_time_left > 0)
11511           player->shield_deadly_time_left--;
11512       }
11513     }
11514
11515     if (!local_player->LevelSolved && !level.use_step_counter)
11516     {
11517       TimePlayed++;
11518
11519       if (TimeLeft > 0)
11520       {
11521         TimeLeft--;
11522
11523         if (TimeLeft <= 10 && setup.time_limit)
11524           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11525
11526 #if 1
11527         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11528
11529         DisplayGameControlValues();
11530 #else
11531         DrawGameValue_Time(TimeLeft);
11532 #endif
11533
11534         if (!TimeLeft && setup.time_limit)
11535         {
11536           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11537             level.native_em_level->lev->killed_out_of_time = TRUE;
11538           else
11539             for (i = 0; i < MAX_PLAYERS; i++)
11540               KillPlayer(&stored_player[i]);
11541         }
11542       }
11543 #if 1
11544       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11545       {
11546         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11547
11548         DisplayGameControlValues();
11549       }
11550 #else
11551       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11552         DrawGameValue_Time(TimePlayed);
11553 #endif
11554
11555       level.native_em_level->lev->time =
11556         (level.time == 0 ? TimePlayed : TimeLeft);
11557     }
11558
11559     if (tape.recording || tape.playing)
11560       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11561   }
11562
11563 #if 1
11564   UpdateAndDisplayGameControlValues();
11565 #else
11566   UpdateGameDoorValues();
11567   DrawGameDoorValues();
11568 #endif
11569 }
11570
11571 void AdvanceFrameAndPlayerCounters(int player_nr)
11572 {
11573   int i;
11574
11575   /* advance frame counters (global frame counter and time frame counter) */
11576   FrameCounter++;
11577   TimeFrames++;
11578
11579   /* advance player counters (counters for move delay, move animation etc.) */
11580   for (i = 0; i < MAX_PLAYERS; i++)
11581   {
11582     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11583     int move_delay_value = stored_player[i].move_delay_value;
11584     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11585
11586     if (!advance_player_counters)       /* not all players may be affected */
11587       continue;
11588
11589 #if USE_NEW_PLAYER_ANIM
11590     if (move_frames == 0)       /* less than one move per game frame */
11591     {
11592       int stepsize = TILEX / move_delay_value;
11593       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11594       int count = (stored_player[i].is_moving ?
11595                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11596
11597       if (count % delay == 0)
11598         move_frames = 1;
11599     }
11600 #endif
11601
11602     stored_player[i].Frame += move_frames;
11603
11604     if (stored_player[i].MovPos != 0)
11605       stored_player[i].StepFrame += move_frames;
11606
11607     if (stored_player[i].move_delay > 0)
11608       stored_player[i].move_delay--;
11609
11610     /* due to bugs in previous versions, counter must count up, not down */
11611     if (stored_player[i].push_delay != -1)
11612       stored_player[i].push_delay++;
11613
11614     if (stored_player[i].drop_delay > 0)
11615       stored_player[i].drop_delay--;
11616
11617     if (stored_player[i].is_dropping_pressed)
11618       stored_player[i].drop_pressed_delay++;
11619   }
11620 }
11621
11622 void StartGameActions(boolean init_network_game, boolean record_tape,
11623                       long random_seed)
11624 {
11625   unsigned long new_random_seed = InitRND(random_seed);
11626
11627   if (record_tape)
11628     TapeStartRecording(new_random_seed);
11629
11630 #if defined(NETWORK_AVALIABLE)
11631   if (init_network_game)
11632   {
11633     SendToServer_StartPlaying();
11634
11635     return;
11636   }
11637 #endif
11638
11639   InitGame();
11640 }
11641
11642 void GameActions()
11643 {
11644   static unsigned long game_frame_delay = 0;
11645   unsigned long game_frame_delay_value;
11646   byte *recorded_player_action;
11647   byte summarized_player_action = 0;
11648   byte tape_action[MAX_PLAYERS];
11649   int i;
11650
11651   /* detect endless loops, caused by custom element programming */
11652   if (recursion_loop_detected && recursion_loop_depth == 0)
11653   {
11654     char *message = getStringCat3("Internal Error ! Element ",
11655                                   EL_NAME(recursion_loop_element),
11656                                   " caused endless loop ! Quit the game ?");
11657
11658     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11659           EL_NAME(recursion_loop_element));
11660
11661     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11662
11663     recursion_loop_detected = FALSE;    /* if game should be continued */
11664
11665     free(message);
11666
11667     return;
11668   }
11669
11670   if (game.restart_level)
11671     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11672
11673   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11674   {
11675     if (level.native_em_level->lev->home == 0)  /* all players at home */
11676     {
11677       PlayerWins(local_player);
11678
11679       AllPlayersGone = TRUE;
11680
11681       level.native_em_level->lev->home = -1;
11682     }
11683
11684     if (level.native_em_level->ply[0]->alive == 0 &&
11685         level.native_em_level->ply[1]->alive == 0 &&
11686         level.native_em_level->ply[2]->alive == 0 &&
11687         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11688       AllPlayersGone = TRUE;
11689   }
11690
11691   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11692     GameWon();
11693
11694   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11695     TapeStop();
11696
11697   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11698     return;
11699
11700   game_frame_delay_value =
11701     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11702
11703   if (tape.playing && tape.warp_forward && !tape.pausing)
11704     game_frame_delay_value = 0;
11705
11706   /* ---------- main game synchronization point ---------- */
11707
11708   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11709
11710   if (network_playing && !network_player_action_received)
11711   {
11712     /* try to get network player actions in time */
11713
11714 #if defined(NETWORK_AVALIABLE)
11715     /* last chance to get network player actions without main loop delay */
11716     HandleNetworking();
11717 #endif
11718
11719     /* game was quit by network peer */
11720     if (game_status != GAME_MODE_PLAYING)
11721       return;
11722
11723     if (!network_player_action_received)
11724       return;           /* failed to get network player actions in time */
11725
11726     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11727   }
11728
11729   if (tape.pausing)
11730     return;
11731
11732   /* at this point we know that we really continue executing the game */
11733
11734   network_player_action_received = FALSE;
11735
11736   /* when playing tape, read previously recorded player input from tape data */
11737   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11738
11739 #if 1
11740   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11741   if (tape.pausing)
11742     return;
11743 #endif
11744
11745   if (tape.set_centered_player)
11746   {
11747     game.centered_player_nr_next = tape.centered_player_nr_next;
11748     game.set_centered_player = TRUE;
11749   }
11750
11751   for (i = 0; i < MAX_PLAYERS; i++)
11752   {
11753     summarized_player_action |= stored_player[i].action;
11754
11755     if (!network_playing)
11756       stored_player[i].effective_action = stored_player[i].action;
11757   }
11758
11759 #if defined(NETWORK_AVALIABLE)
11760   if (network_playing)
11761     SendToServer_MovePlayer(summarized_player_action);
11762 #endif
11763
11764   if (!options.network && !setup.team_mode)
11765     local_player->effective_action = summarized_player_action;
11766
11767   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11768   {
11769     for (i = 0; i < MAX_PLAYERS; i++)
11770       stored_player[i].effective_action =
11771         (i == game.centered_player_nr ? summarized_player_action : 0);
11772   }
11773
11774   if (recorded_player_action != NULL)
11775     for (i = 0; i < MAX_PLAYERS; i++)
11776       stored_player[i].effective_action = recorded_player_action[i];
11777
11778   for (i = 0; i < MAX_PLAYERS; i++)
11779   {
11780     tape_action[i] = stored_player[i].effective_action;
11781
11782     /* (this can only happen in the R'n'D game engine) */
11783     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11784       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11785   }
11786
11787   /* only record actions from input devices, but not programmed actions */
11788   if (tape.recording)
11789     TapeRecordAction(tape_action);
11790
11791   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11792   {
11793     GameActions_EM_Main();
11794   }
11795   else
11796   {
11797     GameActions_RND();
11798   }
11799 }
11800
11801 void GameActions_EM_Main()
11802 {
11803   byte effective_action[MAX_PLAYERS];
11804   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11805   int i;
11806
11807   for (i = 0; i < MAX_PLAYERS; i++)
11808     effective_action[i] = stored_player[i].effective_action;
11809
11810   GameActions_EM(effective_action, warp_mode);
11811
11812   CheckLevelTime();
11813
11814   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11815 }
11816
11817 void GameActions_RND()
11818 {
11819   int magic_wall_x = 0, magic_wall_y = 0;
11820   int i, x, y, element, graphic;
11821
11822   InitPlayfieldScanModeVars();
11823
11824 #if USE_ONE_MORE_CHANGE_PER_FRAME
11825   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11826   {
11827     SCAN_PLAYFIELD(x, y)
11828     {
11829       ChangeCount[x][y] = 0;
11830       ChangeEvent[x][y] = -1;
11831     }
11832   }
11833 #endif
11834
11835   if (game.set_centered_player)
11836   {
11837     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11838
11839     /* switching to "all players" only possible if all players fit to screen */
11840     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11841     {
11842       game.centered_player_nr_next = game.centered_player_nr;
11843       game.set_centered_player = FALSE;
11844     }
11845
11846     /* do not switch focus to non-existing (or non-active) player */
11847     if (game.centered_player_nr_next >= 0 &&
11848         !stored_player[game.centered_player_nr_next].active)
11849     {
11850       game.centered_player_nr_next = game.centered_player_nr;
11851       game.set_centered_player = FALSE;
11852     }
11853   }
11854
11855   if (game.set_centered_player &&
11856       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11857   {
11858     int sx, sy;
11859
11860     if (game.centered_player_nr_next == -1)
11861     {
11862       setScreenCenteredToAllPlayers(&sx, &sy);
11863     }
11864     else
11865     {
11866       sx = stored_player[game.centered_player_nr_next].jx;
11867       sy = stored_player[game.centered_player_nr_next].jy;
11868     }
11869
11870     game.centered_player_nr = game.centered_player_nr_next;
11871     game.set_centered_player = FALSE;
11872
11873     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11874     DrawGameDoorValues();
11875   }
11876
11877   for (i = 0; i < MAX_PLAYERS; i++)
11878   {
11879     int actual_player_action = stored_player[i].effective_action;
11880
11881 #if 1
11882     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11883        - rnd_equinox_tetrachloride 048
11884        - rnd_equinox_tetrachloride_ii 096
11885        - rnd_emanuel_schmieg 002
11886        - doctor_sloan_ww 001, 020
11887     */
11888     if (stored_player[i].MovPos == 0)
11889       CheckGravityMovement(&stored_player[i]);
11890 #endif
11891
11892     /* overwrite programmed action with tape action */
11893     if (stored_player[i].programmed_action)
11894       actual_player_action = stored_player[i].programmed_action;
11895
11896     PlayerActions(&stored_player[i], actual_player_action);
11897
11898     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11899   }
11900
11901   ScrollScreen(NULL, SCROLL_GO_ON);
11902
11903   /* for backwards compatibility, the following code emulates a fixed bug that
11904      occured when pushing elements (causing elements that just made their last
11905      pushing step to already (if possible) make their first falling step in the
11906      same game frame, which is bad); this code is also needed to use the famous
11907      "spring push bug" which is used in older levels and might be wanted to be
11908      used also in newer levels, but in this case the buggy pushing code is only
11909      affecting the "spring" element and no other elements */
11910
11911   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11912   {
11913     for (i = 0; i < MAX_PLAYERS; i++)
11914     {
11915       struct PlayerInfo *player = &stored_player[i];
11916       int x = player->jx;
11917       int y = player->jy;
11918
11919       if (player->active && player->is_pushing && player->is_moving &&
11920           IS_MOVING(x, y) &&
11921           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11922            Feld[x][y] == EL_SPRING))
11923       {
11924         ContinueMoving(x, y);
11925
11926         /* continue moving after pushing (this is actually a bug) */
11927         if (!IS_MOVING(x, y))
11928           Stop[x][y] = FALSE;
11929       }
11930     }
11931   }
11932
11933 #if 0
11934   debug_print_timestamp(0, "start main loop profiling");
11935 #endif
11936
11937   SCAN_PLAYFIELD(x, y)
11938   {
11939     ChangeCount[x][y] = 0;
11940     ChangeEvent[x][y] = -1;
11941
11942     /* this must be handled before main playfield loop */
11943     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11944     {
11945       MovDelay[x][y]--;
11946       if (MovDelay[x][y] <= 0)
11947         RemoveField(x, y);
11948     }
11949
11950 #if USE_NEW_SNAP_DELAY
11951     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11952     {
11953       MovDelay[x][y]--;
11954       if (MovDelay[x][y] <= 0)
11955       {
11956         RemoveField(x, y);
11957         DrawLevelField(x, y);
11958
11959         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11960       }
11961     }
11962 #endif
11963
11964 #if DEBUG
11965     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11966     {
11967       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11968       printf("GameActions(): This should never happen!\n");
11969
11970       ChangePage[x][y] = -1;
11971     }
11972 #endif
11973
11974     Stop[x][y] = FALSE;
11975     if (WasJustMoving[x][y] > 0)
11976       WasJustMoving[x][y]--;
11977     if (WasJustFalling[x][y] > 0)
11978       WasJustFalling[x][y]--;
11979     if (CheckCollision[x][y] > 0)
11980       CheckCollision[x][y]--;
11981     if (CheckImpact[x][y] > 0)
11982       CheckImpact[x][y]--;
11983
11984     GfxFrame[x][y]++;
11985
11986     /* reset finished pushing action (not done in ContinueMoving() to allow
11987        continuous pushing animation for elements with zero push delay) */
11988     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11989     {
11990       ResetGfxAnimation(x, y);
11991       DrawLevelField(x, y);
11992     }
11993
11994 #if DEBUG
11995     if (IS_BLOCKED(x, y))
11996     {
11997       int oldx, oldy;
11998
11999       Blocked2Moving(x, y, &oldx, &oldy);
12000       if (!IS_MOVING(oldx, oldy))
12001       {
12002         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12003         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12004         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12005         printf("GameActions(): This should never happen!\n");
12006       }
12007     }
12008 #endif
12009   }
12010
12011 #if 0
12012   debug_print_timestamp(0, "- time for pre-main loop:");
12013 #endif
12014
12015 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12016   SCAN_PLAYFIELD(x, y)
12017   {
12018     element = Feld[x][y];
12019     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12020
12021 #if 1
12022     {
12023 #if 1
12024       int element2 = element;
12025       int graphic2 = graphic;
12026 #else
12027       int element2 = Feld[x][y];
12028       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12029 #endif
12030       int last_gfx_frame = GfxFrame[x][y];
12031
12032       if (graphic_info[graphic2].anim_global_sync)
12033         GfxFrame[x][y] = FrameCounter;
12034       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12035         GfxFrame[x][y] = CustomValue[x][y];
12036       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12037         GfxFrame[x][y] = element_info[element2].collect_score;
12038       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12039         GfxFrame[x][y] = ChangeDelay[x][y];
12040
12041       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12042         DrawLevelGraphicAnimation(x, y, graphic2);
12043     }
12044 #else
12045     ResetGfxFrame(x, y, TRUE);
12046 #endif
12047
12048 #if 1
12049     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12050         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12051       ResetRandomAnimationValue(x, y);
12052 #endif
12053
12054 #if 1
12055     SetRandomAnimationValue(x, y);
12056 #endif
12057
12058 #if 1
12059     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12060 #endif
12061   }
12062 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12063
12064 #if 0
12065   debug_print_timestamp(0, "- time for TEST loop:     -->");
12066 #endif
12067
12068   SCAN_PLAYFIELD(x, y)
12069   {
12070     element = Feld[x][y];
12071     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12072
12073     ResetGfxFrame(x, y, TRUE);
12074
12075     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12076         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12077       ResetRandomAnimationValue(x, y);
12078
12079     SetRandomAnimationValue(x, y);
12080
12081     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12082
12083     if (IS_INACTIVE(element))
12084     {
12085       if (IS_ANIMATED(graphic))
12086         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12087
12088       continue;
12089     }
12090
12091     /* this may take place after moving, so 'element' may have changed */
12092     if (IS_CHANGING(x, y) &&
12093         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12094     {
12095       int page = element_info[element].event_page_nr[CE_DELAY];
12096
12097 #if 1
12098       HandleElementChange(x, y, page);
12099 #else
12100       if (CAN_CHANGE(element))
12101         HandleElementChange(x, y, page);
12102
12103       if (HAS_ACTION(element))
12104         ExecuteCustomElementAction(x, y, element, page);
12105 #endif
12106
12107       element = Feld[x][y];
12108       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12109     }
12110
12111 #if 0   // ---------------------------------------------------------------------
12112
12113     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12114     {
12115       StartMoving(x, y);
12116
12117       element = Feld[x][y];
12118       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12119
12120       if (IS_ANIMATED(graphic) &&
12121           !IS_MOVING(x, y) &&
12122           !Stop[x][y])
12123         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12124
12125       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12126         DrawTwinkleOnField(x, y);
12127     }
12128     else if (IS_MOVING(x, y))
12129       ContinueMoving(x, y);
12130     else
12131     {
12132       switch (element)
12133       {
12134         case EL_ACID:
12135         case EL_EXIT_OPEN:
12136         case EL_EM_EXIT_OPEN:
12137         case EL_SP_EXIT_OPEN:
12138         case EL_STEEL_EXIT_OPEN:
12139         case EL_EM_STEEL_EXIT_OPEN:
12140         case EL_SP_TERMINAL:
12141         case EL_SP_TERMINAL_ACTIVE:
12142         case EL_EXTRA_TIME:
12143         case EL_SHIELD_NORMAL:
12144         case EL_SHIELD_DEADLY:
12145           if (IS_ANIMATED(graphic))
12146             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12147           break;
12148
12149         case EL_DYNAMITE_ACTIVE:
12150         case EL_EM_DYNAMITE_ACTIVE:
12151         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12152         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12153         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12154         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12155         case EL_SP_DISK_RED_ACTIVE:
12156           CheckDynamite(x, y);
12157           break;
12158
12159         case EL_AMOEBA_GROWING:
12160           AmoebeWaechst(x, y);
12161           break;
12162
12163         case EL_AMOEBA_SHRINKING:
12164           AmoebaDisappearing(x, y);
12165           break;
12166
12167 #if !USE_NEW_AMOEBA_CODE
12168         case EL_AMOEBA_WET:
12169         case EL_AMOEBA_DRY:
12170         case EL_AMOEBA_FULL:
12171         case EL_BD_AMOEBA:
12172         case EL_EMC_DRIPPER:
12173           AmoebeAbleger(x, y);
12174           break;
12175 #endif
12176
12177         case EL_GAME_OF_LIFE:
12178         case EL_BIOMAZE:
12179           Life(x, y);
12180           break;
12181
12182         case EL_EXIT_CLOSED:
12183           CheckExit(x, y);
12184           break;
12185
12186         case EL_EM_EXIT_CLOSED:
12187           CheckExitEM(x, y);
12188           break;
12189
12190         case EL_STEEL_EXIT_CLOSED:
12191           CheckExitSteel(x, y);
12192           break;
12193
12194         case EL_EM_STEEL_EXIT_CLOSED:
12195           CheckExitSteelEM(x, y);
12196           break;
12197
12198         case EL_SP_EXIT_CLOSED:
12199           CheckExitSP(x, y);
12200           break;
12201
12202         case EL_EXPANDABLE_WALL_GROWING:
12203         case EL_EXPANDABLE_STEELWALL_GROWING:
12204           MauerWaechst(x, y);
12205           break;
12206
12207         case EL_EXPANDABLE_WALL:
12208         case EL_EXPANDABLE_WALL_HORIZONTAL:
12209         case EL_EXPANDABLE_WALL_VERTICAL:
12210         case EL_EXPANDABLE_WALL_ANY:
12211         case EL_BD_EXPANDABLE_WALL:
12212           MauerAbleger(x, y);
12213           break;
12214
12215         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12216         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12217         case EL_EXPANDABLE_STEELWALL_ANY:
12218           MauerAblegerStahl(x, y);
12219           break;
12220
12221         case EL_FLAMES:
12222           CheckForDragon(x, y);
12223           break;
12224
12225         case EL_EXPLOSION:
12226           break;
12227
12228         case EL_ELEMENT_SNAPPING:
12229         case EL_DIAGONAL_SHRINKING:
12230         case EL_DIAGONAL_GROWING:
12231         {
12232           graphic =
12233             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12234
12235           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12236           break;
12237         }
12238
12239         default:
12240           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12241             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12242           break;
12243       }
12244     }
12245
12246 #else   // ---------------------------------------------------------------------
12247
12248     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12249     {
12250       StartMoving(x, y);
12251
12252       element = Feld[x][y];
12253       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12254
12255       if (IS_ANIMATED(graphic) &&
12256           !IS_MOVING(x, y) &&
12257           !Stop[x][y])
12258         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12259
12260       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12261         DrawTwinkleOnField(x, y);
12262     }
12263     else if ((element == EL_ACID ||
12264               element == EL_EXIT_OPEN ||
12265               element == EL_EM_EXIT_OPEN ||
12266               element == EL_SP_EXIT_OPEN ||
12267               element == EL_STEEL_EXIT_OPEN ||
12268               element == EL_EM_STEEL_EXIT_OPEN ||
12269               element == EL_SP_TERMINAL ||
12270               element == EL_SP_TERMINAL_ACTIVE ||
12271               element == EL_EXTRA_TIME ||
12272               element == EL_SHIELD_NORMAL ||
12273               element == EL_SHIELD_DEADLY) &&
12274              IS_ANIMATED(graphic))
12275       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12276     else if (IS_MOVING(x, y))
12277       ContinueMoving(x, y);
12278     else if (IS_ACTIVE_BOMB(element))
12279       CheckDynamite(x, y);
12280     else if (element == EL_AMOEBA_GROWING)
12281       AmoebeWaechst(x, y);
12282     else if (element == EL_AMOEBA_SHRINKING)
12283       AmoebaDisappearing(x, y);
12284
12285 #if !USE_NEW_AMOEBA_CODE
12286     else if (IS_AMOEBALIVE(element))
12287       AmoebeAbleger(x, y);
12288 #endif
12289
12290     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12291       Life(x, y);
12292     else if (element == EL_EXIT_CLOSED)
12293       CheckExit(x, y);
12294     else if (element == EL_EM_EXIT_CLOSED)
12295       CheckExitEM(x, y);
12296     else if (element == EL_STEEL_EXIT_CLOSED)
12297       CheckExitSteel(x, y);
12298     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12299       CheckExitSteelEM(x, y);
12300     else if (element == EL_SP_EXIT_CLOSED)
12301       CheckExitSP(x, y);
12302     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12303              element == EL_EXPANDABLE_STEELWALL_GROWING)
12304       MauerWaechst(x, y);
12305     else if (element == EL_EXPANDABLE_WALL ||
12306              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12307              element == EL_EXPANDABLE_WALL_VERTICAL ||
12308              element == EL_EXPANDABLE_WALL_ANY ||
12309              element == EL_BD_EXPANDABLE_WALL)
12310       MauerAbleger(x, y);
12311     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12312              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12313              element == EL_EXPANDABLE_STEELWALL_ANY)
12314       MauerAblegerStahl(x, y);
12315     else if (element == EL_FLAMES)
12316       CheckForDragon(x, y);
12317     else if (element == EL_EXPLOSION)
12318       ; /* drawing of correct explosion animation is handled separately */
12319     else if (element == EL_ELEMENT_SNAPPING ||
12320              element == EL_DIAGONAL_SHRINKING ||
12321              element == EL_DIAGONAL_GROWING)
12322     {
12323       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12324
12325       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12326     }
12327     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12328       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12329
12330 #endif  // ---------------------------------------------------------------------
12331
12332     if (IS_BELT_ACTIVE(element))
12333       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12334
12335     if (game.magic_wall_active)
12336     {
12337       int jx = local_player->jx, jy = local_player->jy;
12338
12339       /* play the element sound at the position nearest to the player */
12340       if ((element == EL_MAGIC_WALL_FULL ||
12341            element == EL_MAGIC_WALL_ACTIVE ||
12342            element == EL_MAGIC_WALL_EMPTYING ||
12343            element == EL_BD_MAGIC_WALL_FULL ||
12344            element == EL_BD_MAGIC_WALL_ACTIVE ||
12345            element == EL_BD_MAGIC_WALL_EMPTYING ||
12346            element == EL_DC_MAGIC_WALL_FULL ||
12347            element == EL_DC_MAGIC_WALL_ACTIVE ||
12348            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12349           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12350       {
12351         magic_wall_x = x;
12352         magic_wall_y = y;
12353       }
12354     }
12355   }
12356
12357 #if 0
12358   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12359 #endif
12360
12361 #if USE_NEW_AMOEBA_CODE
12362   /* new experimental amoeba growth stuff */
12363   if (!(FrameCounter % 8))
12364   {
12365     static unsigned long random = 1684108901;
12366
12367     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12368     {
12369       x = RND(lev_fieldx);
12370       y = RND(lev_fieldy);
12371       element = Feld[x][y];
12372
12373       if (!IS_PLAYER(x,y) &&
12374           (element == EL_EMPTY ||
12375            CAN_GROW_INTO(element) ||
12376            element == EL_QUICKSAND_EMPTY ||
12377            element == EL_QUICKSAND_FAST_EMPTY ||
12378            element == EL_ACID_SPLASH_LEFT ||
12379            element == EL_ACID_SPLASH_RIGHT))
12380       {
12381         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12382             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12383             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12384             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12385           Feld[x][y] = EL_AMOEBA_DROP;
12386       }
12387
12388       random = random * 129 + 1;
12389     }
12390   }
12391 #endif
12392
12393 #if 0
12394   if (game.explosions_delayed)
12395 #endif
12396   {
12397     game.explosions_delayed = FALSE;
12398
12399     SCAN_PLAYFIELD(x, y)
12400     {
12401       element = Feld[x][y];
12402
12403       if (ExplodeField[x][y])
12404         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12405       else if (element == EL_EXPLOSION)
12406         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12407
12408       ExplodeField[x][y] = EX_TYPE_NONE;
12409     }
12410
12411     game.explosions_delayed = TRUE;
12412   }
12413
12414   if (game.magic_wall_active)
12415   {
12416     if (!(game.magic_wall_time_left % 4))
12417     {
12418       int element = Feld[magic_wall_x][magic_wall_y];
12419
12420       if (element == EL_BD_MAGIC_WALL_FULL ||
12421           element == EL_BD_MAGIC_WALL_ACTIVE ||
12422           element == EL_BD_MAGIC_WALL_EMPTYING)
12423         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12424       else if (element == EL_DC_MAGIC_WALL_FULL ||
12425                element == EL_DC_MAGIC_WALL_ACTIVE ||
12426                element == EL_DC_MAGIC_WALL_EMPTYING)
12427         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12428       else
12429         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12430     }
12431
12432     if (game.magic_wall_time_left > 0)
12433     {
12434       game.magic_wall_time_left--;
12435
12436       if (!game.magic_wall_time_left)
12437       {
12438         SCAN_PLAYFIELD(x, y)
12439         {
12440           element = Feld[x][y];
12441
12442           if (element == EL_MAGIC_WALL_ACTIVE ||
12443               element == EL_MAGIC_WALL_FULL)
12444           {
12445             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12446             DrawLevelField(x, y);
12447           }
12448           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12449                    element == EL_BD_MAGIC_WALL_FULL)
12450           {
12451             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12452             DrawLevelField(x, y);
12453           }
12454           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12455                    element == EL_DC_MAGIC_WALL_FULL)
12456           {
12457             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12458             DrawLevelField(x, y);
12459           }
12460         }
12461
12462         game.magic_wall_active = FALSE;
12463       }
12464     }
12465   }
12466
12467   if (game.light_time_left > 0)
12468   {
12469     game.light_time_left--;
12470
12471     if (game.light_time_left == 0)
12472       RedrawAllLightSwitchesAndInvisibleElements();
12473   }
12474
12475   if (game.timegate_time_left > 0)
12476   {
12477     game.timegate_time_left--;
12478
12479     if (game.timegate_time_left == 0)
12480       CloseAllOpenTimegates();
12481   }
12482
12483   if (game.lenses_time_left > 0)
12484   {
12485     game.lenses_time_left--;
12486
12487     if (game.lenses_time_left == 0)
12488       RedrawAllInvisibleElementsForLenses();
12489   }
12490
12491   if (game.magnify_time_left > 0)
12492   {
12493     game.magnify_time_left--;
12494
12495     if (game.magnify_time_left == 0)
12496       RedrawAllInvisibleElementsForMagnifier();
12497   }
12498
12499   for (i = 0; i < MAX_PLAYERS; i++)
12500   {
12501     struct PlayerInfo *player = &stored_player[i];
12502
12503     if (SHIELD_ON(player))
12504     {
12505       if (player->shield_deadly_time_left)
12506         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12507       else if (player->shield_normal_time_left)
12508         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12509     }
12510   }
12511
12512   CheckLevelTime();
12513
12514   DrawAllPlayers();
12515   PlayAllPlayersSound();
12516
12517   if (options.debug)                    /* calculate frames per second */
12518   {
12519     static unsigned long fps_counter = 0;
12520     static int fps_frames = 0;
12521     unsigned long fps_delay_ms = Counter() - fps_counter;
12522
12523     fps_frames++;
12524
12525     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12526     {
12527       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12528
12529       fps_frames = 0;
12530       fps_counter = Counter();
12531     }
12532
12533     redraw_mask |= REDRAW_FPS;
12534   }
12535
12536   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12537
12538   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12539   {
12540     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12541
12542     local_player->show_envelope = 0;
12543   }
12544
12545 #if 0
12546   debug_print_timestamp(0, "stop main loop profiling ");
12547   printf("----------------------------------------------------------\n");
12548 #endif
12549
12550   /* use random number generator in every frame to make it less predictable */
12551   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12552     RND(1);
12553 }
12554
12555 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12556 {
12557   int min_x = x, min_y = y, max_x = x, max_y = y;
12558   int i;
12559
12560   for (i = 0; i < MAX_PLAYERS; i++)
12561   {
12562     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12563
12564     if (!stored_player[i].active || &stored_player[i] == player)
12565       continue;
12566
12567     min_x = MIN(min_x, jx);
12568     min_y = MIN(min_y, jy);
12569     max_x = MAX(max_x, jx);
12570     max_y = MAX(max_y, jy);
12571   }
12572
12573   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12574 }
12575
12576 static boolean AllPlayersInVisibleScreen()
12577 {
12578   int i;
12579
12580   for (i = 0; i < MAX_PLAYERS; i++)
12581   {
12582     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12583
12584     if (!stored_player[i].active)
12585       continue;
12586
12587     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12588       return FALSE;
12589   }
12590
12591   return TRUE;
12592 }
12593
12594 void ScrollLevel(int dx, int dy)
12595 {
12596 #if 1
12597   static Bitmap *bitmap_db_field2 = NULL;
12598   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12599   int x, y;
12600 #else
12601   int i, x, y;
12602 #endif
12603
12604 #if 0
12605   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12606   /* only horizontal XOR vertical scroll direction allowed */
12607   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12608     return;
12609 #endif
12610
12611 #if 1
12612   if (bitmap_db_field2 == NULL)
12613     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12614
12615   /* needed when blitting directly to same bitmap -- should not be needed with
12616      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12617   BlitBitmap(drawto_field, bitmap_db_field2,
12618              FX + TILEX * (dx == -1) - softscroll_offset,
12619              FY + TILEY * (dy == -1) - softscroll_offset,
12620              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12621              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12622              FX + TILEX * (dx == 1) - softscroll_offset,
12623              FY + TILEY * (dy == 1) - softscroll_offset);
12624   BlitBitmap(bitmap_db_field2, drawto_field,
12625              FX + TILEX * (dx == 1) - softscroll_offset,
12626              FY + TILEY * (dy == 1) - softscroll_offset,
12627              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12628              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12629              FX + TILEX * (dx == 1) - softscroll_offset,
12630              FY + TILEY * (dy == 1) - softscroll_offset);
12631
12632 #else
12633
12634 #if 0
12635   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12636   int xsize = (BX2 - BX1 + 1);
12637   int ysize = (BY2 - BY1 + 1);
12638   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12639   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12640   int step  = (start < end ? +1 : -1);
12641
12642   for (i = start; i != end; i += step)
12643   {
12644     BlitBitmap(drawto_field, drawto_field,
12645                FX + TILEX * (dx != 0 ? i + step : 0),
12646                FY + TILEY * (dy != 0 ? i + step : 0),
12647                TILEX * (dx != 0 ? 1 : xsize),
12648                TILEY * (dy != 0 ? 1 : ysize),
12649                FX + TILEX * (dx != 0 ? i : 0),
12650                FY + TILEY * (dy != 0 ? i : 0));
12651   }
12652
12653 #else
12654
12655   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12656
12657   BlitBitmap(drawto_field, drawto_field,
12658              FX + TILEX * (dx == -1) - softscroll_offset,
12659              FY + TILEY * (dy == -1) - softscroll_offset,
12660              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12661              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12662              FX + TILEX * (dx == 1) - softscroll_offset,
12663              FY + TILEY * (dy == 1) - softscroll_offset);
12664 #endif
12665 #endif
12666
12667   if (dx != 0)
12668   {
12669     x = (dx == 1 ? BX1 : BX2);
12670     for (y = BY1; y <= BY2; y++)
12671       DrawScreenField(x, y);
12672   }
12673
12674   if (dy != 0)
12675   {
12676     y = (dy == 1 ? BY1 : BY2);
12677     for (x = BX1; x <= BX2; x++)
12678       DrawScreenField(x, y);
12679   }
12680
12681   redraw_mask |= REDRAW_FIELD;
12682 }
12683
12684 static boolean canFallDown(struct PlayerInfo *player)
12685 {
12686   int jx = player->jx, jy = player->jy;
12687
12688   return (IN_LEV_FIELD(jx, jy + 1) &&
12689           (IS_FREE(jx, jy + 1) ||
12690            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12691           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12692           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12693 }
12694
12695 static boolean canPassField(int x, int y, int move_dir)
12696 {
12697   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12698   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12699   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12700   int nextx = x + dx;
12701   int nexty = y + dy;
12702   int element = Feld[x][y];
12703
12704   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12705           !CAN_MOVE(element) &&
12706           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12707           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12708           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12709 }
12710
12711 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12712 {
12713   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12714   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12715   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12716   int newx = x + dx;
12717   int newy = y + dy;
12718
12719   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12720           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12721           (IS_DIGGABLE(Feld[newx][newy]) ||
12722            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12723            canPassField(newx, newy, move_dir)));
12724 }
12725
12726 static void CheckGravityMovement(struct PlayerInfo *player)
12727 {
12728 #if USE_PLAYER_GRAVITY
12729   if (player->gravity && !player->programmed_action)
12730 #else
12731   if (game.gravity && !player->programmed_action)
12732 #endif
12733   {
12734     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12735     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12736     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12737     int jx = player->jx, jy = player->jy;
12738     boolean player_is_moving_to_valid_field =
12739       (!player_is_snapping &&
12740        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12741         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12742     boolean player_can_fall_down = canFallDown(player);
12743
12744     if (player_can_fall_down &&
12745         !player_is_moving_to_valid_field)
12746       player->programmed_action = MV_DOWN;
12747   }
12748 }
12749
12750 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12751 {
12752   return CheckGravityMovement(player);
12753
12754 #if USE_PLAYER_GRAVITY
12755   if (player->gravity && !player->programmed_action)
12756 #else
12757   if (game.gravity && !player->programmed_action)
12758 #endif
12759   {
12760     int jx = player->jx, jy = player->jy;
12761     boolean field_under_player_is_free =
12762       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12763     boolean player_is_standing_on_valid_field =
12764       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12765        (IS_WALKABLE(Feld[jx][jy]) &&
12766         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12767
12768     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12769       player->programmed_action = MV_DOWN;
12770   }
12771 }
12772
12773 /*
12774   MovePlayerOneStep()
12775   -----------------------------------------------------------------------------
12776   dx, dy:               direction (non-diagonal) to try to move the player to
12777   real_dx, real_dy:     direction as read from input device (can be diagonal)
12778 */
12779
12780 boolean MovePlayerOneStep(struct PlayerInfo *player,
12781                           int dx, int dy, int real_dx, int real_dy)
12782 {
12783   int jx = player->jx, jy = player->jy;
12784   int new_jx = jx + dx, new_jy = jy + dy;
12785 #if !USE_FIXED_DONT_RUN_INTO
12786   int element;
12787 #endif
12788   int can_move;
12789   boolean player_can_move = !player->cannot_move;
12790
12791   if (!player->active || (!dx && !dy))
12792     return MP_NO_ACTION;
12793
12794   player->MovDir = (dx < 0 ? MV_LEFT :
12795                     dx > 0 ? MV_RIGHT :
12796                     dy < 0 ? MV_UP :
12797                     dy > 0 ? MV_DOWN :  MV_NONE);
12798
12799   if (!IN_LEV_FIELD(new_jx, new_jy))
12800     return MP_NO_ACTION;
12801
12802   if (!player_can_move)
12803   {
12804     if (player->MovPos == 0)
12805     {
12806       player->is_moving = FALSE;
12807       player->is_digging = FALSE;
12808       player->is_collecting = FALSE;
12809       player->is_snapping = FALSE;
12810       player->is_pushing = FALSE;
12811     }
12812   }
12813
12814 #if 1
12815   if (!options.network && game.centered_player_nr == -1 &&
12816       !AllPlayersInSight(player, new_jx, new_jy))
12817     return MP_NO_ACTION;
12818 #else
12819   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12820     return MP_NO_ACTION;
12821 #endif
12822
12823 #if !USE_FIXED_DONT_RUN_INTO
12824   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12825
12826   /* (moved to DigField()) */
12827   if (player_can_move && DONT_RUN_INTO(element))
12828   {
12829     if (element == EL_ACID && dx == 0 && dy == 1)
12830     {
12831       SplashAcid(new_jx, new_jy);
12832       Feld[jx][jy] = EL_PLAYER_1;
12833       InitMovingField(jx, jy, MV_DOWN);
12834       Store[jx][jy] = EL_ACID;
12835       ContinueMoving(jx, jy);
12836       BuryPlayer(player);
12837     }
12838     else
12839       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12840
12841     return MP_MOVING;
12842   }
12843 #endif
12844
12845   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12846   if (can_move != MP_MOVING)
12847     return can_move;
12848
12849   /* check if DigField() has caused relocation of the player */
12850   if (player->jx != jx || player->jy != jy)
12851     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12852
12853   StorePlayer[jx][jy] = 0;
12854   player->last_jx = jx;
12855   player->last_jy = jy;
12856   player->jx = new_jx;
12857   player->jy = new_jy;
12858   StorePlayer[new_jx][new_jy] = player->element_nr;
12859
12860   if (player->move_delay_value_next != -1)
12861   {
12862     player->move_delay_value = player->move_delay_value_next;
12863     player->move_delay_value_next = -1;
12864   }
12865
12866   player->MovPos =
12867     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12868
12869   player->step_counter++;
12870
12871   PlayerVisit[jx][jy] = FrameCounter;
12872
12873 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12874   player->is_moving = TRUE;
12875 #endif
12876
12877 #if 1
12878   /* should better be called in MovePlayer(), but this breaks some tapes */
12879   ScrollPlayer(player, SCROLL_INIT);
12880 #endif
12881
12882   return MP_MOVING;
12883 }
12884
12885 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12886 {
12887   int jx = player->jx, jy = player->jy;
12888   int old_jx = jx, old_jy = jy;
12889   int moved = MP_NO_ACTION;
12890
12891   if (!player->active)
12892     return FALSE;
12893
12894   if (!dx && !dy)
12895   {
12896     if (player->MovPos == 0)
12897     {
12898       player->is_moving = FALSE;
12899       player->is_digging = FALSE;
12900       player->is_collecting = FALSE;
12901       player->is_snapping = FALSE;
12902       player->is_pushing = FALSE;
12903     }
12904
12905     return FALSE;
12906   }
12907
12908   if (player->move_delay > 0)
12909     return FALSE;
12910
12911   player->move_delay = -1;              /* set to "uninitialized" value */
12912
12913   /* store if player is automatically moved to next field */
12914   player->is_auto_moving = (player->programmed_action != MV_NONE);
12915
12916   /* remove the last programmed player action */
12917   player->programmed_action = 0;
12918
12919   if (player->MovPos)
12920   {
12921     /* should only happen if pre-1.2 tape recordings are played */
12922     /* this is only for backward compatibility */
12923
12924     int original_move_delay_value = player->move_delay_value;
12925
12926 #if DEBUG
12927     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12928            tape.counter);
12929 #endif
12930
12931     /* scroll remaining steps with finest movement resolution */
12932     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12933
12934     while (player->MovPos)
12935     {
12936       ScrollPlayer(player, SCROLL_GO_ON);
12937       ScrollScreen(NULL, SCROLL_GO_ON);
12938
12939       AdvanceFrameAndPlayerCounters(player->index_nr);
12940
12941       DrawAllPlayers();
12942       BackToFront();
12943     }
12944
12945     player->move_delay_value = original_move_delay_value;
12946   }
12947
12948   player->is_active = FALSE;
12949
12950   if (player->last_move_dir & MV_HORIZONTAL)
12951   {
12952     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12953       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12954   }
12955   else
12956   {
12957     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12958       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12959   }
12960
12961 #if USE_FIXED_BORDER_RUNNING_GFX
12962   if (!moved && !player->is_active)
12963   {
12964     player->is_moving = FALSE;
12965     player->is_digging = FALSE;
12966     player->is_collecting = FALSE;
12967     player->is_snapping = FALSE;
12968     player->is_pushing = FALSE;
12969   }
12970 #endif
12971
12972   jx = player->jx;
12973   jy = player->jy;
12974
12975 #if 1
12976   if (moved & MP_MOVING && !ScreenMovPos &&
12977       (player->index_nr == game.centered_player_nr ||
12978        game.centered_player_nr == -1))
12979 #else
12980   if (moved & MP_MOVING && !ScreenMovPos &&
12981       (player == local_player || !options.network))
12982 #endif
12983   {
12984     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12985     int offset = game.scroll_delay_value;
12986
12987     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12988     {
12989       /* actual player has left the screen -- scroll in that direction */
12990       if (jx != old_jx)         /* player has moved horizontally */
12991         scroll_x += (jx - old_jx);
12992       else                      /* player has moved vertically */
12993         scroll_y += (jy - old_jy);
12994     }
12995     else
12996     {
12997       if (jx != old_jx)         /* player has moved horizontally */
12998       {
12999         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13000             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13001           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13002
13003         /* don't scroll over playfield boundaries */
13004         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13005           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13006
13007         /* don't scroll more than one field at a time */
13008         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13009
13010         /* don't scroll against the player's moving direction */
13011         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13012             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13013           scroll_x = old_scroll_x;
13014       }
13015       else                      /* player has moved vertically */
13016       {
13017         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13018             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13019           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13020
13021         /* don't scroll over playfield boundaries */
13022         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13023           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13024
13025         /* don't scroll more than one field at a time */
13026         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13027
13028         /* don't scroll against the player's moving direction */
13029         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13030             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13031           scroll_y = old_scroll_y;
13032       }
13033     }
13034
13035     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13036     {
13037 #if 1
13038       if (!options.network && game.centered_player_nr == -1 &&
13039           !AllPlayersInVisibleScreen())
13040       {
13041         scroll_x = old_scroll_x;
13042         scroll_y = old_scroll_y;
13043       }
13044       else
13045 #else
13046       if (!options.network && !AllPlayersInVisibleScreen())
13047       {
13048         scroll_x = old_scroll_x;
13049         scroll_y = old_scroll_y;
13050       }
13051       else
13052 #endif
13053       {
13054         ScrollScreen(player, SCROLL_INIT);
13055         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13056       }
13057     }
13058   }
13059
13060   player->StepFrame = 0;
13061
13062   if (moved & MP_MOVING)
13063   {
13064     if (old_jx != jx && old_jy == jy)
13065       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13066     else if (old_jx == jx && old_jy != jy)
13067       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13068
13069     DrawLevelField(jx, jy);     /* for "crumbled sand" */
13070
13071     player->last_move_dir = player->MovDir;
13072     player->is_moving = TRUE;
13073     player->is_snapping = FALSE;
13074     player->is_switching = FALSE;
13075     player->is_dropping = FALSE;
13076     player->is_dropping_pressed = FALSE;
13077     player->drop_pressed_delay = 0;
13078
13079 #if 0
13080     /* should better be called here than above, but this breaks some tapes */
13081     ScrollPlayer(player, SCROLL_INIT);
13082 #endif
13083   }
13084   else
13085   {
13086     CheckGravityMovementWhenNotMoving(player);
13087
13088     player->is_moving = FALSE;
13089
13090     /* at this point, the player is allowed to move, but cannot move right now
13091        (e.g. because of something blocking the way) -- ensure that the player
13092        is also allowed to move in the next frame (in old versions before 3.1.1,
13093        the player was forced to wait again for eight frames before next try) */
13094
13095     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13096       player->move_delay = 0;   /* allow direct movement in the next frame */
13097   }
13098
13099   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13100     player->move_delay = player->move_delay_value;
13101
13102   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13103   {
13104     TestIfPlayerTouchesBadThing(jx, jy);
13105     TestIfPlayerTouchesCustomElement(jx, jy);
13106   }
13107
13108   if (!player->active)
13109     RemovePlayer(player);
13110
13111   return moved;
13112 }
13113
13114 void ScrollPlayer(struct PlayerInfo *player, int mode)
13115 {
13116   int jx = player->jx, jy = player->jy;
13117   int last_jx = player->last_jx, last_jy = player->last_jy;
13118   int move_stepsize = TILEX / player->move_delay_value;
13119
13120 #if USE_NEW_PLAYER_SPEED
13121   if (!player->active)
13122     return;
13123
13124   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13125     return;
13126 #else
13127   if (!player->active || player->MovPos == 0)
13128     return;
13129 #endif
13130
13131   if (mode == SCROLL_INIT)
13132   {
13133     player->actual_frame_counter = FrameCounter;
13134     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13135
13136     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13137         Feld[last_jx][last_jy] == EL_EMPTY)
13138     {
13139       int last_field_block_delay = 0;   /* start with no blocking at all */
13140       int block_delay_adjustment = player->block_delay_adjustment;
13141
13142       /* if player blocks last field, add delay for exactly one move */
13143       if (player->block_last_field)
13144       {
13145         last_field_block_delay += player->move_delay_value;
13146
13147         /* when blocking enabled, prevent moving up despite gravity */
13148 #if USE_PLAYER_GRAVITY
13149         if (player->gravity && player->MovDir == MV_UP)
13150           block_delay_adjustment = -1;
13151 #else
13152         if (game.gravity && player->MovDir == MV_UP)
13153           block_delay_adjustment = -1;
13154 #endif
13155       }
13156
13157       /* add block delay adjustment (also possible when not blocking) */
13158       last_field_block_delay += block_delay_adjustment;
13159
13160       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13161       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13162     }
13163
13164 #if USE_NEW_PLAYER_SPEED
13165     if (player->MovPos != 0)    /* player has not yet reached destination */
13166       return;
13167 #else
13168     return;
13169 #endif
13170   }
13171   else if (!FrameReached(&player->actual_frame_counter, 1))
13172     return;
13173
13174 #if USE_NEW_PLAYER_SPEED
13175   if (player->MovPos != 0)
13176   {
13177     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13178     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13179
13180     /* before DrawPlayer() to draw correct player graphic for this case */
13181     if (player->MovPos == 0)
13182       CheckGravityMovement(player);
13183   }
13184 #else
13185   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13186   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13187
13188   /* before DrawPlayer() to draw correct player graphic for this case */
13189   if (player->MovPos == 0)
13190     CheckGravityMovement(player);
13191 #endif
13192
13193   if (player->MovPos == 0)      /* player reached destination field */
13194   {
13195     if (player->move_delay_reset_counter > 0)
13196     {
13197       player->move_delay_reset_counter--;
13198
13199       if (player->move_delay_reset_counter == 0)
13200       {
13201         /* continue with normal speed after quickly moving through gate */
13202         HALVE_PLAYER_SPEED(player);
13203
13204         /* be able to make the next move without delay */
13205         player->move_delay = 0;
13206       }
13207     }
13208
13209     player->last_jx = jx;
13210     player->last_jy = jy;
13211
13212     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13213         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13214         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13215         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13216         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13217         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13218     {
13219       DrawPlayer(player);       /* needed here only to cleanup last field */
13220       RemovePlayer(player);
13221
13222       if (local_player->friends_still_needed == 0 ||
13223           IS_SP_ELEMENT(Feld[jx][jy]))
13224         PlayerWins(player);
13225     }
13226
13227     /* this breaks one level: "machine", level 000 */
13228     {
13229       int move_direction = player->MovDir;
13230       int enter_side = MV_DIR_OPPOSITE(move_direction);
13231       int leave_side = move_direction;
13232       int old_jx = last_jx;
13233       int old_jy = last_jy;
13234       int old_element = Feld[old_jx][old_jy];
13235       int new_element = Feld[jx][jy];
13236
13237       if (IS_CUSTOM_ELEMENT(old_element))
13238         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13239                                    CE_LEFT_BY_PLAYER,
13240                                    player->index_bit, leave_side);
13241
13242       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13243                                           CE_PLAYER_LEAVES_X,
13244                                           player->index_bit, leave_side);
13245
13246       if (IS_CUSTOM_ELEMENT(new_element))
13247         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13248                                    player->index_bit, enter_side);
13249
13250       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13251                                           CE_PLAYER_ENTERS_X,
13252                                           player->index_bit, enter_side);
13253
13254       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13255                                         CE_MOVE_OF_X, move_direction);
13256     }
13257
13258     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13259     {
13260       TestIfPlayerTouchesBadThing(jx, jy);
13261       TestIfPlayerTouchesCustomElement(jx, jy);
13262
13263       /* needed because pushed element has not yet reached its destination,
13264          so it would trigger a change event at its previous field location */
13265       if (!player->is_pushing)
13266         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13267
13268       if (!player->active)
13269         RemovePlayer(player);
13270     }
13271
13272     if (!local_player->LevelSolved && level.use_step_counter)
13273     {
13274       int i;
13275
13276       TimePlayed++;
13277
13278       if (TimeLeft > 0)
13279       {
13280         TimeLeft--;
13281
13282         if (TimeLeft <= 10 && setup.time_limit)
13283           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13284
13285 #if 1
13286         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13287
13288         DisplayGameControlValues();
13289 #else
13290         DrawGameValue_Time(TimeLeft);
13291 #endif
13292
13293         if (!TimeLeft && setup.time_limit)
13294           for (i = 0; i < MAX_PLAYERS; i++)
13295             KillPlayer(&stored_player[i]);
13296       }
13297 #if 1
13298       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13299       {
13300         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13301
13302         DisplayGameControlValues();
13303       }
13304 #else
13305       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13306         DrawGameValue_Time(TimePlayed);
13307 #endif
13308     }
13309
13310     if (tape.single_step && tape.recording && !tape.pausing &&
13311         !player->programmed_action)
13312       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13313   }
13314 }
13315
13316 void ScrollScreen(struct PlayerInfo *player, int mode)
13317 {
13318   static unsigned long screen_frame_counter = 0;
13319
13320   if (mode == SCROLL_INIT)
13321   {
13322     /* set scrolling step size according to actual player's moving speed */
13323     ScrollStepSize = TILEX / player->move_delay_value;
13324
13325     screen_frame_counter = FrameCounter;
13326     ScreenMovDir = player->MovDir;
13327     ScreenMovPos = player->MovPos;
13328     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13329     return;
13330   }
13331   else if (!FrameReached(&screen_frame_counter, 1))
13332     return;
13333
13334   if (ScreenMovPos)
13335   {
13336     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13337     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13338     redraw_mask |= REDRAW_FIELD;
13339   }
13340   else
13341     ScreenMovDir = MV_NONE;
13342 }
13343
13344 void TestIfPlayerTouchesCustomElement(int x, int y)
13345 {
13346   static int xy[4][2] =
13347   {
13348     { 0, -1 },
13349     { -1, 0 },
13350     { +1, 0 },
13351     { 0, +1 }
13352   };
13353   static int trigger_sides[4][2] =
13354   {
13355     /* center side       border side */
13356     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13357     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13358     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13359     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13360   };
13361   static int touch_dir[4] =
13362   {
13363     MV_LEFT | MV_RIGHT,
13364     MV_UP   | MV_DOWN,
13365     MV_UP   | MV_DOWN,
13366     MV_LEFT | MV_RIGHT
13367   };
13368   int center_element = Feld[x][y];      /* should always be non-moving! */
13369   int i;
13370
13371   for (i = 0; i < NUM_DIRECTIONS; i++)
13372   {
13373     int xx = x + xy[i][0];
13374     int yy = y + xy[i][1];
13375     int center_side = trigger_sides[i][0];
13376     int border_side = trigger_sides[i][1];
13377     int border_element;
13378
13379     if (!IN_LEV_FIELD(xx, yy))
13380       continue;
13381
13382     if (IS_PLAYER(x, y))
13383     {
13384       struct PlayerInfo *player = PLAYERINFO(x, y);
13385
13386       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13387         border_element = Feld[xx][yy];          /* may be moving! */
13388       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13389         border_element = Feld[xx][yy];
13390       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13391         border_element = MovingOrBlocked2Element(xx, yy);
13392       else
13393         continue;               /* center and border element do not touch */
13394
13395       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13396                                  player->index_bit, border_side);
13397       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13398                                           CE_PLAYER_TOUCHES_X,
13399                                           player->index_bit, border_side);
13400     }
13401     else if (IS_PLAYER(xx, yy))
13402     {
13403       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13404
13405       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13406       {
13407         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13408           continue;             /* center and border element do not touch */
13409       }
13410
13411       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13412                                  player->index_bit, center_side);
13413       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13414                                           CE_PLAYER_TOUCHES_X,
13415                                           player->index_bit, center_side);
13416       break;
13417     }
13418   }
13419 }
13420
13421 #if USE_ELEMENT_TOUCHING_BUGFIX
13422
13423 void TestIfElementTouchesCustomElement(int x, int y)
13424 {
13425   static int xy[4][2] =
13426   {
13427     { 0, -1 },
13428     { -1, 0 },
13429     { +1, 0 },
13430     { 0, +1 }
13431   };
13432   static int trigger_sides[4][2] =
13433   {
13434     /* center side      border side */
13435     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13436     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13437     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13438     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13439   };
13440   static int touch_dir[4] =
13441   {
13442     MV_LEFT | MV_RIGHT,
13443     MV_UP   | MV_DOWN,
13444     MV_UP   | MV_DOWN,
13445     MV_LEFT | MV_RIGHT
13446   };
13447   boolean change_center_element = FALSE;
13448   int center_element = Feld[x][y];      /* should always be non-moving! */
13449   int border_element_old[NUM_DIRECTIONS];
13450   int i;
13451
13452   for (i = 0; i < NUM_DIRECTIONS; i++)
13453   {
13454     int xx = x + xy[i][0];
13455     int yy = y + xy[i][1];
13456     int border_element;
13457
13458     border_element_old[i] = -1;
13459
13460     if (!IN_LEV_FIELD(xx, yy))
13461       continue;
13462
13463     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13464       border_element = Feld[xx][yy];    /* may be moving! */
13465     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13466       border_element = Feld[xx][yy];
13467     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13468       border_element = MovingOrBlocked2Element(xx, yy);
13469     else
13470       continue;                 /* center and border element do not touch */
13471
13472     border_element_old[i] = border_element;
13473   }
13474
13475   for (i = 0; i < NUM_DIRECTIONS; i++)
13476   {
13477     int xx = x + xy[i][0];
13478     int yy = y + xy[i][1];
13479     int center_side = trigger_sides[i][0];
13480     int border_element = border_element_old[i];
13481
13482     if (border_element == -1)
13483       continue;
13484
13485     /* check for change of border element */
13486     CheckElementChangeBySide(xx, yy, border_element, center_element,
13487                              CE_TOUCHING_X, center_side);
13488   }
13489
13490   for (i = 0; i < NUM_DIRECTIONS; i++)
13491   {
13492     int border_side = trigger_sides[i][1];
13493     int border_element = border_element_old[i];
13494
13495     if (border_element == -1)
13496       continue;
13497
13498     /* check for change of center element (but change it only once) */
13499     if (!change_center_element)
13500       change_center_element =
13501         CheckElementChangeBySide(x, y, center_element, border_element,
13502                                  CE_TOUCHING_X, border_side);
13503   }
13504 }
13505
13506 #else
13507
13508 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13509 {
13510   static int xy[4][2] =
13511   {
13512     { 0, -1 },
13513     { -1, 0 },
13514     { +1, 0 },
13515     { 0, +1 }
13516   };
13517   static int trigger_sides[4][2] =
13518   {
13519     /* center side      border side */
13520     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13521     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13522     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13523     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13524   };
13525   static int touch_dir[4] =
13526   {
13527     MV_LEFT | MV_RIGHT,
13528     MV_UP   | MV_DOWN,
13529     MV_UP   | MV_DOWN,
13530     MV_LEFT | MV_RIGHT
13531   };
13532   boolean change_center_element = FALSE;
13533   int center_element = Feld[x][y];      /* should always be non-moving! */
13534   int i;
13535
13536   for (i = 0; i < NUM_DIRECTIONS; i++)
13537   {
13538     int xx = x + xy[i][0];
13539     int yy = y + xy[i][1];
13540     int center_side = trigger_sides[i][0];
13541     int border_side = trigger_sides[i][1];
13542     int border_element;
13543
13544     if (!IN_LEV_FIELD(xx, yy))
13545       continue;
13546
13547     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13548       border_element = Feld[xx][yy];    /* may be moving! */
13549     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13550       border_element = Feld[xx][yy];
13551     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13552       border_element = MovingOrBlocked2Element(xx, yy);
13553     else
13554       continue;                 /* center and border element do not touch */
13555
13556     /* check for change of center element (but change it only once) */
13557     if (!change_center_element)
13558       change_center_element =
13559         CheckElementChangeBySide(x, y, center_element, border_element,
13560                                  CE_TOUCHING_X, border_side);
13561
13562     /* check for change of border element */
13563     CheckElementChangeBySide(xx, yy, border_element, center_element,
13564                              CE_TOUCHING_X, center_side);
13565   }
13566 }
13567
13568 #endif
13569
13570 void TestIfElementHitsCustomElement(int x, int y, int direction)
13571 {
13572   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13573   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13574   int hitx = x + dx, hity = y + dy;
13575   int hitting_element = Feld[x][y];
13576   int touched_element;
13577
13578   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13579     return;
13580
13581   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13582                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13583
13584   if (IN_LEV_FIELD(hitx, hity))
13585   {
13586     int opposite_direction = MV_DIR_OPPOSITE(direction);
13587     int hitting_side = direction;
13588     int touched_side = opposite_direction;
13589     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13590                           MovDir[hitx][hity] != direction ||
13591                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13592
13593     object_hit = TRUE;
13594
13595     if (object_hit)
13596     {
13597       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13598                                CE_HITTING_X, touched_side);
13599
13600       CheckElementChangeBySide(hitx, hity, touched_element,
13601                                hitting_element, CE_HIT_BY_X, hitting_side);
13602
13603       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13604                                CE_HIT_BY_SOMETHING, opposite_direction);
13605     }
13606   }
13607
13608   /* "hitting something" is also true when hitting the playfield border */
13609   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13610                            CE_HITTING_SOMETHING, direction);
13611 }
13612
13613 #if 0
13614 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13615 {
13616   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13617   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13618   int hitx = x + dx, hity = y + dy;
13619   int hitting_element = Feld[x][y];
13620   int touched_element;
13621 #if 0
13622   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13623                         !IS_FREE(hitx, hity) &&
13624                         (!IS_MOVING(hitx, hity) ||
13625                          MovDir[hitx][hity] != direction ||
13626                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13627 #endif
13628
13629   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13630     return;
13631
13632 #if 0
13633   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13634     return;
13635 #endif
13636
13637   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13638                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13639
13640   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13641                            EP_CAN_SMASH_EVERYTHING, direction);
13642
13643   if (IN_LEV_FIELD(hitx, hity))
13644   {
13645     int opposite_direction = MV_DIR_OPPOSITE(direction);
13646     int hitting_side = direction;
13647     int touched_side = opposite_direction;
13648 #if 0
13649     int touched_element = MovingOrBlocked2Element(hitx, hity);
13650 #endif
13651 #if 1
13652     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13653                           MovDir[hitx][hity] != direction ||
13654                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13655
13656     object_hit = TRUE;
13657 #endif
13658
13659     if (object_hit)
13660     {
13661       int i;
13662
13663       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13664                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13665
13666       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13667                                CE_OTHER_IS_SMASHING, touched_side);
13668
13669       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13670                                CE_OTHER_GETS_SMASHED, hitting_side);
13671     }
13672   }
13673 }
13674 #endif
13675
13676 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13677 {
13678   int i, kill_x = -1, kill_y = -1;
13679
13680   int bad_element = -1;
13681   static int test_xy[4][2] =
13682   {
13683     { 0, -1 },
13684     { -1, 0 },
13685     { +1, 0 },
13686     { 0, +1 }
13687   };
13688   static int test_dir[4] =
13689   {
13690     MV_UP,
13691     MV_LEFT,
13692     MV_RIGHT,
13693     MV_DOWN
13694   };
13695
13696   for (i = 0; i < NUM_DIRECTIONS; i++)
13697   {
13698     int test_x, test_y, test_move_dir, test_element;
13699
13700     test_x = good_x + test_xy[i][0];
13701     test_y = good_y + test_xy[i][1];
13702
13703     if (!IN_LEV_FIELD(test_x, test_y))
13704       continue;
13705
13706     test_move_dir =
13707       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13708
13709     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13710
13711     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13712        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13713     */
13714     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13715         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13716     {
13717       kill_x = test_x;
13718       kill_y = test_y;
13719       bad_element = test_element;
13720
13721       break;
13722     }
13723   }
13724
13725   if (kill_x != -1 || kill_y != -1)
13726   {
13727     if (IS_PLAYER(good_x, good_y))
13728     {
13729       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13730
13731       if (player->shield_deadly_time_left > 0 &&
13732           !IS_INDESTRUCTIBLE(bad_element))
13733         Bang(kill_x, kill_y);
13734       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13735         KillPlayer(player);
13736     }
13737     else
13738       Bang(good_x, good_y);
13739   }
13740 }
13741
13742 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13743 {
13744   int i, kill_x = -1, kill_y = -1;
13745   int bad_element = Feld[bad_x][bad_y];
13746   static int test_xy[4][2] =
13747   {
13748     { 0, -1 },
13749     { -1, 0 },
13750     { +1, 0 },
13751     { 0, +1 }
13752   };
13753   static int touch_dir[4] =
13754   {
13755     MV_LEFT | MV_RIGHT,
13756     MV_UP   | MV_DOWN,
13757     MV_UP   | MV_DOWN,
13758     MV_LEFT | MV_RIGHT
13759   };
13760   static int test_dir[4] =
13761   {
13762     MV_UP,
13763     MV_LEFT,
13764     MV_RIGHT,
13765     MV_DOWN
13766   };
13767
13768   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13769     return;
13770
13771   for (i = 0; i < NUM_DIRECTIONS; i++)
13772   {
13773     int test_x, test_y, test_move_dir, test_element;
13774
13775     test_x = bad_x + test_xy[i][0];
13776     test_y = bad_y + test_xy[i][1];
13777     if (!IN_LEV_FIELD(test_x, test_y))
13778       continue;
13779
13780     test_move_dir =
13781       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13782
13783     test_element = Feld[test_x][test_y];
13784
13785     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13786        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13787     */
13788     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13789         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13790     {
13791       /* good thing is player or penguin that does not move away */
13792       if (IS_PLAYER(test_x, test_y))
13793       {
13794         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13795
13796         if (bad_element == EL_ROBOT && player->is_moving)
13797           continue;     /* robot does not kill player if he is moving */
13798
13799         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13800         {
13801           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13802             continue;           /* center and border element do not touch */
13803         }
13804
13805         kill_x = test_x;
13806         kill_y = test_y;
13807         break;
13808       }
13809       else if (test_element == EL_PENGUIN)
13810       {
13811         kill_x = test_x;
13812         kill_y = test_y;
13813         break;
13814       }
13815     }
13816   }
13817
13818   if (kill_x != -1 || kill_y != -1)
13819   {
13820     if (IS_PLAYER(kill_x, kill_y))
13821     {
13822       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13823
13824       if (player->shield_deadly_time_left > 0 &&
13825           !IS_INDESTRUCTIBLE(bad_element))
13826         Bang(bad_x, bad_y);
13827       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13828         KillPlayer(player);
13829     }
13830     else
13831       Bang(kill_x, kill_y);
13832   }
13833 }
13834
13835 void TestIfPlayerTouchesBadThing(int x, int y)
13836 {
13837   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13838 }
13839
13840 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13841 {
13842   TestIfGoodThingHitsBadThing(x, y, move_dir);
13843 }
13844
13845 void TestIfBadThingTouchesPlayer(int x, int y)
13846 {
13847   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13848 }
13849
13850 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13851 {
13852   TestIfBadThingHitsGoodThing(x, y, move_dir);
13853 }
13854
13855 void TestIfFriendTouchesBadThing(int x, int y)
13856 {
13857   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13858 }
13859
13860 void TestIfBadThingTouchesFriend(int x, int y)
13861 {
13862   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13863 }
13864
13865 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13866 {
13867   int i, kill_x = bad_x, kill_y = bad_y;
13868   static int xy[4][2] =
13869   {
13870     { 0, -1 },
13871     { -1, 0 },
13872     { +1, 0 },
13873     { 0, +1 }
13874   };
13875
13876   for (i = 0; i < NUM_DIRECTIONS; i++)
13877   {
13878     int x, y, element;
13879
13880     x = bad_x + xy[i][0];
13881     y = bad_y + xy[i][1];
13882     if (!IN_LEV_FIELD(x, y))
13883       continue;
13884
13885     element = Feld[x][y];
13886     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13887         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13888     {
13889       kill_x = x;
13890       kill_y = y;
13891       break;
13892     }
13893   }
13894
13895   if (kill_x != bad_x || kill_y != bad_y)
13896     Bang(bad_x, bad_y);
13897 }
13898
13899 void KillPlayer(struct PlayerInfo *player)
13900 {
13901   int jx = player->jx, jy = player->jy;
13902
13903   if (!player->active)
13904     return;
13905
13906   /* the following code was introduced to prevent an infinite loop when calling
13907      -> Bang()
13908      -> CheckTriggeredElementChangeExt()
13909      -> ExecuteCustomElementAction()
13910      -> KillPlayer()
13911      -> (infinitely repeating the above sequence of function calls)
13912      which occurs when killing the player while having a CE with the setting
13913      "kill player X when explosion of <player X>"; the solution using a new
13914      field "player->killed" was chosen for backwards compatibility, although
13915      clever use of the fields "player->active" etc. would probably also work */
13916 #if 1
13917   if (player->killed)
13918     return;
13919 #endif
13920
13921   player->killed = TRUE;
13922
13923   /* remove accessible field at the player's position */
13924   Feld[jx][jy] = EL_EMPTY;
13925
13926   /* deactivate shield (else Bang()/Explode() would not work right) */
13927   player->shield_normal_time_left = 0;
13928   player->shield_deadly_time_left = 0;
13929
13930   Bang(jx, jy);
13931   BuryPlayer(player);
13932 }
13933
13934 static void KillPlayerUnlessEnemyProtected(int x, int y)
13935 {
13936   if (!PLAYER_ENEMY_PROTECTED(x, y))
13937     KillPlayer(PLAYERINFO(x, y));
13938 }
13939
13940 static void KillPlayerUnlessExplosionProtected(int x, int y)
13941 {
13942   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13943     KillPlayer(PLAYERINFO(x, y));
13944 }
13945
13946 void BuryPlayer(struct PlayerInfo *player)
13947 {
13948   int jx = player->jx, jy = player->jy;
13949
13950   if (!player->active)
13951     return;
13952
13953   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13954   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13955
13956   player->GameOver = TRUE;
13957   RemovePlayer(player);
13958 }
13959
13960 void RemovePlayer(struct PlayerInfo *player)
13961 {
13962   int jx = player->jx, jy = player->jy;
13963   int i, found = FALSE;
13964
13965   player->present = FALSE;
13966   player->active = FALSE;
13967
13968   if (!ExplodeField[jx][jy])
13969     StorePlayer[jx][jy] = 0;
13970
13971   if (player->is_moving)
13972     DrawLevelField(player->last_jx, player->last_jy);
13973
13974   for (i = 0; i < MAX_PLAYERS; i++)
13975     if (stored_player[i].active)
13976       found = TRUE;
13977
13978   if (!found)
13979     AllPlayersGone = TRUE;
13980
13981   ExitX = ZX = jx;
13982   ExitY = ZY = jy;
13983 }
13984
13985 #if USE_NEW_SNAP_DELAY
13986 static void setFieldForSnapping(int x, int y, int element, int direction)
13987 {
13988   struct ElementInfo *ei = &element_info[element];
13989   int direction_bit = MV_DIR_TO_BIT(direction);
13990   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13991   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13992                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13993
13994   Feld[x][y] = EL_ELEMENT_SNAPPING;
13995   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13996
13997   ResetGfxAnimation(x, y);
13998
13999   GfxElement[x][y] = element;
14000   GfxAction[x][y] = action;
14001   GfxDir[x][y] = direction;
14002   GfxFrame[x][y] = -1;
14003 }
14004 #endif
14005
14006 /*
14007   =============================================================================
14008   checkDiagonalPushing()
14009   -----------------------------------------------------------------------------
14010   check if diagonal input device direction results in pushing of object
14011   (by checking if the alternative direction is walkable, diggable, ...)
14012   =============================================================================
14013 */
14014
14015 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14016                                     int x, int y, int real_dx, int real_dy)
14017 {
14018   int jx, jy, dx, dy, xx, yy;
14019
14020   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14021     return TRUE;
14022
14023   /* diagonal direction: check alternative direction */
14024   jx = player->jx;
14025   jy = player->jy;
14026   dx = x - jx;
14027   dy = y - jy;
14028   xx = jx + (dx == 0 ? real_dx : 0);
14029   yy = jy + (dy == 0 ? real_dy : 0);
14030
14031   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14032 }
14033
14034 /*
14035   =============================================================================
14036   DigField()
14037   -----------------------------------------------------------------------------
14038   x, y:                 field next to player (non-diagonal) to try to dig to
14039   real_dx, real_dy:     direction as read from input device (can be diagonal)
14040   =============================================================================
14041 */
14042
14043 int DigField(struct PlayerInfo *player,
14044              int oldx, int oldy, int x, int y,
14045              int real_dx, int real_dy, int mode)
14046 {
14047   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14048   boolean player_was_pushing = player->is_pushing;
14049   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14050   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14051   int jx = oldx, jy = oldy;
14052   int dx = x - jx, dy = y - jy;
14053   int nextx = x + dx, nexty = y + dy;
14054   int move_direction = (dx == -1 ? MV_LEFT  :
14055                         dx == +1 ? MV_RIGHT :
14056                         dy == -1 ? MV_UP    :
14057                         dy == +1 ? MV_DOWN  : MV_NONE);
14058   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14059   int dig_side = MV_DIR_OPPOSITE(move_direction);
14060   int old_element = Feld[jx][jy];
14061 #if USE_FIXED_DONT_RUN_INTO
14062   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14063 #else
14064   int element;
14065 #endif
14066   int collect_count;
14067
14068   if (is_player)                /* function can also be called by EL_PENGUIN */
14069   {
14070     if (player->MovPos == 0)
14071     {
14072       player->is_digging = FALSE;
14073       player->is_collecting = FALSE;
14074     }
14075
14076     if (player->MovPos == 0)    /* last pushing move finished */
14077       player->is_pushing = FALSE;
14078
14079     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14080     {
14081       player->is_switching = FALSE;
14082       player->push_delay = -1;
14083
14084       return MP_NO_ACTION;
14085     }
14086   }
14087
14088 #if !USE_FIXED_DONT_RUN_INTO
14089   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14090     return MP_NO_ACTION;
14091 #endif
14092
14093   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14094     old_element = Back[jx][jy];
14095
14096   /* in case of element dropped at player position, check background */
14097   else if (Back[jx][jy] != EL_EMPTY &&
14098            game.engine_version >= VERSION_IDENT(2,2,0,0))
14099     old_element = Back[jx][jy];
14100
14101   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14102     return MP_NO_ACTION;        /* field has no opening in this direction */
14103
14104   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14105     return MP_NO_ACTION;        /* field has no opening in this direction */
14106
14107 #if USE_FIXED_DONT_RUN_INTO
14108   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14109   {
14110     SplashAcid(x, y);
14111
14112     Feld[jx][jy] = player->artwork_element;
14113     InitMovingField(jx, jy, MV_DOWN);
14114     Store[jx][jy] = EL_ACID;
14115     ContinueMoving(jx, jy);
14116     BuryPlayer(player);
14117
14118     return MP_DONT_RUN_INTO;
14119   }
14120 #endif
14121
14122 #if USE_FIXED_DONT_RUN_INTO
14123   if (player_can_move && DONT_RUN_INTO(element))
14124   {
14125     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14126
14127     return MP_DONT_RUN_INTO;
14128   }
14129 #endif
14130
14131 #if USE_FIXED_DONT_RUN_INTO
14132   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14133     return MP_NO_ACTION;
14134 #endif
14135
14136 #if !USE_FIXED_DONT_RUN_INTO
14137   element = Feld[x][y];
14138 #endif
14139
14140   collect_count = element_info[element].collect_count_initial;
14141
14142   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14143     return MP_NO_ACTION;
14144
14145   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14146     player_can_move = player_can_move_or_snap;
14147
14148   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14149       game.engine_version >= VERSION_IDENT(2,2,0,0))
14150   {
14151     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14152                                player->index_bit, dig_side);
14153     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14154                                         player->index_bit, dig_side);
14155
14156     if (element == EL_DC_LANDMINE)
14157       Bang(x, y);
14158
14159     if (Feld[x][y] != element)          /* field changed by snapping */
14160       return MP_ACTION;
14161
14162     return MP_NO_ACTION;
14163   }
14164
14165 #if USE_PLAYER_GRAVITY
14166   if (player->gravity && is_player && !player->is_auto_moving &&
14167       canFallDown(player) && move_direction != MV_DOWN &&
14168       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14169     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14170 #else
14171   if (game.gravity && is_player && !player->is_auto_moving &&
14172       canFallDown(player) && move_direction != MV_DOWN &&
14173       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14174     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14175 #endif
14176
14177   if (player_can_move &&
14178       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14179   {
14180     int sound_element = SND_ELEMENT(element);
14181     int sound_action = ACTION_WALKING;
14182
14183     if (IS_RND_GATE(element))
14184     {
14185       if (!player->key[RND_GATE_NR(element)])
14186         return MP_NO_ACTION;
14187     }
14188     else if (IS_RND_GATE_GRAY(element))
14189     {
14190       if (!player->key[RND_GATE_GRAY_NR(element)])
14191         return MP_NO_ACTION;
14192     }
14193     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14194     {
14195       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14196         return MP_NO_ACTION;
14197     }
14198     else if (element == EL_EXIT_OPEN ||
14199              element == EL_EM_EXIT_OPEN ||
14200              element == EL_STEEL_EXIT_OPEN ||
14201              element == EL_EM_STEEL_EXIT_OPEN ||
14202              element == EL_SP_EXIT_OPEN ||
14203              element == EL_SP_EXIT_OPENING)
14204     {
14205       sound_action = ACTION_PASSING;    /* player is passing exit */
14206     }
14207     else if (element == EL_EMPTY)
14208     {
14209       sound_action = ACTION_MOVING;             /* nothing to walk on */
14210     }
14211
14212     /* play sound from background or player, whatever is available */
14213     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14214       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14215     else
14216       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14217   }
14218   else if (player_can_move &&
14219            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14220   {
14221     if (!ACCESS_FROM(element, opposite_direction))
14222       return MP_NO_ACTION;      /* field not accessible from this direction */
14223
14224     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14225       return MP_NO_ACTION;
14226
14227     if (IS_EM_GATE(element))
14228     {
14229       if (!player->key[EM_GATE_NR(element)])
14230         return MP_NO_ACTION;
14231     }
14232     else if (IS_EM_GATE_GRAY(element))
14233     {
14234       if (!player->key[EM_GATE_GRAY_NR(element)])
14235         return MP_NO_ACTION;
14236     }
14237     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14238     {
14239       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14240         return MP_NO_ACTION;
14241     }
14242     else if (IS_EMC_GATE(element))
14243     {
14244       if (!player->key[EMC_GATE_NR(element)])
14245         return MP_NO_ACTION;
14246     }
14247     else if (IS_EMC_GATE_GRAY(element))
14248     {
14249       if (!player->key[EMC_GATE_GRAY_NR(element)])
14250         return MP_NO_ACTION;
14251     }
14252     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14253     {
14254       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14255         return MP_NO_ACTION;
14256     }
14257     else if (element == EL_DC_GATE_WHITE ||
14258              element == EL_DC_GATE_WHITE_GRAY ||
14259              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14260     {
14261       if (player->num_white_keys == 0)
14262         return MP_NO_ACTION;
14263
14264       player->num_white_keys--;
14265     }
14266     else if (IS_SP_PORT(element))
14267     {
14268       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14269           element == EL_SP_GRAVITY_PORT_RIGHT ||
14270           element == EL_SP_GRAVITY_PORT_UP ||
14271           element == EL_SP_GRAVITY_PORT_DOWN)
14272 #if USE_PLAYER_GRAVITY
14273         player->gravity = !player->gravity;
14274 #else
14275         game.gravity = !game.gravity;
14276 #endif
14277       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14278                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14279                element == EL_SP_GRAVITY_ON_PORT_UP ||
14280                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14281 #if USE_PLAYER_GRAVITY
14282         player->gravity = TRUE;
14283 #else
14284         game.gravity = TRUE;
14285 #endif
14286       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14287                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14288                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14289                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14290 #if USE_PLAYER_GRAVITY
14291         player->gravity = FALSE;
14292 #else
14293         game.gravity = FALSE;
14294 #endif
14295     }
14296
14297     /* automatically move to the next field with double speed */
14298     player->programmed_action = move_direction;
14299
14300     if (player->move_delay_reset_counter == 0)
14301     {
14302       player->move_delay_reset_counter = 2;     /* two double speed steps */
14303
14304       DOUBLE_PLAYER_SPEED(player);
14305     }
14306
14307     PlayLevelSoundAction(x, y, ACTION_PASSING);
14308   }
14309   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14310   {
14311     RemoveField(x, y);
14312
14313     if (mode != DF_SNAP)
14314     {
14315       GfxElement[x][y] = GFX_ELEMENT(element);
14316       player->is_digging = TRUE;
14317     }
14318
14319     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14320
14321     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14322                                         player->index_bit, dig_side);
14323
14324     if (mode == DF_SNAP)
14325     {
14326 #if USE_NEW_SNAP_DELAY
14327       if (level.block_snap_field)
14328         setFieldForSnapping(x, y, element, move_direction);
14329       else
14330         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14331 #else
14332       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14333 #endif
14334
14335       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14336                                           player->index_bit, dig_side);
14337     }
14338   }
14339   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14340   {
14341     RemoveField(x, y);
14342
14343     if (is_player && mode != DF_SNAP)
14344     {
14345       GfxElement[x][y] = element;
14346       player->is_collecting = TRUE;
14347     }
14348
14349     if (element == EL_SPEED_PILL)
14350     {
14351       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14352     }
14353     else if (element == EL_EXTRA_TIME && level.time > 0)
14354     {
14355       TimeLeft += level.extra_time;
14356
14357 #if 1
14358       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14359
14360       DisplayGameControlValues();
14361 #else
14362       DrawGameValue_Time(TimeLeft);
14363 #endif
14364     }
14365     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14366     {
14367       player->shield_normal_time_left += level.shield_normal_time;
14368       if (element == EL_SHIELD_DEADLY)
14369         player->shield_deadly_time_left += level.shield_deadly_time;
14370     }
14371     else if (element == EL_DYNAMITE ||
14372              element == EL_EM_DYNAMITE ||
14373              element == EL_SP_DISK_RED)
14374     {
14375       if (player->inventory_size < MAX_INVENTORY_SIZE)
14376         player->inventory_element[player->inventory_size++] = element;
14377
14378       DrawGameDoorValues();
14379     }
14380     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14381     {
14382       player->dynabomb_count++;
14383       player->dynabombs_left++;
14384     }
14385     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14386     {
14387       player->dynabomb_size++;
14388     }
14389     else if (element == EL_DYNABOMB_INCREASE_POWER)
14390     {
14391       player->dynabomb_xl = TRUE;
14392     }
14393     else if (IS_KEY(element))
14394     {
14395       player->key[KEY_NR(element)] = TRUE;
14396
14397       DrawGameDoorValues();
14398     }
14399     else if (element == EL_DC_KEY_WHITE)
14400     {
14401       player->num_white_keys++;
14402
14403       /* display white keys? */
14404       /* DrawGameDoorValues(); */
14405     }
14406     else if (IS_ENVELOPE(element))
14407     {
14408       player->show_envelope = element;
14409     }
14410     else if (element == EL_EMC_LENSES)
14411     {
14412       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14413
14414       RedrawAllInvisibleElementsForLenses();
14415     }
14416     else if (element == EL_EMC_MAGNIFIER)
14417     {
14418       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14419
14420       RedrawAllInvisibleElementsForMagnifier();
14421     }
14422     else if (IS_DROPPABLE(element) ||
14423              IS_THROWABLE(element))     /* can be collected and dropped */
14424     {
14425       int i;
14426
14427       if (collect_count == 0)
14428         player->inventory_infinite_element = element;
14429       else
14430         for (i = 0; i < collect_count; i++)
14431           if (player->inventory_size < MAX_INVENTORY_SIZE)
14432             player->inventory_element[player->inventory_size++] = element;
14433
14434       DrawGameDoorValues();
14435     }
14436     else if (collect_count > 0)
14437     {
14438       local_player->gems_still_needed -= collect_count;
14439       if (local_player->gems_still_needed < 0)
14440         local_player->gems_still_needed = 0;
14441
14442 #if 1
14443       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14444
14445       DisplayGameControlValues();
14446 #else
14447       DrawGameValue_Emeralds(local_player->gems_still_needed);
14448 #endif
14449     }
14450
14451     RaiseScoreElement(element);
14452     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14453
14454     if (is_player)
14455       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14456                                           player->index_bit, dig_side);
14457
14458     if (mode == DF_SNAP)
14459     {
14460 #if USE_NEW_SNAP_DELAY
14461       if (level.block_snap_field)
14462         setFieldForSnapping(x, y, element, move_direction);
14463       else
14464         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14465 #else
14466       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14467 #endif
14468
14469       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14470                                           player->index_bit, dig_side);
14471     }
14472   }
14473   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14474   {
14475     if (mode == DF_SNAP && element != EL_BD_ROCK)
14476       return MP_NO_ACTION;
14477
14478     if (CAN_FALL(element) && dy)
14479       return MP_NO_ACTION;
14480
14481     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14482         !(element == EL_SPRING && level.use_spring_bug))
14483       return MP_NO_ACTION;
14484
14485     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14486         ((move_direction & MV_VERTICAL &&
14487           ((element_info[element].move_pattern & MV_LEFT &&
14488             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14489            (element_info[element].move_pattern & MV_RIGHT &&
14490             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14491          (move_direction & MV_HORIZONTAL &&
14492           ((element_info[element].move_pattern & MV_UP &&
14493             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14494            (element_info[element].move_pattern & MV_DOWN &&
14495             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14496       return MP_NO_ACTION;
14497
14498     /* do not push elements already moving away faster than player */
14499     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14500         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14501       return MP_NO_ACTION;
14502
14503     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14504     {
14505       if (player->push_delay_value == -1 || !player_was_pushing)
14506         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14507     }
14508     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14509     {
14510       if (player->push_delay_value == -1)
14511         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14512     }
14513     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14514     {
14515       if (!player->is_pushing)
14516         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14517     }
14518
14519     player->is_pushing = TRUE;
14520     player->is_active = TRUE;
14521
14522     if (!(IN_LEV_FIELD(nextx, nexty) &&
14523           (IS_FREE(nextx, nexty) ||
14524            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14525             IS_SB_ELEMENT(element)))))
14526       return MP_NO_ACTION;
14527
14528     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14529       return MP_NO_ACTION;
14530
14531     if (player->push_delay == -1)       /* new pushing; restart delay */
14532       player->push_delay = 0;
14533
14534     if (player->push_delay < player->push_delay_value &&
14535         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14536         element != EL_SPRING && element != EL_BALLOON)
14537     {
14538       /* make sure that there is no move delay before next try to push */
14539       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14540         player->move_delay = 0;
14541
14542       return MP_NO_ACTION;
14543     }
14544
14545     if (IS_SB_ELEMENT(element))
14546     {
14547       if (element == EL_SOKOBAN_FIELD_FULL)
14548       {
14549         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14550         local_player->sokobanfields_still_needed++;
14551       }
14552
14553       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14554       {
14555         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14556         local_player->sokobanfields_still_needed--;
14557       }
14558
14559       Feld[x][y] = EL_SOKOBAN_OBJECT;
14560
14561       if (Back[x][y] == Back[nextx][nexty])
14562         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14563       else if (Back[x][y] != 0)
14564         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14565                                     ACTION_EMPTYING);
14566       else
14567         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14568                                     ACTION_FILLING);
14569
14570       if (local_player->sokobanfields_still_needed == 0 &&
14571           game.emulation == EMU_SOKOBAN)
14572       {
14573         PlayerWins(player);
14574
14575         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14576       }
14577     }
14578     else
14579       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14580
14581     InitMovingField(x, y, move_direction);
14582     GfxAction[x][y] = ACTION_PUSHING;
14583
14584     if (mode == DF_SNAP)
14585       ContinueMoving(x, y);
14586     else
14587       MovPos[x][y] = (dx != 0 ? dx : dy);
14588
14589     Pushed[x][y] = TRUE;
14590     Pushed[nextx][nexty] = TRUE;
14591
14592     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14593       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14594     else
14595       player->push_delay_value = -1;    /* get new value later */
14596
14597     /* check for element change _after_ element has been pushed */
14598     if (game.use_change_when_pushing_bug)
14599     {
14600       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14601                                  player->index_bit, dig_side);
14602       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14603                                           player->index_bit, dig_side);
14604     }
14605   }
14606   else if (IS_SWITCHABLE(element))
14607   {
14608     if (PLAYER_SWITCHING(player, x, y))
14609     {
14610       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14611                                           player->index_bit, dig_side);
14612
14613       return MP_ACTION;
14614     }
14615
14616     player->is_switching = TRUE;
14617     player->switch_x = x;
14618     player->switch_y = y;
14619
14620     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14621
14622     if (element == EL_ROBOT_WHEEL)
14623     {
14624       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14625       ZX = x;
14626       ZY = y;
14627
14628       game.robot_wheel_active = TRUE;
14629
14630       DrawLevelField(x, y);
14631     }
14632     else if (element == EL_SP_TERMINAL)
14633     {
14634       int xx, yy;
14635
14636       SCAN_PLAYFIELD(xx, yy)
14637       {
14638         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14639           Bang(xx, yy);
14640         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14641           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14642       }
14643     }
14644     else if (IS_BELT_SWITCH(element))
14645     {
14646       ToggleBeltSwitch(x, y);
14647     }
14648     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14649              element == EL_SWITCHGATE_SWITCH_DOWN ||
14650              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14651              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14652     {
14653       ToggleSwitchgateSwitch(x, y);
14654     }
14655     else if (element == EL_LIGHT_SWITCH ||
14656              element == EL_LIGHT_SWITCH_ACTIVE)
14657     {
14658       ToggleLightSwitch(x, y);
14659     }
14660     else if (element == EL_TIMEGATE_SWITCH ||
14661              element == EL_DC_TIMEGATE_SWITCH)
14662     {
14663       ActivateTimegateSwitch(x, y);
14664     }
14665     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14666              element == EL_BALLOON_SWITCH_RIGHT ||
14667              element == EL_BALLOON_SWITCH_UP    ||
14668              element == EL_BALLOON_SWITCH_DOWN  ||
14669              element == EL_BALLOON_SWITCH_NONE  ||
14670              element == EL_BALLOON_SWITCH_ANY)
14671     {
14672       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14673                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14674                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14675                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14676                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14677                              move_direction);
14678     }
14679     else if (element == EL_LAMP)
14680     {
14681       Feld[x][y] = EL_LAMP_ACTIVE;
14682       local_player->lights_still_needed--;
14683
14684       ResetGfxAnimation(x, y);
14685       DrawLevelField(x, y);
14686     }
14687     else if (element == EL_TIME_ORB_FULL)
14688     {
14689       Feld[x][y] = EL_TIME_ORB_EMPTY;
14690
14691       if (level.time > 0 || level.use_time_orb_bug)
14692       {
14693         TimeLeft += level.time_orb_time;
14694
14695 #if 1
14696         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14697
14698         DisplayGameControlValues();
14699 #else
14700         DrawGameValue_Time(TimeLeft);
14701 #endif
14702       }
14703
14704       ResetGfxAnimation(x, y);
14705       DrawLevelField(x, y);
14706     }
14707     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14708              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14709     {
14710       int xx, yy;
14711
14712       game.ball_state = !game.ball_state;
14713
14714       SCAN_PLAYFIELD(xx, yy)
14715       {
14716         int e = Feld[xx][yy];
14717
14718         if (game.ball_state)
14719         {
14720           if (e == EL_EMC_MAGIC_BALL)
14721             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14722           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14723             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14724         }
14725         else
14726         {
14727           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14728             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14729           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14730             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14731         }
14732       }
14733     }
14734
14735     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14736                                         player->index_bit, dig_side);
14737
14738     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14739                                         player->index_bit, dig_side);
14740
14741     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14742                                         player->index_bit, dig_side);
14743
14744     return MP_ACTION;
14745   }
14746   else
14747   {
14748     if (!PLAYER_SWITCHING(player, x, y))
14749     {
14750       player->is_switching = TRUE;
14751       player->switch_x = x;
14752       player->switch_y = y;
14753
14754       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14755                                  player->index_bit, dig_side);
14756       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14757                                           player->index_bit, dig_side);
14758
14759       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14760                                  player->index_bit, dig_side);
14761       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14762                                           player->index_bit, dig_side);
14763     }
14764
14765     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14766                                player->index_bit, dig_side);
14767     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14768                                         player->index_bit, dig_side);
14769
14770     return MP_NO_ACTION;
14771   }
14772
14773   player->push_delay = -1;
14774
14775   if (is_player)                /* function can also be called by EL_PENGUIN */
14776   {
14777     if (Feld[x][y] != element)          /* really digged/collected something */
14778     {
14779       player->is_collecting = !player->is_digging;
14780       player->is_active = TRUE;
14781     }
14782   }
14783
14784   return MP_MOVING;
14785 }
14786
14787 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14788 {
14789   int jx = player->jx, jy = player->jy;
14790   int x = jx + dx, y = jy + dy;
14791   int snap_direction = (dx == -1 ? MV_LEFT  :
14792                         dx == +1 ? MV_RIGHT :
14793                         dy == -1 ? MV_UP    :
14794                         dy == +1 ? MV_DOWN  : MV_NONE);
14795   boolean can_continue_snapping = (level.continuous_snapping &&
14796                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14797
14798   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14799     return FALSE;
14800
14801   if (!player->active || !IN_LEV_FIELD(x, y))
14802     return FALSE;
14803
14804   if (dx && dy)
14805     return FALSE;
14806
14807   if (!dx && !dy)
14808   {
14809     if (player->MovPos == 0)
14810       player->is_pushing = FALSE;
14811
14812     player->is_snapping = FALSE;
14813
14814     if (player->MovPos == 0)
14815     {
14816       player->is_moving = FALSE;
14817       player->is_digging = FALSE;
14818       player->is_collecting = FALSE;
14819     }
14820
14821     return FALSE;
14822   }
14823
14824 #if USE_NEW_CONTINUOUS_SNAPPING
14825   /* prevent snapping with already pressed snap key when not allowed */
14826   if (player->is_snapping && !can_continue_snapping)
14827     return FALSE;
14828 #else
14829   if (player->is_snapping)
14830     return FALSE;
14831 #endif
14832
14833   player->MovDir = snap_direction;
14834
14835   if (player->MovPos == 0)
14836   {
14837     player->is_moving = FALSE;
14838     player->is_digging = FALSE;
14839     player->is_collecting = FALSE;
14840   }
14841
14842   player->is_dropping = FALSE;
14843   player->is_dropping_pressed = FALSE;
14844   player->drop_pressed_delay = 0;
14845
14846   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14847     return FALSE;
14848
14849   player->is_snapping = TRUE;
14850   player->is_active = TRUE;
14851
14852   if (player->MovPos == 0)
14853   {
14854     player->is_moving = FALSE;
14855     player->is_digging = FALSE;
14856     player->is_collecting = FALSE;
14857   }
14858
14859   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14860     DrawLevelField(player->last_jx, player->last_jy);
14861
14862   DrawLevelField(x, y);
14863
14864   return TRUE;
14865 }
14866
14867 boolean DropElement(struct PlayerInfo *player)
14868 {
14869   int old_element, new_element;
14870   int dropx = player->jx, dropy = player->jy;
14871   int drop_direction = player->MovDir;
14872   int drop_side = drop_direction;
14873 #if 1
14874   int drop_element = get_next_dropped_element(player);
14875 #else
14876   int drop_element = (player->inventory_size > 0 ?
14877                       player->inventory_element[player->inventory_size - 1] :
14878                       player->inventory_infinite_element != EL_UNDEFINED ?
14879                       player->inventory_infinite_element :
14880                       player->dynabombs_left > 0 ?
14881                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14882                       EL_UNDEFINED);
14883 #endif
14884
14885   player->is_dropping_pressed = TRUE;
14886
14887   /* do not drop an element on top of another element; when holding drop key
14888      pressed without moving, dropped element must move away before the next
14889      element can be dropped (this is especially important if the next element
14890      is dynamite, which can be placed on background for historical reasons) */
14891   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14892     return MP_ACTION;
14893
14894   if (IS_THROWABLE(drop_element))
14895   {
14896     dropx += GET_DX_FROM_DIR(drop_direction);
14897     dropy += GET_DY_FROM_DIR(drop_direction);
14898
14899     if (!IN_LEV_FIELD(dropx, dropy))
14900       return FALSE;
14901   }
14902
14903   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14904   new_element = drop_element;           /* default: no change when dropping */
14905
14906   /* check if player is active, not moving and ready to drop */
14907   if (!player->active || player->MovPos || player->drop_delay > 0)
14908     return FALSE;
14909
14910   /* check if player has anything that can be dropped */
14911   if (new_element == EL_UNDEFINED)
14912     return FALSE;
14913
14914   /* check if drop key was pressed long enough for EM style dynamite */
14915   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14916     return FALSE;
14917
14918   /* check if anything can be dropped at the current position */
14919   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14920     return FALSE;
14921
14922   /* collected custom elements can only be dropped on empty fields */
14923   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14924     return FALSE;
14925
14926   if (old_element != EL_EMPTY)
14927     Back[dropx][dropy] = old_element;   /* store old element on this field */
14928
14929   ResetGfxAnimation(dropx, dropy);
14930   ResetRandomAnimationValue(dropx, dropy);
14931
14932   if (player->inventory_size > 0 ||
14933       player->inventory_infinite_element != EL_UNDEFINED)
14934   {
14935     if (player->inventory_size > 0)
14936     {
14937       player->inventory_size--;
14938
14939       DrawGameDoorValues();
14940
14941       if (new_element == EL_DYNAMITE)
14942         new_element = EL_DYNAMITE_ACTIVE;
14943       else if (new_element == EL_EM_DYNAMITE)
14944         new_element = EL_EM_DYNAMITE_ACTIVE;
14945       else if (new_element == EL_SP_DISK_RED)
14946         new_element = EL_SP_DISK_RED_ACTIVE;
14947     }
14948
14949     Feld[dropx][dropy] = new_element;
14950
14951     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14952       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14953                           el2img(Feld[dropx][dropy]), 0);
14954
14955     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14956
14957     /* needed if previous element just changed to "empty" in the last frame */
14958     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14959
14960     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14961                                player->index_bit, drop_side);
14962     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14963                                         CE_PLAYER_DROPS_X,
14964                                         player->index_bit, drop_side);
14965
14966     TestIfElementTouchesCustomElement(dropx, dropy);
14967   }
14968   else          /* player is dropping a dyna bomb */
14969   {
14970     player->dynabombs_left--;
14971
14972     Feld[dropx][dropy] = new_element;
14973
14974     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14975       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14976                           el2img(Feld[dropx][dropy]), 0);
14977
14978     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14979   }
14980
14981   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14982     InitField_WithBug1(dropx, dropy, FALSE);
14983
14984   new_element = Feld[dropx][dropy];     /* element might have changed */
14985
14986   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14987       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14988   {
14989     int move_direction, nextx, nexty;
14990
14991     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14992       MovDir[dropx][dropy] = drop_direction;
14993
14994     move_direction = MovDir[dropx][dropy];
14995     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14996     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14997
14998     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14999
15000 #if USE_FIX_IMPACT_COLLISION
15001     /* do not cause impact style collision by dropping elements that can fall */
15002     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15003 #else
15004     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15005 #endif
15006   }
15007
15008   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15009   player->is_dropping = TRUE;
15010
15011   player->drop_pressed_delay = 0;
15012   player->is_dropping_pressed = FALSE;
15013
15014   player->drop_x = dropx;
15015   player->drop_y = dropy;
15016
15017   return TRUE;
15018 }
15019
15020 /* ------------------------------------------------------------------------- */
15021 /* game sound playing functions                                              */
15022 /* ------------------------------------------------------------------------- */
15023
15024 static int *loop_sound_frame = NULL;
15025 static int *loop_sound_volume = NULL;
15026
15027 void InitPlayLevelSound()
15028 {
15029   int num_sounds = getSoundListSize();
15030
15031   checked_free(loop_sound_frame);
15032   checked_free(loop_sound_volume);
15033
15034   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15035   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15036 }
15037
15038 static void PlayLevelSound(int x, int y, int nr)
15039 {
15040   int sx = SCREENX(x), sy = SCREENY(y);
15041   int volume, stereo_position;
15042   int max_distance = 8;
15043   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15044
15045   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15046       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15047     return;
15048
15049   if (!IN_LEV_FIELD(x, y) ||
15050       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15051       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15052     return;
15053
15054   volume = SOUND_MAX_VOLUME;
15055
15056   if (!IN_SCR_FIELD(sx, sy))
15057   {
15058     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15059     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15060
15061     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15062   }
15063
15064   stereo_position = (SOUND_MAX_LEFT +
15065                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15066                      (SCR_FIELDX + 2 * max_distance));
15067
15068   if (IS_LOOP_SOUND(nr))
15069   {
15070     /* This assures that quieter loop sounds do not overwrite louder ones,
15071        while restarting sound volume comparison with each new game frame. */
15072
15073     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15074       return;
15075
15076     loop_sound_volume[nr] = volume;
15077     loop_sound_frame[nr] = FrameCounter;
15078   }
15079
15080   PlaySoundExt(nr, volume, stereo_position, type);
15081 }
15082
15083 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15084 {
15085   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15086                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15087                  y < LEVELY(BY1) ? LEVELY(BY1) :
15088                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15089                  sound_action);
15090 }
15091
15092 static void PlayLevelSoundAction(int x, int y, int action)
15093 {
15094   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15095 }
15096
15097 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15098 {
15099   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15100
15101   if (sound_effect != SND_UNDEFINED)
15102     PlayLevelSound(x, y, sound_effect);
15103 }
15104
15105 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15106                                               int action)
15107 {
15108   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15109
15110   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15111     PlayLevelSound(x, y, sound_effect);
15112 }
15113
15114 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15115 {
15116   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15117
15118   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15119     PlayLevelSound(x, y, sound_effect);
15120 }
15121
15122 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15123 {
15124   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15125
15126   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15127     StopSound(sound_effect);
15128 }
15129
15130 static void PlayLevelMusic()
15131 {
15132   if (levelset.music[level_nr] != MUS_UNDEFINED)
15133     PlayMusic(levelset.music[level_nr]);        /* from config file */
15134   else
15135     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15136 }
15137
15138 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15139 {
15140   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15141   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15142   int x = xx - 1 - offset;
15143   int y = yy - 1 - offset;
15144
15145   switch (sample)
15146   {
15147     case SAMPLE_blank:
15148       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15149       break;
15150
15151     case SAMPLE_roll:
15152       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15153       break;
15154
15155     case SAMPLE_stone:
15156       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15157       break;
15158
15159     case SAMPLE_nut:
15160       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15161       break;
15162
15163     case SAMPLE_crack:
15164       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15165       break;
15166
15167     case SAMPLE_bug:
15168       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15169       break;
15170
15171     case SAMPLE_tank:
15172       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15173       break;
15174
15175     case SAMPLE_android_clone:
15176       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15177       break;
15178
15179     case SAMPLE_android_move:
15180       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15181       break;
15182
15183     case SAMPLE_spring:
15184       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15185       break;
15186
15187     case SAMPLE_slurp:
15188       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15189       break;
15190
15191     case SAMPLE_eater:
15192       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15193       break;
15194
15195     case SAMPLE_eater_eat:
15196       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15197       break;
15198
15199     case SAMPLE_alien:
15200       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15201       break;
15202
15203     case SAMPLE_collect:
15204       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15205       break;
15206
15207     case SAMPLE_diamond:
15208       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15209       break;
15210
15211     case SAMPLE_squash:
15212       /* !!! CHECK THIS !!! */
15213 #if 1
15214       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15215 #else
15216       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15217 #endif
15218       break;
15219
15220     case SAMPLE_wonderfall:
15221       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15222       break;
15223
15224     case SAMPLE_drip:
15225       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15226       break;
15227
15228     case SAMPLE_push:
15229       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15230       break;
15231
15232     case SAMPLE_dirt:
15233       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15234       break;
15235
15236     case SAMPLE_acid:
15237       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15238       break;
15239
15240     case SAMPLE_ball:
15241       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15242       break;
15243
15244     case SAMPLE_grow:
15245       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15246       break;
15247
15248     case SAMPLE_wonder:
15249       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15250       break;
15251
15252     case SAMPLE_door:
15253       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15254       break;
15255
15256     case SAMPLE_exit_open:
15257       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15258       break;
15259
15260     case SAMPLE_exit_leave:
15261       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15262       break;
15263
15264     case SAMPLE_dynamite:
15265       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15266       break;
15267
15268     case SAMPLE_tick:
15269       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15270       break;
15271
15272     case SAMPLE_press:
15273       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15274       break;
15275
15276     case SAMPLE_wheel:
15277       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15278       break;
15279
15280     case SAMPLE_boom:
15281       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15282       break;
15283
15284     case SAMPLE_die:
15285       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15286       break;
15287
15288     case SAMPLE_time:
15289       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15290       break;
15291
15292     default:
15293       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15294       break;
15295   }
15296 }
15297
15298 #if 0
15299 void ChangeTime(int value)
15300 {
15301   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15302
15303   *time += value;
15304
15305   /* EMC game engine uses value from time counter of RND game engine */
15306   level.native_em_level->lev->time = *time;
15307
15308   DrawGameValue_Time(*time);
15309 }
15310
15311 void RaiseScore(int value)
15312 {
15313   /* EMC game engine and RND game engine have separate score counters */
15314   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15315                 &level.native_em_level->lev->score : &local_player->score);
15316
15317   *score += value;
15318
15319   DrawGameValue_Score(*score);
15320 }
15321 #endif
15322
15323 void RaiseScore(int value)
15324 {
15325   local_player->score += value;
15326
15327 #if 1
15328   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15329
15330   DisplayGameControlValues();
15331 #else
15332   DrawGameValue_Score(local_player->score);
15333 #endif
15334 }
15335
15336 void RaiseScoreElement(int element)
15337 {
15338   switch (element)
15339   {
15340     case EL_EMERALD:
15341     case EL_BD_DIAMOND:
15342     case EL_EMERALD_YELLOW:
15343     case EL_EMERALD_RED:
15344     case EL_EMERALD_PURPLE:
15345     case EL_SP_INFOTRON:
15346       RaiseScore(level.score[SC_EMERALD]);
15347       break;
15348     case EL_DIAMOND:
15349       RaiseScore(level.score[SC_DIAMOND]);
15350       break;
15351     case EL_CRYSTAL:
15352       RaiseScore(level.score[SC_CRYSTAL]);
15353       break;
15354     case EL_PEARL:
15355       RaiseScore(level.score[SC_PEARL]);
15356       break;
15357     case EL_BUG:
15358     case EL_BD_BUTTERFLY:
15359     case EL_SP_ELECTRON:
15360       RaiseScore(level.score[SC_BUG]);
15361       break;
15362     case EL_SPACESHIP:
15363     case EL_BD_FIREFLY:
15364     case EL_SP_SNIKSNAK:
15365       RaiseScore(level.score[SC_SPACESHIP]);
15366       break;
15367     case EL_YAMYAM:
15368     case EL_DARK_YAMYAM:
15369       RaiseScore(level.score[SC_YAMYAM]);
15370       break;
15371     case EL_ROBOT:
15372       RaiseScore(level.score[SC_ROBOT]);
15373       break;
15374     case EL_PACMAN:
15375       RaiseScore(level.score[SC_PACMAN]);
15376       break;
15377     case EL_NUT:
15378       RaiseScore(level.score[SC_NUT]);
15379       break;
15380     case EL_DYNAMITE:
15381     case EL_EM_DYNAMITE:
15382     case EL_SP_DISK_RED:
15383     case EL_DYNABOMB_INCREASE_NUMBER:
15384     case EL_DYNABOMB_INCREASE_SIZE:
15385     case EL_DYNABOMB_INCREASE_POWER:
15386       RaiseScore(level.score[SC_DYNAMITE]);
15387       break;
15388     case EL_SHIELD_NORMAL:
15389     case EL_SHIELD_DEADLY:
15390       RaiseScore(level.score[SC_SHIELD]);
15391       break;
15392     case EL_EXTRA_TIME:
15393       RaiseScore(level.extra_time_score);
15394       break;
15395     case EL_KEY_1:
15396     case EL_KEY_2:
15397     case EL_KEY_3:
15398     case EL_KEY_4:
15399     case EL_EM_KEY_1:
15400     case EL_EM_KEY_2:
15401     case EL_EM_KEY_3:
15402     case EL_EM_KEY_4:
15403     case EL_EMC_KEY_5:
15404     case EL_EMC_KEY_6:
15405     case EL_EMC_KEY_7:
15406     case EL_EMC_KEY_8:
15407     case EL_DC_KEY_WHITE:
15408       RaiseScore(level.score[SC_KEY]);
15409       break;
15410     default:
15411       RaiseScore(element_info[element].collect_score);
15412       break;
15413   }
15414 }
15415
15416 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15417 {
15418   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15419   {
15420 #if defined(NETWORK_AVALIABLE)
15421     if (options.network)
15422       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15423     else
15424 #endif
15425     {
15426       if (quick_quit)
15427       {
15428 #if 1
15429
15430 #if 1
15431         FadeSkipNextFadeIn();
15432 #else
15433         fading = fading_none;
15434 #endif
15435
15436 #else
15437         OpenDoor(DOOR_CLOSE_1);
15438 #endif
15439
15440         game_status = GAME_MODE_MAIN;
15441
15442 #if 1
15443         DrawAndFadeInMainMenu(REDRAW_FIELD);
15444 #else
15445         DrawMainMenu();
15446 #endif
15447       }
15448       else
15449       {
15450 #if 0
15451         FadeOut(REDRAW_FIELD);
15452 #endif
15453
15454         game_status = GAME_MODE_MAIN;
15455
15456         DrawAndFadeInMainMenu(REDRAW_FIELD);
15457       }
15458     }
15459   }
15460   else          /* continue playing the game */
15461   {
15462     if (tape.playing && tape.deactivate_display)
15463       TapeDeactivateDisplayOff(TRUE);
15464
15465     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15466
15467     if (tape.playing && tape.deactivate_display)
15468       TapeDeactivateDisplayOn();
15469   }
15470 }
15471
15472 void RequestQuitGame(boolean ask_if_really_quit)
15473 {
15474   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15475   boolean skip_request = AllPlayersGone || quick_quit;
15476
15477   RequestQuitGameExt(skip_request, quick_quit,
15478                      "Do you really want to quit the game ?");
15479 }
15480
15481
15482 /* ------------------------------------------------------------------------- */
15483 /* random generator functions                                                */
15484 /* ------------------------------------------------------------------------- */
15485
15486 unsigned int InitEngineRandom_RND(long seed)
15487 {
15488   game.num_random_calls = 0;
15489
15490 #if 0
15491   unsigned int rnd_seed = InitEngineRandom(seed);
15492
15493   printf("::: START RND: %d\n", rnd_seed);
15494
15495   return rnd_seed;
15496 #else
15497
15498   return InitEngineRandom(seed);
15499
15500 #endif
15501
15502 }
15503
15504 unsigned int RND(int max)
15505 {
15506   if (max > 0)
15507   {
15508     game.num_random_calls++;
15509
15510     return GetEngineRandom(max);
15511   }
15512
15513   return 0;
15514 }
15515
15516
15517 /* ------------------------------------------------------------------------- */
15518 /* game engine snapshot handling functions                                   */
15519 /* ------------------------------------------------------------------------- */
15520
15521 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15522
15523 struct EngineSnapshotInfo
15524 {
15525   /* runtime values for custom element collect score */
15526   int collect_score[NUM_CUSTOM_ELEMENTS];
15527
15528   /* runtime values for group element choice position */
15529   int choice_pos[NUM_GROUP_ELEMENTS];
15530
15531   /* runtime values for belt position animations */
15532   int belt_graphic[4 * NUM_BELT_PARTS];
15533   int belt_anim_mode[4 * NUM_BELT_PARTS];
15534 };
15535
15536 struct EngineSnapshotNodeInfo
15537 {
15538   void *buffer_orig;
15539   void *buffer_copy;
15540   int size;
15541 };
15542
15543 static struct EngineSnapshotInfo engine_snapshot_rnd;
15544 static ListNode *engine_snapshot_list = NULL;
15545 static char *snapshot_level_identifier = NULL;
15546 static int snapshot_level_nr = -1;
15547
15548 void FreeEngineSnapshot()
15549 {
15550   while (engine_snapshot_list != NULL)
15551     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15552                        checked_free);
15553
15554   setString(&snapshot_level_identifier, NULL);
15555   snapshot_level_nr = -1;
15556 }
15557
15558 static void SaveEngineSnapshotValues_RND()
15559 {
15560   static int belt_base_active_element[4] =
15561   {
15562     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15563     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15564     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15565     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15566   };
15567   int i, j;
15568
15569   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15570   {
15571     int element = EL_CUSTOM_START + i;
15572
15573     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15574   }
15575
15576   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15577   {
15578     int element = EL_GROUP_START + i;
15579
15580     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15581   }
15582
15583   for (i = 0; i < 4; i++)
15584   {
15585     for (j = 0; j < NUM_BELT_PARTS; j++)
15586     {
15587       int element = belt_base_active_element[i] + j;
15588       int graphic = el2img(element);
15589       int anim_mode = graphic_info[graphic].anim_mode;
15590
15591       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15592       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15593     }
15594   }
15595 }
15596
15597 static void LoadEngineSnapshotValues_RND()
15598 {
15599   unsigned long num_random_calls = game.num_random_calls;
15600   int i, j;
15601
15602   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15603   {
15604     int element = EL_CUSTOM_START + i;
15605
15606     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15607   }
15608
15609   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15610   {
15611     int element = EL_GROUP_START + i;
15612
15613     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15614   }
15615
15616   for (i = 0; i < 4; i++)
15617   {
15618     for (j = 0; j < NUM_BELT_PARTS; j++)
15619     {
15620       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15621       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15622
15623       graphic_info[graphic].anim_mode = anim_mode;
15624     }
15625   }
15626
15627   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15628   {
15629     InitRND(tape.random_seed);
15630     for (i = 0; i < num_random_calls; i++)
15631       RND(1);
15632   }
15633
15634   if (game.num_random_calls != num_random_calls)
15635   {
15636     Error(ERR_INFO, "number of random calls out of sync");
15637     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15638     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15639     Error(ERR_EXIT, "this should not happen -- please debug");
15640   }
15641 }
15642
15643 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15644 {
15645   struct EngineSnapshotNodeInfo *bi =
15646     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15647
15648   bi->buffer_orig = buffer;
15649   bi->buffer_copy = checked_malloc(size);
15650   bi->size = size;
15651
15652   memcpy(bi->buffer_copy, buffer, size);
15653
15654   addNodeToList(&engine_snapshot_list, NULL, bi);
15655 }
15656
15657 void SaveEngineSnapshot()
15658 {
15659   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15660
15661   if (level_editor_test_game)   /* do not save snapshots from editor */
15662     return;
15663
15664   /* copy some special values to a structure better suited for the snapshot */
15665
15666   SaveEngineSnapshotValues_RND();
15667   SaveEngineSnapshotValues_EM();
15668
15669   /* save values stored in special snapshot structure */
15670
15671   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15672   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15673
15674   /* save further RND engine values */
15675
15676   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15677   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15678   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15679
15680   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15681   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15682   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15683   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15684
15685   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15686   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15687   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15688   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15689   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15690
15691   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15692   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15693   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15694
15695   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15696
15697   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15698
15699   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15700   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15701
15702   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15703   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15704   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15705   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15706   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15707   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15708   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15709   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15710   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15711   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15712   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15713   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15714   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15715   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15716   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15717   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15718   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15719   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15720
15721   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15722   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15723
15724   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15725   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15726   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15727
15728   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15729   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15730
15731   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15732   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15733   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15734   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15735   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15736
15737   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15738   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15739
15740   /* save level identification information */
15741
15742   setString(&snapshot_level_identifier, leveldir_current->identifier);
15743   snapshot_level_nr = level_nr;
15744
15745 #if 0
15746   ListNode *node = engine_snapshot_list;
15747   int num_bytes = 0;
15748
15749   while (node != NULL)
15750   {
15751     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15752
15753     node = node->next;
15754   }
15755
15756   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15757 #endif
15758 }
15759
15760 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15761 {
15762   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15763 }
15764
15765 void LoadEngineSnapshot()
15766 {
15767   ListNode *node = engine_snapshot_list;
15768
15769   if (engine_snapshot_list == NULL)
15770     return;
15771
15772   while (node != NULL)
15773   {
15774     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15775
15776     node = node->next;
15777   }
15778
15779   /* restore special values from snapshot structure */
15780
15781   LoadEngineSnapshotValues_RND();
15782   LoadEngineSnapshotValues_EM();
15783 }
15784
15785 boolean CheckEngineSnapshot()
15786 {
15787   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15788           snapshot_level_nr == level_nr);
15789 }
15790
15791
15792 /* ---------- new game button stuff ---------------------------------------- */
15793
15794 /* graphic position values for game buttons */
15795 #define GAME_BUTTON_XSIZE       30
15796 #define GAME_BUTTON_YSIZE       30
15797 #define GAME_BUTTON_XPOS        5
15798 #define GAME_BUTTON_YPOS        215
15799 #define SOUND_BUTTON_XPOS       5
15800 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15801
15802 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15803 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15804 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15805 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15806 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15807 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15808
15809 static struct
15810 {
15811   int *x, *y;
15812   int gd_x, gd_y;
15813   int gadget_id;
15814   char *infotext;
15815 } gamebutton_info[NUM_GAME_BUTTONS] =
15816 {
15817 #if 1
15818   {
15819     &game.button.stop.x,        &game.button.stop.y,
15820     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15821     GAME_CTRL_ID_STOP,
15822     "stop game"
15823   },
15824   {
15825     &game.button.pause.x,       &game.button.pause.y,
15826     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15827     GAME_CTRL_ID_PAUSE,
15828     "pause game"
15829   },
15830   {
15831     &game.button.play.x,        &game.button.play.y,
15832     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15833     GAME_CTRL_ID_PLAY,
15834     "play game"
15835   },
15836   {
15837     &game.button.sound_music.x, &game.button.sound_music.y,
15838     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15839     SOUND_CTRL_ID_MUSIC,
15840     "background music on/off"
15841   },
15842   {
15843     &game.button.sound_loops.x, &game.button.sound_loops.y,
15844     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15845     SOUND_CTRL_ID_LOOPS,
15846     "sound loops on/off"
15847   },
15848   {
15849     &game.button.sound_simple.x,&game.button.sound_simple.y,
15850     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15851     SOUND_CTRL_ID_SIMPLE,
15852     "normal sounds on/off"
15853   }
15854 #else
15855   {
15856     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15857     GAME_CTRL_ID_STOP,
15858     "stop game"
15859   },
15860   {
15861     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15862     GAME_CTRL_ID_PAUSE,
15863     "pause game"
15864   },
15865   {
15866     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15867     GAME_CTRL_ID_PLAY,
15868     "play game"
15869   },
15870   {
15871     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15872     SOUND_CTRL_ID_MUSIC,
15873     "background music on/off"
15874   },
15875   {
15876     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15877     SOUND_CTRL_ID_LOOPS,
15878     "sound loops on/off"
15879   },
15880   {
15881     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15882     SOUND_CTRL_ID_SIMPLE,
15883     "normal sounds on/off"
15884   }
15885 #endif
15886 };
15887
15888 void CreateGameButtons()
15889 {
15890   int i;
15891
15892   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15893   {
15894     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15895     struct GadgetInfo *gi;
15896     int button_type;
15897     boolean checked;
15898     unsigned long event_mask;
15899     int x, y;
15900     int gd_xoffset, gd_yoffset;
15901     int gd_x1, gd_x2, gd_y1, gd_y2;
15902     int id = i;
15903
15904     x = DX + *gamebutton_info[i].x;
15905     y = DY + *gamebutton_info[i].y;
15906     gd_xoffset = gamebutton_info[i].gd_x;
15907     gd_yoffset = gamebutton_info[i].gd_y;
15908     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15909     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15910
15911     if (id == GAME_CTRL_ID_STOP ||
15912         id == GAME_CTRL_ID_PAUSE ||
15913         id == GAME_CTRL_ID_PLAY)
15914     {
15915       button_type = GD_TYPE_NORMAL_BUTTON;
15916       checked = FALSE;
15917       event_mask = GD_EVENT_RELEASED;
15918       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15919       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15920     }
15921     else
15922     {
15923       button_type = GD_TYPE_CHECK_BUTTON;
15924       checked =
15925         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15926          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15927          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15928       event_mask = GD_EVENT_PRESSED;
15929       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15930       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15931     }
15932
15933     gi = CreateGadget(GDI_CUSTOM_ID, id,
15934                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15935 #if 1
15936                       GDI_X, x,
15937                       GDI_Y, y,
15938 #else
15939                       GDI_X, DX + gd_xoffset,
15940                       GDI_Y, DY + gd_yoffset,
15941 #endif
15942                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15943                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15944                       GDI_TYPE, button_type,
15945                       GDI_STATE, GD_BUTTON_UNPRESSED,
15946                       GDI_CHECKED, checked,
15947                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15948                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15949                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15950                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15951                       GDI_DIRECT_DRAW, FALSE,
15952                       GDI_EVENT_MASK, event_mask,
15953                       GDI_CALLBACK_ACTION, HandleGameButtons,
15954                       GDI_END);
15955
15956     if (gi == NULL)
15957       Error(ERR_EXIT, "cannot create gadget");
15958
15959     game_gadget[id] = gi;
15960   }
15961 }
15962
15963 void FreeGameButtons()
15964 {
15965   int i;
15966
15967   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15968     FreeGadget(game_gadget[i]);
15969 }
15970
15971 static void MapGameButtons()
15972 {
15973   int i;
15974
15975   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15976     MapGadget(game_gadget[i]);
15977 }
15978
15979 void UnmapGameButtons()
15980 {
15981   int i;
15982
15983   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15984     UnmapGadget(game_gadget[i]);
15985 }
15986
15987 void RedrawGameButtons()
15988 {
15989   int i;
15990
15991   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15992     RedrawGadget(game_gadget[i]);
15993 }
15994
15995 static void HandleGameButtons(struct GadgetInfo *gi)
15996 {
15997   int id = gi->custom_id;
15998
15999   if (game_status != GAME_MODE_PLAYING)
16000     return;
16001
16002   switch (id)
16003   {
16004     case GAME_CTRL_ID_STOP:
16005       if (tape.playing)
16006         TapeStop();
16007       else
16008         RequestQuitGame(TRUE);
16009       break;
16010
16011     case GAME_CTRL_ID_PAUSE:
16012       if (options.network)
16013       {
16014 #if defined(NETWORK_AVALIABLE)
16015         if (tape.pausing)
16016           SendToServer_ContinuePlaying();
16017         else
16018           SendToServer_PausePlaying();
16019 #endif
16020       }
16021       else
16022         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16023       break;
16024
16025     case GAME_CTRL_ID_PLAY:
16026       if (tape.pausing)
16027       {
16028 #if defined(NETWORK_AVALIABLE)
16029         if (options.network)
16030           SendToServer_ContinuePlaying();
16031         else
16032 #endif
16033         {
16034           tape.pausing = FALSE;
16035           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16036         }
16037       }
16038       break;
16039
16040     case SOUND_CTRL_ID_MUSIC:
16041       if (setup.sound_music)
16042       { 
16043         setup.sound_music = FALSE;
16044         FadeMusic();
16045       }
16046       else if (audio.music_available)
16047       { 
16048         setup.sound = setup.sound_music = TRUE;
16049
16050         SetAudioMode(setup.sound);
16051
16052         PlayLevelMusic();
16053       }
16054       break;
16055
16056     case SOUND_CTRL_ID_LOOPS:
16057       if (setup.sound_loops)
16058         setup.sound_loops = FALSE;
16059       else if (audio.loops_available)
16060       {
16061         setup.sound = setup.sound_loops = TRUE;
16062         SetAudioMode(setup.sound);
16063       }
16064       break;
16065
16066     case SOUND_CTRL_ID_SIMPLE:
16067       if (setup.sound_simple)
16068         setup.sound_simple = FALSE;
16069       else if (audio.sound_available)
16070       {
16071         setup.sound = setup.sound_simple = TRUE;
16072         SetAudioMode(setup.sound);
16073       }
16074       break;
16075
16076     default:
16077       break;
16078   }
16079 }