rnd-20070415-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_TIME                         30
165 #define GAME_PANEL_TIME_HH                      31
166 #define GAME_PANEL_TIME_MM                      32
167 #define GAME_PANEL_TIME_SS                      33
168 #define GAME_PANEL_SHIELD_NORMAL                34
169 #define GAME_PANEL_SHIELD_NORMAL_TIME           35
170 #define GAME_PANEL_SHIELD_DEADLY                36
171 #define GAME_PANEL_SHIELD_DEADLY_TIME           37
172 #define GAME_PANEL_EXIT                         38
173 #define GAME_PANEL_EMC_MAGIC_BALL               39
174 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        40
175 #define GAME_PANEL_LIGHT_SWITCH                 41
176 #define GAME_PANEL_LIGHT_SWITCH_TIME            42
177 #define GAME_PANEL_TIMEGATE_SWITCH              43
178 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         44
179 #define GAME_PANEL_SWITCHGATE_SWITCH            45
180 #define GAME_PANEL_EMC_LENSES                   46
181 #define GAME_PANEL_EMC_LENSES_TIME              47
182 #define GAME_PANEL_EMC_MAGNIFIER                48
183 #define GAME_PANEL_EMC_MAGNIFIER_TIME           49
184 #define GAME_PANEL_BALLOON_SWITCH               50
185 #define GAME_PANEL_DYNABOMB_NUMBER              51
186 #define GAME_PANEL_DYNABOMB_SIZE                52
187 #define GAME_PANEL_DYNABOMB_POWER               53
188 #define GAME_PANEL_PENGUINS                     54
189 #define GAME_PANEL_SOKOBAN_OBJECTS              55
190 #define GAME_PANEL_SOKOBAN_FIELDS               56
191 #define GAME_PANEL_ROBOT_WHEEL                  57
192 #define GAME_PANEL_CONVEYOR_BELT_1              58
193 #define GAME_PANEL_CONVEYOR_BELT_2              59
194 #define GAME_PANEL_CONVEYOR_BELT_3              60
195 #define GAME_PANEL_CONVEYOR_BELT_4              61
196 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       62
197 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       63
198 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       64
199 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       65
200 #define GAME_PANEL_MAGIC_WALL                   66
201 #define GAME_PANEL_MAGIC_WALL_TIME              67
202 #define GAME_PANEL_GRAVITY_STATE                68
203 #define GAME_PANEL_GRAPHIC_1                    69
204 #define GAME_PANEL_GRAPHIC_2                    70
205 #define GAME_PANEL_GRAPHIC_3                    71
206 #define GAME_PANEL_GRAPHIC_4                    72
207 #define GAME_PANEL_GRAPHIC_5                    73
208 #define GAME_PANEL_GRAPHIC_6                    74
209 #define GAME_PANEL_GRAPHIC_7                    75
210 #define GAME_PANEL_GRAPHIC_8                    76
211 #define GAME_PANEL_ELEMENT_1                    77
212 #define GAME_PANEL_ELEMENT_2                    78
213 #define GAME_PANEL_ELEMENT_3                    79
214 #define GAME_PANEL_ELEMENT_4                    80
215 #define GAME_PANEL_ELEMENT_5                    81
216 #define GAME_PANEL_ELEMENT_6                    82
217 #define GAME_PANEL_ELEMENT_7                    83
218 #define GAME_PANEL_ELEMENT_8                    84
219 #define GAME_PANEL_ELEMENT_COUNT_1              85
220 #define GAME_PANEL_ELEMENT_COUNT_2              86
221 #define GAME_PANEL_ELEMENT_COUNT_3              87
222 #define GAME_PANEL_ELEMENT_COUNT_4              88
223 #define GAME_PANEL_ELEMENT_COUNT_5              89
224 #define GAME_PANEL_ELEMENT_COUNT_6              90
225 #define GAME_PANEL_ELEMENT_COUNT_7              91
226 #define GAME_PANEL_ELEMENT_COUNT_8              92
227 #define GAME_PANEL_CE_SCORE_1                   93
228 #define GAME_PANEL_CE_SCORE_2                   94
229 #define GAME_PANEL_CE_SCORE_3                   95
230 #define GAME_PANEL_CE_SCORE_4                   96
231 #define GAME_PANEL_CE_SCORE_5                   97
232 #define GAME_PANEL_CE_SCORE_6                   98
233 #define GAME_PANEL_CE_SCORE_7                   99
234 #define GAME_PANEL_CE_SCORE_8                   100
235 #define GAME_PANEL_CE_SCORE_1_ELEMENT           101
236 #define GAME_PANEL_CE_SCORE_2_ELEMENT           102
237 #define GAME_PANEL_CE_SCORE_3_ELEMENT           103
238 #define GAME_PANEL_CE_SCORE_4_ELEMENT           104
239 #define GAME_PANEL_CE_SCORE_5_ELEMENT           105
240 #define GAME_PANEL_CE_SCORE_6_ELEMENT           106
241 #define GAME_PANEL_CE_SCORE_7_ELEMENT           107
242 #define GAME_PANEL_CE_SCORE_8_ELEMENT           108
243 #define GAME_PANEL_PLAYER_NAME                  109
244 #define GAME_PANEL_LEVEL_NAME                   110
245 #define GAME_PANEL_LEVEL_AUTHOR                 111
246
247 #define NUM_GAME_PANEL_CONTROLS                 112
248
249 struct GamePanelOrderInfo
250 {
251   int nr;
252   int sort_priority;
253 };
254
255 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
256
257 struct GamePanelControlInfo
258 {
259   int nr;
260
261   struct TextPosInfo *pos;
262   int type;
263
264   int value, last_value;
265   int frame, last_frame;
266   int gfx_frame;
267   int gfx_random;
268 };
269
270 static struct GamePanelControlInfo game_panel_controls[] =
271 {
272   {
273     GAME_PANEL_LEVEL_NUMBER,
274     &game.panel.level_number,
275     TYPE_INTEGER,
276   },
277   {
278     GAME_PANEL_GEMS,
279     &game.panel.gems,
280     TYPE_INTEGER,
281   },
282   {
283     GAME_PANEL_INVENTORY_COUNT,
284     &game.panel.inventory_count,
285     TYPE_INTEGER,
286   },
287   {
288     GAME_PANEL_INVENTORY_FIRST_1,
289     &game.panel.inventory_first[0],
290     TYPE_ELEMENT,
291   },
292   {
293     GAME_PANEL_INVENTORY_FIRST_2,
294     &game.panel.inventory_first[1],
295     TYPE_ELEMENT,
296   },
297   {
298     GAME_PANEL_INVENTORY_FIRST_3,
299     &game.panel.inventory_first[2],
300     TYPE_ELEMENT,
301   },
302   {
303     GAME_PANEL_INVENTORY_FIRST_4,
304     &game.panel.inventory_first[3],
305     TYPE_ELEMENT,
306   },
307   {
308     GAME_PANEL_INVENTORY_FIRST_5,
309     &game.panel.inventory_first[4],
310     TYPE_ELEMENT,
311   },
312   {
313     GAME_PANEL_INVENTORY_FIRST_6,
314     &game.panel.inventory_first[5],
315     TYPE_ELEMENT,
316   },
317   {
318     GAME_PANEL_INVENTORY_FIRST_7,
319     &game.panel.inventory_first[6],
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_PANEL_INVENTORY_FIRST_8,
324     &game.panel.inventory_first[7],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_PANEL_INVENTORY_LAST_1,
329     &game.panel.inventory_last[0],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_PANEL_INVENTORY_LAST_2,
334     &game.panel.inventory_last[1],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_PANEL_INVENTORY_LAST_3,
339     &game.panel.inventory_last[2],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_PANEL_INVENTORY_LAST_4,
344     &game.panel.inventory_last[3],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_PANEL_INVENTORY_LAST_5,
349     &game.panel.inventory_last[4],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_PANEL_INVENTORY_LAST_6,
354     &game.panel.inventory_last[5],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_PANEL_INVENTORY_LAST_7,
359     &game.panel.inventory_last[6],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_PANEL_INVENTORY_LAST_8,
364     &game.panel.inventory_last[7],
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_PANEL_KEY_1,
369     &game.panel.key[0],
370     TYPE_ELEMENT,
371   },
372   {
373     GAME_PANEL_KEY_2,
374     &game.panel.key[1],
375     TYPE_ELEMENT,
376   },
377   {
378     GAME_PANEL_KEY_3,
379     &game.panel.key[2],
380     TYPE_ELEMENT,
381   },
382   {
383     GAME_PANEL_KEY_4,
384     &game.panel.key[3],
385     TYPE_ELEMENT,
386   },
387   {
388     GAME_PANEL_KEY_5,
389     &game.panel.key[4],
390     TYPE_ELEMENT,
391   },
392   {
393     GAME_PANEL_KEY_6,
394     &game.panel.key[5],
395     TYPE_ELEMENT,
396   },
397   {
398     GAME_PANEL_KEY_7,
399     &game.panel.key[6],
400     TYPE_ELEMENT,
401   },
402   {
403     GAME_PANEL_KEY_8,
404     &game.panel.key[7],
405     TYPE_ELEMENT,
406   },
407   {
408     GAME_PANEL_KEY_WHITE,
409     &game.panel.key_white,
410     TYPE_ELEMENT,
411   },
412   {
413     GAME_PANEL_KEY_WHITE_COUNT,
414     &game.panel.key_white_count,
415     TYPE_INTEGER,
416   },
417   {
418     GAME_PANEL_SCORE,
419     &game.panel.score,
420     TYPE_INTEGER,
421   },
422   {
423     GAME_PANEL_TIME,
424     &game.panel.time,
425     TYPE_INTEGER,
426   },
427   {
428     GAME_PANEL_TIME_HH,
429     &game.panel.time_hh,
430     TYPE_INTEGER,
431   },
432   {
433     GAME_PANEL_TIME_MM,
434     &game.panel.time_mm,
435     TYPE_INTEGER,
436   },
437   {
438     GAME_PANEL_TIME_SS,
439     &game.panel.time_ss,
440     TYPE_INTEGER,
441   },
442   {
443     GAME_PANEL_SHIELD_NORMAL,
444     &game.panel.shield_normal,
445     TYPE_ELEMENT,
446   },
447   {
448     GAME_PANEL_SHIELD_NORMAL_TIME,
449     &game.panel.shield_normal_time,
450     TYPE_INTEGER,
451   },
452   {
453     GAME_PANEL_SHIELD_DEADLY,
454     &game.panel.shield_deadly,
455     TYPE_ELEMENT,
456   },
457   {
458     GAME_PANEL_SHIELD_DEADLY_TIME,
459     &game.panel.shield_deadly_time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_PANEL_EXIT,
464     &game.panel.exit,
465     TYPE_ELEMENT,
466   },
467   {
468     GAME_PANEL_EMC_MAGIC_BALL,
469     &game.panel.emc_magic_ball,
470     TYPE_ELEMENT,
471   },
472   {
473     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
474     &game.panel.emc_magic_ball_switch,
475     TYPE_ELEMENT,
476   },
477   {
478     GAME_PANEL_LIGHT_SWITCH,
479     &game.panel.light_switch,
480     TYPE_ELEMENT,
481   },
482   {
483     GAME_PANEL_LIGHT_SWITCH_TIME,
484     &game.panel.light_switch_time,
485     TYPE_INTEGER,
486   },
487   {
488     GAME_PANEL_TIMEGATE_SWITCH,
489     &game.panel.timegate_switch,
490     TYPE_ELEMENT,
491   },
492   {
493     GAME_PANEL_TIMEGATE_SWITCH_TIME,
494     &game.panel.timegate_switch_time,
495     TYPE_INTEGER,
496   },
497   {
498     GAME_PANEL_SWITCHGATE_SWITCH,
499     &game.panel.switchgate_switch,
500     TYPE_ELEMENT,
501   },
502   {
503     GAME_PANEL_EMC_LENSES,
504     &game.panel.emc_lenses,
505     TYPE_ELEMENT,
506   },
507   {
508     GAME_PANEL_EMC_LENSES_TIME,
509     &game.panel.emc_lenses_time,
510     TYPE_INTEGER,
511   },
512   {
513     GAME_PANEL_EMC_MAGNIFIER,
514     &game.panel.emc_magnifier,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_PANEL_EMC_MAGNIFIER_TIME,
519     &game.panel.emc_magnifier_time,
520     TYPE_INTEGER,
521   },
522   {
523     GAME_PANEL_BALLOON_SWITCH,
524     &game.panel.balloon_switch,
525     TYPE_ELEMENT,
526   },
527   {
528     GAME_PANEL_DYNABOMB_NUMBER,
529     &game.panel.dynabomb_number,
530     TYPE_INTEGER,
531   },
532   {
533     GAME_PANEL_DYNABOMB_SIZE,
534     &game.panel.dynabomb_size,
535     TYPE_INTEGER,
536   },
537   {
538     GAME_PANEL_DYNABOMB_POWER,
539     &game.panel.dynabomb_power,
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_PANEL_PENGUINS,
544     &game.panel.penguins,
545     TYPE_INTEGER,
546   },
547   {
548     GAME_PANEL_SOKOBAN_OBJECTS,
549     &game.panel.sokoban_objects,
550     TYPE_INTEGER,
551   },
552   {
553     GAME_PANEL_SOKOBAN_FIELDS,
554     &game.panel.sokoban_fields,
555     TYPE_INTEGER,
556   },
557   {
558     GAME_PANEL_ROBOT_WHEEL,
559     &game.panel.robot_wheel,
560     TYPE_ELEMENT,
561   },
562   {
563     GAME_PANEL_CONVEYOR_BELT_1,
564     &game.panel.conveyor_belt[0],
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_PANEL_CONVEYOR_BELT_2,
569     &game.panel.conveyor_belt[1],
570     TYPE_ELEMENT,
571   },
572   {
573     GAME_PANEL_CONVEYOR_BELT_3,
574     &game.panel.conveyor_belt[2],
575     TYPE_ELEMENT,
576   },
577   {
578     GAME_PANEL_CONVEYOR_BELT_4,
579     &game.panel.conveyor_belt[3],
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
584     &game.panel.conveyor_belt_switch[0],
585     TYPE_ELEMENT,
586   },
587   {
588     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
589     &game.panel.conveyor_belt_switch[1],
590     TYPE_ELEMENT,
591   },
592   {
593     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
594     &game.panel.conveyor_belt_switch[2],
595     TYPE_ELEMENT,
596   },
597   {
598     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
599     &game.panel.conveyor_belt_switch[3],
600     TYPE_ELEMENT,
601   },
602   {
603     GAME_PANEL_MAGIC_WALL,
604     &game.panel.magic_wall,
605     TYPE_ELEMENT,
606   },
607   {
608     GAME_PANEL_MAGIC_WALL_TIME,
609     &game.panel.magic_wall_time,
610     TYPE_INTEGER,
611   },
612   {
613     GAME_PANEL_GRAVITY_STATE,
614     &game.panel.gravity_state,
615     TYPE_STRING,
616   },
617   {
618     GAME_PANEL_GRAPHIC_1,
619     &game.panel.graphic[0],
620     TYPE_ELEMENT,
621   },
622   {
623     GAME_PANEL_GRAPHIC_2,
624     &game.panel.graphic[1],
625     TYPE_ELEMENT,
626   },
627   {
628     GAME_PANEL_GRAPHIC_3,
629     &game.panel.graphic[2],
630     TYPE_ELEMENT,
631   },
632   {
633     GAME_PANEL_GRAPHIC_4,
634     &game.panel.graphic[3],
635     TYPE_ELEMENT,
636   },
637   {
638     GAME_PANEL_GRAPHIC_5,
639     &game.panel.graphic[4],
640     TYPE_ELEMENT,
641   },
642   {
643     GAME_PANEL_GRAPHIC_6,
644     &game.panel.graphic[5],
645     TYPE_ELEMENT,
646   },
647   {
648     GAME_PANEL_GRAPHIC_7,
649     &game.panel.graphic[6],
650     TYPE_ELEMENT,
651   },
652   {
653     GAME_PANEL_GRAPHIC_8,
654     &game.panel.graphic[7],
655     TYPE_ELEMENT,
656   },
657   {
658     GAME_PANEL_ELEMENT_1,
659     &game.panel.element[0],
660     TYPE_ELEMENT,
661   },
662   {
663     GAME_PANEL_ELEMENT_2,
664     &game.panel.element[1],
665     TYPE_ELEMENT,
666   },
667   {
668     GAME_PANEL_ELEMENT_3,
669     &game.panel.element[2],
670     TYPE_ELEMENT,
671   },
672   {
673     GAME_PANEL_ELEMENT_4,
674     &game.panel.element[3],
675     TYPE_ELEMENT,
676   },
677   {
678     GAME_PANEL_ELEMENT_5,
679     &game.panel.element[4],
680     TYPE_ELEMENT,
681   },
682   {
683     GAME_PANEL_ELEMENT_6,
684     &game.panel.element[5],
685     TYPE_ELEMENT,
686   },
687   {
688     GAME_PANEL_ELEMENT_7,
689     &game.panel.element[6],
690     TYPE_ELEMENT,
691   },
692   {
693     GAME_PANEL_ELEMENT_8,
694     &game.panel.element[7],
695     TYPE_ELEMENT,
696   },
697   {
698     GAME_PANEL_ELEMENT_COUNT_1,
699     &game.panel.element_count[0],
700     TYPE_INTEGER,
701   },
702   {
703     GAME_PANEL_ELEMENT_COUNT_2,
704     &game.panel.element_count[1],
705     TYPE_INTEGER,
706   },
707   {
708     GAME_PANEL_ELEMENT_COUNT_3,
709     &game.panel.element_count[2],
710     TYPE_INTEGER,
711   },
712   {
713     GAME_PANEL_ELEMENT_COUNT_4,
714     &game.panel.element_count[3],
715     TYPE_INTEGER,
716   },
717   {
718     GAME_PANEL_ELEMENT_COUNT_5,
719     &game.panel.element_count[4],
720     TYPE_INTEGER,
721   },
722   {
723     GAME_PANEL_ELEMENT_COUNT_6,
724     &game.panel.element_count[5],
725     TYPE_INTEGER,
726   },
727   {
728     GAME_PANEL_ELEMENT_COUNT_7,
729     &game.panel.element_count[6],
730     TYPE_INTEGER,
731   },
732   {
733     GAME_PANEL_ELEMENT_COUNT_8,
734     &game.panel.element_count[7],
735     TYPE_INTEGER,
736   },
737   {
738     GAME_PANEL_CE_SCORE_1,
739     &game.panel.ce_score[0],
740     TYPE_INTEGER,
741   },
742   {
743     GAME_PANEL_CE_SCORE_2,
744     &game.panel.ce_score[1],
745     TYPE_INTEGER,
746   },
747   {
748     GAME_PANEL_CE_SCORE_3,
749     &game.panel.ce_score[2],
750     TYPE_INTEGER,
751   },
752   {
753     GAME_PANEL_CE_SCORE_4,
754     &game.panel.ce_score[3],
755     TYPE_INTEGER,
756   },
757   {
758     GAME_PANEL_CE_SCORE_5,
759     &game.panel.ce_score[4],
760     TYPE_INTEGER,
761   },
762   {
763     GAME_PANEL_CE_SCORE_6,
764     &game.panel.ce_score[5],
765     TYPE_INTEGER,
766   },
767   {
768     GAME_PANEL_CE_SCORE_7,
769     &game.panel.ce_score[6],
770     TYPE_INTEGER,
771   },
772   {
773     GAME_PANEL_CE_SCORE_8,
774     &game.panel.ce_score[7],
775     TYPE_INTEGER,
776   },
777   {
778     GAME_PANEL_CE_SCORE_1_ELEMENT,
779     &game.panel.ce_score_element[0],
780     TYPE_ELEMENT,
781   },
782   {
783     GAME_PANEL_CE_SCORE_2_ELEMENT,
784     &game.panel.ce_score_element[1],
785     TYPE_ELEMENT,
786   },
787   {
788     GAME_PANEL_CE_SCORE_3_ELEMENT,
789     &game.panel.ce_score_element[2],
790     TYPE_ELEMENT,
791   },
792   {
793     GAME_PANEL_CE_SCORE_4_ELEMENT,
794     &game.panel.ce_score_element[3],
795     TYPE_ELEMENT,
796   },
797   {
798     GAME_PANEL_CE_SCORE_5_ELEMENT,
799     &game.panel.ce_score_element[4],
800     TYPE_ELEMENT,
801   },
802   {
803     GAME_PANEL_CE_SCORE_6_ELEMENT,
804     &game.panel.ce_score_element[5],
805     TYPE_ELEMENT,
806   },
807   {
808     GAME_PANEL_CE_SCORE_7_ELEMENT,
809     &game.panel.ce_score_element[6],
810     TYPE_ELEMENT,
811   },
812   {
813     GAME_PANEL_CE_SCORE_8_ELEMENT,
814     &game.panel.ce_score_element[7],
815     TYPE_ELEMENT,
816   },
817   {
818     GAME_PANEL_PLAYER_NAME,
819     &game.panel.player_name,
820     TYPE_STRING,
821   },
822   {
823     GAME_PANEL_LEVEL_NAME,
824     &game.panel.level_name,
825     TYPE_STRING,
826   },
827   {
828     GAME_PANEL_LEVEL_AUTHOR,
829     &game.panel.level_author,
830     TYPE_STRING,
831   },
832
833   {
834     -1,
835     NULL,
836     -1,
837   }
838 };
839 #endif
840
841
842 /* values for delayed check of falling and moving elements and for collision */
843 #define CHECK_DELAY_MOVING      3
844 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
845 #define CHECK_DELAY_COLLISION   2
846 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
847
848 /* values for initial player move delay (initial delay counter value) */
849 #define INITIAL_MOVE_DELAY_OFF  -1
850 #define INITIAL_MOVE_DELAY_ON   0
851
852 /* values for player movement speed (which is in fact a delay value) */
853 #define MOVE_DELAY_MIN_SPEED    32
854 #define MOVE_DELAY_NORMAL_SPEED 8
855 #define MOVE_DELAY_HIGH_SPEED   4
856 #define MOVE_DELAY_MAX_SPEED    1
857
858 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
859 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
860
861 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
862 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
900         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
901          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
902          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
903          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
904          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
905          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
906          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
907          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
908          (e))
909
910 #define CAN_GROW_INTO(e)                                                \
911         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
912
913 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
914                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
915                                         (condition)))
916
917 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
918                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
919                                         (CAN_MOVE_INTO_ACID(e) &&       \
920                                          Feld[x][y] == EL_ACID) ||      \
921                                         (condition)))
922
923 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
924                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
925                                         (CAN_MOVE_INTO_ACID(e) &&       \
926                                          Feld[x][y] == EL_ACID) ||      \
927                                         (condition)))
928
929 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
930                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
931                                         (condition) ||                  \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Feld[x][y] == EL_ACID) ||      \
934                                         (DONT_COLLIDE_WITH(e) &&        \
935                                          IS_PLAYER(x, y) &&             \
936                                          !PLAYER_ENEMY_PROTECTED(x, y))))
937
938 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
939         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
940
941 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
942         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
943
944 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
946
947 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
948         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
949                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
950
951 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
953
954 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
955         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
956
957 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
959
960 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
962
963 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
964         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
965
966 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
968                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
969                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
970                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
971                                                  IS_FOOD_PENGUIN(Feld[x][y])))
972 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
973         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
974
975 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
977
978 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
980
981 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
982         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
983                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
984
985 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
986
987 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
988                 (!IS_PLAYER(x, y) &&                                    \
989                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
990
991 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
992         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
993
994 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
995 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
996
997 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
998 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
999 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1000 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1001
1002 /* game button identifiers */
1003 #define GAME_CTRL_ID_STOP               0
1004 #define GAME_CTRL_ID_PAUSE              1
1005 #define GAME_CTRL_ID_PLAY               2
1006 #define SOUND_CTRL_ID_MUSIC             3
1007 #define SOUND_CTRL_ID_LOOPS             4
1008 #define SOUND_CTRL_ID_SIMPLE            5
1009
1010 #define NUM_GAME_BUTTONS                6
1011
1012
1013 /* forward declaration for internal use */
1014
1015 static void CreateField(int, int, int);
1016
1017 static void ResetGfxAnimation(int, int);
1018
1019 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1020 static void AdvanceFrameAndPlayerCounters(int);
1021
1022 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1023 static boolean MovePlayer(struct PlayerInfo *, int, int);
1024 static void ScrollPlayer(struct PlayerInfo *, int);
1025 static void ScrollScreen(struct PlayerInfo *, int);
1026
1027 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1028
1029 static void InitBeltMovement(void);
1030 static void CloseAllOpenTimegates(void);
1031 static void CheckGravityMovement(struct PlayerInfo *);
1032 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1033 static void KillPlayerUnlessEnemyProtected(int, int);
1034 static void KillPlayerUnlessExplosionProtected(int, int);
1035
1036 static void TestIfPlayerTouchesCustomElement(int, int);
1037 static void TestIfElementTouchesCustomElement(int, int);
1038 static void TestIfElementHitsCustomElement(int, int, int);
1039 #if 0
1040 static void TestIfElementSmashesCustomElement(int, int, int);
1041 #endif
1042
1043 static void HandleElementChange(int, int, int);
1044 static void ExecuteCustomElementAction(int, int, int, int);
1045 static boolean ChangeElement(int, int, int, int);
1046
1047 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1048 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1049         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1050 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1051         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1052 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1053         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1054 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1055         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1056
1057 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1058 #define CheckElementChange(x, y, e, te, ev)                             \
1059         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1060 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1061         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1062 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1063         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1064
1065 static void PlayLevelSound(int, int, int);
1066 static void PlayLevelSoundNearest(int, int, int);
1067 static void PlayLevelSoundAction(int, int, int);
1068 static void PlayLevelSoundElementAction(int, int, int, int);
1069 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1070 static void PlayLevelSoundActionIfLoop(int, int, int);
1071 static void StopLevelSoundActionIfLoop(int, int, int);
1072 static void PlayLevelMusic();
1073
1074 static void MapGameButtons();
1075 static void HandleGameButtons(struct GadgetInfo *);
1076
1077 int AmoebeNachbarNr(int, int);
1078 void AmoebeUmwandeln(int, int);
1079 void ContinueMoving(int, int);
1080 void Bang(int, int);
1081 void InitMovDir(int, int);
1082 void InitAmoebaNr(int, int);
1083 int NewHiScore(void);
1084
1085 void TestIfGoodThingHitsBadThing(int, int, int);
1086 void TestIfBadThingHitsGoodThing(int, int, int);
1087 void TestIfPlayerTouchesBadThing(int, int);
1088 void TestIfPlayerRunsIntoBadThing(int, int, int);
1089 void TestIfBadThingTouchesPlayer(int, int);
1090 void TestIfBadThingRunsIntoPlayer(int, int, int);
1091 void TestIfFriendTouchesBadThing(int, int);
1092 void TestIfBadThingTouchesFriend(int, int);
1093 void TestIfBadThingTouchesOtherBadThing(int, int);
1094
1095 void KillPlayer(struct PlayerInfo *);
1096 void BuryPlayer(struct PlayerInfo *);
1097 void RemovePlayer(struct PlayerInfo *);
1098
1099 boolean SnapField(struct PlayerInfo *, int, int);
1100 boolean DropElement(struct PlayerInfo *);
1101
1102 static int getInvisibleActiveFromInvisibleElement(int);
1103 static int getInvisibleFromInvisibleActiveElement(int);
1104
1105 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1106
1107 /* for detection of endless loops, caused by custom element programming */
1108 /* (using maximal playfield width x 10 is just a rough approximation) */
1109 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1110
1111 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1112 {                                                                       \
1113   if (recursion_loop_detected)                                          \
1114     return (rc);                                                        \
1115                                                                         \
1116   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1117   {                                                                     \
1118     recursion_loop_detected = TRUE;                                     \
1119     recursion_loop_element = (e);                                       \
1120   }                                                                     \
1121                                                                         \
1122   recursion_loop_depth++;                                               \
1123 }
1124
1125 #define RECURSION_LOOP_DETECTION_END()                                  \
1126 {                                                                       \
1127   recursion_loop_depth--;                                               \
1128 }
1129
1130 static int recursion_loop_depth;
1131 static boolean recursion_loop_detected;
1132 static boolean recursion_loop_element;
1133
1134
1135 /* ------------------------------------------------------------------------- */
1136 /* definition of elements that automatically change to other elements after  */
1137 /* a specified time, eventually calling a function when changing             */
1138 /* ------------------------------------------------------------------------- */
1139
1140 /* forward declaration for changer functions */
1141 static void InitBuggyBase(int, int);
1142 static void WarnBuggyBase(int, int);
1143
1144 static void InitTrap(int, int);
1145 static void ActivateTrap(int, int);
1146 static void ChangeActiveTrap(int, int);
1147
1148 static void InitRobotWheel(int, int);
1149 static void RunRobotWheel(int, int);
1150 static void StopRobotWheel(int, int);
1151
1152 static void InitTimegateWheel(int, int);
1153 static void RunTimegateWheel(int, int);
1154
1155 static void InitMagicBallDelay(int, int);
1156 static void ActivateMagicBall(int, int);
1157
1158 struct ChangingElementInfo
1159 {
1160   int element;
1161   int target_element;
1162   int change_delay;
1163   void (*pre_change_function)(int x, int y);
1164   void (*change_function)(int x, int y);
1165   void (*post_change_function)(int x, int y);
1166 };
1167
1168 static struct ChangingElementInfo change_delay_list[] =
1169 {
1170   {
1171     EL_NUT_BREAKING,
1172     EL_EMERALD,
1173     6,
1174     NULL,
1175     NULL,
1176     NULL
1177   },
1178   {
1179     EL_PEARL_BREAKING,
1180     EL_EMPTY,
1181     8,
1182     NULL,
1183     NULL,
1184     NULL
1185   },
1186   {
1187     EL_EXIT_OPENING,
1188     EL_EXIT_OPEN,
1189     29,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_EXIT_CLOSING,
1196     EL_EXIT_CLOSED,
1197     29,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_STEEL_EXIT_OPENING,
1204     EL_STEEL_EXIT_OPEN,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_STEEL_EXIT_CLOSING,
1212     EL_STEEL_EXIT_CLOSED,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EM_EXIT_OPENING,
1220     EL_EM_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EM_EXIT_CLOSING,
1228 #if 1
1229     EL_EMPTY,
1230 #else
1231     EL_EM_EXIT_CLOSED,
1232 #endif
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_EM_STEEL_EXIT_OPENING,
1240     EL_EM_STEEL_EXIT_OPEN,
1241     29,
1242     NULL,
1243     NULL,
1244     NULL
1245   },
1246   {
1247     EL_EM_STEEL_EXIT_CLOSING,
1248 #if 1
1249     EL_STEELWALL,
1250 #else
1251     EL_EM_STEEL_EXIT_CLOSED,
1252 #endif
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_SP_EXIT_OPENING,
1260     EL_SP_EXIT_OPEN,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_SP_EXIT_CLOSING,
1268     EL_SP_EXIT_CLOSED,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_SWITCHGATE_OPENING,
1276     EL_SWITCHGATE_OPEN,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SWITCHGATE_CLOSING,
1284     EL_SWITCHGATE_CLOSED,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_TIMEGATE_OPENING,
1292     EL_TIMEGATE_OPEN,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_TIMEGATE_CLOSING,
1300     EL_TIMEGATE_CLOSED,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306
1307   {
1308     EL_ACID_SPLASH_LEFT,
1309     EL_EMPTY,
1310     8,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315   {
1316     EL_ACID_SPLASH_RIGHT,
1317     EL_EMPTY,
1318     8,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_SP_BUGGY_BASE,
1325     EL_SP_BUGGY_BASE_ACTIVATING,
1326     0,
1327     InitBuggyBase,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_SP_BUGGY_BASE_ACTIVATING,
1333     EL_SP_BUGGY_BASE_ACTIVE,
1334     0,
1335     InitBuggyBase,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_SP_BUGGY_BASE_ACTIVE,
1341     EL_SP_BUGGY_BASE,
1342     0,
1343     InitBuggyBase,
1344     WarnBuggyBase,
1345     NULL
1346   },
1347   {
1348     EL_TRAP,
1349     EL_TRAP_ACTIVE,
1350     0,
1351     InitTrap,
1352     NULL,
1353     ActivateTrap
1354   },
1355   {
1356     EL_TRAP_ACTIVE,
1357     EL_TRAP,
1358     31,
1359     NULL,
1360     ChangeActiveTrap,
1361     NULL
1362   },
1363   {
1364     EL_ROBOT_WHEEL_ACTIVE,
1365     EL_ROBOT_WHEEL,
1366     0,
1367     InitRobotWheel,
1368     RunRobotWheel,
1369     StopRobotWheel
1370   },
1371   {
1372     EL_TIMEGATE_SWITCH_ACTIVE,
1373     EL_TIMEGATE_SWITCH,
1374     0,
1375     InitTimegateWheel,
1376     RunTimegateWheel,
1377     NULL
1378   },
1379   {
1380     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1381     EL_DC_TIMEGATE_SWITCH,
1382     0,
1383     InitTimegateWheel,
1384     RunTimegateWheel,
1385     NULL
1386   },
1387   {
1388     EL_EMC_MAGIC_BALL_ACTIVE,
1389     EL_EMC_MAGIC_BALL_ACTIVE,
1390     0,
1391     InitMagicBallDelay,
1392     NULL,
1393     ActivateMagicBall
1394   },
1395   {
1396     EL_EMC_SPRING_BUMPER_ACTIVE,
1397     EL_EMC_SPRING_BUMPER,
1398     8,
1399     NULL,
1400     NULL,
1401     NULL
1402   },
1403   {
1404     EL_DIAGONAL_SHRINKING,
1405     EL_UNDEFINED,
1406     0,
1407     NULL,
1408     NULL,
1409     NULL
1410   },
1411   {
1412     EL_DIAGONAL_GROWING,
1413     EL_UNDEFINED,
1414     0,
1415     NULL,
1416     NULL,
1417     NULL,
1418   },
1419
1420   {
1421     EL_UNDEFINED,
1422     EL_UNDEFINED,
1423     -1,
1424     NULL,
1425     NULL,
1426     NULL
1427   }
1428 };
1429
1430 struct
1431 {
1432   int element;
1433   int push_delay_fixed, push_delay_random;
1434 }
1435 push_delay_list[] =
1436 {
1437   { EL_SPRING,                  0, 0 },
1438   { EL_BALLOON,                 0, 0 },
1439
1440   { EL_SOKOBAN_OBJECT,          2, 0 },
1441   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1442   { EL_SATELLITE,               2, 0 },
1443   { EL_SP_DISK_YELLOW,          2, 0 },
1444
1445   { EL_UNDEFINED,               0, 0 },
1446 };
1447
1448 struct
1449 {
1450   int element;
1451   int move_stepsize;
1452 }
1453 move_stepsize_list[] =
1454 {
1455   { EL_AMOEBA_DROP,             2 },
1456   { EL_AMOEBA_DROPPING,         2 },
1457   { EL_QUICKSAND_FILLING,       1 },
1458   { EL_QUICKSAND_EMPTYING,      1 },
1459   { EL_QUICKSAND_FAST_FILLING,  2 },
1460   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1461   { EL_MAGIC_WALL_FILLING,      2 },
1462   { EL_MAGIC_WALL_EMPTYING,     2 },
1463   { EL_BD_MAGIC_WALL_FILLING,   2 },
1464   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1465   { EL_DC_MAGIC_WALL_FILLING,   2 },
1466   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1467
1468   { EL_UNDEFINED,               0 },
1469 };
1470
1471 struct
1472 {
1473   int element;
1474   int count;
1475 }
1476 collect_count_list[] =
1477 {
1478   { EL_EMERALD,                 1 },
1479   { EL_BD_DIAMOND,              1 },
1480   { EL_EMERALD_YELLOW,          1 },
1481   { EL_EMERALD_RED,             1 },
1482   { EL_EMERALD_PURPLE,          1 },
1483   { EL_DIAMOND,                 3 },
1484   { EL_SP_INFOTRON,             1 },
1485   { EL_PEARL,                   5 },
1486   { EL_CRYSTAL,                 8 },
1487
1488   { EL_UNDEFINED,               0 },
1489 };
1490
1491 struct
1492 {
1493   int element;
1494   int direction;
1495 }
1496 access_direction_list[] =
1497 {
1498   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1499   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1500   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1501   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1502   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1503   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1504   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1505   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1506   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1507   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1508   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1509
1510   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1511   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1512   { EL_SP_PORT_UP,                                                   MV_DOWN },
1513   { EL_SP_PORT_DOWN,                                         MV_UP           },
1514   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1515   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1516   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1517   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1518   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1519   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1520   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1521   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1522   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1523   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1524   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1525   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1526   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1527   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1528   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1529
1530   { EL_UNDEFINED,                       MV_NONE                              }
1531 };
1532
1533 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1534
1535 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1536 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1537 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1538                                  IS_JUST_CHANGING(x, y))
1539
1540 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1541
1542 /* static variables for playfield scan mode (scanning forward or backward) */
1543 static int playfield_scan_start_x = 0;
1544 static int playfield_scan_start_y = 0;
1545 static int playfield_scan_delta_x = 1;
1546 static int playfield_scan_delta_y = 1;
1547
1548 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1549                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1550                                      (y) += playfield_scan_delta_y)     \
1551                                 for ((x) = playfield_scan_start_x;      \
1552                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1553                                      (x) += playfield_scan_delta_x)
1554
1555 #ifdef DEBUG
1556 void DEBUG_SetMaximumDynamite()
1557 {
1558   int i;
1559
1560   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1561     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1562       local_player->inventory_element[local_player->inventory_size++] =
1563         EL_DYNAMITE;
1564 }
1565 #endif
1566
1567 static void InitPlayfieldScanModeVars()
1568 {
1569   if (game.use_reverse_scan_direction)
1570   {
1571     playfield_scan_start_x = lev_fieldx - 1;
1572     playfield_scan_start_y = lev_fieldy - 1;
1573
1574     playfield_scan_delta_x = -1;
1575     playfield_scan_delta_y = -1;
1576   }
1577   else
1578   {
1579     playfield_scan_start_x = 0;
1580     playfield_scan_start_y = 0;
1581
1582     playfield_scan_delta_x = 1;
1583     playfield_scan_delta_y = 1;
1584   }
1585 }
1586
1587 static void InitPlayfieldScanMode(int mode)
1588 {
1589   game.use_reverse_scan_direction =
1590     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1591
1592   InitPlayfieldScanModeVars();
1593 }
1594
1595 static int get_move_delay_from_stepsize(int move_stepsize)
1596 {
1597   move_stepsize =
1598     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1599
1600   /* make sure that stepsize value is always a power of 2 */
1601   move_stepsize = (1 << log_2(move_stepsize));
1602
1603   return TILEX / move_stepsize;
1604 }
1605
1606 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1607                                boolean init_game)
1608 {
1609   int player_nr = player->index_nr;
1610   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1611   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1612
1613   /* do no immediately change move delay -- the player might just be moving */
1614   player->move_delay_value_next = move_delay;
1615
1616   /* information if player can move must be set separately */
1617   player->cannot_move = cannot_move;
1618
1619   if (init_game)
1620   {
1621     player->move_delay       = game.initial_move_delay[player_nr];
1622     player->move_delay_value = game.initial_move_delay_value[player_nr];
1623
1624     player->move_delay_value_next = -1;
1625
1626     player->move_delay_reset_counter = 0;
1627   }
1628 }
1629
1630 void GetPlayerConfig()
1631 {
1632   GameFrameDelay = setup.game_frame_delay;
1633
1634   if (!audio.sound_available)
1635     setup.sound_simple = FALSE;
1636
1637   if (!audio.loops_available)
1638     setup.sound_loops = FALSE;
1639
1640   if (!audio.music_available)
1641     setup.sound_music = FALSE;
1642
1643   if (!video.fullscreen_available)
1644     setup.fullscreen = FALSE;
1645
1646   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1647
1648   SetAudioMode(setup.sound);
1649   InitJoysticks();
1650 }
1651
1652 int GetElementFromGroupElement(int element)
1653 {
1654   if (IS_GROUP_ELEMENT(element))
1655   {
1656     struct ElementGroupInfo *group = element_info[element].group;
1657     int last_anim_random_frame = gfx.anim_random_frame;
1658     int element_pos;
1659
1660     if (group->choice_mode == ANIM_RANDOM)
1661       gfx.anim_random_frame = RND(group->num_elements_resolved);
1662
1663     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1664                                     group->choice_mode, 0,
1665                                     group->choice_pos);
1666
1667     if (group->choice_mode == ANIM_RANDOM)
1668       gfx.anim_random_frame = last_anim_random_frame;
1669
1670     group->choice_pos++;
1671
1672     element = group->element_resolved[element_pos];
1673   }
1674
1675   return element;
1676 }
1677
1678 static void InitPlayerField(int x, int y, int element, boolean init_game)
1679 {
1680   if (element == EL_SP_MURPHY)
1681   {
1682     if (init_game)
1683     {
1684       if (stored_player[0].present)
1685       {
1686         Feld[x][y] = EL_SP_MURPHY_CLONE;
1687
1688         return;
1689       }
1690       else
1691       {
1692         stored_player[0].use_murphy = TRUE;
1693
1694         if (!level.use_artwork_element[0])
1695           stored_player[0].artwork_element = EL_SP_MURPHY;
1696       }
1697
1698       Feld[x][y] = EL_PLAYER_1;
1699     }
1700   }
1701
1702   if (init_game)
1703   {
1704     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1705     int jx = player->jx, jy = player->jy;
1706
1707     player->present = TRUE;
1708
1709     player->block_last_field = (element == EL_SP_MURPHY ?
1710                                 level.sp_block_last_field :
1711                                 level.block_last_field);
1712
1713     /* ---------- initialize player's last field block delay --------------- */
1714
1715     /* always start with reliable default value (no adjustment needed) */
1716     player->block_delay_adjustment = 0;
1717
1718     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1719     if (player->block_last_field && element == EL_SP_MURPHY)
1720       player->block_delay_adjustment = 1;
1721
1722     /* special case 2: in game engines before 3.1.1, blocking was different */
1723     if (game.use_block_last_field_bug)
1724       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1725
1726     if (!options.network || player->connected)
1727     {
1728       player->active = TRUE;
1729
1730       /* remove potentially duplicate players */
1731       if (StorePlayer[jx][jy] == Feld[x][y])
1732         StorePlayer[jx][jy] = 0;
1733
1734       StorePlayer[x][y] = Feld[x][y];
1735
1736       if (options.debug)
1737       {
1738         printf("Player %d activated.\n", player->element_nr);
1739         printf("[Local player is %d and currently %s.]\n",
1740                local_player->element_nr,
1741                local_player->active ? "active" : "not active");
1742       }
1743     }
1744
1745     Feld[x][y] = EL_EMPTY;
1746
1747     player->jx = player->last_jx = x;
1748     player->jy = player->last_jy = y;
1749   }
1750 }
1751
1752 static void InitField(int x, int y, boolean init_game)
1753 {
1754   int element = Feld[x][y];
1755
1756   switch (element)
1757   {
1758     case EL_SP_MURPHY:
1759     case EL_PLAYER_1:
1760     case EL_PLAYER_2:
1761     case EL_PLAYER_3:
1762     case EL_PLAYER_4:
1763       InitPlayerField(x, y, element, init_game);
1764       break;
1765
1766     case EL_SOKOBAN_FIELD_PLAYER:
1767       element = Feld[x][y] = EL_PLAYER_1;
1768       InitField(x, y, init_game);
1769
1770       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1771       InitField(x, y, init_game);
1772       break;
1773
1774     case EL_SOKOBAN_FIELD_EMPTY:
1775       local_player->sokobanfields_still_needed++;
1776       break;
1777
1778     case EL_STONEBLOCK:
1779       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1780         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1781       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1782         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1783       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1784         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1785       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1786         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1787       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1788         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1789       break;
1790
1791     case EL_BUG:
1792     case EL_BUG_RIGHT:
1793     case EL_BUG_UP:
1794     case EL_BUG_LEFT:
1795     case EL_BUG_DOWN:
1796     case EL_SPACESHIP:
1797     case EL_SPACESHIP_RIGHT:
1798     case EL_SPACESHIP_UP:
1799     case EL_SPACESHIP_LEFT:
1800     case EL_SPACESHIP_DOWN:
1801     case EL_BD_BUTTERFLY:
1802     case EL_BD_BUTTERFLY_RIGHT:
1803     case EL_BD_BUTTERFLY_UP:
1804     case EL_BD_BUTTERFLY_LEFT:
1805     case EL_BD_BUTTERFLY_DOWN:
1806     case EL_BD_FIREFLY:
1807     case EL_BD_FIREFLY_RIGHT:
1808     case EL_BD_FIREFLY_UP:
1809     case EL_BD_FIREFLY_LEFT:
1810     case EL_BD_FIREFLY_DOWN:
1811     case EL_PACMAN_RIGHT:
1812     case EL_PACMAN_UP:
1813     case EL_PACMAN_LEFT:
1814     case EL_PACMAN_DOWN:
1815     case EL_YAMYAM:
1816     case EL_YAMYAM_LEFT:
1817     case EL_YAMYAM_RIGHT:
1818     case EL_YAMYAM_UP:
1819     case EL_YAMYAM_DOWN:
1820     case EL_DARK_YAMYAM:
1821     case EL_ROBOT:
1822     case EL_PACMAN:
1823     case EL_SP_SNIKSNAK:
1824     case EL_SP_ELECTRON:
1825     case EL_MOLE:
1826     case EL_MOLE_LEFT:
1827     case EL_MOLE_RIGHT:
1828     case EL_MOLE_UP:
1829     case EL_MOLE_DOWN:
1830       InitMovDir(x, y);
1831       break;
1832
1833     case EL_AMOEBA_FULL:
1834     case EL_BD_AMOEBA:
1835       InitAmoebaNr(x, y);
1836       break;
1837
1838     case EL_AMOEBA_DROP:
1839       if (y == lev_fieldy - 1)
1840       {
1841         Feld[x][y] = EL_AMOEBA_GROWING;
1842         Store[x][y] = EL_AMOEBA_WET;
1843       }
1844       break;
1845
1846     case EL_DYNAMITE_ACTIVE:
1847     case EL_SP_DISK_RED_ACTIVE:
1848     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1849     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1850     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1851     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1852       MovDelay[x][y] = 96;
1853       break;
1854
1855     case EL_EM_DYNAMITE_ACTIVE:
1856       MovDelay[x][y] = 32;
1857       break;
1858
1859     case EL_LAMP:
1860       local_player->lights_still_needed++;
1861       break;
1862
1863     case EL_PENGUIN:
1864       local_player->friends_still_needed++;
1865       break;
1866
1867     case EL_PIG:
1868     case EL_DRAGON:
1869       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1870       break;
1871
1872     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1873     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1874     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1875     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1876     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1877     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1878     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1879     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1880     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1881     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1882     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1883     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1884       if (init_game)
1885       {
1886         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1887         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1888         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1889
1890         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1891         {
1892           game.belt_dir[belt_nr] = belt_dir;
1893           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1894         }
1895         else    /* more than one switch -- set it like the first switch */
1896         {
1897           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1898         }
1899       }
1900       break;
1901
1902 #if !USE_BOTH_SWITCHGATE_SWITCHES
1903     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1904       if (init_game)
1905         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1906       break;
1907
1908     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1909       if (init_game)
1910         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1911       break;
1912 #endif
1913
1914     case EL_LIGHT_SWITCH_ACTIVE:
1915       if (init_game)
1916         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1917       break;
1918
1919     case EL_INVISIBLE_STEELWALL:
1920     case EL_INVISIBLE_WALL:
1921     case EL_INVISIBLE_SAND:
1922       if (game.light_time_left > 0 ||
1923           game.lenses_time_left > 0)
1924         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1925       break;
1926
1927     case EL_EMC_MAGIC_BALL:
1928       if (game.ball_state)
1929         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1930       break;
1931
1932     case EL_EMC_MAGIC_BALL_SWITCH:
1933       if (game.ball_state)
1934         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1935       break;
1936
1937     default:
1938       if (IS_CUSTOM_ELEMENT(element))
1939       {
1940         if (CAN_MOVE(element))
1941           InitMovDir(x, y);
1942
1943 #if USE_NEW_CUSTOM_VALUE
1944         if (!element_info[element].use_last_ce_value || init_game)
1945           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1946 #endif
1947       }
1948       else if (IS_GROUP_ELEMENT(element))
1949       {
1950         Feld[x][y] = GetElementFromGroupElement(element);
1951
1952         InitField(x, y, init_game);
1953       }
1954
1955       break;
1956   }
1957
1958   if (!init_game)
1959     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1960 }
1961
1962 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1963 {
1964   InitField(x, y, init_game);
1965
1966   /* not needed to call InitMovDir() -- already done by InitField()! */
1967   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1968       CAN_MOVE(Feld[x][y]))
1969     InitMovDir(x, y);
1970 }
1971
1972 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1973 {
1974   int old_element = Feld[x][y];
1975
1976   InitField(x, y, init_game);
1977
1978   /* not needed to call InitMovDir() -- already done by InitField()! */
1979   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1980       CAN_MOVE(old_element) &&
1981       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1982     InitMovDir(x, y);
1983
1984   /* this case is in fact a combination of not less than three bugs:
1985      first, it calls InitMovDir() for elements that can move, although this is
1986      already done by InitField(); then, it checks the element that was at this
1987      field _before_ the call to InitField() (which can change it); lastly, it
1988      was not called for "mole with direction" elements, which were treated as
1989      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1990   */
1991 }
1992
1993 #if 1
1994
1995 static int get_key_element_from_nr(int key_nr)
1996 {
1997   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1998                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1999                           EL_EM_KEY_1 : EL_KEY_1);
2000
2001   return key_base_element + key_nr;
2002 }
2003
2004 static int get_next_dropped_element(struct PlayerInfo *player)
2005 {
2006   return (player->inventory_size > 0 ?
2007           player->inventory_element[player->inventory_size - 1] :
2008           player->inventory_infinite_element != EL_UNDEFINED ?
2009           player->inventory_infinite_element :
2010           player->dynabombs_left > 0 ?
2011           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2012           EL_UNDEFINED);
2013 }
2014
2015 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2016 {
2017   /* pos >= 0: get element from bottom of the stack;
2018      pos <  0: get element from top of the stack */
2019
2020   if (pos < 0)
2021   {
2022     int min_inventory_size = -pos;
2023     int inventory_pos = player->inventory_size - min_inventory_size;
2024     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2025
2026     return (player->inventory_size >= min_inventory_size ?
2027             player->inventory_element[inventory_pos] :
2028             player->inventory_infinite_element != EL_UNDEFINED ?
2029             player->inventory_infinite_element :
2030             player->dynabombs_left >= min_dynabombs_left ?
2031             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032             EL_UNDEFINED);
2033   }
2034   else
2035   {
2036     int min_dynabombs_left = pos + 1;
2037     int min_inventory_size = pos + 1 - player->dynabombs_left;
2038     int inventory_pos = pos - player->dynabombs_left;
2039
2040     return (player->inventory_infinite_element != EL_UNDEFINED ?
2041             player->inventory_infinite_element :
2042             player->dynabombs_left >= min_dynabombs_left ?
2043             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2044             player->inventory_size >= min_inventory_size ?
2045             player->inventory_element[inventory_pos] :
2046             EL_UNDEFINED);
2047   }
2048 }
2049
2050 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2051 {
2052   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2053   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2054   int compare_result;
2055
2056   if (gpo1->sort_priority != gpo2->sort_priority)
2057     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2058   else
2059     compare_result = gpo1->nr - gpo2->nr;
2060
2061   return compare_result;
2062 }
2063
2064 void InitGameControlValues()
2065 {
2066   int i;
2067
2068   for (i = 0; game_panel_controls[i].nr != -1; i++)
2069   {
2070     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2071     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2072     struct TextPosInfo *pos = gpc->pos;
2073     int nr = gpc->nr;
2074     int type = gpc->type;
2075
2076     if (nr != i)
2077     {
2078       Error(ERR_INFO, "'game_panel_controls' structure corrupted");
2079       Error(ERR_EXIT, "this should not happen -- please debug");
2080     }
2081
2082     /* force update of game controls after initialization */
2083     gpc->value = gpc->last_value = -1;
2084     gpc->frame = gpc->last_frame = -1;
2085     gpc->gfx_frame = -1;
2086
2087     /* determine panel value width for later calculation of alignment */
2088     if (type == TYPE_INTEGER || type == TYPE_STRING)
2089     {
2090       pos->width = pos->size * getFontWidth(pos->font);
2091       pos->height = getFontHeight(pos->font);
2092     }
2093     else if (type == TYPE_ELEMENT)
2094     {
2095       pos->width = pos->size;
2096       pos->height = pos->size;
2097     }
2098
2099     /* fill structure for game panel draw order */
2100     gpo->nr = gpc->nr;
2101     gpo->sort_priority = pos->sort_priority;
2102   }
2103
2104   /* sort game panel controls according to sort_priority and control number */
2105   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2106         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2107 }
2108
2109 void UpdatePlayfieldElementCount()
2110 {
2111   int i, j, x, y;
2112
2113   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2114     element_info[i].element_count = 0;
2115
2116   SCAN_PLAYFIELD(x, y)
2117   {
2118     element_info[Feld[x][y]].element_count++;
2119   }
2120
2121   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2122     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2123       if (IS_IN_GROUP(j, i))
2124         element_info[EL_GROUP_START + i].element_count +=
2125           element_info[j].element_count;
2126 }
2127
2128 void UpdateGameControlValues()
2129 {
2130   int i, k;
2131   int time = (local_player->LevelSolved ?
2132               local_player->LevelSolved_CountingTime :
2133               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2134               level.native_em_level->lev->time :
2135               level.time == 0 ? TimePlayed : TimeLeft);
2136   int score = (local_player->LevelSolved ?
2137                local_player->LevelSolved_CountingScore :
2138                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2139                level.native_em_level->lev->score :
2140                local_player->score);
2141   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142               level.native_em_level->lev->required :
2143               local_player->gems_still_needed);
2144   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2145                      level.native_em_level->lev->required > 0 :
2146                      local_player->gems_still_needed > 0 ||
2147                      local_player->sokobanfields_still_needed > 0 ||
2148                      local_player->lights_still_needed > 0);
2149
2150   UpdatePlayfieldElementCount();
2151
2152   /* update game panel control values */
2153
2154   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2155   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2156
2157   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2158   for (i = 0; i < MAX_NUM_KEYS; i++)
2159     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2160   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2161   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2162
2163   if (game.centered_player_nr == -1)
2164   {
2165     for (i = 0; i < MAX_PLAYERS; i++)
2166     {
2167       for (k = 0; k < MAX_NUM_KEYS; k++)
2168       {
2169         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2170         {
2171           if (level.native_em_level->ply[i]->keys & (1 << k))
2172             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2173               get_key_element_from_nr(k);
2174         }
2175         else if (stored_player[i].key[k])
2176           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2177             get_key_element_from_nr(k);
2178       }
2179
2180       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2181         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2182           level.native_em_level->ply[i]->dynamite;
2183       else
2184         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2185           stored_player[i].inventory_size;
2186
2187       if (stored_player[i].num_white_keys > 0)
2188         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2189           EL_DC_KEY_WHITE;
2190
2191       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2192         stored_player[i].num_white_keys;
2193     }
2194   }
2195   else
2196   {
2197     int player_nr = game.centered_player_nr;
2198
2199     for (k = 0; k < MAX_NUM_KEYS; k++)
2200     {
2201       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2202       {
2203         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2204           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2205             get_key_element_from_nr(k);
2206       }
2207       else if (stored_player[player_nr].key[k])
2208         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2209           get_key_element_from_nr(k);
2210     }
2211
2212     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2213       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2214         level.native_em_level->ply[player_nr]->dynamite;
2215     else
2216       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2217         stored_player[player_nr].inventory_size;
2218
2219     if (stored_player[player_nr].num_white_keys > 0)
2220       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2221
2222     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2223       stored_player[player_nr].num_white_keys;
2224   }
2225
2226   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2227   {
2228     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2229       get_inventory_element_from_pos(local_player, i);
2230     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2231       get_inventory_element_from_pos(local_player, -i - 1);
2232   }
2233
2234   game_panel_controls[GAME_PANEL_SCORE].value = score;
2235
2236   game_panel_controls[GAME_PANEL_TIME].value = time;
2237
2238   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2239   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2240   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2241
2242   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2243     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2244      EL_EMPTY);
2245   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2246     local_player->shield_normal_time_left;
2247   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2248     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2249      EL_EMPTY);
2250   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2251     local_player->shield_deadly_time_left;
2252
2253   game_panel_controls[GAME_PANEL_EXIT].value =
2254     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2255
2256   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2257     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2258   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2259     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2260      EL_EMC_MAGIC_BALL_SWITCH);
2261
2262   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2263     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2264   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2265     game.light_time_left;
2266
2267   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2268     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2269   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2270     game.timegate_time_left;
2271
2272   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2273     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2274
2275   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2276     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2277   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2278     game.lenses_time_left;
2279
2280   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2281     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2282   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2283     game.magnify_time_left;
2284
2285   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2286     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2287      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2288      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2289      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2290      EL_BALLOON_SWITCH_NONE);
2291
2292   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2293     local_player->dynabomb_count;
2294   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2295     local_player->dynabomb_size;
2296   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2297     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2298
2299   game_panel_controls[GAME_PANEL_PENGUINS].value =
2300     local_player->friends_still_needed;
2301
2302   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2303     local_player->sokobanfields_still_needed;
2304   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2305     local_player->sokobanfields_still_needed;
2306
2307   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2308     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2309
2310   for (i = 0; i < NUM_BELTS; i++)
2311   {
2312     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2313       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2314        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2315     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2316       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2317   }
2318
2319   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2320     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2321   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2322     game.magic_wall_time_left;
2323
2324 #if USE_PLAYER_GRAVITY
2325   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2326     local_player->gravity;
2327 #else
2328   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2329 #endif
2330
2331   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2332     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2333
2334   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2335     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2336       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2337        game.panel.element[i].id : EL_UNDEFINED);
2338
2339   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2340     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2341       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2342        element_info[game.panel.element_count[i].id].element_count :
2343        EL_UNDEFINED);
2344
2345   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2346     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2347       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2348        element_info[game.panel.ce_score[i].id].collect_score : 0);
2349
2350   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2351     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2352       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2353        element_info[game.panel.ce_score_element[i].id].collect_score :
2354        EL_UNDEFINED);
2355
2356   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2357   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2358   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2359
2360   /* update game panel control frames */
2361
2362   for (i = 0; game_panel_controls[i].nr != -1; i++)
2363   {
2364     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2365
2366     if (gpc->type == TYPE_ELEMENT)
2367     {
2368       int last_anim_random_frame = gfx.anim_random_frame;
2369       int element = gpc->value;
2370       int graphic = el2panelimg(element);
2371
2372       if (gpc->value != gpc->last_value)
2373       {
2374         gpc->gfx_frame = 0;
2375         gpc->gfx_random = INIT_GFX_RANDOM();
2376       }
2377       else
2378       {
2379         gpc->gfx_frame++;
2380
2381         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2382             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2383           gpc->gfx_random = INIT_GFX_RANDOM();
2384       }
2385
2386       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2387         gfx.anim_random_frame = gpc->gfx_random;
2388
2389       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2390         gpc->gfx_frame = element_info[element].collect_score;
2391
2392       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2393                                             gpc->gfx_frame);
2394
2395       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2396         gfx.anim_random_frame = last_anim_random_frame;
2397     }
2398   }
2399 }
2400
2401 void DisplayGameControlValues()
2402 {
2403   boolean redraw_panel = FALSE;
2404   int i;
2405
2406   for (i = 0; game_panel_controls[i].nr != -1; i++)
2407   {
2408     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2409
2410     if (PANEL_DEACTIVATED(gpc->pos))
2411       continue;
2412
2413     if (gpc->value == gpc->last_value &&
2414         gpc->frame == gpc->last_frame)
2415       continue;
2416
2417     redraw_panel = TRUE;
2418   }
2419
2420   if (!redraw_panel)
2421     return;
2422
2423   /* copy default game door content to main double buffer */
2424   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2425              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2426
2427   /* redraw game control buttons */
2428 #if 1
2429   RedrawGameButtons();
2430 #else
2431   UnmapGameButtons();
2432   MapGameButtons();
2433 #endif
2434
2435   game_status = GAME_MODE_PSEUDO_PANEL;
2436
2437 #if 1
2438   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2439 #else
2440   for (i = 0; game_panel_controls[i].nr != -1; i++)
2441 #endif
2442   {
2443 #if 1
2444     int nr = game_panel_order[i].nr;
2445     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2446 #else
2447     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2448     int nr = gpc->nr;
2449 #endif
2450     struct TextPosInfo *pos = gpc->pos;
2451     int type = gpc->type;
2452     int value = gpc->value;
2453     int frame = gpc->frame;
2454 #if 0
2455     int last_value = gpc->last_value;
2456     int last_frame = gpc->last_frame;
2457 #endif
2458     int size = pos->size;
2459     int font = pos->font;
2460     boolean draw_masked = pos->draw_masked;
2461     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2462
2463     if (PANEL_DEACTIVATED(pos))
2464       continue;
2465
2466 #if 0
2467     if (value == last_value && frame == last_frame)
2468       continue;
2469 #endif
2470
2471     gpc->last_value = value;
2472     gpc->last_frame = frame;
2473
2474 #if 0
2475     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2476 #endif
2477
2478     if (type == TYPE_INTEGER)
2479     {
2480       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2481           nr == GAME_PANEL_TIME)
2482       {
2483         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2484
2485         if (use_dynamic_size)           /* use dynamic number of digits */
2486         {
2487           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2488           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2489           int size2 = size1 + 1;
2490           int font1 = pos->font;
2491           int font2 = pos->font_alt;
2492
2493           size = (value < value_change ? size1 : size2);
2494           font = (value < value_change ? font1 : font2);
2495
2496 #if 0
2497           /* clear background if value just changed its size (dynamic digits) */
2498           if ((last_value < value_change) != (value < value_change))
2499           {
2500             int width1 = size1 * getFontWidth(font1);
2501             int width2 = size2 * getFontWidth(font2);
2502             int max_width = MAX(width1, width2);
2503             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2504
2505             pos->width = max_width;
2506
2507             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2508                                        max_width, max_height);
2509           }
2510 #endif
2511         }
2512       }
2513
2514 #if 1
2515       /* correct text size if "digits" is zero or less */
2516       if (size <= 0)
2517         size = strlen(int2str(value, size));
2518
2519       /* dynamically correct text alignment */
2520       pos->width = size * getFontWidth(font);
2521 #endif
2522
2523       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2524                   int2str(value, size), font, mask_mode);
2525     }
2526     else if (type == TYPE_ELEMENT)
2527     {
2528       int element, graphic;
2529       Bitmap *src_bitmap;
2530       int src_x, src_y;
2531       int width, height;
2532       int dst_x = PANEL_XPOS(pos);
2533       int dst_y = PANEL_YPOS(pos);
2534
2535 #if 1
2536       if (value != EL_UNDEFINED && value != EL_EMPTY)
2537       {
2538         element = value;
2539         graphic = el2panelimg(value);
2540
2541         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2542
2543 #if 1
2544         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2545           size = TILESIZE;
2546 #endif
2547
2548         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2549                               &src_x, &src_y);
2550
2551         width  = graphic_info[graphic].width  * size / TILESIZE;
2552         height = graphic_info[graphic].height * size / TILESIZE;
2553
2554         if (draw_masked)
2555         {
2556           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2557                         dst_x - src_x, dst_y - src_y);
2558           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2559                            dst_x, dst_y);
2560         }
2561         else
2562         {
2563           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2564                      dst_x, dst_y);
2565         }
2566       }
2567 #else
2568       if (value == EL_UNDEFINED || value == EL_EMPTY)
2569       {
2570         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2571         graphic = el2panelimg(element);
2572
2573         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2574         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2575         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2576       }
2577       else
2578       {
2579         element = value;
2580         graphic = el2panelimg(value);
2581
2582         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2583       }
2584
2585       width  = graphic_info[graphic].width  * size / TILESIZE;
2586       height = graphic_info[graphic].height * size / TILESIZE;
2587
2588       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2589 #endif
2590     }
2591     else if (type == TYPE_STRING)
2592     {
2593       boolean active = (value != 0);
2594       char *state_normal = "off";
2595       char *state_active = "on";
2596       char *state = (active ? state_active : state_normal);
2597       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2598                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2599                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2600                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2601
2602       if (nr == GAME_PANEL_GRAVITY_STATE)
2603       {
2604         int font1 = pos->font;          /* (used for normal state) */
2605         int font2 = pos->font_alt;      /* (used for active state) */
2606 #if 0
2607         int size1 = strlen(state_normal);
2608         int size2 = strlen(state_active);
2609         int width1 = size1 * getFontWidth(font1);
2610         int width2 = size2 * getFontWidth(font2);
2611         int max_width = MAX(width1, width2);
2612         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2613
2614         pos->width = max_width;
2615
2616         /* clear background for values that may have changed its size */
2617         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2618                                    max_width, max_height);
2619 #endif
2620
2621         font = (active ? font2 : font1);
2622       }
2623
2624       if (s != NULL)
2625       {
2626         char *s_cut;
2627
2628 #if 1
2629         if (size <= 0)
2630         {
2631           /* don't truncate output if "chars" is zero or less */
2632           size = strlen(s);
2633
2634           /* dynamically correct text alignment */
2635           pos->width = size * getFontWidth(font);
2636         }
2637 #endif
2638
2639         s_cut = getStringCopyN(s, size);
2640
2641         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2642                     s_cut, font, mask_mode);
2643
2644         free(s_cut);
2645       }
2646     }
2647
2648     redraw_mask |= REDRAW_DOOR_1;
2649   }
2650
2651   game_status = GAME_MODE_PLAYING;
2652 }
2653
2654 void DrawGameValue_Emeralds(int value)
2655 {
2656   struct TextPosInfo *pos = &game.panel.gems;
2657 #if 1
2658   int font_nr = pos->font;
2659 #else
2660   int font_nr = FONT_TEXT_2;
2661 #endif
2662   int font_width = getFontWidth(font_nr);
2663   int chars = pos->size;
2664
2665 #if 1
2666   return;       /* !!! USE NEW STUFF !!! */
2667 #endif
2668
2669   if (PANEL_DEACTIVATED(pos))
2670     return;
2671
2672   pos->width = chars * font_width;
2673
2674   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2675 }
2676
2677 void DrawGameValue_Dynamite(int value)
2678 {
2679   struct TextPosInfo *pos = &game.panel.inventory_count;
2680 #if 1
2681   int font_nr = pos->font;
2682 #else
2683   int font_nr = FONT_TEXT_2;
2684 #endif
2685   int font_width = getFontWidth(font_nr);
2686   int chars = pos->size;
2687
2688 #if 1
2689   return;       /* !!! USE NEW STUFF !!! */
2690 #endif
2691
2692   if (PANEL_DEACTIVATED(pos))
2693     return;
2694
2695   pos->width = chars * font_width;
2696
2697   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2698 }
2699
2700 void DrawGameValue_Score(int value)
2701 {
2702   struct TextPosInfo *pos = &game.panel.score;
2703 #if 1
2704   int font_nr = pos->font;
2705 #else
2706   int font_nr = FONT_TEXT_2;
2707 #endif
2708   int font_width = getFontWidth(font_nr);
2709   int chars = pos->size;
2710
2711 #if 1
2712   return;       /* !!! USE NEW STUFF !!! */
2713 #endif
2714
2715   if (PANEL_DEACTIVATED(pos))
2716     return;
2717
2718   pos->width = chars * font_width;
2719
2720   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2721 }
2722
2723 void DrawGameValue_Time(int value)
2724 {
2725   struct TextPosInfo *pos = &game.panel.time;
2726   static int last_value = -1;
2727   int chars1 = 3;
2728   int chars2 = 4;
2729   int chars = pos->size;
2730 #if 1
2731   int font1_nr = pos->font;
2732   int font2_nr = pos->font_alt;
2733 #else
2734   int font1_nr = FONT_TEXT_2;
2735   int font2_nr = FONT_TEXT_1;
2736 #endif
2737   int font_nr = font1_nr;
2738   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2739
2740 #if 1
2741   return;       /* !!! USE NEW STUFF !!! */
2742 #endif
2743
2744   if (PANEL_DEACTIVATED(pos))
2745     return;
2746
2747   if (use_dynamic_chars)                /* use dynamic number of chars */
2748   {
2749     chars   = (value < 1000 ? chars1   : chars2);
2750     font_nr = (value < 1000 ? font1_nr : font2_nr);
2751   }
2752
2753   /* clear background if value just changed its size (dynamic chars only) */
2754   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2755   {
2756     int width1 = chars1 * getFontWidth(font1_nr);
2757     int width2 = chars2 * getFontWidth(font2_nr);
2758     int max_width = MAX(width1, width2);
2759     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2760
2761     pos->width = max_width;
2762
2763     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2764                                max_width, max_height);
2765   }
2766
2767   pos->width = chars * getFontWidth(font_nr);
2768
2769   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2770
2771   last_value = value;
2772 }
2773
2774 void DrawGameValue_Level(int value)
2775 {
2776   struct TextPosInfo *pos = &game.panel.level_number;
2777   int chars1 = 2;
2778   int chars2 = 3;
2779   int chars = pos->size;
2780 #if 1
2781   int font1_nr = pos->font;
2782   int font2_nr = pos->font_alt;
2783 #else
2784   int font1_nr = FONT_TEXT_2;
2785   int font2_nr = FONT_TEXT_1;
2786 #endif
2787   int font_nr = font1_nr;
2788   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2789
2790 #if 1
2791   return;       /* !!! USE NEW STUFF !!! */
2792 #endif
2793
2794   if (PANEL_DEACTIVATED(pos))
2795     return;
2796
2797   if (use_dynamic_chars)                /* use dynamic number of chars */
2798   {
2799     chars   = (level_nr < 100 ? chars1   : chars2);
2800     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2801   }
2802
2803   pos->width = chars * getFontWidth(font_nr);
2804
2805   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2806 }
2807
2808 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2809 {
2810 #if 0
2811   struct TextPosInfo *pos = &game.panel.keys;
2812 #endif
2813 #if 0
2814   int base_key_graphic = EL_KEY_1;
2815 #endif
2816   int i;
2817
2818 #if 1
2819   return;       /* !!! USE NEW STUFF !!! */
2820 #endif
2821
2822 #if 0
2823   if (PANEL_DEACTIVATED(pos))
2824     return;
2825 #endif
2826
2827 #if 0
2828   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2829     base_key_graphic = EL_EM_KEY_1;
2830 #endif
2831
2832 #if 0
2833   pos->width = 4 * MINI_TILEX;
2834 #endif
2835
2836 #if 1
2837   for (i = 0; i < MAX_NUM_KEYS; i++)
2838 #else
2839   /* currently only 4 of 8 possible keys are displayed */
2840   for (i = 0; i < STD_NUM_KEYS; i++)
2841 #endif
2842   {
2843 #if 1
2844     struct TextPosInfo *pos = &game.panel.key[i];
2845 #endif
2846     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2847     int src_y = DOOR_GFX_PAGEY1 + 123;
2848 #if 1
2849     int dst_x = PANEL_XPOS(pos);
2850     int dst_y = PANEL_YPOS(pos);
2851 #else
2852     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2853     int dst_y = PANEL_YPOS(pos);
2854 #endif
2855
2856 #if 1
2857     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2858                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2859                    EL_KEY_1) + i;
2860     int graphic = el2edimg(element);
2861 #endif
2862
2863 #if 1
2864     if (PANEL_DEACTIVATED(pos))
2865       continue;
2866 #endif
2867
2868 #if 0
2869     /* masked blit with tiles from half-size scaled bitmap does not work yet
2870        (no mask bitmap created for these sizes after loading and scaling) --
2871        solution: load without creating mask, scale, then create final mask */
2872
2873     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2874                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2875
2876     if (key[i])
2877     {
2878 #if 0
2879       int graphic = el2edimg(base_key_graphic + i);
2880 #endif
2881       Bitmap *src_bitmap;
2882       int src_x, src_y;
2883
2884       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2885
2886       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2887                     dst_x - src_x, dst_y - src_y);
2888       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2889                        dst_x, dst_y);
2890     }
2891 #else
2892 #if 1
2893     if (key[i])
2894       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2895     else
2896       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2897                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2898 #else
2899     if (key[i])
2900       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2901     else
2902       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2903                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2904 #endif
2905 #endif
2906   }
2907 }
2908
2909 #else
2910
2911 void DrawGameValue_Emeralds(int value)
2912 {
2913   int font_nr = FONT_TEXT_2;
2914   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2915
2916   if (PANEL_DEACTIVATED(game.panel.gems))
2917     return;
2918
2919   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2920 }
2921
2922 void DrawGameValue_Dynamite(int value)
2923 {
2924   int font_nr = FONT_TEXT_2;
2925   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2926
2927   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2928     return;
2929
2930   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2931 }
2932
2933 void DrawGameValue_Score(int value)
2934 {
2935   int font_nr = FONT_TEXT_2;
2936   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2937
2938   if (PANEL_DEACTIVATED(game.panel.score))
2939     return;
2940
2941   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2942 }
2943
2944 void DrawGameValue_Time(int value)
2945 {
2946   int font1_nr = FONT_TEXT_2;
2947 #if 1
2948   int font2_nr = FONT_TEXT_1;
2949 #else
2950   int font2_nr = FONT_LEVEL_NUMBER;
2951 #endif
2952   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2953   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2954
2955   if (PANEL_DEACTIVATED(game.panel.time))
2956     return;
2957
2958   /* clear background if value just changed its size */
2959   if (value == 999 || value == 1000)
2960     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2961
2962   if (value < 1000)
2963     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2964   else
2965     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2966 }
2967
2968 void DrawGameValue_Level(int value)
2969 {
2970   int font1_nr = FONT_TEXT_2;
2971 #if 1
2972   int font2_nr = FONT_TEXT_1;
2973 #else
2974   int font2_nr = FONT_LEVEL_NUMBER;
2975 #endif
2976
2977   if (PANEL_DEACTIVATED(game.panel.level))
2978     return;
2979
2980   if (level_nr < 100)
2981     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2982   else
2983     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2984 }
2985
2986 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2987 {
2988   int base_key_graphic = EL_KEY_1;
2989   int i;
2990
2991   if (PANEL_DEACTIVATED(game.panel.keys))
2992     return;
2993
2994   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2995     base_key_graphic = EL_EM_KEY_1;
2996
2997   /* currently only 4 of 8 possible keys are displayed */
2998   for (i = 0; i < STD_NUM_KEYS; i++)
2999   {
3000     int x = XX_KEYS + i * MINI_TILEX;
3001     int y = YY_KEYS;
3002
3003     if (key[i])
3004       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3005     else
3006       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3007                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3008   }
3009 }
3010
3011 #endif
3012
3013 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3014                        int key_bits)
3015 {
3016   int key[MAX_NUM_KEYS];
3017   int i;
3018
3019   /* prevent EM engine from updating time/score values parallel to GameWon() */
3020   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3021       local_player->LevelSolved)
3022     return;
3023
3024   for (i = 0; i < MAX_NUM_KEYS; i++)
3025     key[i] = key_bits & (1 << i);
3026
3027   DrawGameValue_Level(level_nr);
3028
3029   DrawGameValue_Emeralds(emeralds);
3030   DrawGameValue_Dynamite(dynamite);
3031   DrawGameValue_Score(score);
3032   DrawGameValue_Time(time);
3033
3034   DrawGameValue_Keys(key);
3035 }
3036
3037 void UpdateGameDoorValues()
3038 {
3039   UpdateGameControlValues();
3040 }
3041
3042 void DrawGameDoorValues()
3043 {
3044   DisplayGameControlValues();
3045 }
3046
3047 void DrawGameDoorValues_OLD()
3048 {
3049   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3050   int dynamite_value = 0;
3051   int score_value = (local_player->LevelSolved ? local_player->score_final :
3052                      local_player->score);
3053   int gems_value = local_player->gems_still_needed;
3054   int key_bits = 0;
3055   int i, j;
3056
3057   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3058   {
3059     DrawGameDoorValues_EM();
3060
3061     return;
3062   }
3063
3064   if (game.centered_player_nr == -1)
3065   {
3066     for (i = 0; i < MAX_PLAYERS; i++)
3067     {
3068       for (j = 0; j < MAX_NUM_KEYS; j++)
3069         if (stored_player[i].key[j])
3070           key_bits |= (1 << j);
3071
3072       dynamite_value += stored_player[i].inventory_size;
3073     }
3074   }
3075   else
3076   {
3077     int player_nr = game.centered_player_nr;
3078
3079     for (i = 0; i < MAX_NUM_KEYS; i++)
3080       if (stored_player[player_nr].key[i])
3081         key_bits |= (1 << i);
3082
3083     dynamite_value = stored_player[player_nr].inventory_size;
3084   }
3085
3086   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3087                     key_bits);
3088 }
3089
3090
3091 /*
3092   =============================================================================
3093   InitGameEngine()
3094   -----------------------------------------------------------------------------
3095   initialize game engine due to level / tape version number
3096   =============================================================================
3097 */
3098
3099 static void InitGameEngine()
3100 {
3101   int i, j, k, l, x, y;
3102
3103   /* set game engine from tape file when re-playing, else from level file */
3104   game.engine_version = (tape.playing ? tape.engine_version :
3105                          level.game_version);
3106
3107   /* ---------------------------------------------------------------------- */
3108   /* set flags for bugs and changes according to active game engine version */
3109   /* ---------------------------------------------------------------------- */
3110
3111   /*
3112     Summary of bugfix/change:
3113     Fixed handling for custom elements that change when pushed by the player.
3114
3115     Fixed/changed in version:
3116     3.1.0
3117
3118     Description:
3119     Before 3.1.0, custom elements that "change when pushing" changed directly
3120     after the player started pushing them (until then handled in "DigField()").
3121     Since 3.1.0, these custom elements are not changed until the "pushing"
3122     move of the element is finished (now handled in "ContinueMoving()").
3123
3124     Affected levels/tapes:
3125     The first condition is generally needed for all levels/tapes before version
3126     3.1.0, which might use the old behaviour before it was changed; known tapes
3127     that are affected are some tapes from the level set "Walpurgis Gardens" by
3128     Jamie Cullen.
3129     The second condition is an exception from the above case and is needed for
3130     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3131     above (including some development versions of 3.1.0), but before it was
3132     known that this change would break tapes like the above and was fixed in
3133     3.1.1, so that the changed behaviour was active although the engine version
3134     while recording maybe was before 3.1.0. There is at least one tape that is
3135     affected by this exception, which is the tape for the one-level set "Bug
3136     Machine" by Juergen Bonhagen.
3137   */
3138
3139   game.use_change_when_pushing_bug =
3140     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3141      !(tape.playing &&
3142        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3143        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3144
3145   /*
3146     Summary of bugfix/change:
3147     Fixed handling for blocking the field the player leaves when moving.
3148
3149     Fixed/changed in version:
3150     3.1.1
3151
3152     Description:
3153     Before 3.1.1, when "block last field when moving" was enabled, the field
3154     the player is leaving when moving was blocked for the time of the move,
3155     and was directly unblocked afterwards. This resulted in the last field
3156     being blocked for exactly one less than the number of frames of one player
3157     move. Additionally, even when blocking was disabled, the last field was
3158     blocked for exactly one frame.
3159     Since 3.1.1, due to changes in player movement handling, the last field
3160     is not blocked at all when blocking is disabled. When blocking is enabled,
3161     the last field is blocked for exactly the number of frames of one player
3162     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3163     last field is blocked for exactly one more than the number of frames of
3164     one player move.
3165
3166     Affected levels/tapes:
3167     (!!! yet to be determined -- probably many !!!)
3168   */
3169
3170   game.use_block_last_field_bug =
3171     (game.engine_version < VERSION_IDENT(3,1,1,0));
3172
3173   /*
3174     Summary of bugfix/change:
3175     Changed behaviour of CE changes with multiple changes per single frame.
3176
3177     Fixed/changed in version:
3178     3.2.0-6
3179
3180     Description:
3181     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3182     This resulted in race conditions where CEs seem to behave strange in some
3183     situations (where triggered CE changes were just skipped because there was
3184     already a CE change on that tile in the playfield in that engine frame).
3185     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3186     (The number of changes per frame must be limited in any case, because else
3187     it is easily possible to define CE changes that would result in an infinite
3188     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3189     should be set large enough so that it would only be reached in cases where
3190     the corresponding CE change conditions run into a loop. Therefore, it seems
3191     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3192     maximal number of change pages for custom elements.)
3193
3194     Affected levels/tapes:
3195     Probably many.
3196   */
3197
3198 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3199   game.max_num_changes_per_frame = 1;
3200 #else
3201   game.max_num_changes_per_frame =
3202     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3203 #endif
3204
3205   /* ---------------------------------------------------------------------- */
3206
3207   /* default scan direction: scan playfield from top/left to bottom/right */
3208   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3209
3210   /* dynamically adjust element properties according to game engine version */
3211   InitElementPropertiesEngine(game.engine_version);
3212
3213 #if 0
3214   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3215   printf("          tape version == %06d [%s] [file: %06d]\n",
3216          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3217          tape.file_version);
3218   printf("       => game.engine_version == %06d\n", game.engine_version);
3219 #endif
3220
3221   /* ---------- initialize player's initial move delay --------------------- */
3222
3223   /* dynamically adjust player properties according to level information */
3224   for (i = 0; i < MAX_PLAYERS; i++)
3225     game.initial_move_delay_value[i] =
3226       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3227
3228   /* dynamically adjust player properties according to game engine version */
3229   for (i = 0; i < MAX_PLAYERS; i++)
3230     game.initial_move_delay[i] =
3231       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3232        game.initial_move_delay_value[i] : 0);
3233
3234   /* ---------- initialize player's initial push delay --------------------- */
3235
3236   /* dynamically adjust player properties according to game engine version */
3237   game.initial_push_delay_value =
3238     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3239
3240   /* ---------- initialize changing elements ------------------------------- */
3241
3242   /* initialize changing elements information */
3243   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3244   {
3245     struct ElementInfo *ei = &element_info[i];
3246
3247     /* this pointer might have been changed in the level editor */
3248     ei->change = &ei->change_page[0];
3249
3250     if (!IS_CUSTOM_ELEMENT(i))
3251     {
3252       ei->change->target_element = EL_EMPTY_SPACE;
3253       ei->change->delay_fixed = 0;
3254       ei->change->delay_random = 0;
3255       ei->change->delay_frames = 1;
3256     }
3257
3258     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3259     {
3260       ei->has_change_event[j] = FALSE;
3261
3262       ei->event_page_nr[j] = 0;
3263       ei->event_page[j] = &ei->change_page[0];
3264     }
3265   }
3266
3267   /* add changing elements from pre-defined list */
3268   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3269   {
3270     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3271     struct ElementInfo *ei = &element_info[ch_delay->element];
3272
3273     ei->change->target_element       = ch_delay->target_element;
3274     ei->change->delay_fixed          = ch_delay->change_delay;
3275
3276     ei->change->pre_change_function  = ch_delay->pre_change_function;
3277     ei->change->change_function      = ch_delay->change_function;
3278     ei->change->post_change_function = ch_delay->post_change_function;
3279
3280     ei->change->can_change = TRUE;
3281     ei->change->can_change_or_has_action = TRUE;
3282
3283     ei->has_change_event[CE_DELAY] = TRUE;
3284
3285     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3286     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3287   }
3288
3289   /* ---------- initialize internal run-time variables ------------- */
3290
3291   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3292   {
3293     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3294
3295     for (j = 0; j < ei->num_change_pages; j++)
3296     {
3297       ei->change_page[j].can_change_or_has_action =
3298         (ei->change_page[j].can_change |
3299          ei->change_page[j].has_action);
3300     }
3301   }
3302
3303   /* add change events from custom element configuration */
3304   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3305   {
3306     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3307
3308     for (j = 0; j < ei->num_change_pages; j++)
3309     {
3310       if (!ei->change_page[j].can_change_or_has_action)
3311         continue;
3312
3313       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3314       {
3315         /* only add event page for the first page found with this event */
3316         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3317         {
3318           ei->has_change_event[k] = TRUE;
3319
3320           ei->event_page_nr[k] = j;
3321           ei->event_page[k] = &ei->change_page[j];
3322         }
3323       }
3324     }
3325   }
3326
3327   /* ---------- initialize run-time trigger player and element ------------- */
3328
3329   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3330   {
3331     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3332
3333     for (j = 0; j < ei->num_change_pages; j++)
3334     {
3335       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3336       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3337       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3338       ei->change_page[j].actual_trigger_ce_value = 0;
3339       ei->change_page[j].actual_trigger_ce_score = 0;
3340     }
3341   }
3342
3343   /* ---------- initialize trigger events ---------------------------------- */
3344
3345   /* initialize trigger events information */
3346   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3347     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3348       trigger_events[i][j] = FALSE;
3349
3350   /* add trigger events from element change event properties */
3351   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3352   {
3353     struct ElementInfo *ei = &element_info[i];
3354
3355     for (j = 0; j < ei->num_change_pages; j++)
3356     {
3357       if (!ei->change_page[j].can_change_or_has_action)
3358         continue;
3359
3360       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3361       {
3362         int trigger_element = ei->change_page[j].trigger_element;
3363
3364         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3365         {
3366           if (ei->change_page[j].has_event[k])
3367           {
3368             if (IS_GROUP_ELEMENT(trigger_element))
3369             {
3370               struct ElementGroupInfo *group =
3371                 element_info[trigger_element].group;
3372
3373               for (l = 0; l < group->num_elements_resolved; l++)
3374                 trigger_events[group->element_resolved[l]][k] = TRUE;
3375             }
3376             else if (trigger_element == EL_ANY_ELEMENT)
3377               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3378                 trigger_events[l][k] = TRUE;
3379             else
3380               trigger_events[trigger_element][k] = TRUE;
3381           }
3382         }
3383       }
3384     }
3385   }
3386
3387   /* ---------- initialize push delay -------------------------------------- */
3388
3389   /* initialize push delay values to default */
3390   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3391   {
3392     if (!IS_CUSTOM_ELEMENT(i))
3393     {
3394       /* set default push delay values (corrected since version 3.0.7-1) */
3395       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3396       {
3397         element_info[i].push_delay_fixed = 2;
3398         element_info[i].push_delay_random = 8;
3399       }
3400       else
3401       {
3402         element_info[i].push_delay_fixed = 8;
3403         element_info[i].push_delay_random = 8;
3404       }
3405     }
3406   }
3407
3408   /* set push delay value for certain elements from pre-defined list */
3409   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3410   {
3411     int e = push_delay_list[i].element;
3412
3413     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3414     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3415   }
3416
3417   /* set push delay value for Supaplex elements for newer engine versions */
3418   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3419   {
3420     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3421     {
3422       if (IS_SP_ELEMENT(i))
3423       {
3424         /* set SP push delay to just enough to push under a falling zonk */
3425         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3426
3427         element_info[i].push_delay_fixed  = delay;
3428         element_info[i].push_delay_random = 0;
3429       }
3430     }
3431   }
3432
3433   /* ---------- initialize move stepsize ----------------------------------- */
3434
3435   /* initialize move stepsize values to default */
3436   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3437     if (!IS_CUSTOM_ELEMENT(i))
3438       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3439
3440   /* set move stepsize value for certain elements from pre-defined list */
3441   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3442   {
3443     int e = move_stepsize_list[i].element;
3444
3445     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3446   }
3447
3448   /* ---------- initialize collect score ----------------------------------- */
3449
3450   /* initialize collect score values for custom elements from initial value */
3451   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3452     if (IS_CUSTOM_ELEMENT(i))
3453       element_info[i].collect_score = element_info[i].collect_score_initial;
3454
3455   /* ---------- initialize collect count ----------------------------------- */
3456
3457   /* initialize collect count values for non-custom elements */
3458   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3459     if (!IS_CUSTOM_ELEMENT(i))
3460       element_info[i].collect_count_initial = 0;
3461
3462   /* add collect count values for all elements from pre-defined list */
3463   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3464     element_info[collect_count_list[i].element].collect_count_initial =
3465       collect_count_list[i].count;
3466
3467   /* ---------- initialize access direction -------------------------------- */
3468
3469   /* initialize access direction values to default (access from every side) */
3470   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3471     if (!IS_CUSTOM_ELEMENT(i))
3472       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3473
3474   /* set access direction value for certain elements from pre-defined list */
3475   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3476     element_info[access_direction_list[i].element].access_direction =
3477       access_direction_list[i].direction;
3478
3479   /* ---------- initialize explosion content ------------------------------- */
3480   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3481   {
3482     if (IS_CUSTOM_ELEMENT(i))
3483       continue;
3484
3485     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3486     {
3487       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3488
3489       element_info[i].content.e[x][y] =
3490         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3491          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3492          i == EL_PLAYER_3 ? EL_EMERALD :
3493          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3494          i == EL_MOLE ? EL_EMERALD_RED :
3495          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3496          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3497          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3498          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3499          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3500          i == EL_WALL_EMERALD ? EL_EMERALD :
3501          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3502          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3503          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3504          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3505          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3506          i == EL_WALL_PEARL ? EL_PEARL :
3507          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3508          EL_EMPTY);
3509     }
3510   }
3511
3512   /* ---------- initialize recursion detection ------------------------------ */
3513   recursion_loop_depth = 0;
3514   recursion_loop_detected = FALSE;
3515   recursion_loop_element = EL_UNDEFINED;
3516
3517   /* ---------- initialize graphics engine ---------------------------------- */
3518   game.scroll_delay_value =
3519     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3520      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3521   game.scroll_delay_value =
3522     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3523 }
3524
3525 int get_num_special_action(int element, int action_first, int action_last)
3526 {
3527   int num_special_action = 0;
3528   int i, j;
3529
3530   for (i = action_first; i <= action_last; i++)
3531   {
3532     boolean found = FALSE;
3533
3534     for (j = 0; j < NUM_DIRECTIONS; j++)
3535       if (el_act_dir2img(element, i, j) !=
3536           el_act_dir2img(element, ACTION_DEFAULT, j))
3537         found = TRUE;
3538
3539     if (found)
3540       num_special_action++;
3541     else
3542       break;
3543   }
3544
3545   return num_special_action;
3546 }
3547
3548
3549 /*
3550   =============================================================================
3551   InitGame()
3552   -----------------------------------------------------------------------------
3553   initialize and start new game
3554   =============================================================================
3555 */
3556
3557 void InitGame()
3558 {
3559   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3560   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3561   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3562 #if 0
3563   boolean do_fading = (game_status == GAME_MODE_MAIN);
3564 #endif
3565   int i, j, x, y;
3566
3567   game_status = GAME_MODE_PLAYING;
3568
3569   InitGameEngine();
3570   InitGameControlValues();
3571
3572   /* don't play tapes over network */
3573   network_playing = (options.network && !tape.playing);
3574
3575   for (i = 0; i < MAX_PLAYERS; i++)
3576   {
3577     struct PlayerInfo *player = &stored_player[i];
3578
3579     player->index_nr = i;
3580     player->index_bit = (1 << i);
3581     player->element_nr = EL_PLAYER_1 + i;
3582
3583     player->present = FALSE;
3584     player->active = FALSE;
3585     player->killed = FALSE;
3586
3587     player->action = 0;
3588     player->effective_action = 0;
3589     player->programmed_action = 0;
3590
3591     player->score = 0;
3592     player->score_final = 0;
3593
3594     player->gems_still_needed = level.gems_needed;
3595     player->sokobanfields_still_needed = 0;
3596     player->lights_still_needed = 0;
3597     player->friends_still_needed = 0;
3598
3599     for (j = 0; j < MAX_NUM_KEYS; j++)
3600       player->key[j] = FALSE;
3601
3602     player->num_white_keys = 0;
3603
3604     player->dynabomb_count = 0;
3605     player->dynabomb_size = 1;
3606     player->dynabombs_left = 0;
3607     player->dynabomb_xl = FALSE;
3608
3609     player->MovDir = MV_NONE;
3610     player->MovPos = 0;
3611     player->GfxPos = 0;
3612     player->GfxDir = MV_NONE;
3613     player->GfxAction = ACTION_DEFAULT;
3614     player->Frame = 0;
3615     player->StepFrame = 0;
3616
3617     player->use_murphy = FALSE;
3618     player->artwork_element =
3619       (level.use_artwork_element[i] ? level.artwork_element[i] :
3620        player->element_nr);
3621
3622     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3623     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3624
3625     player->gravity = level.initial_player_gravity[i];
3626
3627     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3628
3629     player->actual_frame_counter = 0;
3630
3631     player->step_counter = 0;
3632
3633     player->last_move_dir = MV_NONE;
3634
3635     player->is_active = FALSE;
3636
3637     player->is_waiting = FALSE;
3638     player->is_moving = FALSE;
3639     player->is_auto_moving = FALSE;
3640     player->is_digging = FALSE;
3641     player->is_snapping = FALSE;
3642     player->is_collecting = FALSE;
3643     player->is_pushing = FALSE;
3644     player->is_switching = FALSE;
3645     player->is_dropping = FALSE;
3646     player->is_dropping_pressed = FALSE;
3647
3648     player->is_bored = FALSE;
3649     player->is_sleeping = FALSE;
3650
3651     player->frame_counter_bored = -1;
3652     player->frame_counter_sleeping = -1;
3653
3654     player->anim_delay_counter = 0;
3655     player->post_delay_counter = 0;
3656
3657     player->dir_waiting = MV_NONE;
3658     player->action_waiting = ACTION_DEFAULT;
3659     player->last_action_waiting = ACTION_DEFAULT;
3660     player->special_action_bored = ACTION_DEFAULT;
3661     player->special_action_sleeping = ACTION_DEFAULT;
3662
3663     player->switch_x = -1;
3664     player->switch_y = -1;
3665
3666     player->drop_x = -1;
3667     player->drop_y = -1;
3668
3669     player->show_envelope = 0;
3670
3671     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3672
3673     player->push_delay       = -1;      /* initialized when pushing starts */
3674     player->push_delay_value = game.initial_push_delay_value;
3675
3676     player->drop_delay = 0;
3677     player->drop_pressed_delay = 0;
3678
3679     player->last_jx = -1;
3680     player->last_jy = -1;
3681     player->jx = -1;
3682     player->jy = -1;
3683
3684     player->shield_normal_time_left = 0;
3685     player->shield_deadly_time_left = 0;
3686
3687     player->inventory_infinite_element = EL_UNDEFINED;
3688     player->inventory_size = 0;
3689
3690     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3691     SnapField(player, 0, 0);
3692
3693     player->LevelSolved = FALSE;
3694     player->GameOver = FALSE;
3695
3696     player->LevelSolved_GameWon = FALSE;
3697     player->LevelSolved_GameEnd = FALSE;
3698     player->LevelSolved_PanelOff = FALSE;
3699     player->LevelSolved_SaveTape = FALSE;
3700     player->LevelSolved_SaveScore = FALSE;
3701     player->LevelSolved_CountingTime = 0;
3702     player->LevelSolved_CountingScore = 0;
3703   }
3704
3705   network_player_action_received = FALSE;
3706
3707 #if defined(NETWORK_AVALIABLE)
3708   /* initial null action */
3709   if (network_playing)
3710     SendToServer_MovePlayer(MV_NONE);
3711 #endif
3712
3713   ZX = ZY = -1;
3714   ExitX = ExitY = -1;
3715
3716   FrameCounter = 0;
3717   TimeFrames = 0;
3718   TimePlayed = 0;
3719   TimeLeft = level.time;
3720   TapeTime = 0;
3721
3722   ScreenMovDir = MV_NONE;
3723   ScreenMovPos = 0;
3724   ScreenGfxPos = 0;
3725
3726   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3727
3728   AllPlayersGone = FALSE;
3729
3730   game.yamyam_content_nr = 0;
3731   game.robot_wheel_active = FALSE;
3732   game.magic_wall_active = FALSE;
3733   game.magic_wall_time_left = 0;
3734   game.light_time_left = 0;
3735   game.timegate_time_left = 0;
3736   game.switchgate_pos = 0;
3737   game.wind_direction = level.wind_direction_initial;
3738
3739 #if !USE_PLAYER_GRAVITY
3740   game.gravity = FALSE;
3741   game.explosions_delayed = TRUE;
3742 #endif
3743
3744   game.lenses_time_left = 0;
3745   game.magnify_time_left = 0;
3746
3747   game.ball_state = level.ball_state_initial;
3748   game.ball_content_nr = 0;
3749
3750   game.envelope_active = FALSE;
3751
3752   /* set focus to local player for network games, else to all players */
3753   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3754   game.centered_player_nr_next = game.centered_player_nr;
3755   game.set_centered_player = FALSE;
3756
3757   if (network_playing && tape.recording)
3758   {
3759     /* store client dependent player focus when recording network games */
3760     tape.centered_player_nr_next = game.centered_player_nr_next;
3761     tape.set_centered_player = TRUE;
3762   }
3763
3764   for (i = 0; i < NUM_BELTS; i++)
3765   {
3766     game.belt_dir[i] = MV_NONE;
3767     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3768   }
3769
3770   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3771     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3772
3773   SCAN_PLAYFIELD(x, y)
3774   {
3775     Feld[x][y] = level.field[x][y];
3776     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3777     ChangeDelay[x][y] = 0;
3778     ChangePage[x][y] = -1;
3779 #if USE_NEW_CUSTOM_VALUE
3780     CustomValue[x][y] = 0;              /* initialized in InitField() */
3781 #endif
3782     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3783     AmoebaNr[x][y] = 0;
3784     WasJustMoving[x][y] = 0;
3785     WasJustFalling[x][y] = 0;
3786     CheckCollision[x][y] = 0;
3787     CheckImpact[x][y] = 0;
3788     Stop[x][y] = FALSE;
3789     Pushed[x][y] = FALSE;
3790
3791     ChangeCount[x][y] = 0;
3792     ChangeEvent[x][y] = -1;
3793
3794     ExplodePhase[x][y] = 0;
3795     ExplodeDelay[x][y] = 0;
3796     ExplodeField[x][y] = EX_TYPE_NONE;
3797
3798     RunnerVisit[x][y] = 0;
3799     PlayerVisit[x][y] = 0;
3800
3801     GfxFrame[x][y] = 0;
3802     GfxRandom[x][y] = INIT_GFX_RANDOM();
3803     GfxElement[x][y] = EL_UNDEFINED;
3804     GfxAction[x][y] = ACTION_DEFAULT;
3805     GfxDir[x][y] = MV_NONE;
3806   }
3807
3808   SCAN_PLAYFIELD(x, y)
3809   {
3810     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3811       emulate_bd = FALSE;
3812     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3813       emulate_sb = FALSE;
3814     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3815       emulate_sp = FALSE;
3816
3817     InitField(x, y, TRUE);
3818
3819     ResetGfxAnimation(x, y);
3820   }
3821
3822   InitBeltMovement();
3823
3824   for (i = 0; i < MAX_PLAYERS; i++)
3825   {
3826     struct PlayerInfo *player = &stored_player[i];
3827
3828     /* set number of special actions for bored and sleeping animation */
3829     player->num_special_action_bored =
3830       get_num_special_action(player->artwork_element,
3831                              ACTION_BORING_1, ACTION_BORING_LAST);
3832     player->num_special_action_sleeping =
3833       get_num_special_action(player->artwork_element,
3834                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3835   }
3836
3837   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3838                     emulate_sb ? EMU_SOKOBAN :
3839                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3840
3841 #if USE_NEW_ALL_SLIPPERY
3842   /* initialize type of slippery elements */
3843   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3844   {
3845     if (!IS_CUSTOM_ELEMENT(i))
3846     {
3847       /* default: elements slip down either to the left or right randomly */
3848       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3849
3850       /* SP style elements prefer to slip down on the left side */
3851       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3852         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3853
3854       /* BD style elements prefer to slip down on the left side */
3855       if (game.emulation == EMU_BOULDERDASH)
3856         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3857     }
3858   }
3859 #endif
3860
3861   /* initialize explosion and ignition delay */
3862   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3863   {
3864     if (!IS_CUSTOM_ELEMENT(i))
3865     {
3866       int num_phase = 8;
3867       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3868                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3869                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3870       int last_phase = (num_phase + 1) * delay;
3871       int half_phase = (num_phase / 2) * delay;
3872
3873       element_info[i].explosion_delay = last_phase - 1;
3874       element_info[i].ignition_delay = half_phase;
3875
3876       if (i == EL_BLACK_ORB)
3877         element_info[i].ignition_delay = 1;
3878     }
3879
3880 #if 0
3881     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3882       element_info[i].explosion_delay = 1;
3883
3884     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3885       element_info[i].ignition_delay = 1;
3886 #endif
3887   }
3888
3889   /* correct non-moving belts to start moving left */
3890   for (i = 0; i < NUM_BELTS; i++)
3891     if (game.belt_dir[i] == MV_NONE)
3892       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3893
3894   /* check if any connected player was not found in playfield */
3895   for (i = 0; i < MAX_PLAYERS; i++)
3896   {
3897     struct PlayerInfo *player = &stored_player[i];
3898
3899     if (player->connected && !player->present)
3900     {
3901       for (j = 0; j < MAX_PLAYERS; j++)
3902       {
3903         struct PlayerInfo *some_player = &stored_player[j];
3904         int jx = some_player->jx, jy = some_player->jy;
3905
3906         /* assign first free player found that is present in the playfield */
3907         if (some_player->present && !some_player->connected)
3908         {
3909           player->present = TRUE;
3910           player->active = TRUE;
3911
3912           some_player->present = FALSE;
3913           some_player->active = FALSE;
3914
3915           player->artwork_element = some_player->artwork_element;
3916
3917           player->block_last_field       = some_player->block_last_field;
3918           player->block_delay_adjustment = some_player->block_delay_adjustment;
3919
3920           StorePlayer[jx][jy] = player->element_nr;
3921           player->jx = player->last_jx = jx;
3922           player->jy = player->last_jy = jy;
3923
3924           break;
3925         }
3926       }
3927     }
3928   }
3929
3930   if (tape.playing)
3931   {
3932     /* when playing a tape, eliminate all players who do not participate */
3933
3934     for (i = 0; i < MAX_PLAYERS; i++)
3935     {
3936       if (stored_player[i].active && !tape.player_participates[i])
3937       {
3938         struct PlayerInfo *player = &stored_player[i];
3939         int jx = player->jx, jy = player->jy;
3940
3941         player->active = FALSE;
3942         StorePlayer[jx][jy] = 0;
3943         Feld[jx][jy] = EL_EMPTY;
3944       }
3945     }
3946   }
3947   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3948   {
3949     /* when in single player mode, eliminate all but the first active player */
3950
3951     for (i = 0; i < MAX_PLAYERS; i++)
3952     {
3953       if (stored_player[i].active)
3954       {
3955         for (j = i + 1; j < MAX_PLAYERS; j++)
3956         {
3957           if (stored_player[j].active)
3958           {
3959             struct PlayerInfo *player = &stored_player[j];
3960             int jx = player->jx, jy = player->jy;
3961
3962             player->active = FALSE;
3963             player->present = FALSE;
3964
3965             StorePlayer[jx][jy] = 0;
3966             Feld[jx][jy] = EL_EMPTY;
3967           }
3968         }
3969       }
3970     }
3971   }
3972
3973   /* when recording the game, store which players take part in the game */
3974   if (tape.recording)
3975   {
3976     for (i = 0; i < MAX_PLAYERS; i++)
3977       if (stored_player[i].active)
3978         tape.player_participates[i] = TRUE;
3979   }
3980
3981   if (options.debug)
3982   {
3983     for (i = 0; i < MAX_PLAYERS; i++)
3984     {
3985       struct PlayerInfo *player = &stored_player[i];
3986
3987       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3988              i+1,
3989              player->present,
3990              player->connected,
3991              player->active);
3992       if (local_player == player)
3993         printf("Player  %d is local player.\n", i+1);
3994     }
3995   }
3996
3997   if (BorderElement == EL_EMPTY)
3998   {
3999     SBX_Left = 0;
4000     SBX_Right = lev_fieldx - SCR_FIELDX;
4001     SBY_Upper = 0;
4002     SBY_Lower = lev_fieldy - SCR_FIELDY;
4003   }
4004   else
4005   {
4006     SBX_Left = -1;
4007     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4008     SBY_Upper = -1;
4009     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4010   }
4011
4012   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4013     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4014
4015   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4016     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4017
4018   /* if local player not found, look for custom element that might create
4019      the player (make some assumptions about the right custom element) */
4020   if (!local_player->present)
4021   {
4022     int start_x = 0, start_y = 0;
4023     int found_rating = 0;
4024     int found_element = EL_UNDEFINED;
4025     int player_nr = local_player->index_nr;
4026
4027     SCAN_PLAYFIELD(x, y)
4028     {
4029       int element = Feld[x][y];
4030       int content;
4031       int xx, yy;
4032       boolean is_player;
4033
4034       if (level.use_start_element[player_nr] &&
4035           level.start_element[player_nr] == element &&
4036           found_rating < 4)
4037       {
4038         start_x = x;
4039         start_y = y;
4040
4041         found_rating = 4;
4042         found_element = element;
4043       }
4044
4045       if (!IS_CUSTOM_ELEMENT(element))
4046         continue;
4047
4048       if (CAN_CHANGE(element))
4049       {
4050         for (i = 0; i < element_info[element].num_change_pages; i++)
4051         {
4052           /* check for player created from custom element as single target */
4053           content = element_info[element].change_page[i].target_element;
4054           is_player = ELEM_IS_PLAYER(content);
4055
4056           if (is_player && (found_rating < 3 ||
4057                             (found_rating == 3 && element < found_element)))
4058           {
4059             start_x = x;
4060             start_y = y;
4061
4062             found_rating = 3;
4063             found_element = element;
4064           }
4065         }
4066       }
4067
4068       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4069       {
4070         /* check for player created from custom element as explosion content */
4071         content = element_info[element].content.e[xx][yy];
4072         is_player = ELEM_IS_PLAYER(content);
4073
4074         if (is_player && (found_rating < 2 ||
4075                           (found_rating == 2 && element < found_element)))
4076         {
4077           start_x = x + xx - 1;
4078           start_y = y + yy - 1;
4079
4080           found_rating = 2;
4081           found_element = element;
4082         }
4083
4084         if (!CAN_CHANGE(element))
4085           continue;
4086
4087         for (i = 0; i < element_info[element].num_change_pages; i++)
4088         {
4089           /* check for player created from custom element as extended target */
4090           content =
4091             element_info[element].change_page[i].target_content.e[xx][yy];
4092
4093           is_player = ELEM_IS_PLAYER(content);
4094
4095           if (is_player && (found_rating < 1 ||
4096                             (found_rating == 1 && element < found_element)))
4097           {
4098             start_x = x + xx - 1;
4099             start_y = y + yy - 1;
4100
4101             found_rating = 1;
4102             found_element = element;
4103           }
4104         }
4105       }
4106     }
4107
4108     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4109                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4110                 start_x - MIDPOSX);
4111
4112     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4113                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4114                 start_y - MIDPOSY);
4115   }
4116   else
4117   {
4118     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4119                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4120                 local_player->jx - MIDPOSX);
4121
4122     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4123                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4124                 local_player->jy - MIDPOSY);
4125   }
4126
4127   /* do not use PLAYING mask for fading out from main screen */
4128   game_status = GAME_MODE_MAIN;
4129
4130   StopAnimation();
4131
4132   if (!game.restart_level)
4133     CloseDoor(DOOR_CLOSE_1);
4134
4135 #if 1
4136   if (level_editor_test_game)
4137     FadeSkipNextFadeIn();
4138   else
4139     FadeSetEnterScreen();
4140 #else
4141   if (level_editor_test_game)
4142     fading = fading_none;
4143   else
4144     fading = menu.destination;
4145 #endif
4146
4147 #if 1
4148   FadeOut(REDRAW_FIELD);
4149 #else
4150   if (do_fading)
4151     FadeOut(REDRAW_FIELD);
4152 #endif
4153
4154   game_status = GAME_MODE_PLAYING;
4155
4156   /* !!! FIX THIS (START) !!! */
4157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4158   {
4159     InitGameEngine_EM();
4160
4161     /* blit playfield from scroll buffer to normal back buffer for fading in */
4162     BlitScreenToBitmap_EM(backbuffer);
4163   }
4164   else
4165   {
4166     DrawLevel();
4167     DrawAllPlayers();
4168
4169     /* after drawing the level, correct some elements */
4170     if (game.timegate_time_left == 0)
4171       CloseAllOpenTimegates();
4172
4173     /* blit playfield from scroll buffer to normal back buffer for fading in */
4174     if (setup.soft_scrolling)
4175       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4176
4177     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4178   }
4179   /* !!! FIX THIS (END) !!! */
4180
4181 #if 1
4182   FadeIn(REDRAW_FIELD);
4183 #else
4184   if (do_fading)
4185     FadeIn(REDRAW_FIELD);
4186
4187   BackToFront();
4188 #endif
4189
4190   if (!game.restart_level)
4191   {
4192     /* copy default game door content to main double buffer */
4193     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4194                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4195   }
4196
4197   SetPanelBackground();
4198   SetDrawBackgroundMask(REDRAW_DOOR_1);
4199
4200   UpdateGameDoorValues();
4201   DrawGameDoorValues();
4202
4203   if (!game.restart_level)
4204   {
4205     UnmapGameButtons();
4206     UnmapTapeButtons();
4207     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4208     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4209     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4210     MapGameButtons();
4211     MapTapeButtons();
4212
4213     /* copy actual game door content to door double buffer for OpenDoor() */
4214     BlitBitmap(drawto, bitmap_db_door,
4215                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4216
4217     OpenDoor(DOOR_OPEN_ALL);
4218
4219     PlaySound(SND_GAME_STARTING);
4220
4221     if (setup.sound_music)
4222       PlayLevelMusic();
4223
4224     KeyboardAutoRepeatOffUnlessAutoplay();
4225
4226     if (options.debug)
4227     {
4228       for (i = 0; i < MAX_PLAYERS; i++)
4229         printf("Player %d %sactive.\n",
4230                i + 1, (stored_player[i].active ? "" : "not "));
4231     }
4232   }
4233
4234 #if 1
4235   UnmapAllGadgets();
4236
4237   MapGameButtons();
4238   MapTapeButtons();
4239 #endif
4240
4241   game.restart_level = FALSE;
4242 }
4243
4244 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4245 {
4246   /* this is used for non-R'n'D game engines to update certain engine values */
4247
4248   /* needed to determine if sounds are played within the visible screen area */
4249   scroll_x = actual_scroll_x;
4250   scroll_y = actual_scroll_y;
4251 }
4252
4253 void InitMovDir(int x, int y)
4254 {
4255   int i, element = Feld[x][y];
4256   static int xy[4][2] =
4257   {
4258     {  0, +1 },
4259     { +1,  0 },
4260     {  0, -1 },
4261     { -1,  0 }
4262   };
4263   static int direction[3][4] =
4264   {
4265     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4266     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4267     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4268   };
4269
4270   switch (element)
4271   {
4272     case EL_BUG_RIGHT:
4273     case EL_BUG_UP:
4274     case EL_BUG_LEFT:
4275     case EL_BUG_DOWN:
4276       Feld[x][y] = EL_BUG;
4277       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4278       break;
4279
4280     case EL_SPACESHIP_RIGHT:
4281     case EL_SPACESHIP_UP:
4282     case EL_SPACESHIP_LEFT:
4283     case EL_SPACESHIP_DOWN:
4284       Feld[x][y] = EL_SPACESHIP;
4285       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4286       break;
4287
4288     case EL_BD_BUTTERFLY_RIGHT:
4289     case EL_BD_BUTTERFLY_UP:
4290     case EL_BD_BUTTERFLY_LEFT:
4291     case EL_BD_BUTTERFLY_DOWN:
4292       Feld[x][y] = EL_BD_BUTTERFLY;
4293       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4294       break;
4295
4296     case EL_BD_FIREFLY_RIGHT:
4297     case EL_BD_FIREFLY_UP:
4298     case EL_BD_FIREFLY_LEFT:
4299     case EL_BD_FIREFLY_DOWN:
4300       Feld[x][y] = EL_BD_FIREFLY;
4301       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4302       break;
4303
4304     case EL_PACMAN_RIGHT:
4305     case EL_PACMAN_UP:
4306     case EL_PACMAN_LEFT:
4307     case EL_PACMAN_DOWN:
4308       Feld[x][y] = EL_PACMAN;
4309       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4310       break;
4311
4312     case EL_YAMYAM_LEFT:
4313     case EL_YAMYAM_RIGHT:
4314     case EL_YAMYAM_UP:
4315     case EL_YAMYAM_DOWN:
4316       Feld[x][y] = EL_YAMYAM;
4317       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4318       break;
4319
4320     case EL_SP_SNIKSNAK:
4321       MovDir[x][y] = MV_UP;
4322       break;
4323
4324     case EL_SP_ELECTRON:
4325       MovDir[x][y] = MV_LEFT;
4326       break;
4327
4328     case EL_MOLE_LEFT:
4329     case EL_MOLE_RIGHT:
4330     case EL_MOLE_UP:
4331     case EL_MOLE_DOWN:
4332       Feld[x][y] = EL_MOLE;
4333       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4334       break;
4335
4336     default:
4337       if (IS_CUSTOM_ELEMENT(element))
4338       {
4339         struct ElementInfo *ei = &element_info[element];
4340         int move_direction_initial = ei->move_direction_initial;
4341         int move_pattern = ei->move_pattern;
4342
4343         if (move_direction_initial == MV_START_PREVIOUS)
4344         {
4345           if (MovDir[x][y] != MV_NONE)
4346             return;
4347
4348           move_direction_initial = MV_START_AUTOMATIC;
4349         }
4350
4351         if (move_direction_initial == MV_START_RANDOM)
4352           MovDir[x][y] = 1 << RND(4);
4353         else if (move_direction_initial & MV_ANY_DIRECTION)
4354           MovDir[x][y] = move_direction_initial;
4355         else if (move_pattern == MV_ALL_DIRECTIONS ||
4356                  move_pattern == MV_TURNING_LEFT ||
4357                  move_pattern == MV_TURNING_RIGHT ||
4358                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4359                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4360                  move_pattern == MV_TURNING_RANDOM)
4361           MovDir[x][y] = 1 << RND(4);
4362         else if (move_pattern == MV_HORIZONTAL)
4363           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4364         else if (move_pattern == MV_VERTICAL)
4365           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4366         else if (move_pattern & MV_ANY_DIRECTION)
4367           MovDir[x][y] = element_info[element].move_pattern;
4368         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4369                  move_pattern == MV_ALONG_RIGHT_SIDE)
4370         {
4371           /* use random direction as default start direction */
4372           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4373             MovDir[x][y] = 1 << RND(4);
4374
4375           for (i = 0; i < NUM_DIRECTIONS; i++)
4376           {
4377             int x1 = x + xy[i][0];
4378             int y1 = y + xy[i][1];
4379
4380             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4381             {
4382               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4383                 MovDir[x][y] = direction[0][i];
4384               else
4385                 MovDir[x][y] = direction[1][i];
4386
4387               break;
4388             }
4389           }
4390         }                
4391       }
4392       else
4393       {
4394         MovDir[x][y] = 1 << RND(4);
4395
4396         if (element != EL_BUG &&
4397             element != EL_SPACESHIP &&
4398             element != EL_BD_BUTTERFLY &&
4399             element != EL_BD_FIREFLY)
4400           break;
4401
4402         for (i = 0; i < NUM_DIRECTIONS; i++)
4403         {
4404           int x1 = x + xy[i][0];
4405           int y1 = y + xy[i][1];
4406
4407           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4408           {
4409             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4410             {
4411               MovDir[x][y] = direction[0][i];
4412               break;
4413             }
4414             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4415                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4416             {
4417               MovDir[x][y] = direction[1][i];
4418               break;
4419             }
4420           }
4421         }
4422       }
4423       break;
4424   }
4425
4426   GfxDir[x][y] = MovDir[x][y];
4427 }
4428
4429 void InitAmoebaNr(int x, int y)
4430 {
4431   int i;
4432   int group_nr = AmoebeNachbarNr(x, y);
4433
4434   if (group_nr == 0)
4435   {
4436     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4437     {
4438       if (AmoebaCnt[i] == 0)
4439       {
4440         group_nr = i;
4441         break;
4442       }
4443     }
4444   }
4445
4446   AmoebaNr[x][y] = group_nr;
4447   AmoebaCnt[group_nr]++;
4448   AmoebaCnt2[group_nr]++;
4449 }
4450
4451 static void PlayerWins(struct PlayerInfo *player)
4452 {
4453   player->LevelSolved = TRUE;
4454   player->GameOver = TRUE;
4455
4456   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4457                          level.native_em_level->lev->score : player->score);
4458
4459   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4460   player->LevelSolved_CountingScore = player->score_final;
4461 }
4462
4463 void GameWon()
4464 {
4465   static int time, time_final;
4466   static int score, score_final;
4467   static int game_over_delay_1 = 0;
4468   static int game_over_delay_2 = 0;
4469   int game_over_delay_value_1 = 50;
4470   int game_over_delay_value_2 = 50;
4471
4472   if (!local_player->LevelSolved_GameWon)
4473   {
4474     int i;
4475
4476     /* do not start end game actions before the player stops moving (to exit) */
4477     if (local_player->MovPos)
4478       return;
4479
4480     local_player->LevelSolved_GameWon = TRUE;
4481     local_player->LevelSolved_SaveTape = tape.recording;
4482     local_player->LevelSolved_SaveScore = !tape.playing;
4483
4484     if (tape.auto_play)         /* tape might already be stopped here */
4485       tape.auto_play_level_solved = TRUE;
4486
4487 #if 1
4488     TapeStop();
4489 #endif
4490
4491     game_over_delay_1 = game_over_delay_value_1;
4492     game_over_delay_2 = game_over_delay_value_2;
4493
4494     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4495     score = score_final = local_player->score_final;
4496
4497     if (TimeLeft > 0)
4498     {
4499       time_final = 0;
4500       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4501     }
4502     else if (level.time == 0 && TimePlayed < 999)
4503     {
4504       time_final = 999;
4505       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4506     }
4507
4508     local_player->score_final = score_final;
4509
4510     if (level_editor_test_game)
4511     {
4512       time = time_final;
4513       score = score_final;
4514
4515 #if 1
4516       local_player->LevelSolved_CountingTime = time;
4517       local_player->LevelSolved_CountingScore = score;
4518
4519       game_panel_controls[GAME_PANEL_TIME].value = time;
4520       game_panel_controls[GAME_PANEL_SCORE].value = score;
4521
4522       DisplayGameControlValues();
4523 #else
4524       DrawGameValue_Time(time);
4525       DrawGameValue_Score(score);
4526 #endif
4527     }
4528
4529     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4530     {
4531       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4532       {
4533         /* close exit door after last player */
4534         if ((AllPlayersGone &&
4535              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4536               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4537               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4538             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4539             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4540         {
4541           int element = Feld[ExitX][ExitY];
4542
4543 #if 0
4544           if (element == EL_EM_EXIT_OPEN ||
4545               element == EL_EM_STEEL_EXIT_OPEN)
4546           {
4547             Bang(ExitX, ExitY);
4548           }
4549           else
4550 #endif
4551           {
4552             Feld[ExitX][ExitY] =
4553               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4554                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4555                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4556                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4557                EL_EM_STEEL_EXIT_CLOSING);
4558
4559             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4560           }
4561         }
4562
4563         /* player disappears */
4564         DrawLevelField(ExitX, ExitY);
4565       }
4566
4567       for (i = 0; i < MAX_PLAYERS; i++)
4568       {
4569         struct PlayerInfo *player = &stored_player[i];
4570
4571         if (player->present)
4572         {
4573           RemovePlayer(player);
4574
4575           /* player disappears */
4576           DrawLevelField(player->jx, player->jy);
4577         }
4578       }
4579     }
4580
4581     PlaySound(SND_GAME_WINNING);
4582   }
4583
4584   if (game_over_delay_1 > 0)
4585   {
4586     game_over_delay_1--;
4587
4588     return;
4589   }
4590
4591   if (time != time_final)
4592   {
4593     int time_to_go = ABS(time_final - time);
4594     int time_count_dir = (time < time_final ? +1 : -1);
4595     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4596
4597     time  += time_count_steps * time_count_dir;
4598     score += time_count_steps * level.score[SC_TIME_BONUS];
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     if (time == time_final)
4614       StopSound(SND_GAME_LEVELTIME_BONUS);
4615     else if (setup.sound_loops)
4616       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4617     else
4618       PlaySound(SND_GAME_LEVELTIME_BONUS);
4619
4620     return;
4621   }
4622
4623   local_player->LevelSolved_PanelOff = TRUE;
4624
4625   if (game_over_delay_2 > 0)
4626   {
4627     game_over_delay_2--;
4628
4629     return;
4630   }
4631
4632 #if 1
4633   GameEnd();
4634 #endif
4635 }
4636
4637 void GameEnd()
4638 {
4639   int hi_pos;
4640   boolean raise_level = FALSE;
4641
4642   local_player->LevelSolved_GameEnd = TRUE;
4643
4644   CloseDoor(DOOR_CLOSE_1);
4645
4646   if (local_player->LevelSolved_SaveTape)
4647   {
4648 #if 0
4649     TapeStop();
4650 #endif
4651
4652 #if 1
4653     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4654 #else
4655     SaveTape(tape.level_nr);            /* ask to save tape */
4656 #endif
4657   }
4658
4659   if (level_editor_test_game)
4660   {
4661     game_status = GAME_MODE_MAIN;
4662
4663 #if 1
4664     DrawAndFadeInMainMenu(REDRAW_FIELD);
4665 #else
4666     DrawMainMenu();
4667 #endif
4668
4669     return;
4670   }
4671
4672   if (!local_player->LevelSolved_SaveScore)
4673   {
4674 #if 1
4675     FadeOut(REDRAW_FIELD);
4676 #endif
4677
4678     game_status = GAME_MODE_MAIN;
4679
4680     DrawAndFadeInMainMenu(REDRAW_FIELD);
4681
4682     return;
4683   }
4684
4685   if (level_nr == leveldir_current->handicap_level)
4686   {
4687     leveldir_current->handicap_level++;
4688     SaveLevelSetup_SeriesInfo();
4689   }
4690
4691   if (level_nr < leveldir_current->last_level)
4692     raise_level = TRUE;                 /* advance to next level */
4693
4694   if ((hi_pos = NewHiScore()) >= 0) 
4695   {
4696     game_status = GAME_MODE_SCORES;
4697
4698     DrawHallOfFame(hi_pos);
4699
4700     if (raise_level)
4701     {
4702       level_nr++;
4703       TapeErase();
4704     }
4705   }
4706   else
4707   {
4708 #if 1
4709     FadeOut(REDRAW_FIELD);
4710 #endif
4711
4712     game_status = GAME_MODE_MAIN;
4713
4714     if (raise_level)
4715     {
4716       level_nr++;
4717       TapeErase();
4718     }
4719
4720     DrawAndFadeInMainMenu(REDRAW_FIELD);
4721   }
4722 }
4723
4724 int NewHiScore()
4725 {
4726   int k, l;
4727   int position = -1;
4728
4729   LoadScore(level_nr);
4730
4731   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4732       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4733     return -1;
4734
4735   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4736   {
4737     if (local_player->score_final > highscore[k].Score)
4738     {
4739       /* player has made it to the hall of fame */
4740
4741       if (k < MAX_SCORE_ENTRIES - 1)
4742       {
4743         int m = MAX_SCORE_ENTRIES - 1;
4744
4745 #ifdef ONE_PER_NAME
4746         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4747           if (strEqual(setup.player_name, highscore[l].Name))
4748             m = l;
4749         if (m == k)     /* player's new highscore overwrites his old one */
4750           goto put_into_list;
4751 #endif
4752
4753         for (l = m; l > k; l--)
4754         {
4755           strcpy(highscore[l].Name, highscore[l - 1].Name);
4756           highscore[l].Score = highscore[l - 1].Score;
4757         }
4758       }
4759
4760 #ifdef ONE_PER_NAME
4761       put_into_list:
4762 #endif
4763       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4764       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4765       highscore[k].Score = local_player->score_final; 
4766       position = k;
4767       break;
4768     }
4769
4770 #ifdef ONE_PER_NAME
4771     else if (!strncmp(setup.player_name, highscore[k].Name,
4772                       MAX_PLAYER_NAME_LEN))
4773       break;    /* player already there with a higher score */
4774 #endif
4775
4776   }
4777
4778   if (position >= 0) 
4779     SaveScore(level_nr);
4780
4781   return position;
4782 }
4783
4784 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4785 {
4786   int element = Feld[x][y];
4787   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4788   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4789   int horiz_move = (dx != 0);
4790   int sign = (horiz_move ? dx : dy);
4791   int step = sign * element_info[element].move_stepsize;
4792
4793   /* special values for move stepsize for spring and things on conveyor belt */
4794   if (horiz_move)
4795   {
4796     if (CAN_FALL(element) &&
4797         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4798       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4799     else if (element == EL_SPRING)
4800       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4801   }
4802
4803   return step;
4804 }
4805
4806 inline static int getElementMoveStepsize(int x, int y)
4807 {
4808   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4809 }
4810
4811 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4812 {
4813   if (player->GfxAction != action || player->GfxDir != dir)
4814   {
4815 #if 0
4816     printf("Player frame reset! (%d => %d, %d => %d)\n",
4817            player->GfxAction, action, player->GfxDir, dir);
4818 #endif
4819
4820     player->GfxAction = action;
4821     player->GfxDir = dir;
4822     player->Frame = 0;
4823     player->StepFrame = 0;
4824   }
4825 }
4826
4827 #if USE_GFX_RESET_GFX_ANIMATION
4828 static void ResetGfxFrame(int x, int y, boolean redraw)
4829 {
4830   int element = Feld[x][y];
4831   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4832   int last_gfx_frame = GfxFrame[x][y];
4833
4834   if (graphic_info[graphic].anim_global_sync)
4835     GfxFrame[x][y] = FrameCounter;
4836   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4837     GfxFrame[x][y] = CustomValue[x][y];
4838   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4839     GfxFrame[x][y] = element_info[element].collect_score;
4840   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4841     GfxFrame[x][y] = ChangeDelay[x][y];
4842
4843   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4844     DrawLevelGraphicAnimation(x, y, graphic);
4845 }
4846 #endif
4847
4848 static void ResetGfxAnimation(int x, int y)
4849 {
4850   GfxAction[x][y] = ACTION_DEFAULT;
4851   GfxDir[x][y] = MovDir[x][y];
4852   GfxFrame[x][y] = 0;
4853
4854 #if USE_GFX_RESET_GFX_ANIMATION
4855   ResetGfxFrame(x, y, FALSE);
4856 #endif
4857 }
4858
4859 static void ResetRandomAnimationValue(int x, int y)
4860 {
4861   GfxRandom[x][y] = INIT_GFX_RANDOM();
4862 }
4863
4864 void InitMovingField(int x, int y, int direction)
4865 {
4866   int element = Feld[x][y];
4867   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4868   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4869   int newx = x + dx;
4870   int newy = y + dy;
4871   boolean is_moving_before, is_moving_after;
4872 #if 0
4873   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4874 #endif
4875
4876   /* check if element was/is moving or being moved before/after mode change */
4877 #if 1
4878 #if 1
4879   is_moving_before = (WasJustMoving[x][y] != 0);
4880 #else
4881   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4882   is_moving_before = WasJustMoving[x][y];
4883 #endif
4884 #else
4885   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4886 #endif
4887   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4888
4889   /* reset animation only for moving elements which change direction of moving
4890      or which just started or stopped moving
4891      (else CEs with property "can move" / "not moving" are reset each frame) */
4892 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4893 #if 1
4894   if (is_moving_before != is_moving_after ||
4895       direction != MovDir[x][y])
4896     ResetGfxAnimation(x, y);
4897 #else
4898   if ((is_moving_before || is_moving_after) && !continues_moving)
4899     ResetGfxAnimation(x, y);
4900 #endif
4901 #else
4902   if (!continues_moving)
4903     ResetGfxAnimation(x, y);
4904 #endif
4905
4906   MovDir[x][y] = direction;
4907   GfxDir[x][y] = direction;
4908
4909 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4910   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4911                      direction == MV_DOWN && CAN_FALL(element) ?
4912                      ACTION_FALLING : ACTION_MOVING);
4913 #else
4914   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4915                      ACTION_FALLING : ACTION_MOVING);
4916 #endif
4917
4918   /* this is needed for CEs with property "can move" / "not moving" */
4919
4920   if (is_moving_after)
4921   {
4922     if (Feld[newx][newy] == EL_EMPTY)
4923       Feld[newx][newy] = EL_BLOCKED;
4924
4925     MovDir[newx][newy] = MovDir[x][y];
4926
4927 #if USE_NEW_CUSTOM_VALUE
4928     CustomValue[newx][newy] = CustomValue[x][y];
4929 #endif
4930
4931     GfxFrame[newx][newy] = GfxFrame[x][y];
4932     GfxRandom[newx][newy] = GfxRandom[x][y];
4933     GfxAction[newx][newy] = GfxAction[x][y];
4934     GfxDir[newx][newy] = GfxDir[x][y];
4935   }
4936 }
4937
4938 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4939 {
4940   int direction = MovDir[x][y];
4941   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4942   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4943
4944   *goes_to_x = newx;
4945   *goes_to_y = newy;
4946 }
4947
4948 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4949 {
4950   int oldx = x, oldy = y;
4951   int direction = MovDir[x][y];
4952
4953   if (direction == MV_LEFT)
4954     oldx++;
4955   else if (direction == MV_RIGHT)
4956     oldx--;
4957   else if (direction == MV_UP)
4958     oldy++;
4959   else if (direction == MV_DOWN)
4960     oldy--;
4961
4962   *comes_from_x = oldx;
4963   *comes_from_y = oldy;
4964 }
4965
4966 int MovingOrBlocked2Element(int x, int y)
4967 {
4968   int element = Feld[x][y];
4969
4970   if (element == EL_BLOCKED)
4971   {
4972     int oldx, oldy;
4973
4974     Blocked2Moving(x, y, &oldx, &oldy);
4975     return Feld[oldx][oldy];
4976   }
4977   else
4978     return element;
4979 }
4980
4981 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4982 {
4983   /* like MovingOrBlocked2Element(), but if element is moving
4984      and (x,y) is the field the moving element is just leaving,
4985      return EL_BLOCKED instead of the element value */
4986   int element = Feld[x][y];
4987
4988   if (IS_MOVING(x, y))
4989   {
4990     if (element == EL_BLOCKED)
4991     {
4992       int oldx, oldy;
4993
4994       Blocked2Moving(x, y, &oldx, &oldy);
4995       return Feld[oldx][oldy];
4996     }
4997     else
4998       return EL_BLOCKED;
4999   }
5000   else
5001     return element;
5002 }
5003
5004 static void RemoveField(int x, int y)
5005 {
5006   Feld[x][y] = EL_EMPTY;
5007
5008   MovPos[x][y] = 0;
5009   MovDir[x][y] = 0;
5010   MovDelay[x][y] = 0;
5011
5012 #if USE_NEW_CUSTOM_VALUE
5013   CustomValue[x][y] = 0;
5014 #endif
5015
5016   AmoebaNr[x][y] = 0;
5017   ChangeDelay[x][y] = 0;
5018   ChangePage[x][y] = -1;
5019   Pushed[x][y] = FALSE;
5020
5021 #if 0
5022   ExplodeField[x][y] = EX_TYPE_NONE;
5023 #endif
5024
5025   GfxElement[x][y] = EL_UNDEFINED;
5026   GfxAction[x][y] = ACTION_DEFAULT;
5027   GfxDir[x][y] = MV_NONE;
5028 }
5029
5030 void RemoveMovingField(int x, int y)
5031 {
5032   int oldx = x, oldy = y, newx = x, newy = y;
5033   int element = Feld[x][y];
5034   int next_element = EL_UNDEFINED;
5035
5036   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5037     return;
5038
5039   if (IS_MOVING(x, y))
5040   {
5041     Moving2Blocked(x, y, &newx, &newy);
5042
5043     if (Feld[newx][newy] != EL_BLOCKED)
5044     {
5045       /* element is moving, but target field is not free (blocked), but
5046          already occupied by something different (example: acid pool);
5047          in this case, only remove the moving field, but not the target */
5048
5049       RemoveField(oldx, oldy);
5050
5051       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5052
5053       DrawLevelField(oldx, oldy);
5054
5055       return;
5056     }
5057   }
5058   else if (element == EL_BLOCKED)
5059   {
5060     Blocked2Moving(x, y, &oldx, &oldy);
5061     if (!IS_MOVING(oldx, oldy))
5062       return;
5063   }
5064
5065   if (element == EL_BLOCKED &&
5066       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5067        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5068        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5069        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5070        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5071        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5072     next_element = get_next_element(Feld[oldx][oldy]);
5073
5074   RemoveField(oldx, oldy);
5075   RemoveField(newx, newy);
5076
5077   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5078
5079   if (next_element != EL_UNDEFINED)
5080     Feld[oldx][oldy] = next_element;
5081
5082   DrawLevelField(oldx, oldy);
5083   DrawLevelField(newx, newy);
5084 }
5085
5086 void DrawDynamite(int x, int y)
5087 {
5088   int sx = SCREENX(x), sy = SCREENY(y);
5089   int graphic = el2img(Feld[x][y]);
5090   int frame;
5091
5092   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5093     return;
5094
5095   if (IS_WALKABLE_INSIDE(Back[x][y]))
5096     return;
5097
5098   if (Back[x][y])
5099     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5100   else if (Store[x][y])
5101     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5102
5103   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5104
5105   if (Back[x][y] || Store[x][y])
5106     DrawGraphicThruMask(sx, sy, graphic, frame);
5107   else
5108     DrawGraphic(sx, sy, graphic, frame);
5109 }
5110
5111 void CheckDynamite(int x, int y)
5112 {
5113   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5114   {
5115     MovDelay[x][y]--;
5116
5117     if (MovDelay[x][y] != 0)
5118     {
5119       DrawDynamite(x, y);
5120       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5121
5122       return;
5123     }
5124   }
5125
5126   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5127
5128   Bang(x, y);
5129 }
5130
5131 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5132 {
5133   boolean num_checked_players = 0;
5134   int i;
5135
5136   for (i = 0; i < MAX_PLAYERS; i++)
5137   {
5138     if (stored_player[i].active)
5139     {
5140       int sx = stored_player[i].jx;
5141       int sy = stored_player[i].jy;
5142
5143       if (num_checked_players == 0)
5144       {
5145         *sx1 = *sx2 = sx;
5146         *sy1 = *sy2 = sy;
5147       }
5148       else
5149       {
5150         *sx1 = MIN(*sx1, sx);
5151         *sy1 = MIN(*sy1, sy);
5152         *sx2 = MAX(*sx2, sx);
5153         *sy2 = MAX(*sy2, sy);
5154       }
5155
5156       num_checked_players++;
5157     }
5158   }
5159 }
5160
5161 static boolean checkIfAllPlayersFitToScreen_RND()
5162 {
5163   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5164
5165   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5166
5167   return (sx2 - sx1 < SCR_FIELDX &&
5168           sy2 - sy1 < SCR_FIELDY);
5169 }
5170
5171 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5172 {
5173   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5174
5175   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5176
5177   *sx = (sx1 + sx2) / 2;
5178   *sy = (sy1 + sy2) / 2;
5179 }
5180
5181 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5182                         boolean center_screen, boolean quick_relocation)
5183 {
5184   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5185   boolean no_delay = (tape.warp_forward);
5186   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5187   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5188
5189   if (quick_relocation)
5190   {
5191     int offset = game.scroll_delay_value;
5192
5193     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5194     {
5195       if (!level.shifted_relocation || center_screen)
5196       {
5197         /* quick relocation (without scrolling), with centering of screen */
5198
5199         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5200                     x > SBX_Right + MIDPOSX ? SBX_Right :
5201                     x - MIDPOSX);
5202
5203         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5204                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5205                     y - MIDPOSY);
5206       }
5207       else
5208       {
5209         /* quick relocation (without scrolling), but do not center screen */
5210
5211         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5212                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5213                                old_x - MIDPOSX);
5214
5215         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5216                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5217                                old_y - MIDPOSY);
5218
5219         int offset_x = x + (scroll_x - center_scroll_x);
5220         int offset_y = y + (scroll_y - center_scroll_y);
5221
5222         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5223                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5224                     offset_x - MIDPOSX);
5225
5226         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5227                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5228                     offset_y - MIDPOSY);
5229       }
5230     }
5231     else
5232     {
5233       /* quick relocation (without scrolling), inside visible screen area */
5234
5235       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5236           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5237         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5238
5239       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5240           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5241         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5242
5243       /* don't scroll over playfield boundaries */
5244       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5245         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5246
5247       /* don't scroll over playfield boundaries */
5248       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5249         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5250     }
5251
5252     RedrawPlayfield(TRUE, 0,0,0,0);
5253   }
5254   else
5255   {
5256 #if 1
5257     int scroll_xx, scroll_yy;
5258
5259     if (!level.shifted_relocation || center_screen)
5260     {
5261       /* visible relocation (with scrolling), with centering of screen */
5262
5263       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5264                    x > SBX_Right + MIDPOSX ? SBX_Right :
5265                    x - MIDPOSX);
5266
5267       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5268                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5269                    y - MIDPOSY);
5270     }
5271     else
5272     {
5273       /* visible relocation (with scrolling), but do not center screen */
5274
5275       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5276                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5277                              old_x - MIDPOSX);
5278
5279       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5280                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5281                              old_y - MIDPOSY);
5282
5283       int offset_x = x + (scroll_x - center_scroll_x);
5284       int offset_y = y + (scroll_y - center_scroll_y);
5285
5286       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5287                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5288                    offset_x - MIDPOSX);
5289
5290       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5291                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5292                    offset_y - MIDPOSY);
5293     }
5294
5295 #else
5296
5297     /* visible relocation (with scrolling), with centering of screen */
5298
5299     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5300                      x > SBX_Right + MIDPOSX ? SBX_Right :
5301                      x - MIDPOSX);
5302
5303     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5304                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5305                      y - MIDPOSY);
5306 #endif
5307
5308     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5309
5310     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5311     {
5312       int dx = 0, dy = 0;
5313       int fx = FX, fy = FY;
5314
5315       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5316       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5317
5318       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5319         break;
5320
5321       scroll_x -= dx;
5322       scroll_y -= dy;
5323
5324       fx += dx * TILEX / 2;
5325       fy += dy * TILEY / 2;
5326
5327       ScrollLevel(dx, dy);
5328       DrawAllPlayers();
5329
5330       /* scroll in two steps of half tile size to make things smoother */
5331       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5332       FlushDisplay();
5333       Delay(wait_delay_value);
5334
5335       /* scroll second step to align at full tile size */
5336       BackToFront();
5337       Delay(wait_delay_value);
5338     }
5339
5340     DrawAllPlayers();
5341     BackToFront();
5342     Delay(wait_delay_value);
5343   }
5344 }
5345
5346 void RelocatePlayer(int jx, int jy, int el_player_raw)
5347 {
5348   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5349   int player_nr = GET_PLAYER_NR(el_player);
5350   struct PlayerInfo *player = &stored_player[player_nr];
5351   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5352   boolean no_delay = (tape.warp_forward);
5353   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5354   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5355   int old_jx = player->jx;
5356   int old_jy = player->jy;
5357   int old_element = Feld[old_jx][old_jy];
5358   int element = Feld[jx][jy];
5359   boolean player_relocated = (old_jx != jx || old_jy != jy);
5360
5361   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5362   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5363   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5364   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5365   int leave_side_horiz = move_dir_horiz;
5366   int leave_side_vert  = move_dir_vert;
5367   int enter_side = enter_side_horiz | enter_side_vert;
5368   int leave_side = leave_side_horiz | leave_side_vert;
5369
5370   if (player->GameOver)         /* do not reanimate dead player */
5371     return;
5372
5373   if (!player_relocated)        /* no need to relocate the player */
5374     return;
5375
5376   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5377   {
5378     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5379     DrawLevelField(jx, jy);
5380   }
5381
5382   if (player->present)
5383   {
5384     while (player->MovPos)
5385     {
5386       ScrollPlayer(player, SCROLL_GO_ON);
5387       ScrollScreen(NULL, SCROLL_GO_ON);
5388
5389       AdvanceFrameAndPlayerCounters(player->index_nr);
5390
5391       DrawPlayer(player);
5392
5393       BackToFront();
5394       Delay(wait_delay_value);
5395     }
5396
5397     DrawPlayer(player);         /* needed here only to cleanup last field */
5398     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5399
5400     player->is_moving = FALSE;
5401   }
5402
5403   if (IS_CUSTOM_ELEMENT(old_element))
5404     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5405                                CE_LEFT_BY_PLAYER,
5406                                player->index_bit, leave_side);
5407
5408   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5409                                       CE_PLAYER_LEAVES_X,
5410                                       player->index_bit, leave_side);
5411
5412   Feld[jx][jy] = el_player;
5413   InitPlayerField(jx, jy, el_player, TRUE);
5414
5415   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5416   {
5417     Feld[jx][jy] = element;
5418     InitField(jx, jy, FALSE);
5419   }
5420
5421   /* only visually relocate centered player */
5422   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5423                      FALSE, level.instant_relocation);
5424
5425   TestIfPlayerTouchesBadThing(jx, jy);
5426   TestIfPlayerTouchesCustomElement(jx, jy);
5427
5428   if (IS_CUSTOM_ELEMENT(element))
5429     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5430                                player->index_bit, enter_side);
5431
5432   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5433                                       player->index_bit, enter_side);
5434 }
5435
5436 void Explode(int ex, int ey, int phase, int mode)
5437 {
5438   int x, y;
5439   int last_phase;
5440   int border_element;
5441
5442   /* !!! eliminate this variable !!! */
5443   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5444
5445   if (game.explosions_delayed)
5446   {
5447     ExplodeField[ex][ey] = mode;
5448     return;
5449   }
5450
5451   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5452   {
5453     int center_element = Feld[ex][ey];
5454     int artwork_element, explosion_element;     /* set these values later */
5455
5456 #if 0
5457     /* --- This is only really needed (and now handled) in "Impact()". --- */
5458     /* do not explode moving elements that left the explode field in time */
5459     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5460         center_element == EL_EMPTY &&
5461         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5462       return;
5463 #endif
5464
5465 #if 0
5466     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5467     if (mode == EX_TYPE_NORMAL ||
5468         mode == EX_TYPE_CENTER ||
5469         mode == EX_TYPE_CROSS)
5470       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5471 #endif
5472
5473     /* remove things displayed in background while burning dynamite */
5474     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5475       Back[ex][ey] = 0;
5476
5477     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5478     {
5479       /* put moving element to center field (and let it explode there) */
5480       center_element = MovingOrBlocked2Element(ex, ey);
5481       RemoveMovingField(ex, ey);
5482       Feld[ex][ey] = center_element;
5483     }
5484
5485     /* now "center_element" is finally determined -- set related values now */
5486     artwork_element = center_element;           /* for custom player artwork */
5487     explosion_element = center_element;         /* for custom player artwork */
5488
5489     if (IS_PLAYER(ex, ey))
5490     {
5491       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5492
5493       artwork_element = stored_player[player_nr].artwork_element;
5494
5495       if (level.use_explosion_element[player_nr])
5496       {
5497         explosion_element = level.explosion_element[player_nr];
5498         artwork_element = explosion_element;
5499       }
5500     }
5501
5502 #if 1
5503     if (mode == EX_TYPE_NORMAL ||
5504         mode == EX_TYPE_CENTER ||
5505         mode == EX_TYPE_CROSS)
5506       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5507 #endif
5508
5509     last_phase = element_info[explosion_element].explosion_delay + 1;
5510
5511     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5512     {
5513       int xx = x - ex + 1;
5514       int yy = y - ey + 1;
5515       int element;
5516
5517       if (!IN_LEV_FIELD(x, y) ||
5518           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5519           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5520         continue;
5521
5522       element = Feld[x][y];
5523
5524       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5525       {
5526         element = MovingOrBlocked2Element(x, y);
5527
5528         if (!IS_EXPLOSION_PROOF(element))
5529           RemoveMovingField(x, y);
5530       }
5531
5532       /* indestructible elements can only explode in center (but not flames) */
5533       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5534                                            mode == EX_TYPE_BORDER)) ||
5535           element == EL_FLAMES)
5536         continue;
5537
5538       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5539          behaviour, for example when touching a yamyam that explodes to rocks
5540          with active deadly shield, a rock is created under the player !!! */
5541       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5542 #if 0
5543       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5544           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5545            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5546 #else
5547       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5548 #endif
5549       {
5550         if (IS_ACTIVE_BOMB(element))
5551         {
5552           /* re-activate things under the bomb like gate or penguin */
5553           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5554           Back[x][y] = 0;
5555         }
5556
5557         continue;
5558       }
5559
5560       /* save walkable background elements while explosion on same tile */
5561       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5562           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5563         Back[x][y] = element;
5564
5565       /* ignite explodable elements reached by other explosion */
5566       if (element == EL_EXPLOSION)
5567         element = Store2[x][y];
5568
5569       if (AmoebaNr[x][y] &&
5570           (element == EL_AMOEBA_FULL ||
5571            element == EL_BD_AMOEBA ||
5572            element == EL_AMOEBA_GROWING))
5573       {
5574         AmoebaCnt[AmoebaNr[x][y]]--;
5575         AmoebaCnt2[AmoebaNr[x][y]]--;
5576       }
5577
5578       RemoveField(x, y);
5579
5580       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5581       {
5582         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5583
5584         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5585
5586         if (PLAYERINFO(ex, ey)->use_murphy)
5587           Store[x][y] = EL_EMPTY;
5588       }
5589
5590       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5591          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5592       else if (ELEM_IS_PLAYER(center_element))
5593         Store[x][y] = EL_EMPTY;
5594       else if (center_element == EL_YAMYAM)
5595         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5596       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5597         Store[x][y] = element_info[center_element].content.e[xx][yy];
5598 #if 1
5599       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5600          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5601          otherwise) -- FIX THIS !!! */
5602       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5603         Store[x][y] = element_info[element].content.e[1][1];
5604 #else
5605       else if (!CAN_EXPLODE(element))
5606         Store[x][y] = element_info[element].content.e[1][1];
5607 #endif
5608       else
5609         Store[x][y] = EL_EMPTY;
5610
5611       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5612           center_element == EL_AMOEBA_TO_DIAMOND)
5613         Store2[x][y] = element;
5614
5615       Feld[x][y] = EL_EXPLOSION;
5616       GfxElement[x][y] = artwork_element;
5617
5618       ExplodePhase[x][y] = 1;
5619       ExplodeDelay[x][y] = last_phase;
5620
5621       Stop[x][y] = TRUE;
5622     }
5623
5624     if (center_element == EL_YAMYAM)
5625       game.yamyam_content_nr =
5626         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5627
5628     return;
5629   }
5630
5631   if (Stop[ex][ey])
5632     return;
5633
5634   x = ex;
5635   y = ey;
5636
5637   if (phase == 1)
5638     GfxFrame[x][y] = 0;         /* restart explosion animation */
5639
5640   last_phase = ExplodeDelay[x][y];
5641
5642   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5643
5644 #ifdef DEBUG
5645
5646   /* activate this even in non-DEBUG version until cause for crash in
5647      getGraphicAnimationFrame() (see below) is found and eliminated */
5648
5649 #endif
5650 #if 1
5651
5652 #if 1
5653   /* this can happen if the player leaves an explosion just in time */
5654   if (GfxElement[x][y] == EL_UNDEFINED)
5655     GfxElement[x][y] = EL_EMPTY;
5656 #else
5657   if (GfxElement[x][y] == EL_UNDEFINED)
5658   {
5659     printf("\n\n");
5660     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5661     printf("Explode(): This should never happen!\n");
5662     printf("\n\n");
5663
5664     GfxElement[x][y] = EL_EMPTY;
5665   }
5666 #endif
5667
5668 #endif
5669
5670   border_element = Store2[x][y];
5671   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5672     border_element = StorePlayer[x][y];
5673
5674   if (phase == element_info[border_element].ignition_delay ||
5675       phase == last_phase)
5676   {
5677     boolean border_explosion = FALSE;
5678
5679     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5680         !PLAYER_EXPLOSION_PROTECTED(x, y))
5681     {
5682       KillPlayerUnlessExplosionProtected(x, y);
5683       border_explosion = TRUE;
5684     }
5685     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5686     {
5687       Feld[x][y] = Store2[x][y];
5688       Store2[x][y] = 0;
5689       Bang(x, y);
5690       border_explosion = TRUE;
5691     }
5692     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5693     {
5694       AmoebeUmwandeln(x, y);
5695       Store2[x][y] = 0;
5696       border_explosion = TRUE;
5697     }
5698
5699     /* if an element just explodes due to another explosion (chain-reaction),
5700        do not immediately end the new explosion when it was the last frame of
5701        the explosion (as it would be done in the following "if"-statement!) */
5702     if (border_explosion && phase == last_phase)
5703       return;
5704   }
5705
5706   if (phase == last_phase)
5707   {
5708     int element;
5709
5710     element = Feld[x][y] = Store[x][y];
5711     Store[x][y] = Store2[x][y] = 0;
5712     GfxElement[x][y] = EL_UNDEFINED;
5713
5714     /* player can escape from explosions and might therefore be still alive */
5715     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5716         element <= EL_PLAYER_IS_EXPLODING_4)
5717     {
5718       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5719       int explosion_element = EL_PLAYER_1 + player_nr;
5720       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5721       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5722
5723       if (level.use_explosion_element[player_nr])
5724         explosion_element = level.explosion_element[player_nr];
5725
5726       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5727                     element_info[explosion_element].content.e[xx][yy]);
5728     }
5729
5730     /* restore probably existing indestructible background element */
5731     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5732       element = Feld[x][y] = Back[x][y];
5733     Back[x][y] = 0;
5734
5735     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5736     GfxDir[x][y] = MV_NONE;
5737     ChangeDelay[x][y] = 0;
5738     ChangePage[x][y] = -1;
5739
5740 #if USE_NEW_CUSTOM_VALUE
5741     CustomValue[x][y] = 0;
5742 #endif
5743
5744     InitField_WithBug2(x, y, FALSE);
5745
5746     DrawLevelField(x, y);
5747
5748     TestIfElementTouchesCustomElement(x, y);
5749
5750     if (GFX_CRUMBLED(element))
5751       DrawLevelFieldCrumbledSandNeighbours(x, y);
5752
5753     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5754       StorePlayer[x][y] = 0;
5755
5756     if (ELEM_IS_PLAYER(element))
5757       RelocatePlayer(x, y, element);
5758   }
5759   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5760   {
5761     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5762     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5763
5764     if (phase == delay)
5765       DrawLevelFieldCrumbledSand(x, y);
5766
5767     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5768     {
5769       DrawLevelElement(x, y, Back[x][y]);
5770       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5771     }
5772     else if (IS_WALKABLE_UNDER(Back[x][y]))
5773     {
5774       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5775       DrawLevelElementThruMask(x, y, Back[x][y]);
5776     }
5777     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5778       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5779   }
5780 }
5781
5782 void DynaExplode(int ex, int ey)
5783 {
5784   int i, j;
5785   int dynabomb_element = Feld[ex][ey];
5786   int dynabomb_size = 1;
5787   boolean dynabomb_xl = FALSE;
5788   struct PlayerInfo *player;
5789   static int xy[4][2] =
5790   {
5791     { 0, -1 },
5792     { -1, 0 },
5793     { +1, 0 },
5794     { 0, +1 }
5795   };
5796
5797   if (IS_ACTIVE_BOMB(dynabomb_element))
5798   {
5799     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5800     dynabomb_size = player->dynabomb_size;
5801     dynabomb_xl = player->dynabomb_xl;
5802     player->dynabombs_left++;
5803   }
5804
5805   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5806
5807   for (i = 0; i < NUM_DIRECTIONS; i++)
5808   {
5809     for (j = 1; j <= dynabomb_size; j++)
5810     {
5811       int x = ex + j * xy[i][0];
5812       int y = ey + j * xy[i][1];
5813       int element;
5814
5815       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5816         break;
5817
5818       element = Feld[x][y];
5819
5820       /* do not restart explosions of fields with active bombs */
5821       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5822         continue;
5823
5824       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5825
5826       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5827           !IS_DIGGABLE(element) && !dynabomb_xl)
5828         break;
5829     }
5830   }
5831 }
5832
5833 void Bang(int x, int y)
5834 {
5835   int element = MovingOrBlocked2Element(x, y);
5836   int explosion_type = EX_TYPE_NORMAL;
5837
5838   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5839   {
5840     struct PlayerInfo *player = PLAYERINFO(x, y);
5841
5842     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5843                             player->element_nr);
5844
5845     if (level.use_explosion_element[player->index_nr])
5846     {
5847       int explosion_element = level.explosion_element[player->index_nr];
5848
5849       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5850         explosion_type = EX_TYPE_CROSS;
5851       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5852         explosion_type = EX_TYPE_CENTER;
5853     }
5854   }
5855
5856   switch (element)
5857   {
5858     case EL_BUG:
5859     case EL_SPACESHIP:
5860     case EL_BD_BUTTERFLY:
5861     case EL_BD_FIREFLY:
5862     case EL_YAMYAM:
5863     case EL_DARK_YAMYAM:
5864     case EL_ROBOT:
5865     case EL_PACMAN:
5866     case EL_MOLE:
5867       RaiseScoreElement(element);
5868       break;
5869
5870     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5871     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5872     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5873     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5874     case EL_DYNABOMB_INCREASE_NUMBER:
5875     case EL_DYNABOMB_INCREASE_SIZE:
5876     case EL_DYNABOMB_INCREASE_POWER:
5877       explosion_type = EX_TYPE_DYNA;
5878       break;
5879
5880     case EL_DC_LANDMINE:
5881 #if 0
5882     case EL_EM_EXIT_OPEN:
5883     case EL_EM_STEEL_EXIT_OPEN:
5884 #endif
5885       explosion_type = EX_TYPE_CENTER;
5886       break;
5887
5888     case EL_PENGUIN:
5889     case EL_LAMP:
5890     case EL_LAMP_ACTIVE:
5891     case EL_AMOEBA_TO_DIAMOND:
5892       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5893         explosion_type = EX_TYPE_CENTER;
5894       break;
5895
5896     default:
5897       if (element_info[element].explosion_type == EXPLODES_CROSS)
5898         explosion_type = EX_TYPE_CROSS;
5899       else if (element_info[element].explosion_type == EXPLODES_1X1)
5900         explosion_type = EX_TYPE_CENTER;
5901       break;
5902   }
5903
5904   if (explosion_type == EX_TYPE_DYNA)
5905     DynaExplode(x, y);
5906   else
5907     Explode(x, y, EX_PHASE_START, explosion_type);
5908
5909   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5910 }
5911
5912 void SplashAcid(int x, int y)
5913 {
5914   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5915       (!IN_LEV_FIELD(x - 1, y - 2) ||
5916        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5917     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5918
5919   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5920       (!IN_LEV_FIELD(x + 1, y - 2) ||
5921        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5922     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5923
5924   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5925 }
5926
5927 static void InitBeltMovement()
5928 {
5929   static int belt_base_element[4] =
5930   {
5931     EL_CONVEYOR_BELT_1_LEFT,
5932     EL_CONVEYOR_BELT_2_LEFT,
5933     EL_CONVEYOR_BELT_3_LEFT,
5934     EL_CONVEYOR_BELT_4_LEFT
5935   };
5936   static int belt_base_active_element[4] =
5937   {
5938     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5939     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5940     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5941     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5942   };
5943
5944   int x, y, i, j;
5945
5946   /* set frame order for belt animation graphic according to belt direction */
5947   for (i = 0; i < NUM_BELTS; i++)
5948   {
5949     int belt_nr = i;
5950
5951     for (j = 0; j < NUM_BELT_PARTS; j++)
5952     {
5953       int element = belt_base_active_element[belt_nr] + j;
5954       int graphic_1 = el2img(element);
5955       int graphic_2 = el2panelimg(element);
5956
5957       if (game.belt_dir[i] == MV_LEFT)
5958       {
5959         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5960         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5961       }
5962       else
5963       {
5964         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5965         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5966       }
5967     }
5968   }
5969
5970   SCAN_PLAYFIELD(x, y)
5971   {
5972     int element = Feld[x][y];
5973
5974     for (i = 0; i < NUM_BELTS; i++)
5975     {
5976       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5977       {
5978         int e_belt_nr = getBeltNrFromBeltElement(element);
5979         int belt_nr = i;
5980
5981         if (e_belt_nr == belt_nr)
5982         {
5983           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5984
5985           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5986         }
5987       }
5988     }
5989   }
5990 }
5991
5992 static void ToggleBeltSwitch(int x, int y)
5993 {
5994   static int belt_base_element[4] =
5995   {
5996     EL_CONVEYOR_BELT_1_LEFT,
5997     EL_CONVEYOR_BELT_2_LEFT,
5998     EL_CONVEYOR_BELT_3_LEFT,
5999     EL_CONVEYOR_BELT_4_LEFT
6000   };
6001   static int belt_base_active_element[4] =
6002   {
6003     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6004     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6005     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6006     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6007   };
6008   static int belt_base_switch_element[4] =
6009   {
6010     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6011     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6012     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6013     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6014   };
6015   static int belt_move_dir[4] =
6016   {
6017     MV_LEFT,
6018     MV_NONE,
6019     MV_RIGHT,
6020     MV_NONE,
6021   };
6022
6023   int element = Feld[x][y];
6024   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6025   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6026   int belt_dir = belt_move_dir[belt_dir_nr];
6027   int xx, yy, i;
6028
6029   if (!IS_BELT_SWITCH(element))
6030     return;
6031
6032   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6033   game.belt_dir[belt_nr] = belt_dir;
6034
6035   if (belt_dir_nr == 3)
6036     belt_dir_nr = 1;
6037
6038   /* set frame order for belt animation graphic according to belt direction */
6039   for (i = 0; i < NUM_BELT_PARTS; i++)
6040   {
6041     int element = belt_base_active_element[belt_nr] + i;
6042     int graphic_1 = el2img(element);
6043     int graphic_2 = el2panelimg(element);
6044
6045     if (belt_dir == MV_LEFT)
6046     {
6047       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6048       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6049     }
6050     else
6051     {
6052       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6053       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6054     }
6055   }
6056
6057   SCAN_PLAYFIELD(xx, yy)
6058   {
6059     int element = Feld[xx][yy];
6060
6061     if (IS_BELT_SWITCH(element))
6062     {
6063       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6064
6065       if (e_belt_nr == belt_nr)
6066       {
6067         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6068         DrawLevelField(xx, yy);
6069       }
6070     }
6071     else if (IS_BELT(element) && belt_dir != MV_NONE)
6072     {
6073       int e_belt_nr = getBeltNrFromBeltElement(element);
6074
6075       if (e_belt_nr == belt_nr)
6076       {
6077         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6078
6079         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6080         DrawLevelField(xx, yy);
6081       }
6082     }
6083     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6084     {
6085       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6086
6087       if (e_belt_nr == belt_nr)
6088       {
6089         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6090
6091         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6092         DrawLevelField(xx, yy);
6093       }
6094     }
6095   }
6096 }
6097
6098 static void ToggleSwitchgateSwitch(int x, int y)
6099 {
6100   int xx, yy;
6101
6102   game.switchgate_pos = !game.switchgate_pos;
6103
6104   SCAN_PLAYFIELD(xx, yy)
6105   {
6106     int element = Feld[xx][yy];
6107
6108 #if !USE_BOTH_SWITCHGATE_SWITCHES
6109     if (element == EL_SWITCHGATE_SWITCH_UP ||
6110         element == EL_SWITCHGATE_SWITCH_DOWN)
6111     {
6112       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6113       DrawLevelField(xx, yy);
6114     }
6115     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6116              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6117     {
6118       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6119       DrawLevelField(xx, yy);
6120     }
6121 #else
6122     if (element == EL_SWITCHGATE_SWITCH_UP)
6123     {
6124       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6125       DrawLevelField(xx, yy);
6126     }
6127     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6128     {
6129       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6130       DrawLevelField(xx, yy);
6131     }
6132     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6133     {
6134       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6135       DrawLevelField(xx, yy);
6136     }
6137     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6138     {
6139       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6140       DrawLevelField(xx, yy);
6141     }
6142 #endif
6143     else if (element == EL_SWITCHGATE_OPEN ||
6144              element == EL_SWITCHGATE_OPENING)
6145     {
6146       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6147
6148       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6149     }
6150     else if (element == EL_SWITCHGATE_CLOSED ||
6151              element == EL_SWITCHGATE_CLOSING)
6152     {
6153       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6154
6155       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6156     }
6157   }
6158 }
6159
6160 static int getInvisibleActiveFromInvisibleElement(int element)
6161 {
6162   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6163           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6164           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6165           element);
6166 }
6167
6168 static int getInvisibleFromInvisibleActiveElement(int element)
6169 {
6170   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6171           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6172           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6173           element);
6174 }
6175
6176 static void RedrawAllLightSwitchesAndInvisibleElements()
6177 {
6178   int x, y;
6179
6180   SCAN_PLAYFIELD(x, y)
6181   {
6182     int element = Feld[x][y];
6183
6184     if (element == EL_LIGHT_SWITCH &&
6185         game.light_time_left > 0)
6186     {
6187       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6188       DrawLevelField(x, y);
6189     }
6190     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6191              game.light_time_left == 0)
6192     {
6193       Feld[x][y] = EL_LIGHT_SWITCH;
6194       DrawLevelField(x, y);
6195     }
6196     else if (element == EL_EMC_DRIPPER &&
6197              game.light_time_left > 0)
6198     {
6199       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6200       DrawLevelField(x, y);
6201     }
6202     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6203              game.light_time_left == 0)
6204     {
6205       Feld[x][y] = EL_EMC_DRIPPER;
6206       DrawLevelField(x, y);
6207     }
6208     else if (element == EL_INVISIBLE_STEELWALL ||
6209              element == EL_INVISIBLE_WALL ||
6210              element == EL_INVISIBLE_SAND)
6211     {
6212       if (game.light_time_left > 0)
6213         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6214
6215       DrawLevelField(x, y);
6216
6217       /* uncrumble neighbour fields, if needed */
6218       if (element == EL_INVISIBLE_SAND)
6219         DrawLevelFieldCrumbledSandNeighbours(x, y);
6220     }
6221     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6222              element == EL_INVISIBLE_WALL_ACTIVE ||
6223              element == EL_INVISIBLE_SAND_ACTIVE)
6224     {
6225       if (game.light_time_left == 0)
6226         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6227
6228       DrawLevelField(x, y);
6229
6230       /* re-crumble neighbour fields, if needed */
6231       if (element == EL_INVISIBLE_SAND)
6232         DrawLevelFieldCrumbledSandNeighbours(x, y);
6233     }
6234   }
6235 }
6236
6237 static void RedrawAllInvisibleElementsForLenses()
6238 {
6239   int x, y;
6240
6241   SCAN_PLAYFIELD(x, y)
6242   {
6243     int element = Feld[x][y];
6244
6245     if (element == EL_EMC_DRIPPER &&
6246         game.lenses_time_left > 0)
6247     {
6248       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6249       DrawLevelField(x, y);
6250     }
6251     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6252              game.lenses_time_left == 0)
6253     {
6254       Feld[x][y] = EL_EMC_DRIPPER;
6255       DrawLevelField(x, y);
6256     }
6257     else if (element == EL_INVISIBLE_STEELWALL ||
6258              element == EL_INVISIBLE_WALL ||
6259              element == EL_INVISIBLE_SAND)
6260     {
6261       if (game.lenses_time_left > 0)
6262         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6263
6264       DrawLevelField(x, y);
6265
6266       /* uncrumble neighbour fields, if needed */
6267       if (element == EL_INVISIBLE_SAND)
6268         DrawLevelFieldCrumbledSandNeighbours(x, y);
6269     }
6270     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6271              element == EL_INVISIBLE_WALL_ACTIVE ||
6272              element == EL_INVISIBLE_SAND_ACTIVE)
6273     {
6274       if (game.lenses_time_left == 0)
6275         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6276
6277       DrawLevelField(x, y);
6278
6279       /* re-crumble neighbour fields, if needed */
6280       if (element == EL_INVISIBLE_SAND)
6281         DrawLevelFieldCrumbledSandNeighbours(x, y);
6282     }
6283   }
6284 }
6285
6286 static void RedrawAllInvisibleElementsForMagnifier()
6287 {
6288   int x, y;
6289
6290   SCAN_PLAYFIELD(x, y)
6291   {
6292     int element = Feld[x][y];
6293
6294     if (element == EL_EMC_FAKE_GRASS &&
6295         game.magnify_time_left > 0)
6296     {
6297       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6298       DrawLevelField(x, y);
6299     }
6300     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6301              game.magnify_time_left == 0)
6302     {
6303       Feld[x][y] = EL_EMC_FAKE_GRASS;
6304       DrawLevelField(x, y);
6305     }
6306     else if (IS_GATE_GRAY(element) &&
6307              game.magnify_time_left > 0)
6308     {
6309       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6310                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6311                     IS_EM_GATE_GRAY(element) ?
6312                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6313                     IS_EMC_GATE_GRAY(element) ?
6314                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6315                     element);
6316       DrawLevelField(x, y);
6317     }
6318     else if (IS_GATE_GRAY_ACTIVE(element) &&
6319              game.magnify_time_left == 0)
6320     {
6321       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6322                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6323                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6324                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6325                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6326                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6327                     element);
6328       DrawLevelField(x, y);
6329     }
6330   }
6331 }
6332
6333 static void ToggleLightSwitch(int x, int y)
6334 {
6335   int element = Feld[x][y];
6336
6337   game.light_time_left =
6338     (element == EL_LIGHT_SWITCH ?
6339      level.time_light * FRAMES_PER_SECOND : 0);
6340
6341   RedrawAllLightSwitchesAndInvisibleElements();
6342 }
6343
6344 static void ActivateTimegateSwitch(int x, int y)
6345 {
6346   int xx, yy;
6347
6348   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6349
6350   SCAN_PLAYFIELD(xx, yy)
6351   {
6352     int element = Feld[xx][yy];
6353
6354     if (element == EL_TIMEGATE_CLOSED ||
6355         element == EL_TIMEGATE_CLOSING)
6356     {
6357       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6358       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6359     }
6360
6361     /*
6362     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6363     {
6364       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6365       DrawLevelField(xx, yy);
6366     }
6367     */
6368
6369   }
6370
6371 #if 1
6372   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6373                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6374 #else
6375   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6376 #endif
6377 }
6378
6379 void Impact(int x, int y)
6380 {
6381   boolean last_line = (y == lev_fieldy - 1);
6382   boolean object_hit = FALSE;
6383   boolean impact = (last_line || object_hit);
6384   int element = Feld[x][y];
6385   int smashed = EL_STEELWALL;
6386
6387   if (!last_line)       /* check if element below was hit */
6388   {
6389     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6390       return;
6391
6392     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6393                                          MovDir[x][y + 1] != MV_DOWN ||
6394                                          MovPos[x][y + 1] <= TILEY / 2));
6395
6396     /* do not smash moving elements that left the smashed field in time */
6397     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6398         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6399       object_hit = FALSE;
6400
6401 #if USE_QUICKSAND_IMPACT_BUGFIX
6402     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6403     {
6404       RemoveMovingField(x, y + 1);
6405       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6406       Feld[x][y + 2] = EL_ROCK;
6407       DrawLevelField(x, y + 2);
6408
6409       object_hit = TRUE;
6410     }
6411
6412     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6413     {
6414       RemoveMovingField(x, y + 1);
6415       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6416       Feld[x][y + 2] = EL_ROCK;
6417       DrawLevelField(x, y + 2);
6418
6419       object_hit = TRUE;
6420     }
6421 #endif
6422
6423     if (object_hit)
6424       smashed = MovingOrBlocked2Element(x, y + 1);
6425
6426     impact = (last_line || object_hit);
6427   }
6428
6429   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6430   {
6431     SplashAcid(x, y + 1);
6432     return;
6433   }
6434
6435   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6436   /* only reset graphic animation if graphic really changes after impact */
6437   if (impact &&
6438       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6439   {
6440     ResetGfxAnimation(x, y);
6441     DrawLevelField(x, y);
6442   }
6443
6444   if (impact && CAN_EXPLODE_IMPACT(element))
6445   {
6446     Bang(x, y);
6447     return;
6448   }
6449   else if (impact && element == EL_PEARL &&
6450            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6451   {
6452     ResetGfxAnimation(x, y);
6453
6454     Feld[x][y] = EL_PEARL_BREAKING;
6455     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6456     return;
6457   }
6458   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6459   {
6460     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6461
6462     return;
6463   }
6464
6465   if (impact && element == EL_AMOEBA_DROP)
6466   {
6467     if (object_hit && IS_PLAYER(x, y + 1))
6468       KillPlayerUnlessEnemyProtected(x, y + 1);
6469     else if (object_hit && smashed == EL_PENGUIN)
6470       Bang(x, y + 1);
6471     else
6472     {
6473       Feld[x][y] = EL_AMOEBA_GROWING;
6474       Store[x][y] = EL_AMOEBA_WET;
6475
6476       ResetRandomAnimationValue(x, y);
6477     }
6478     return;
6479   }
6480
6481   if (object_hit)               /* check which object was hit */
6482   {
6483     if ((CAN_PASS_MAGIC_WALL(element) && 
6484          (smashed == EL_MAGIC_WALL ||
6485           smashed == EL_BD_MAGIC_WALL)) ||
6486         (CAN_PASS_DC_MAGIC_WALL(element) &&
6487          smashed == EL_DC_MAGIC_WALL))
6488     {
6489       int xx, yy;
6490       int activated_magic_wall =
6491         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6492          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6493          EL_DC_MAGIC_WALL_ACTIVE);
6494
6495       /* activate magic wall / mill */
6496       SCAN_PLAYFIELD(xx, yy)
6497       {
6498         if (Feld[xx][yy] == smashed)
6499           Feld[xx][yy] = activated_magic_wall;
6500       }
6501
6502       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6503       game.magic_wall_active = TRUE;
6504
6505       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6506                             SND_MAGIC_WALL_ACTIVATING :
6507                             smashed == EL_BD_MAGIC_WALL ?
6508                             SND_BD_MAGIC_WALL_ACTIVATING :
6509                             SND_DC_MAGIC_WALL_ACTIVATING));
6510     }
6511
6512     if (IS_PLAYER(x, y + 1))
6513     {
6514       if (CAN_SMASH_PLAYER(element))
6515       {
6516         KillPlayerUnlessEnemyProtected(x, y + 1);
6517         return;
6518       }
6519     }
6520     else if (smashed == EL_PENGUIN)
6521     {
6522       if (CAN_SMASH_PLAYER(element))
6523       {
6524         Bang(x, y + 1);
6525         return;
6526       }
6527     }
6528     else if (element == EL_BD_DIAMOND)
6529     {
6530       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6531       {
6532         Bang(x, y + 1);
6533         return;
6534       }
6535     }
6536     else if (((element == EL_SP_INFOTRON ||
6537                element == EL_SP_ZONK) &&
6538               (smashed == EL_SP_SNIKSNAK ||
6539                smashed == EL_SP_ELECTRON ||
6540                smashed == EL_SP_DISK_ORANGE)) ||
6541              (element == EL_SP_INFOTRON &&
6542               smashed == EL_SP_DISK_YELLOW))
6543     {
6544       Bang(x, y + 1);
6545       return;
6546     }
6547     else if (CAN_SMASH_EVERYTHING(element))
6548     {
6549       if (IS_CLASSIC_ENEMY(smashed) ||
6550           CAN_EXPLODE_SMASHED(smashed))
6551       {
6552         Bang(x, y + 1);
6553         return;
6554       }
6555       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6556       {
6557         if (smashed == EL_LAMP ||
6558             smashed == EL_LAMP_ACTIVE)
6559         {
6560           Bang(x, y + 1);
6561           return;
6562         }
6563         else if (smashed == EL_NUT)
6564         {
6565           Feld[x][y + 1] = EL_NUT_BREAKING;
6566           PlayLevelSound(x, y, SND_NUT_BREAKING);
6567           RaiseScoreElement(EL_NUT);
6568           return;
6569         }
6570         else if (smashed == EL_PEARL)
6571         {
6572           ResetGfxAnimation(x, y);
6573
6574           Feld[x][y + 1] = EL_PEARL_BREAKING;
6575           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6576           return;
6577         }
6578         else if (smashed == EL_DIAMOND)
6579         {
6580           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6581           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6582           return;
6583         }
6584         else if (IS_BELT_SWITCH(smashed))
6585         {
6586           ToggleBeltSwitch(x, y + 1);
6587         }
6588         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6589                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6590                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6591                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6592         {
6593           ToggleSwitchgateSwitch(x, y + 1);
6594         }
6595         else if (smashed == EL_LIGHT_SWITCH ||
6596                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6597         {
6598           ToggleLightSwitch(x, y + 1);
6599         }
6600         else
6601         {
6602 #if 0
6603           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6604 #endif
6605
6606           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6607
6608           CheckElementChangeBySide(x, y + 1, smashed, element,
6609                                    CE_SWITCHED, CH_SIDE_TOP);
6610           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6611                                             CH_SIDE_TOP);
6612         }
6613       }
6614       else
6615       {
6616         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6617       }
6618     }
6619   }
6620
6621   /* play sound of magic wall / mill */
6622   if (!last_line &&
6623       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6624        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6625        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6626   {
6627     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6628       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6629     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6630       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6631     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6632       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6633
6634     return;
6635   }
6636
6637   /* play sound of object that hits the ground */
6638   if (last_line || object_hit)
6639     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6640 }
6641
6642 inline static void TurnRoundExt(int x, int y)
6643 {
6644   static struct
6645   {
6646     int dx, dy;
6647   } move_xy[] =
6648   {
6649     {  0,  0 },
6650     { -1,  0 },
6651     { +1,  0 },
6652     {  0,  0 },
6653     {  0, -1 },
6654     {  0,  0 }, { 0, 0 }, { 0, 0 },
6655     {  0, +1 }
6656   };
6657   static struct
6658   {
6659     int left, right, back;
6660   } turn[] =
6661   {
6662     { 0,        0,              0        },
6663     { MV_DOWN,  MV_UP,          MV_RIGHT },
6664     { MV_UP,    MV_DOWN,        MV_LEFT  },
6665     { 0,        0,              0        },
6666     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6667     { 0,        0,              0        },
6668     { 0,        0,              0        },
6669     { 0,        0,              0        },
6670     { MV_RIGHT, MV_LEFT,        MV_UP    }
6671   };
6672
6673   int element = Feld[x][y];
6674   int move_pattern = element_info[element].move_pattern;
6675
6676   int old_move_dir = MovDir[x][y];
6677   int left_dir  = turn[old_move_dir].left;
6678   int right_dir = turn[old_move_dir].right;
6679   int back_dir  = turn[old_move_dir].back;
6680
6681   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6682   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6683   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6684   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6685
6686   int left_x  = x + left_dx,  left_y  = y + left_dy;
6687   int right_x = x + right_dx, right_y = y + right_dy;
6688   int move_x  = x + move_dx,  move_y  = y + move_dy;
6689
6690   int xx, yy;
6691
6692   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6693   {
6694     TestIfBadThingTouchesOtherBadThing(x, y);
6695
6696     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6697       MovDir[x][y] = right_dir;
6698     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6699       MovDir[x][y] = left_dir;
6700
6701     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6702       MovDelay[x][y] = 9;
6703     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6704       MovDelay[x][y] = 1;
6705   }
6706   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6707   {
6708     TestIfBadThingTouchesOtherBadThing(x, y);
6709
6710     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6711       MovDir[x][y] = left_dir;
6712     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6713       MovDir[x][y] = right_dir;
6714
6715     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6716       MovDelay[x][y] = 9;
6717     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6718       MovDelay[x][y] = 1;
6719   }
6720   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6721   {
6722     TestIfBadThingTouchesOtherBadThing(x, y);
6723
6724     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6725       MovDir[x][y] = left_dir;
6726     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6727       MovDir[x][y] = right_dir;
6728
6729     if (MovDir[x][y] != old_move_dir)
6730       MovDelay[x][y] = 9;
6731   }
6732   else if (element == EL_YAMYAM)
6733   {
6734     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6735     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6736
6737     if (can_turn_left && can_turn_right)
6738       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6739     else if (can_turn_left)
6740       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6741     else if (can_turn_right)
6742       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6743     else
6744       MovDir[x][y] = back_dir;
6745
6746     MovDelay[x][y] = 16 + 16 * RND(3);
6747   }
6748   else if (element == EL_DARK_YAMYAM)
6749   {
6750     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6751                                                          left_x, left_y);
6752     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6753                                                          right_x, right_y);
6754
6755     if (can_turn_left && can_turn_right)
6756       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6757     else if (can_turn_left)
6758       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6759     else if (can_turn_right)
6760       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6761     else
6762       MovDir[x][y] = back_dir;
6763
6764     MovDelay[x][y] = 16 + 16 * RND(3);
6765   }
6766   else if (element == EL_PACMAN)
6767   {
6768     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6769     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6770
6771     if (can_turn_left && can_turn_right)
6772       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6773     else if (can_turn_left)
6774       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6775     else if (can_turn_right)
6776       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6777     else
6778       MovDir[x][y] = back_dir;
6779
6780     MovDelay[x][y] = 6 + RND(40);
6781   }
6782   else if (element == EL_PIG)
6783   {
6784     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6785     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6786     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6787     boolean should_turn_left, should_turn_right, should_move_on;
6788     int rnd_value = 24;
6789     int rnd = RND(rnd_value);
6790
6791     should_turn_left = (can_turn_left &&
6792                         (!can_move_on ||
6793                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6794                                                    y + back_dy + left_dy)));
6795     should_turn_right = (can_turn_right &&
6796                          (!can_move_on ||
6797                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6798                                                     y + back_dy + right_dy)));
6799     should_move_on = (can_move_on &&
6800                       (!can_turn_left ||
6801                        !can_turn_right ||
6802                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6803                                                  y + move_dy + left_dy) ||
6804                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6805                                                  y + move_dy + right_dy)));
6806
6807     if (should_turn_left || should_turn_right || should_move_on)
6808     {
6809       if (should_turn_left && should_turn_right && should_move_on)
6810         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6811                         rnd < 2 * rnd_value / 3 ? right_dir :
6812                         old_move_dir);
6813       else if (should_turn_left && should_turn_right)
6814         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6815       else if (should_turn_left && should_move_on)
6816         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6817       else if (should_turn_right && should_move_on)
6818         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6819       else if (should_turn_left)
6820         MovDir[x][y] = left_dir;
6821       else if (should_turn_right)
6822         MovDir[x][y] = right_dir;
6823       else if (should_move_on)
6824         MovDir[x][y] = old_move_dir;
6825     }
6826     else if (can_move_on && rnd > rnd_value / 8)
6827       MovDir[x][y] = old_move_dir;
6828     else if (can_turn_left && can_turn_right)
6829       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6830     else if (can_turn_left && rnd > rnd_value / 8)
6831       MovDir[x][y] = left_dir;
6832     else if (can_turn_right && rnd > rnd_value/8)
6833       MovDir[x][y] = right_dir;
6834     else
6835       MovDir[x][y] = back_dir;
6836
6837     xx = x + move_xy[MovDir[x][y]].dx;
6838     yy = y + move_xy[MovDir[x][y]].dy;
6839
6840     if (!IN_LEV_FIELD(xx, yy) ||
6841         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6842       MovDir[x][y] = old_move_dir;
6843
6844     MovDelay[x][y] = 0;
6845   }
6846   else if (element == EL_DRAGON)
6847   {
6848     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6849     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6850     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6851     int rnd_value = 24;
6852     int rnd = RND(rnd_value);
6853
6854     if (can_move_on && rnd > rnd_value / 8)
6855       MovDir[x][y] = old_move_dir;
6856     else if (can_turn_left && can_turn_right)
6857       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6858     else if (can_turn_left && rnd > rnd_value / 8)
6859       MovDir[x][y] = left_dir;
6860     else if (can_turn_right && rnd > rnd_value / 8)
6861       MovDir[x][y] = right_dir;
6862     else
6863       MovDir[x][y] = back_dir;
6864
6865     xx = x + move_xy[MovDir[x][y]].dx;
6866     yy = y + move_xy[MovDir[x][y]].dy;
6867
6868     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6869       MovDir[x][y] = old_move_dir;
6870
6871     MovDelay[x][y] = 0;
6872   }
6873   else if (element == EL_MOLE)
6874   {
6875     boolean can_move_on =
6876       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6877                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6878                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6879     if (!can_move_on)
6880     {
6881       boolean can_turn_left =
6882         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6883                               IS_AMOEBOID(Feld[left_x][left_y])));
6884
6885       boolean can_turn_right =
6886         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6887                               IS_AMOEBOID(Feld[right_x][right_y])));
6888
6889       if (can_turn_left && can_turn_right)
6890         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6891       else if (can_turn_left)
6892         MovDir[x][y] = left_dir;
6893       else
6894         MovDir[x][y] = right_dir;
6895     }
6896
6897     if (MovDir[x][y] != old_move_dir)
6898       MovDelay[x][y] = 9;
6899   }
6900   else if (element == EL_BALLOON)
6901   {
6902     MovDir[x][y] = game.wind_direction;
6903     MovDelay[x][y] = 0;
6904   }
6905   else if (element == EL_SPRING)
6906   {
6907 #if USE_NEW_SPRING_BUMPER
6908     if (MovDir[x][y] & MV_HORIZONTAL)
6909     {
6910       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6911           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6912       {
6913         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6914         ResetGfxAnimation(move_x, move_y);
6915         DrawLevelField(move_x, move_y);
6916
6917         MovDir[x][y] = back_dir;
6918       }
6919       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6920                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6921         MovDir[x][y] = MV_NONE;
6922     }
6923 #else
6924     if (MovDir[x][y] & MV_HORIZONTAL &&
6925         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6926          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6927       MovDir[x][y] = MV_NONE;
6928 #endif
6929
6930     MovDelay[x][y] = 0;
6931   }
6932   else if (element == EL_ROBOT ||
6933            element == EL_SATELLITE ||
6934            element == EL_PENGUIN ||
6935            element == EL_EMC_ANDROID)
6936   {
6937     int attr_x = -1, attr_y = -1;
6938
6939     if (AllPlayersGone)
6940     {
6941       attr_x = ExitX;
6942       attr_y = ExitY;
6943     }
6944     else
6945     {
6946       int i;
6947
6948       for (i = 0; i < MAX_PLAYERS; i++)
6949       {
6950         struct PlayerInfo *player = &stored_player[i];
6951         int jx = player->jx, jy = player->jy;
6952
6953         if (!player->active)
6954           continue;
6955
6956         if (attr_x == -1 ||
6957             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6958         {
6959           attr_x = jx;
6960           attr_y = jy;
6961         }
6962       }
6963     }
6964
6965     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6966         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6967          game.engine_version < VERSION_IDENT(3,1,0,0)))
6968     {
6969       attr_x = ZX;
6970       attr_y = ZY;
6971     }
6972
6973     if (element == EL_PENGUIN)
6974     {
6975       int i;
6976       static int xy[4][2] =
6977       {
6978         { 0, -1 },
6979         { -1, 0 },
6980         { +1, 0 },
6981         { 0, +1 }
6982       };
6983
6984       for (i = 0; i < NUM_DIRECTIONS; i++)
6985       {
6986         int ex = x + xy[i][0];
6987         int ey = y + xy[i][1];
6988
6989         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6990                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6991                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6992                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6993         {
6994           attr_x = ex;
6995           attr_y = ey;
6996           break;
6997         }
6998       }
6999     }
7000
7001     MovDir[x][y] = MV_NONE;
7002     if (attr_x < x)
7003       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7004     else if (attr_x > x)
7005       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7006     if (attr_y < y)
7007       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7008     else if (attr_y > y)
7009       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7010
7011     if (element == EL_ROBOT)
7012     {
7013       int newx, newy;
7014
7015       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7016         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7017       Moving2Blocked(x, y, &newx, &newy);
7018
7019       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7020         MovDelay[x][y] = 8 + 8 * !RND(3);
7021       else
7022         MovDelay[x][y] = 16;
7023     }
7024     else if (element == EL_PENGUIN)
7025     {
7026       int newx, newy;
7027
7028       MovDelay[x][y] = 1;
7029
7030       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7031       {
7032         boolean first_horiz = RND(2);
7033         int new_move_dir = MovDir[x][y];
7034
7035         MovDir[x][y] =
7036           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7037         Moving2Blocked(x, y, &newx, &newy);
7038
7039         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7040           return;
7041
7042         MovDir[x][y] =
7043           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7044         Moving2Blocked(x, y, &newx, &newy);
7045
7046         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7047           return;
7048
7049         MovDir[x][y] = old_move_dir;
7050         return;
7051       }
7052     }
7053     else if (element == EL_SATELLITE)
7054     {
7055       int newx, newy;
7056
7057       MovDelay[x][y] = 1;
7058
7059       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7060       {
7061         boolean first_horiz = RND(2);
7062         int new_move_dir = MovDir[x][y];
7063
7064         MovDir[x][y] =
7065           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7066         Moving2Blocked(x, y, &newx, &newy);
7067
7068         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7069           return;
7070
7071         MovDir[x][y] =
7072           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7073         Moving2Blocked(x, y, &newx, &newy);
7074
7075         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7076           return;
7077
7078         MovDir[x][y] = old_move_dir;
7079         return;
7080       }
7081     }
7082     else if (element == EL_EMC_ANDROID)
7083     {
7084       static int check_pos[16] =
7085       {
7086         -1,             /*  0 => (invalid)          */
7087         7,              /*  1 => MV_LEFT            */
7088         3,              /*  2 => MV_RIGHT           */
7089         -1,             /*  3 => (invalid)          */
7090         1,              /*  4 =>            MV_UP   */
7091         0,              /*  5 => MV_LEFT  | MV_UP   */
7092         2,              /*  6 => MV_RIGHT | MV_UP   */
7093         -1,             /*  7 => (invalid)          */
7094         5,              /*  8 =>            MV_DOWN */
7095         6,              /*  9 => MV_LEFT  | MV_DOWN */
7096         4,              /* 10 => MV_RIGHT | MV_DOWN */
7097         -1,             /* 11 => (invalid)          */
7098         -1,             /* 12 => (invalid)          */
7099         -1,             /* 13 => (invalid)          */
7100         -1,             /* 14 => (invalid)          */
7101         -1,             /* 15 => (invalid)          */
7102       };
7103       static struct
7104       {
7105         int dx, dy;
7106         int dir;
7107       } check_xy[8] =
7108       {
7109         { -1, -1,       MV_LEFT  | MV_UP   },
7110         {  0, -1,                  MV_UP   },
7111         { +1, -1,       MV_RIGHT | MV_UP   },
7112         { +1,  0,       MV_RIGHT           },
7113         { +1, +1,       MV_RIGHT | MV_DOWN },
7114         {  0, +1,                  MV_DOWN },
7115         { -1, +1,       MV_LEFT  | MV_DOWN },
7116         { -1,  0,       MV_LEFT            },
7117       };
7118       int start_pos, check_order;
7119       boolean can_clone = FALSE;
7120       int i;
7121
7122       /* check if there is any free field around current position */
7123       for (i = 0; i < 8; i++)
7124       {
7125         int newx = x + check_xy[i].dx;
7126         int newy = y + check_xy[i].dy;
7127
7128         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7129         {
7130           can_clone = TRUE;
7131
7132           break;
7133         }
7134       }
7135
7136       if (can_clone)            /* randomly find an element to clone */
7137       {
7138         can_clone = FALSE;
7139
7140         start_pos = check_pos[RND(8)];
7141         check_order = (RND(2) ? -1 : +1);
7142
7143         for (i = 0; i < 8; i++)
7144         {
7145           int pos_raw = start_pos + i * check_order;
7146           int pos = (pos_raw + 8) % 8;
7147           int newx = x + check_xy[pos].dx;
7148           int newy = y + check_xy[pos].dy;
7149
7150           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7151           {
7152             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7153             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7154
7155             Store[x][y] = Feld[newx][newy];
7156
7157             can_clone = TRUE;
7158
7159             break;
7160           }
7161         }
7162       }
7163
7164       if (can_clone)            /* randomly find a direction to move */
7165       {
7166         can_clone = FALSE;
7167
7168         start_pos = check_pos[RND(8)];
7169         check_order = (RND(2) ? -1 : +1);
7170
7171         for (i = 0; i < 8; i++)
7172         {
7173           int pos_raw = start_pos + i * check_order;
7174           int pos = (pos_raw + 8) % 8;
7175           int newx = x + check_xy[pos].dx;
7176           int newy = y + check_xy[pos].dy;
7177           int new_move_dir = check_xy[pos].dir;
7178
7179           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7180           {
7181             MovDir[x][y] = new_move_dir;
7182             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7183
7184             can_clone = TRUE;
7185
7186             break;
7187           }
7188         }
7189       }
7190
7191       if (can_clone)            /* cloning and moving successful */
7192         return;
7193
7194       /* cannot clone -- try to move towards player */
7195
7196       start_pos = check_pos[MovDir[x][y] & 0x0f];
7197       check_order = (RND(2) ? -1 : +1);
7198
7199       for (i = 0; i < 3; i++)
7200       {
7201         /* first check start_pos, then previous/next or (next/previous) pos */
7202         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7203         int pos = (pos_raw + 8) % 8;
7204         int newx = x + check_xy[pos].dx;
7205         int newy = y + check_xy[pos].dy;
7206         int new_move_dir = check_xy[pos].dir;
7207
7208         if (IS_PLAYER(newx, newy))
7209           break;
7210
7211         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7212         {
7213           MovDir[x][y] = new_move_dir;
7214           MovDelay[x][y] = level.android_move_time * 8 + 1;
7215
7216           break;
7217         }
7218       }
7219     }
7220   }
7221   else if (move_pattern == MV_TURNING_LEFT ||
7222            move_pattern == MV_TURNING_RIGHT ||
7223            move_pattern == MV_TURNING_LEFT_RIGHT ||
7224            move_pattern == MV_TURNING_RIGHT_LEFT ||
7225            move_pattern == MV_TURNING_RANDOM ||
7226            move_pattern == MV_ALL_DIRECTIONS)
7227   {
7228     boolean can_turn_left =
7229       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7230     boolean can_turn_right =
7231       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7232
7233     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7234       return;
7235
7236     if (move_pattern == MV_TURNING_LEFT)
7237       MovDir[x][y] = left_dir;
7238     else if (move_pattern == MV_TURNING_RIGHT)
7239       MovDir[x][y] = right_dir;
7240     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7241       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7242     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7243       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7244     else if (move_pattern == MV_TURNING_RANDOM)
7245       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7246                       can_turn_right && !can_turn_left ? right_dir :
7247                       RND(2) ? left_dir : right_dir);
7248     else if (can_turn_left && can_turn_right)
7249       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7250     else if (can_turn_left)
7251       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7252     else if (can_turn_right)
7253       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7254     else
7255       MovDir[x][y] = back_dir;
7256
7257     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7258   }
7259   else if (move_pattern == MV_HORIZONTAL ||
7260            move_pattern == MV_VERTICAL)
7261   {
7262     if (move_pattern & old_move_dir)
7263       MovDir[x][y] = back_dir;
7264     else if (move_pattern == MV_HORIZONTAL)
7265       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7266     else if (move_pattern == MV_VERTICAL)
7267       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7268
7269     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7270   }
7271   else if (move_pattern & MV_ANY_DIRECTION)
7272   {
7273     MovDir[x][y] = move_pattern;
7274     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7275   }
7276   else if (move_pattern & MV_WIND_DIRECTION)
7277   {
7278     MovDir[x][y] = game.wind_direction;
7279     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7280   }
7281   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7282   {
7283     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7284       MovDir[x][y] = left_dir;
7285     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7286       MovDir[x][y] = right_dir;
7287
7288     if (MovDir[x][y] != old_move_dir)
7289       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7290   }
7291   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7292   {
7293     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7294       MovDir[x][y] = right_dir;
7295     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7296       MovDir[x][y] = left_dir;
7297
7298     if (MovDir[x][y] != old_move_dir)
7299       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7300   }
7301   else if (move_pattern == MV_TOWARDS_PLAYER ||
7302            move_pattern == MV_AWAY_FROM_PLAYER)
7303   {
7304     int attr_x = -1, attr_y = -1;
7305     int newx, newy;
7306     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7307
7308     if (AllPlayersGone)
7309     {
7310       attr_x = ExitX;
7311       attr_y = ExitY;
7312     }
7313     else
7314     {
7315       int i;
7316
7317       for (i = 0; i < MAX_PLAYERS; i++)
7318       {
7319         struct PlayerInfo *player = &stored_player[i];
7320         int jx = player->jx, jy = player->jy;
7321
7322         if (!player->active)
7323           continue;
7324
7325         if (attr_x == -1 ||
7326             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7327         {
7328           attr_x = jx;
7329           attr_y = jy;
7330         }
7331       }
7332     }
7333
7334     MovDir[x][y] = MV_NONE;
7335     if (attr_x < x)
7336       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7337     else if (attr_x > x)
7338       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7339     if (attr_y < y)
7340       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7341     else if (attr_y > y)
7342       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7343
7344     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7345
7346     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7347     {
7348       boolean first_horiz = RND(2);
7349       int new_move_dir = MovDir[x][y];
7350
7351       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7352       {
7353         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7354         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7355
7356         return;
7357       }
7358
7359       MovDir[x][y] =
7360         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7361       Moving2Blocked(x, y, &newx, &newy);
7362
7363       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7364         return;
7365
7366       MovDir[x][y] =
7367         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7368       Moving2Blocked(x, y, &newx, &newy);
7369
7370       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7371         return;
7372
7373       MovDir[x][y] = old_move_dir;
7374     }
7375   }
7376   else if (move_pattern == MV_WHEN_PUSHED ||
7377            move_pattern == MV_WHEN_DROPPED)
7378   {
7379     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7380       MovDir[x][y] = MV_NONE;
7381
7382     MovDelay[x][y] = 0;
7383   }
7384   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7385   {
7386     static int test_xy[7][2] =
7387     {
7388       { 0, -1 },
7389       { -1, 0 },
7390       { +1, 0 },
7391       { 0, +1 },
7392       { 0, -1 },
7393       { -1, 0 },
7394       { +1, 0 },
7395     };
7396     static int test_dir[7] =
7397     {
7398       MV_UP,
7399       MV_LEFT,
7400       MV_RIGHT,
7401       MV_DOWN,
7402       MV_UP,
7403       MV_LEFT,
7404       MV_RIGHT,
7405     };
7406     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7407     int move_preference = -1000000;     /* start with very low preference */
7408     int new_move_dir = MV_NONE;
7409     int start_test = RND(4);
7410     int i;
7411
7412     for (i = 0; i < NUM_DIRECTIONS; i++)
7413     {
7414       int move_dir = test_dir[start_test + i];
7415       int move_dir_preference;
7416
7417       xx = x + test_xy[start_test + i][0];
7418       yy = y + test_xy[start_test + i][1];
7419
7420       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7421           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7422       {
7423         new_move_dir = move_dir;
7424
7425         break;
7426       }
7427
7428       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7429         continue;
7430
7431       move_dir_preference = -1 * RunnerVisit[xx][yy];
7432       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7433         move_dir_preference = PlayerVisit[xx][yy];
7434
7435       if (move_dir_preference > move_preference)
7436       {
7437         /* prefer field that has not been visited for the longest time */
7438         move_preference = move_dir_preference;
7439         new_move_dir = move_dir;
7440       }
7441       else if (move_dir_preference == move_preference &&
7442                move_dir == old_move_dir)
7443       {
7444         /* prefer last direction when all directions are preferred equally */
7445         move_preference = move_dir_preference;
7446         new_move_dir = move_dir;
7447       }
7448     }
7449
7450     MovDir[x][y] = new_move_dir;
7451     if (old_move_dir != new_move_dir)
7452       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7453   }
7454 }
7455
7456 static void TurnRound(int x, int y)
7457 {
7458   int direction = MovDir[x][y];
7459
7460   TurnRoundExt(x, y);
7461
7462   GfxDir[x][y] = MovDir[x][y];
7463
7464   if (direction != MovDir[x][y])
7465     GfxFrame[x][y] = 0;
7466
7467   if (MovDelay[x][y])
7468     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7469
7470   ResetGfxFrame(x, y, FALSE);
7471 }
7472
7473 static boolean JustBeingPushed(int x, int y)
7474 {
7475   int i;
7476
7477   for (i = 0; i < MAX_PLAYERS; i++)
7478   {
7479     struct PlayerInfo *player = &stored_player[i];
7480
7481     if (player->active && player->is_pushing && player->MovPos)
7482     {
7483       int next_jx = player->jx + (player->jx - player->last_jx);
7484       int next_jy = player->jy + (player->jy - player->last_jy);
7485
7486       if (x == next_jx && y == next_jy)
7487         return TRUE;
7488     }
7489   }
7490
7491   return FALSE;
7492 }
7493
7494 void StartMoving(int x, int y)
7495 {
7496   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7497   int element = Feld[x][y];
7498
7499   if (Stop[x][y])
7500     return;
7501
7502   if (MovDelay[x][y] == 0)
7503     GfxAction[x][y] = ACTION_DEFAULT;
7504
7505   if (CAN_FALL(element) && y < lev_fieldy - 1)
7506   {
7507     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7508         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7509       if (JustBeingPushed(x, y))
7510         return;
7511
7512     if (element == EL_QUICKSAND_FULL)
7513     {
7514       if (IS_FREE(x, y + 1))
7515       {
7516         InitMovingField(x, y, MV_DOWN);
7517         started_moving = TRUE;
7518
7519         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7520 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7521         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7522           Store[x][y] = EL_ROCK;
7523 #else
7524         Store[x][y] = EL_ROCK;
7525 #endif
7526
7527         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7528       }
7529       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7530       {
7531         if (!MovDelay[x][y])
7532           MovDelay[x][y] = TILEY + 1;
7533
7534         if (MovDelay[x][y])
7535         {
7536           MovDelay[x][y]--;
7537           if (MovDelay[x][y])
7538             return;
7539         }
7540
7541         Feld[x][y] = EL_QUICKSAND_EMPTY;
7542         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7543         Store[x][y + 1] = Store[x][y];
7544         Store[x][y] = 0;
7545
7546         PlayLevelSoundAction(x, y, ACTION_FILLING);
7547       }
7548     }
7549     else if (element == EL_QUICKSAND_FAST_FULL)
7550     {
7551       if (IS_FREE(x, y + 1))
7552       {
7553         InitMovingField(x, y, MV_DOWN);
7554         started_moving = TRUE;
7555
7556         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7557 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7558         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7559           Store[x][y] = EL_ROCK;
7560 #else
7561         Store[x][y] = EL_ROCK;
7562 #endif
7563
7564         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7565       }
7566       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7567       {
7568         if (!MovDelay[x][y])
7569           MovDelay[x][y] = TILEY + 1;
7570
7571         if (MovDelay[x][y])
7572         {
7573           MovDelay[x][y]--;
7574           if (MovDelay[x][y])
7575             return;
7576         }
7577
7578         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7579         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7580         Store[x][y + 1] = Store[x][y];
7581         Store[x][y] = 0;
7582
7583         PlayLevelSoundAction(x, y, ACTION_FILLING);
7584       }
7585     }
7586     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7587              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7588     {
7589       InitMovingField(x, y, MV_DOWN);
7590       started_moving = TRUE;
7591
7592       Feld[x][y] = EL_QUICKSAND_FILLING;
7593       Store[x][y] = element;
7594
7595       PlayLevelSoundAction(x, y, ACTION_FILLING);
7596     }
7597     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7598              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7599     {
7600       InitMovingField(x, y, MV_DOWN);
7601       started_moving = TRUE;
7602
7603       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7604       Store[x][y] = element;
7605
7606       PlayLevelSoundAction(x, y, ACTION_FILLING);
7607     }
7608     else if (element == EL_MAGIC_WALL_FULL)
7609     {
7610       if (IS_FREE(x, y + 1))
7611       {
7612         InitMovingField(x, y, MV_DOWN);
7613         started_moving = TRUE;
7614
7615         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7616         Store[x][y] = EL_CHANGED(Store[x][y]);
7617       }
7618       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7619       {
7620         if (!MovDelay[x][y])
7621           MovDelay[x][y] = TILEY/4 + 1;
7622
7623         if (MovDelay[x][y])
7624         {
7625           MovDelay[x][y]--;
7626           if (MovDelay[x][y])
7627             return;
7628         }
7629
7630         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7631         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7632         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7633         Store[x][y] = 0;
7634       }
7635     }
7636     else if (element == EL_BD_MAGIC_WALL_FULL)
7637     {
7638       if (IS_FREE(x, y + 1))
7639       {
7640         InitMovingField(x, y, MV_DOWN);
7641         started_moving = TRUE;
7642
7643         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7644         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7645       }
7646       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7647       {
7648         if (!MovDelay[x][y])
7649           MovDelay[x][y] = TILEY/4 + 1;
7650
7651         if (MovDelay[x][y])
7652         {
7653           MovDelay[x][y]--;
7654           if (MovDelay[x][y])
7655             return;
7656         }
7657
7658         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7659         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7660         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7661         Store[x][y] = 0;
7662       }
7663     }
7664     else if (element == EL_DC_MAGIC_WALL_FULL)
7665     {
7666       if (IS_FREE(x, y + 1))
7667       {
7668         InitMovingField(x, y, MV_DOWN);
7669         started_moving = TRUE;
7670
7671         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7672         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7673       }
7674       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7675       {
7676         if (!MovDelay[x][y])
7677           MovDelay[x][y] = TILEY/4 + 1;
7678
7679         if (MovDelay[x][y])
7680         {
7681           MovDelay[x][y]--;
7682           if (MovDelay[x][y])
7683             return;
7684         }
7685
7686         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7687         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7688         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7689         Store[x][y] = 0;
7690       }
7691     }
7692     else if ((CAN_PASS_MAGIC_WALL(element) &&
7693               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7694                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7695              (CAN_PASS_DC_MAGIC_WALL(element) &&
7696               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7697
7698     {
7699       InitMovingField(x, y, MV_DOWN);
7700       started_moving = TRUE;
7701
7702       Feld[x][y] =
7703         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7704          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7705          EL_DC_MAGIC_WALL_FILLING);
7706       Store[x][y] = element;
7707     }
7708     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7709     {
7710       SplashAcid(x, y + 1);
7711
7712       InitMovingField(x, y, MV_DOWN);
7713       started_moving = TRUE;
7714
7715       Store[x][y] = EL_ACID;
7716     }
7717     else if (
7718 #if USE_FIX_IMPACT_COLLISION
7719              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7720               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7721 #else
7722              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7723               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7724 #endif
7725              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7726               CAN_FALL(element) && WasJustFalling[x][y] &&
7727               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7728
7729              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7730               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7731               (Feld[x][y + 1] == EL_BLOCKED)))
7732     {
7733       /* this is needed for a special case not covered by calling "Impact()"
7734          from "ContinueMoving()": if an element moves to a tile directly below
7735          another element which was just falling on that tile (which was empty
7736          in the previous frame), the falling element above would just stop
7737          instead of smashing the element below (in previous version, the above
7738          element was just checked for "moving" instead of "falling", resulting
7739          in incorrect smashes caused by horizontal movement of the above
7740          element; also, the case of the player being the element to smash was
7741          simply not covered here... :-/ ) */
7742
7743       CheckCollision[x][y] = 0;
7744       CheckImpact[x][y] = 0;
7745
7746       Impact(x, y);
7747     }
7748     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7749     {
7750       if (MovDir[x][y] == MV_NONE)
7751       {
7752         InitMovingField(x, y, MV_DOWN);
7753         started_moving = TRUE;
7754       }
7755     }
7756     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7757     {
7758       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7759         MovDir[x][y] = MV_DOWN;
7760
7761       InitMovingField(x, y, MV_DOWN);
7762       started_moving = TRUE;
7763     }
7764     else if (element == EL_AMOEBA_DROP)
7765     {
7766       Feld[x][y] = EL_AMOEBA_GROWING;
7767       Store[x][y] = EL_AMOEBA_WET;
7768     }
7769     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7770               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7771              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7772              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7773     {
7774       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7775                                 (IS_FREE(x - 1, y + 1) ||
7776                                  Feld[x - 1][y + 1] == EL_ACID));
7777       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7778                                 (IS_FREE(x + 1, y + 1) ||
7779                                  Feld[x + 1][y + 1] == EL_ACID));
7780       boolean can_fall_any  = (can_fall_left || can_fall_right);
7781       boolean can_fall_both = (can_fall_left && can_fall_right);
7782       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7783
7784 #if USE_NEW_ALL_SLIPPERY
7785       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7786       {
7787         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7788           can_fall_right = FALSE;
7789         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7790           can_fall_left = FALSE;
7791         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7792           can_fall_right = FALSE;
7793         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7794           can_fall_left = FALSE;
7795
7796         can_fall_any  = (can_fall_left || can_fall_right);
7797         can_fall_both = FALSE;
7798       }
7799 #else
7800       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7801       {
7802         if (slippery_type == SLIPPERY_ONLY_LEFT)
7803           can_fall_right = FALSE;
7804         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7805           can_fall_left = FALSE;
7806         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7807           can_fall_right = FALSE;
7808         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7809           can_fall_left = FALSE;
7810
7811         can_fall_any  = (can_fall_left || can_fall_right);
7812         can_fall_both = (can_fall_left && can_fall_right);
7813       }
7814 #endif
7815
7816 #if USE_NEW_ALL_SLIPPERY
7817 #else
7818 #if USE_NEW_SP_SLIPPERY
7819       /* !!! better use the same properties as for custom elements here !!! */
7820       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7821                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7822       {
7823         can_fall_right = FALSE;         /* slip down on left side */
7824         can_fall_both = FALSE;
7825       }
7826 #endif
7827 #endif
7828
7829 #if USE_NEW_ALL_SLIPPERY
7830       if (can_fall_both)
7831       {
7832         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7833           can_fall_right = FALSE;       /* slip down on left side */
7834         else
7835           can_fall_left = !(can_fall_right = RND(2));
7836
7837         can_fall_both = FALSE;
7838       }
7839 #else
7840       if (can_fall_both)
7841       {
7842         if (game.emulation == EMU_BOULDERDASH ||
7843             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7844           can_fall_right = FALSE;       /* slip down on left side */
7845         else
7846           can_fall_left = !(can_fall_right = RND(2));
7847
7848         can_fall_both = FALSE;
7849       }
7850 #endif
7851
7852       if (can_fall_any)
7853       {
7854         /* if not determined otherwise, prefer left side for slipping down */
7855         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7856         started_moving = TRUE;
7857       }
7858     }
7859 #if 0
7860     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7861 #else
7862     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7863 #endif
7864     {
7865       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7866       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7867       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7868       int belt_dir = game.belt_dir[belt_nr];
7869
7870       if ((belt_dir == MV_LEFT  && left_is_free) ||
7871           (belt_dir == MV_RIGHT && right_is_free))
7872       {
7873         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7874
7875         InitMovingField(x, y, belt_dir);
7876         started_moving = TRUE;
7877
7878         Pushed[x][y] = TRUE;
7879         Pushed[nextx][y] = TRUE;
7880
7881         GfxAction[x][y] = ACTION_DEFAULT;
7882       }
7883       else
7884       {
7885         MovDir[x][y] = 0;       /* if element was moving, stop it */
7886       }
7887     }
7888   }
7889
7890   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7891 #if 0
7892   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7893 #else
7894   if (CAN_MOVE(element) && !started_moving)
7895 #endif
7896   {
7897     int move_pattern = element_info[element].move_pattern;
7898     int newx, newy;
7899
7900 #if 0
7901 #if DEBUG
7902     if (MovDir[x][y] == MV_NONE)
7903     {
7904       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7905              x, y, element, element_info[element].token_name);
7906       printf("StartMoving(): This should never happen!\n");
7907     }
7908 #endif
7909 #endif
7910
7911     Moving2Blocked(x, y, &newx, &newy);
7912
7913     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7914       return;
7915
7916     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7917         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7918     {
7919       WasJustMoving[x][y] = 0;
7920       CheckCollision[x][y] = 0;
7921
7922       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7923
7924       if (Feld[x][y] != element)        /* element has changed */
7925         return;
7926     }
7927
7928     if (!MovDelay[x][y])        /* start new movement phase */
7929     {
7930       /* all objects that can change their move direction after each step
7931          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7932
7933       if (element != EL_YAMYAM &&
7934           element != EL_DARK_YAMYAM &&
7935           element != EL_PACMAN &&
7936           !(move_pattern & MV_ANY_DIRECTION) &&
7937           move_pattern != MV_TURNING_LEFT &&
7938           move_pattern != MV_TURNING_RIGHT &&
7939           move_pattern != MV_TURNING_LEFT_RIGHT &&
7940           move_pattern != MV_TURNING_RIGHT_LEFT &&
7941           move_pattern != MV_TURNING_RANDOM)
7942       {
7943         TurnRound(x, y);
7944
7945         if (MovDelay[x][y] && (element == EL_BUG ||
7946                                element == EL_SPACESHIP ||
7947                                element == EL_SP_SNIKSNAK ||
7948                                element == EL_SP_ELECTRON ||
7949                                element == EL_MOLE))
7950           DrawLevelField(x, y);
7951       }
7952     }
7953
7954     if (MovDelay[x][y])         /* wait some time before next movement */
7955     {
7956       MovDelay[x][y]--;
7957
7958       if (element == EL_ROBOT ||
7959           element == EL_YAMYAM ||
7960           element == EL_DARK_YAMYAM)
7961       {
7962         DrawLevelElementAnimationIfNeeded(x, y, element);
7963         PlayLevelSoundAction(x, y, ACTION_WAITING);
7964       }
7965       else if (element == EL_SP_ELECTRON)
7966         DrawLevelElementAnimationIfNeeded(x, y, element);
7967       else if (element == EL_DRAGON)
7968       {
7969         int i;
7970         int dir = MovDir[x][y];
7971         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7972         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7973         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7974                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7975                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7976                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7977         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7978
7979         GfxAction[x][y] = ACTION_ATTACKING;
7980
7981         if (IS_PLAYER(x, y))
7982           DrawPlayerField(x, y);
7983         else
7984           DrawLevelField(x, y);
7985
7986         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7987
7988         for (i = 1; i <= 3; i++)
7989         {
7990           int xx = x + i * dx;
7991           int yy = y + i * dy;
7992           int sx = SCREENX(xx);
7993           int sy = SCREENY(yy);
7994           int flame_graphic = graphic + (i - 1);
7995
7996           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7997             break;
7998
7999           if (MovDelay[x][y])
8000           {
8001             int flamed = MovingOrBlocked2Element(xx, yy);
8002
8003             /* !!! */
8004 #if 0
8005             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8006               Bang(xx, yy);
8007             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8008               RemoveMovingField(xx, yy);
8009             else
8010               RemoveField(xx, yy);
8011 #else
8012             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8013               Bang(xx, yy);
8014             else
8015               RemoveMovingField(xx, yy);
8016 #endif
8017
8018             ChangeDelay[xx][yy] = 0;
8019
8020             Feld[xx][yy] = EL_FLAMES;
8021
8022             if (IN_SCR_FIELD(sx, sy))
8023             {
8024               DrawLevelFieldCrumbledSand(xx, yy);
8025               DrawGraphic(sx, sy, flame_graphic, frame);
8026             }
8027           }
8028           else
8029           {
8030             if (Feld[xx][yy] == EL_FLAMES)
8031               Feld[xx][yy] = EL_EMPTY;
8032             DrawLevelField(xx, yy);
8033           }
8034         }
8035       }
8036
8037       if (MovDelay[x][y])       /* element still has to wait some time */
8038       {
8039         PlayLevelSoundAction(x, y, ACTION_WAITING);
8040
8041         return;
8042       }
8043     }
8044
8045     /* now make next step */
8046
8047     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8048
8049     if (DONT_COLLIDE_WITH(element) &&
8050         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8051         !PLAYER_ENEMY_PROTECTED(newx, newy))
8052     {
8053       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8054
8055       return;
8056     }
8057
8058     else if (CAN_MOVE_INTO_ACID(element) &&
8059              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8060              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8061              (MovDir[x][y] == MV_DOWN ||
8062               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8063     {
8064       SplashAcid(newx, newy);
8065       Store[x][y] = EL_ACID;
8066     }
8067     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8068     {
8069       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8070           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8071           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8072           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8073       {
8074         RemoveField(x, y);
8075         DrawLevelField(x, y);
8076
8077         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8078         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8079           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8080
8081         local_player->friends_still_needed--;
8082         if (!local_player->friends_still_needed &&
8083             !local_player->GameOver && AllPlayersGone)
8084           PlayerWins(local_player);
8085
8086         return;
8087       }
8088       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8089       {
8090         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8091           DrawLevelField(newx, newy);
8092         else
8093           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8094       }
8095       else if (!IS_FREE(newx, newy))
8096       {
8097         GfxAction[x][y] = ACTION_WAITING;
8098
8099         if (IS_PLAYER(x, y))
8100           DrawPlayerField(x, y);
8101         else
8102           DrawLevelField(x, y);
8103
8104         return;
8105       }
8106     }
8107     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8108     {
8109       if (IS_FOOD_PIG(Feld[newx][newy]))
8110       {
8111         if (IS_MOVING(newx, newy))
8112           RemoveMovingField(newx, newy);
8113         else
8114         {
8115           Feld[newx][newy] = EL_EMPTY;
8116           DrawLevelField(newx, newy);
8117         }
8118
8119         PlayLevelSound(x, y, SND_PIG_DIGGING);
8120       }
8121       else if (!IS_FREE(newx, newy))
8122       {
8123         if (IS_PLAYER(x, y))
8124           DrawPlayerField(x, y);
8125         else
8126           DrawLevelField(x, y);
8127
8128         return;
8129       }
8130     }
8131     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8132     {
8133       if (Store[x][y] != EL_EMPTY)
8134       {
8135         boolean can_clone = FALSE;
8136         int xx, yy;
8137
8138         /* check if element to clone is still there */
8139         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8140         {
8141           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8142           {
8143             can_clone = TRUE;
8144
8145             break;
8146           }
8147         }
8148
8149         /* cannot clone or target field not free anymore -- do not clone */
8150         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8151           Store[x][y] = EL_EMPTY;
8152       }
8153
8154       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8155       {
8156         if (IS_MV_DIAGONAL(MovDir[x][y]))
8157         {
8158           int diagonal_move_dir = MovDir[x][y];
8159           int stored = Store[x][y];
8160           int change_delay = 8;
8161           int graphic;
8162
8163           /* android is moving diagonally */
8164
8165           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8166
8167           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8168           GfxElement[x][y] = EL_EMC_ANDROID;
8169           GfxAction[x][y] = ACTION_SHRINKING;
8170           GfxDir[x][y] = diagonal_move_dir;
8171           ChangeDelay[x][y] = change_delay;
8172
8173           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8174                                    GfxDir[x][y]);
8175
8176           DrawLevelGraphicAnimation(x, y, graphic);
8177           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8178
8179           if (Feld[newx][newy] == EL_ACID)
8180           {
8181             SplashAcid(newx, newy);
8182
8183             return;
8184           }
8185
8186           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8187
8188           Store[newx][newy] = EL_EMC_ANDROID;
8189           GfxElement[newx][newy] = EL_EMC_ANDROID;
8190           GfxAction[newx][newy] = ACTION_GROWING;
8191           GfxDir[newx][newy] = diagonal_move_dir;
8192           ChangeDelay[newx][newy] = change_delay;
8193
8194           graphic = el_act_dir2img(GfxElement[newx][newy],
8195                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8196
8197           DrawLevelGraphicAnimation(newx, newy, graphic);
8198           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8199
8200           return;
8201         }
8202         else
8203         {
8204           Feld[newx][newy] = EL_EMPTY;
8205           DrawLevelField(newx, newy);
8206
8207           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8208         }
8209       }
8210       else if (!IS_FREE(newx, newy))
8211       {
8212 #if 0
8213         if (IS_PLAYER(x, y))
8214           DrawPlayerField(x, y);
8215         else
8216           DrawLevelField(x, y);
8217 #endif
8218
8219         return;
8220       }
8221     }
8222     else if (IS_CUSTOM_ELEMENT(element) &&
8223              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8224     {
8225       int new_element = Feld[newx][newy];
8226
8227       if (!IS_FREE(newx, newy))
8228       {
8229         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8230                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8231                       ACTION_BREAKING);
8232
8233         /* no element can dig solid indestructible elements */
8234         if (IS_INDESTRUCTIBLE(new_element) &&
8235             !IS_DIGGABLE(new_element) &&
8236             !IS_COLLECTIBLE(new_element))
8237           return;
8238
8239         if (AmoebaNr[newx][newy] &&
8240             (new_element == EL_AMOEBA_FULL ||
8241              new_element == EL_BD_AMOEBA ||
8242              new_element == EL_AMOEBA_GROWING))
8243         {
8244           AmoebaCnt[AmoebaNr[newx][newy]]--;
8245           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8246         }
8247
8248         if (IS_MOVING(newx, newy))
8249           RemoveMovingField(newx, newy);
8250         else
8251         {
8252           RemoveField(newx, newy);
8253           DrawLevelField(newx, newy);
8254         }
8255
8256         /* if digged element was about to explode, prevent the explosion */
8257         ExplodeField[newx][newy] = EX_TYPE_NONE;
8258
8259         PlayLevelSoundAction(x, y, action);
8260       }
8261
8262       Store[newx][newy] = EL_EMPTY;
8263 #if 1
8264       /* this makes it possible to leave the removed element again */
8265       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8266         Store[newx][newy] = new_element;
8267 #else
8268       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8269       {
8270         int move_leave_element = element_info[element].move_leave_element;
8271
8272         /* this makes it possible to leave the removed element again */
8273         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8274                              new_element : move_leave_element);
8275       }
8276 #endif
8277
8278       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8279       {
8280         RunnerVisit[x][y] = FrameCounter;
8281         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8282       }
8283     }
8284     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8285     {
8286       if (!IS_FREE(newx, newy))
8287       {
8288         if (IS_PLAYER(x, y))
8289           DrawPlayerField(x, y);
8290         else
8291           DrawLevelField(x, y);
8292
8293         return;
8294       }
8295       else
8296       {
8297         boolean wanna_flame = !RND(10);
8298         int dx = newx - x, dy = newy - y;
8299         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8300         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8301         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8302                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8303         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8304                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8305
8306         if ((wanna_flame ||
8307              IS_CLASSIC_ENEMY(element1) ||
8308              IS_CLASSIC_ENEMY(element2)) &&
8309             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8310             element1 != EL_FLAMES && element2 != EL_FLAMES)
8311         {
8312           ResetGfxAnimation(x, y);
8313           GfxAction[x][y] = ACTION_ATTACKING;
8314
8315           if (IS_PLAYER(x, y))
8316             DrawPlayerField(x, y);
8317           else
8318             DrawLevelField(x, y);
8319
8320           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8321
8322           MovDelay[x][y] = 50;
8323
8324           /* !!! */
8325 #if 0
8326           RemoveField(newx, newy);
8327 #endif
8328           Feld[newx][newy] = EL_FLAMES;
8329           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8330           {
8331 #if 0
8332             RemoveField(newx1, newy1);
8333 #endif
8334             Feld[newx1][newy1] = EL_FLAMES;
8335           }
8336           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8337           {
8338 #if 0
8339             RemoveField(newx2, newy2);
8340 #endif
8341             Feld[newx2][newy2] = EL_FLAMES;
8342           }
8343
8344           return;
8345         }
8346       }
8347     }
8348     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8349              Feld[newx][newy] == EL_DIAMOND)
8350     {
8351       if (IS_MOVING(newx, newy))
8352         RemoveMovingField(newx, newy);
8353       else
8354       {
8355         Feld[newx][newy] = EL_EMPTY;
8356         DrawLevelField(newx, newy);
8357       }
8358
8359       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8360     }
8361     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8362              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8363     {
8364       if (AmoebaNr[newx][newy])
8365       {
8366         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8367         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8368             Feld[newx][newy] == EL_BD_AMOEBA)
8369           AmoebaCnt[AmoebaNr[newx][newy]]--;
8370       }
8371
8372 #if 0
8373       /* !!! test !!! */
8374       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8375       {
8376         RemoveMovingField(newx, newy);
8377       }
8378 #else
8379       if (IS_MOVING(newx, newy))
8380       {
8381         RemoveMovingField(newx, newy);
8382       }
8383 #endif
8384       else
8385       {
8386         Feld[newx][newy] = EL_EMPTY;
8387         DrawLevelField(newx, newy);
8388       }
8389
8390       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8391     }
8392     else if ((element == EL_PACMAN || element == EL_MOLE)
8393              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8394     {
8395       if (AmoebaNr[newx][newy])
8396       {
8397         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8398         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8399             Feld[newx][newy] == EL_BD_AMOEBA)
8400           AmoebaCnt[AmoebaNr[newx][newy]]--;
8401       }
8402
8403       if (element == EL_MOLE)
8404       {
8405         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8406         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8407
8408         ResetGfxAnimation(x, y);
8409         GfxAction[x][y] = ACTION_DIGGING;
8410         DrawLevelField(x, y);
8411
8412         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8413
8414         return;                         /* wait for shrinking amoeba */
8415       }
8416       else      /* element == EL_PACMAN */
8417       {
8418         Feld[newx][newy] = EL_EMPTY;
8419         DrawLevelField(newx, newy);
8420         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8421       }
8422     }
8423     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8424              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8425               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8426     {
8427       /* wait for shrinking amoeba to completely disappear */
8428       return;
8429     }
8430     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8431     {
8432       /* object was running against a wall */
8433
8434       TurnRound(x, y);
8435
8436 #if 0
8437       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8438       if (move_pattern & MV_ANY_DIRECTION &&
8439           move_pattern == MovDir[x][y])
8440       {
8441         int blocking_element =
8442           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8443
8444         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8445                                  MovDir[x][y]);
8446
8447         element = Feld[x][y];   /* element might have changed */
8448       }
8449 #endif
8450
8451       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8452         DrawLevelElementAnimation(x, y, element);
8453
8454       if (DONT_TOUCH(element))
8455         TestIfBadThingTouchesPlayer(x, y);
8456
8457       return;
8458     }
8459
8460     InitMovingField(x, y, MovDir[x][y]);
8461
8462     PlayLevelSoundAction(x, y, ACTION_MOVING);
8463   }
8464
8465   if (MovDir[x][y])
8466     ContinueMoving(x, y);
8467 }
8468
8469 void ContinueMoving(int x, int y)
8470 {
8471   int element = Feld[x][y];
8472   struct ElementInfo *ei = &element_info[element];
8473   int direction = MovDir[x][y];
8474   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8475   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8476   int newx = x + dx, newy = y + dy;
8477   int stored = Store[x][y];
8478   int stored_new = Store[newx][newy];
8479   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8480   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8481   boolean last_line = (newy == lev_fieldy - 1);
8482
8483   MovPos[x][y] += getElementMoveStepsize(x, y);
8484
8485   if (pushed_by_player) /* special case: moving object pushed by player */
8486     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8487
8488   if (ABS(MovPos[x][y]) < TILEX)
8489   {
8490 #if 0
8491     int ee = Feld[x][y];
8492     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8493     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8494
8495     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8496            x, y, ABS(MovPos[x][y]),
8497            ee, gg, ff,
8498            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8499 #endif
8500
8501     DrawLevelField(x, y);
8502
8503     return;     /* element is still moving */
8504   }
8505
8506   /* element reached destination field */
8507
8508   Feld[x][y] = EL_EMPTY;
8509   Feld[newx][newy] = element;
8510   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8511
8512   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8513   {
8514     element = Feld[newx][newy] = EL_ACID;
8515   }
8516   else if (element == EL_MOLE)
8517   {
8518     Feld[x][y] = EL_SAND;
8519
8520     DrawLevelFieldCrumbledSandNeighbours(x, y);
8521   }
8522   else if (element == EL_QUICKSAND_FILLING)
8523   {
8524     element = Feld[newx][newy] = get_next_element(element);
8525     Store[newx][newy] = Store[x][y];
8526   }
8527   else if (element == EL_QUICKSAND_EMPTYING)
8528   {
8529     Feld[x][y] = get_next_element(element);
8530     element = Feld[newx][newy] = Store[x][y];
8531   }
8532   else if (element == EL_QUICKSAND_FAST_FILLING)
8533   {
8534     element = Feld[newx][newy] = get_next_element(element);
8535     Store[newx][newy] = Store[x][y];
8536   }
8537   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8538   {
8539     Feld[x][y] = get_next_element(element);
8540     element = Feld[newx][newy] = Store[x][y];
8541   }
8542   else if (element == EL_MAGIC_WALL_FILLING)
8543   {
8544     element = Feld[newx][newy] = get_next_element(element);
8545     if (!game.magic_wall_active)
8546       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8547     Store[newx][newy] = Store[x][y];
8548   }
8549   else if (element == EL_MAGIC_WALL_EMPTYING)
8550   {
8551     Feld[x][y] = get_next_element(element);
8552     if (!game.magic_wall_active)
8553       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8554     element = Feld[newx][newy] = Store[x][y];
8555
8556 #if USE_NEW_CUSTOM_VALUE
8557     InitField(newx, newy, FALSE);
8558 #endif
8559   }
8560   else if (element == EL_BD_MAGIC_WALL_FILLING)
8561   {
8562     element = Feld[newx][newy] = get_next_element(element);
8563     if (!game.magic_wall_active)
8564       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8565     Store[newx][newy] = Store[x][y];
8566   }
8567   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8568   {
8569     Feld[x][y] = get_next_element(element);
8570     if (!game.magic_wall_active)
8571       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8572     element = Feld[newx][newy] = Store[x][y];
8573
8574 #if USE_NEW_CUSTOM_VALUE
8575     InitField(newx, newy, FALSE);
8576 #endif
8577   }
8578   else if (element == EL_DC_MAGIC_WALL_FILLING)
8579   {
8580     element = Feld[newx][newy] = get_next_element(element);
8581     if (!game.magic_wall_active)
8582       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8583     Store[newx][newy] = Store[x][y];
8584   }
8585   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8586   {
8587     Feld[x][y] = get_next_element(element);
8588     if (!game.magic_wall_active)
8589       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8590     element = Feld[newx][newy] = Store[x][y];
8591
8592 #if USE_NEW_CUSTOM_VALUE
8593     InitField(newx, newy, FALSE);
8594 #endif
8595   }
8596   else if (element == EL_AMOEBA_DROPPING)
8597   {
8598     Feld[x][y] = get_next_element(element);
8599     element = Feld[newx][newy] = Store[x][y];
8600   }
8601   else if (element == EL_SOKOBAN_OBJECT)
8602   {
8603     if (Back[x][y])
8604       Feld[x][y] = Back[x][y];
8605
8606     if (Back[newx][newy])
8607       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8608
8609     Back[x][y] = Back[newx][newy] = 0;
8610   }
8611
8612   Store[x][y] = EL_EMPTY;
8613   MovPos[x][y] = 0;
8614   MovDir[x][y] = 0;
8615   MovDelay[x][y] = 0;
8616
8617   MovDelay[newx][newy] = 0;
8618
8619   if (CAN_CHANGE_OR_HAS_ACTION(element))
8620   {
8621     /* copy element change control values to new field */
8622     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8623     ChangePage[newx][newy]  = ChangePage[x][y];
8624     ChangeCount[newx][newy] = ChangeCount[x][y];
8625     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8626   }
8627
8628 #if USE_NEW_CUSTOM_VALUE
8629     CustomValue[newx][newy] = CustomValue[x][y];
8630 #endif
8631
8632   ChangeDelay[x][y] = 0;
8633   ChangePage[x][y] = -1;
8634   ChangeCount[x][y] = 0;
8635   ChangeEvent[x][y] = -1;
8636
8637 #if USE_NEW_CUSTOM_VALUE
8638   CustomValue[x][y] = 0;
8639 #endif
8640
8641   /* copy animation control values to new field */
8642   GfxFrame[newx][newy]  = GfxFrame[x][y];
8643   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8644   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8645   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8646
8647   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8648
8649   /* some elements can leave other elements behind after moving */
8650 #if 1
8651   if (ei->move_leave_element != EL_EMPTY &&
8652       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8653       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8654 #else
8655   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8656       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8657       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8658 #endif
8659   {
8660     int move_leave_element = ei->move_leave_element;
8661
8662 #if 1
8663 #if 1
8664     /* this makes it possible to leave the removed element again */
8665     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8666       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8667 #else
8668     /* this makes it possible to leave the removed element again */
8669     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8670       move_leave_element = stored;
8671 #endif
8672 #else
8673     /* this makes it possible to leave the removed element again */
8674     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8675         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8676       move_leave_element = stored;
8677 #endif
8678
8679     Feld[x][y] = move_leave_element;
8680
8681     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8682       MovDir[x][y] = direction;
8683
8684     InitField(x, y, FALSE);
8685
8686     if (GFX_CRUMBLED(Feld[x][y]))
8687       DrawLevelFieldCrumbledSandNeighbours(x, y);
8688
8689     if (ELEM_IS_PLAYER(move_leave_element))
8690       RelocatePlayer(x, y, move_leave_element);
8691   }
8692
8693   /* do this after checking for left-behind element */
8694   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8695
8696   if (!CAN_MOVE(element) ||
8697       (CAN_FALL(element) && direction == MV_DOWN &&
8698        (element == EL_SPRING ||
8699         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8700         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8701     GfxDir[x][y] = MovDir[newx][newy] = 0;
8702
8703   DrawLevelField(x, y);
8704   DrawLevelField(newx, newy);
8705
8706   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8707
8708   /* prevent pushed element from moving on in pushed direction */
8709   if (pushed_by_player && CAN_MOVE(element) &&
8710       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8711       !(element_info[element].move_pattern & direction))
8712     TurnRound(newx, newy);
8713
8714   /* prevent elements on conveyor belt from moving on in last direction */
8715   if (pushed_by_conveyor && CAN_FALL(element) &&
8716       direction & MV_HORIZONTAL)
8717     MovDir[newx][newy] = 0;
8718
8719   if (!pushed_by_player)
8720   {
8721     int nextx = newx + dx, nexty = newy + dy;
8722     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8723
8724     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8725
8726     if (CAN_FALL(element) && direction == MV_DOWN)
8727       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8728
8729     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8730       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8731
8732 #if USE_FIX_IMPACT_COLLISION
8733     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8734       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8735 #endif
8736   }
8737
8738   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8739   {
8740     TestIfBadThingTouchesPlayer(newx, newy);
8741     TestIfBadThingTouchesFriend(newx, newy);
8742
8743     if (!IS_CUSTOM_ELEMENT(element))
8744       TestIfBadThingTouchesOtherBadThing(newx, newy);
8745   }
8746   else if (element == EL_PENGUIN)
8747     TestIfFriendTouchesBadThing(newx, newy);
8748
8749   /* give the player one last chance (one more frame) to move away */
8750   if (CAN_FALL(element) && direction == MV_DOWN &&
8751       (last_line || (!IS_FREE(x, newy + 1) &&
8752                      (!IS_PLAYER(x, newy + 1) ||
8753                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8754     Impact(x, newy);
8755
8756   if (pushed_by_player && !game.use_change_when_pushing_bug)
8757   {
8758     int push_side = MV_DIR_OPPOSITE(direction);
8759     struct PlayerInfo *player = PLAYERINFO(x, y);
8760
8761     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8762                                player->index_bit, push_side);
8763     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8764                                         player->index_bit, push_side);
8765   }
8766
8767   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8768     MovDelay[newx][newy] = 1;
8769
8770   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8771
8772   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8773
8774 #if 0
8775   if (ChangePage[newx][newy] != -1)             /* delayed change */
8776   {
8777     int page = ChangePage[newx][newy];
8778     struct ElementChangeInfo *change = &ei->change_page[page];
8779
8780     ChangePage[newx][newy] = -1;
8781
8782     if (change->can_change)
8783     {
8784       if (ChangeElement(newx, newy, element, page))
8785       {
8786         if (change->post_change_function)
8787           change->post_change_function(newx, newy);
8788       }
8789     }
8790
8791     if (change->has_action)
8792       ExecuteCustomElementAction(newx, newy, element, page);
8793   }
8794 #endif
8795
8796   TestIfElementHitsCustomElement(newx, newy, direction);
8797   TestIfPlayerTouchesCustomElement(newx, newy);
8798   TestIfElementTouchesCustomElement(newx, newy);
8799
8800   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8801       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8802     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8803                              MV_DIR_OPPOSITE(direction));
8804 }
8805
8806 int AmoebeNachbarNr(int ax, int ay)
8807 {
8808   int i;
8809   int element = Feld[ax][ay];
8810   int group_nr = 0;
8811   static int xy[4][2] =
8812   {
8813     { 0, -1 },
8814     { -1, 0 },
8815     { +1, 0 },
8816     { 0, +1 }
8817   };
8818
8819   for (i = 0; i < NUM_DIRECTIONS; i++)
8820   {
8821     int x = ax + xy[i][0];
8822     int y = ay + xy[i][1];
8823
8824     if (!IN_LEV_FIELD(x, y))
8825       continue;
8826
8827     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8828       group_nr = AmoebaNr[x][y];
8829   }
8830
8831   return group_nr;
8832 }
8833
8834 void AmoebenVereinigen(int ax, int ay)
8835 {
8836   int i, x, y, xx, yy;
8837   int new_group_nr = AmoebaNr[ax][ay];
8838   static int xy[4][2] =
8839   {
8840     { 0, -1 },
8841     { -1, 0 },
8842     { +1, 0 },
8843     { 0, +1 }
8844   };
8845
8846   if (new_group_nr == 0)
8847     return;
8848
8849   for (i = 0; i < NUM_DIRECTIONS; i++)
8850   {
8851     x = ax + xy[i][0];
8852     y = ay + xy[i][1];
8853
8854     if (!IN_LEV_FIELD(x, y))
8855       continue;
8856
8857     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8858          Feld[x][y] == EL_BD_AMOEBA ||
8859          Feld[x][y] == EL_AMOEBA_DEAD) &&
8860         AmoebaNr[x][y] != new_group_nr)
8861     {
8862       int old_group_nr = AmoebaNr[x][y];
8863
8864       if (old_group_nr == 0)
8865         return;
8866
8867       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8868       AmoebaCnt[old_group_nr] = 0;
8869       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8870       AmoebaCnt2[old_group_nr] = 0;
8871
8872       SCAN_PLAYFIELD(xx, yy)
8873       {
8874         if (AmoebaNr[xx][yy] == old_group_nr)
8875           AmoebaNr[xx][yy] = new_group_nr;
8876       }
8877     }
8878   }
8879 }
8880
8881 void AmoebeUmwandeln(int ax, int ay)
8882 {
8883   int i, x, y;
8884
8885   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8886   {
8887     int group_nr = AmoebaNr[ax][ay];
8888
8889 #ifdef DEBUG
8890     if (group_nr == 0)
8891     {
8892       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8893       printf("AmoebeUmwandeln(): This should never happen!\n");
8894       return;
8895     }
8896 #endif
8897
8898     SCAN_PLAYFIELD(x, y)
8899     {
8900       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8901       {
8902         AmoebaNr[x][y] = 0;
8903         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8904       }
8905     }
8906
8907     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8908                             SND_AMOEBA_TURNING_TO_GEM :
8909                             SND_AMOEBA_TURNING_TO_ROCK));
8910     Bang(ax, ay);
8911   }
8912   else
8913   {
8914     static int xy[4][2] =
8915     {
8916       { 0, -1 },
8917       { -1, 0 },
8918       { +1, 0 },
8919       { 0, +1 }
8920     };
8921
8922     for (i = 0; i < NUM_DIRECTIONS; i++)
8923     {
8924       x = ax + xy[i][0];
8925       y = ay + xy[i][1];
8926
8927       if (!IN_LEV_FIELD(x, y))
8928         continue;
8929
8930       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8931       {
8932         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8933                               SND_AMOEBA_TURNING_TO_GEM :
8934                               SND_AMOEBA_TURNING_TO_ROCK));
8935         Bang(x, y);
8936       }
8937     }
8938   }
8939 }
8940
8941 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8942 {
8943   int x, y;
8944   int group_nr = AmoebaNr[ax][ay];
8945   boolean done = FALSE;
8946
8947 #ifdef DEBUG
8948   if (group_nr == 0)
8949   {
8950     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8951     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8952     return;
8953   }
8954 #endif
8955
8956   SCAN_PLAYFIELD(x, y)
8957   {
8958     if (AmoebaNr[x][y] == group_nr &&
8959         (Feld[x][y] == EL_AMOEBA_DEAD ||
8960          Feld[x][y] == EL_BD_AMOEBA ||
8961          Feld[x][y] == EL_AMOEBA_GROWING))
8962     {
8963       AmoebaNr[x][y] = 0;
8964       Feld[x][y] = new_element;
8965       InitField(x, y, FALSE);
8966       DrawLevelField(x, y);
8967       done = TRUE;
8968     }
8969   }
8970
8971   if (done)
8972     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8973                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8974                             SND_BD_AMOEBA_TURNING_TO_GEM));
8975 }
8976
8977 void AmoebeWaechst(int x, int y)
8978 {
8979   static unsigned long sound_delay = 0;
8980   static unsigned long sound_delay_value = 0;
8981
8982   if (!MovDelay[x][y])          /* start new growing cycle */
8983   {
8984     MovDelay[x][y] = 7;
8985
8986     if (DelayReached(&sound_delay, sound_delay_value))
8987     {
8988       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8989       sound_delay_value = 30;
8990     }
8991   }
8992
8993   if (MovDelay[x][y])           /* wait some time before growing bigger */
8994   {
8995     MovDelay[x][y]--;
8996     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8997     {
8998       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8999                                            6 - MovDelay[x][y]);
9000
9001       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9002     }
9003
9004     if (!MovDelay[x][y])
9005     {
9006       Feld[x][y] = Store[x][y];
9007       Store[x][y] = 0;
9008       DrawLevelField(x, y);
9009     }
9010   }
9011 }
9012
9013 void AmoebaDisappearing(int x, int y)
9014 {
9015   static unsigned long sound_delay = 0;
9016   static unsigned long sound_delay_value = 0;
9017
9018   if (!MovDelay[x][y])          /* start new shrinking cycle */
9019   {
9020     MovDelay[x][y] = 7;
9021
9022     if (DelayReached(&sound_delay, sound_delay_value))
9023       sound_delay_value = 30;
9024   }
9025
9026   if (MovDelay[x][y])           /* wait some time before shrinking */
9027   {
9028     MovDelay[x][y]--;
9029     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9030     {
9031       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9032                                            6 - MovDelay[x][y]);
9033
9034       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9035     }
9036
9037     if (!MovDelay[x][y])
9038     {
9039       Feld[x][y] = EL_EMPTY;
9040       DrawLevelField(x, y);
9041
9042       /* don't let mole enter this field in this cycle;
9043          (give priority to objects falling to this field from above) */
9044       Stop[x][y] = TRUE;
9045     }
9046   }
9047 }
9048
9049 void AmoebeAbleger(int ax, int ay)
9050 {
9051   int i;
9052   int element = Feld[ax][ay];
9053   int graphic = el2img(element);
9054   int newax = ax, neway = ay;
9055   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9056   static int xy[4][2] =
9057   {
9058     { 0, -1 },
9059     { -1, 0 },
9060     { +1, 0 },
9061     { 0, +1 }
9062   };
9063
9064   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9065   {
9066     Feld[ax][ay] = EL_AMOEBA_DEAD;
9067     DrawLevelField(ax, ay);
9068     return;
9069   }
9070
9071   if (IS_ANIMATED(graphic))
9072     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9073
9074   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9075     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9076
9077   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9078   {
9079     MovDelay[ax][ay]--;
9080     if (MovDelay[ax][ay])
9081       return;
9082   }
9083
9084   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9085   {
9086     int start = RND(4);
9087     int x = ax + xy[start][0];
9088     int y = ay + xy[start][1];
9089
9090     if (!IN_LEV_FIELD(x, y))
9091       return;
9092
9093     if (IS_FREE(x, y) ||
9094         CAN_GROW_INTO(Feld[x][y]) ||
9095         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9096         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9097     {
9098       newax = x;
9099       neway = y;
9100     }
9101
9102     if (newax == ax && neway == ay)
9103       return;
9104   }
9105   else                          /* normal or "filled" (BD style) amoeba */
9106   {
9107     int start = RND(4);
9108     boolean waiting_for_player = FALSE;
9109
9110     for (i = 0; i < NUM_DIRECTIONS; i++)
9111     {
9112       int j = (start + i) % 4;
9113       int x = ax + xy[j][0];
9114       int y = ay + xy[j][1];
9115
9116       if (!IN_LEV_FIELD(x, y))
9117         continue;
9118
9119       if (IS_FREE(x, y) ||
9120           CAN_GROW_INTO(Feld[x][y]) ||
9121           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9122           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9123       {
9124         newax = x;
9125         neway = y;
9126         break;
9127       }
9128       else if (IS_PLAYER(x, y))
9129         waiting_for_player = TRUE;
9130     }
9131
9132     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9133     {
9134       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9135       {
9136         Feld[ax][ay] = EL_AMOEBA_DEAD;
9137         DrawLevelField(ax, ay);
9138         AmoebaCnt[AmoebaNr[ax][ay]]--;
9139
9140         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9141         {
9142           if (element == EL_AMOEBA_FULL)
9143             AmoebeUmwandeln(ax, ay);
9144           else if (element == EL_BD_AMOEBA)
9145             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9146         }
9147       }
9148       return;
9149     }
9150     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9151     {
9152       /* amoeba gets larger by growing in some direction */
9153
9154       int new_group_nr = AmoebaNr[ax][ay];
9155
9156 #ifdef DEBUG
9157   if (new_group_nr == 0)
9158   {
9159     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9160     printf("AmoebeAbleger(): This should never happen!\n");
9161     return;
9162   }
9163 #endif
9164
9165       AmoebaNr[newax][neway] = new_group_nr;
9166       AmoebaCnt[new_group_nr]++;
9167       AmoebaCnt2[new_group_nr]++;
9168
9169       /* if amoeba touches other amoeba(s) after growing, unify them */
9170       AmoebenVereinigen(newax, neway);
9171
9172       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9173       {
9174         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9175         return;
9176       }
9177     }
9178   }
9179
9180   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9181       (neway == lev_fieldy - 1 && newax != ax))
9182   {
9183     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9184     Store[newax][neway] = element;
9185   }
9186   else if (neway == ay || element == EL_EMC_DRIPPER)
9187   {
9188     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9189
9190     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9191   }
9192   else
9193   {
9194     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9195     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9196     Store[ax][ay] = EL_AMOEBA_DROP;
9197     ContinueMoving(ax, ay);
9198     return;
9199   }
9200
9201   DrawLevelField(newax, neway);
9202 }
9203
9204 void Life(int ax, int ay)
9205 {
9206   int x1, y1, x2, y2;
9207   int life_time = 40;
9208   int element = Feld[ax][ay];
9209   int graphic = el2img(element);
9210   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9211                          level.biomaze);
9212   boolean changed = FALSE;
9213
9214   if (IS_ANIMATED(graphic))
9215     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9216
9217   if (Stop[ax][ay])
9218     return;
9219
9220   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9221     MovDelay[ax][ay] = life_time;
9222
9223   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9224   {
9225     MovDelay[ax][ay]--;
9226     if (MovDelay[ax][ay])
9227       return;
9228   }
9229
9230   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9231   {
9232     int xx = ax+x1, yy = ay+y1;
9233     int nachbarn = 0;
9234
9235     if (!IN_LEV_FIELD(xx, yy))
9236       continue;
9237
9238     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9239     {
9240       int x = xx+x2, y = yy+y2;
9241
9242       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9243         continue;
9244
9245       if (((Feld[x][y] == element ||
9246             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9247            !Stop[x][y]) ||
9248           (IS_FREE(x, y) && Stop[x][y]))
9249         nachbarn++;
9250     }
9251
9252     if (xx == ax && yy == ay)           /* field in the middle */
9253     {
9254       if (nachbarn < life_parameter[0] ||
9255           nachbarn > life_parameter[1])
9256       {
9257         Feld[xx][yy] = EL_EMPTY;
9258         if (!Stop[xx][yy])
9259           DrawLevelField(xx, yy);
9260         Stop[xx][yy] = TRUE;
9261         changed = TRUE;
9262       }
9263     }
9264     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9265     {                                   /* free border field */
9266       if (nachbarn >= life_parameter[2] &&
9267           nachbarn <= life_parameter[3])
9268       {
9269         Feld[xx][yy] = element;
9270         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9271         if (!Stop[xx][yy])
9272           DrawLevelField(xx, yy);
9273         Stop[xx][yy] = TRUE;
9274         changed = TRUE;
9275       }
9276     }
9277   }
9278
9279   if (changed)
9280     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9281                    SND_GAME_OF_LIFE_GROWING);
9282 }
9283
9284 static void InitRobotWheel(int x, int y)
9285 {
9286   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9287 }
9288
9289 static void RunRobotWheel(int x, int y)
9290 {
9291   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9292 }
9293
9294 static void StopRobotWheel(int x, int y)
9295 {
9296   if (ZX == x && ZY == y)
9297   {
9298     ZX = ZY = -1;
9299
9300     game.robot_wheel_active = FALSE;
9301   }
9302 }
9303
9304 static void InitTimegateWheel(int x, int y)
9305 {
9306   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9307 }
9308
9309 static void RunTimegateWheel(int x, int y)
9310 {
9311   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9312 }
9313
9314 static void InitMagicBallDelay(int x, int y)
9315 {
9316 #if 1
9317   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9318 #else
9319   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9320 #endif
9321 }
9322
9323 static void ActivateMagicBall(int bx, int by)
9324 {
9325   int x, y;
9326
9327   if (level.ball_random)
9328   {
9329     int pos_border = RND(8);    /* select one of the eight border elements */
9330     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9331     int xx = pos_content % 3;
9332     int yy = pos_content / 3;
9333
9334     x = bx - 1 + xx;
9335     y = by - 1 + yy;
9336
9337     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9338       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9339   }
9340   else
9341   {
9342     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9343     {
9344       int xx = x - bx + 1;
9345       int yy = y - by + 1;
9346
9347       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9348         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9349     }
9350   }
9351
9352   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9353 }
9354
9355 void CheckExit(int x, int y)
9356 {
9357   if (local_player->gems_still_needed > 0 ||
9358       local_player->sokobanfields_still_needed > 0 ||
9359       local_player->lights_still_needed > 0)
9360   {
9361     int element = Feld[x][y];
9362     int graphic = el2img(element);
9363
9364     if (IS_ANIMATED(graphic))
9365       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9366
9367     return;
9368   }
9369
9370   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9371     return;
9372
9373   Feld[x][y] = EL_EXIT_OPENING;
9374
9375   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9376 }
9377
9378 void CheckExitEM(int x, int y)
9379 {
9380   if (local_player->gems_still_needed > 0 ||
9381       local_player->sokobanfields_still_needed > 0 ||
9382       local_player->lights_still_needed > 0)
9383   {
9384     int element = Feld[x][y];
9385     int graphic = el2img(element);
9386
9387     if (IS_ANIMATED(graphic))
9388       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9389
9390     return;
9391   }
9392
9393   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9394     return;
9395
9396   Feld[x][y] = EL_EM_EXIT_OPENING;
9397
9398   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9399 }
9400
9401 void CheckExitSteel(int x, int y)
9402 {
9403   if (local_player->gems_still_needed > 0 ||
9404       local_player->sokobanfields_still_needed > 0 ||
9405       local_player->lights_still_needed > 0)
9406   {
9407     int element = Feld[x][y];
9408     int graphic = el2img(element);
9409
9410     if (IS_ANIMATED(graphic))
9411       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9412
9413     return;
9414   }
9415
9416   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9417     return;
9418
9419   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9420
9421   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9422 }
9423
9424 void CheckExitSteelEM(int x, int y)
9425 {
9426   if (local_player->gems_still_needed > 0 ||
9427       local_player->sokobanfields_still_needed > 0 ||
9428       local_player->lights_still_needed > 0)
9429   {
9430     int element = Feld[x][y];
9431     int graphic = el2img(element);
9432
9433     if (IS_ANIMATED(graphic))
9434       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9435
9436     return;
9437   }
9438
9439   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9440     return;
9441
9442   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9443
9444   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9445 }
9446
9447 void CheckExitSP(int x, int y)
9448 {
9449   if (local_player->gems_still_needed > 0)
9450   {
9451     int element = Feld[x][y];
9452     int graphic = el2img(element);
9453
9454     if (IS_ANIMATED(graphic))
9455       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9456
9457     return;
9458   }
9459
9460   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9461     return;
9462
9463   Feld[x][y] = EL_SP_EXIT_OPENING;
9464
9465   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9466 }
9467
9468 static void CloseAllOpenTimegates()
9469 {
9470   int x, y;
9471
9472   SCAN_PLAYFIELD(x, y)
9473   {
9474     int element = Feld[x][y];
9475
9476     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9477     {
9478       Feld[x][y] = EL_TIMEGATE_CLOSING;
9479
9480       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9481     }
9482   }
9483 }
9484
9485 void DrawTwinkleOnField(int x, int y)
9486 {
9487   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9488     return;
9489
9490   if (Feld[x][y] == EL_BD_DIAMOND)
9491     return;
9492
9493   if (MovDelay[x][y] == 0)      /* next animation frame */
9494     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9495
9496   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9497   {
9498     MovDelay[x][y]--;
9499
9500     if (setup.direct_draw && MovDelay[x][y])
9501       SetDrawtoField(DRAW_BUFFERED);
9502
9503     DrawLevelElementAnimation(x, y, Feld[x][y]);
9504
9505     if (MovDelay[x][y] != 0)
9506     {
9507       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9508                                            10 - MovDelay[x][y]);
9509
9510       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9511
9512       if (setup.direct_draw)
9513       {
9514         int dest_x, dest_y;
9515
9516         dest_x = FX + SCREENX(x) * TILEX;
9517         dest_y = FY + SCREENY(y) * TILEY;
9518
9519         BlitBitmap(drawto_field, window,
9520                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9521         SetDrawtoField(DRAW_DIRECT);
9522       }
9523     }
9524   }
9525 }
9526
9527 void MauerWaechst(int x, int y)
9528 {
9529   int delay = 6;
9530
9531   if (!MovDelay[x][y])          /* next animation frame */
9532     MovDelay[x][y] = 3 * delay;
9533
9534   if (MovDelay[x][y])           /* wait some time before next frame */
9535   {
9536     MovDelay[x][y]--;
9537
9538     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9539     {
9540       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9541       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9542
9543       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9544     }
9545
9546     if (!MovDelay[x][y])
9547     {
9548       if (MovDir[x][y] == MV_LEFT)
9549       {
9550         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9551           DrawLevelField(x - 1, y);
9552       }
9553       else if (MovDir[x][y] == MV_RIGHT)
9554       {
9555         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9556           DrawLevelField(x + 1, y);
9557       }
9558       else if (MovDir[x][y] == MV_UP)
9559       {
9560         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9561           DrawLevelField(x, y - 1);
9562       }
9563       else
9564       {
9565         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9566           DrawLevelField(x, y + 1);
9567       }
9568
9569       Feld[x][y] = Store[x][y];
9570       Store[x][y] = 0;
9571       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9572       DrawLevelField(x, y);
9573     }
9574   }
9575 }
9576
9577 void MauerAbleger(int ax, int ay)
9578 {
9579   int element = Feld[ax][ay];
9580   int graphic = el2img(element);
9581   boolean oben_frei = FALSE, unten_frei = FALSE;
9582   boolean links_frei = FALSE, rechts_frei = FALSE;
9583   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9584   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9585   boolean new_wall = FALSE;
9586
9587   if (IS_ANIMATED(graphic))
9588     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9589
9590   if (!MovDelay[ax][ay])        /* start building new wall */
9591     MovDelay[ax][ay] = 6;
9592
9593   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9594   {
9595     MovDelay[ax][ay]--;
9596     if (MovDelay[ax][ay])
9597       return;
9598   }
9599
9600   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9601     oben_frei = TRUE;
9602   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9603     unten_frei = TRUE;
9604   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9605     links_frei = TRUE;
9606   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9607     rechts_frei = TRUE;
9608
9609   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9610       element == EL_EXPANDABLE_WALL_ANY)
9611   {
9612     if (oben_frei)
9613     {
9614       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9615       Store[ax][ay-1] = element;
9616       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9617       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9618         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9619                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9620       new_wall = TRUE;
9621     }
9622     if (unten_frei)
9623     {
9624       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9625       Store[ax][ay+1] = element;
9626       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9627       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9628         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9629                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9630       new_wall = TRUE;
9631     }
9632   }
9633
9634   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9635       element == EL_EXPANDABLE_WALL_ANY ||
9636       element == EL_EXPANDABLE_WALL ||
9637       element == EL_BD_EXPANDABLE_WALL)
9638   {
9639     if (links_frei)
9640     {
9641       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9642       Store[ax-1][ay] = element;
9643       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9644       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9645         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9646                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9647       new_wall = TRUE;
9648     }
9649
9650     if (rechts_frei)
9651     {
9652       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9653       Store[ax+1][ay] = element;
9654       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9655       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9656         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9657                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9658       new_wall = TRUE;
9659     }
9660   }
9661
9662   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9663     DrawLevelField(ax, ay);
9664
9665   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9666     oben_massiv = TRUE;
9667   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9668     unten_massiv = TRUE;
9669   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9670     links_massiv = TRUE;
9671   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9672     rechts_massiv = TRUE;
9673
9674   if (((oben_massiv && unten_massiv) ||
9675        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9676        element == EL_EXPANDABLE_WALL) &&
9677       ((links_massiv && rechts_massiv) ||
9678        element == EL_EXPANDABLE_WALL_VERTICAL))
9679     Feld[ax][ay] = EL_WALL;
9680
9681   if (new_wall)
9682     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9683 }
9684
9685 void MauerAblegerStahl(int ax, int ay)
9686 {
9687   int element = Feld[ax][ay];
9688   int graphic = el2img(element);
9689   boolean oben_frei = FALSE, unten_frei = FALSE;
9690   boolean links_frei = FALSE, rechts_frei = FALSE;
9691   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9692   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9693   boolean new_wall = FALSE;
9694
9695   if (IS_ANIMATED(graphic))
9696     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9697
9698   if (!MovDelay[ax][ay])        /* start building new wall */
9699     MovDelay[ax][ay] = 6;
9700
9701   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9702   {
9703     MovDelay[ax][ay]--;
9704     if (MovDelay[ax][ay])
9705       return;
9706   }
9707
9708   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9709     oben_frei = TRUE;
9710   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9711     unten_frei = TRUE;
9712   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9713     links_frei = TRUE;
9714   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9715     rechts_frei = TRUE;
9716
9717   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9718       element == EL_EXPANDABLE_STEELWALL_ANY)
9719   {
9720     if (oben_frei)
9721     {
9722       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9723       Store[ax][ay-1] = element;
9724       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9725       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9726         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9727                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9728       new_wall = TRUE;
9729     }
9730     if (unten_frei)
9731     {
9732       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9733       Store[ax][ay+1] = element;
9734       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9735       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9736         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9737                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9738       new_wall = TRUE;
9739     }
9740   }
9741
9742   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9743       element == EL_EXPANDABLE_STEELWALL_ANY)
9744   {
9745     if (links_frei)
9746     {
9747       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9748       Store[ax-1][ay] = element;
9749       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9750       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9751         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9752                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9753       new_wall = TRUE;
9754     }
9755
9756     if (rechts_frei)
9757     {
9758       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9759       Store[ax+1][ay] = element;
9760       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9761       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9762         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9763                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9764       new_wall = TRUE;
9765     }
9766   }
9767
9768   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9769     oben_massiv = TRUE;
9770   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9771     unten_massiv = TRUE;
9772   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9773     links_massiv = TRUE;
9774   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9775     rechts_massiv = TRUE;
9776
9777   if (((oben_massiv && unten_massiv) ||
9778        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9779       ((links_massiv && rechts_massiv) ||
9780        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9781     Feld[ax][ay] = EL_WALL;
9782
9783   if (new_wall)
9784     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9785 }
9786
9787 void CheckForDragon(int x, int y)
9788 {
9789   int i, j;
9790   boolean dragon_found = FALSE;
9791   static int xy[4][2] =
9792   {
9793     { 0, -1 },
9794     { -1, 0 },
9795     { +1, 0 },
9796     { 0, +1 }
9797   };
9798
9799   for (i = 0; i < NUM_DIRECTIONS; i++)
9800   {
9801     for (j = 0; j < 4; j++)
9802     {
9803       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9804
9805       if (IN_LEV_FIELD(xx, yy) &&
9806           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9807       {
9808         if (Feld[xx][yy] == EL_DRAGON)
9809           dragon_found = TRUE;
9810       }
9811       else
9812         break;
9813     }
9814   }
9815
9816   if (!dragon_found)
9817   {
9818     for (i = 0; i < NUM_DIRECTIONS; i++)
9819     {
9820       for (j = 0; j < 3; j++)
9821       {
9822         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9823   
9824         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9825         {
9826           Feld[xx][yy] = EL_EMPTY;
9827           DrawLevelField(xx, yy);
9828         }
9829         else
9830           break;
9831       }
9832     }
9833   }
9834 }
9835
9836 static void InitBuggyBase(int x, int y)
9837 {
9838   int element = Feld[x][y];
9839   int activating_delay = FRAMES_PER_SECOND / 4;
9840
9841   ChangeDelay[x][y] =
9842     (element == EL_SP_BUGGY_BASE ?
9843      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9844      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9845      activating_delay :
9846      element == EL_SP_BUGGY_BASE_ACTIVE ?
9847      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9848 }
9849
9850 static void WarnBuggyBase(int x, int y)
9851 {
9852   int i;
9853   static int xy[4][2] =
9854   {
9855     { 0, -1 },
9856     { -1, 0 },
9857     { +1, 0 },
9858     { 0, +1 }
9859   };
9860
9861   for (i = 0; i < NUM_DIRECTIONS; i++)
9862   {
9863     int xx = x + xy[i][0];
9864     int yy = y + xy[i][1];
9865
9866     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9867     {
9868       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9869
9870       break;
9871     }
9872   }
9873 }
9874
9875 static void InitTrap(int x, int y)
9876 {
9877   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9878 }
9879
9880 static void ActivateTrap(int x, int y)
9881 {
9882   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9883 }
9884
9885 static void ChangeActiveTrap(int x, int y)
9886 {
9887   int graphic = IMG_TRAP_ACTIVE;
9888
9889   /* if new animation frame was drawn, correct crumbled sand border */
9890   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9891     DrawLevelFieldCrumbledSand(x, y);
9892 }
9893
9894 static int getSpecialActionElement(int element, int number, int base_element)
9895 {
9896   return (element != EL_EMPTY ? element :
9897           number != -1 ? base_element + number - 1 :
9898           EL_EMPTY);
9899 }
9900
9901 static int getModifiedActionNumber(int value_old, int operator, int operand,
9902                                    int value_min, int value_max)
9903 {
9904   int value_new = (operator == CA_MODE_SET      ? operand :
9905                    operator == CA_MODE_ADD      ? value_old + operand :
9906                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9907                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9908                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9909                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9910                    value_old);
9911
9912   return (value_new < value_min ? value_min :
9913           value_new > value_max ? value_max :
9914           value_new);
9915 }
9916
9917 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9918 {
9919   struct ElementInfo *ei = &element_info[element];
9920   struct ElementChangeInfo *change = &ei->change_page[page];
9921   int target_element = change->target_element;
9922   int action_type = change->action_type;
9923   int action_mode = change->action_mode;
9924   int action_arg = change->action_arg;
9925   int i;
9926
9927   if (!change->has_action)
9928     return;
9929
9930   /* ---------- determine action paramater values -------------------------- */
9931
9932   int level_time_value =
9933     (level.time > 0 ? TimeLeft :
9934      TimePlayed);
9935
9936   int action_arg_element =
9937     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9938      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9939      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9940      EL_EMPTY);
9941
9942   int action_arg_direction =
9943     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9944      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9945      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9946      change->actual_trigger_side :
9947      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9948      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9949      MV_NONE);
9950
9951   int action_arg_number_min =
9952     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9953      CA_ARG_MIN);
9954
9955   int action_arg_number_max =
9956     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9957      action_type == CA_SET_LEVEL_GEMS ? 999 :
9958      action_type == CA_SET_LEVEL_TIME ? 9999 :
9959      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9960      action_type == CA_SET_CE_VALUE ? 9999 :
9961      action_type == CA_SET_CE_SCORE ? 9999 :
9962      CA_ARG_MAX);
9963
9964   int action_arg_number_reset =
9965     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9966      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9967      action_type == CA_SET_LEVEL_TIME ? level.time :
9968      action_type == CA_SET_LEVEL_SCORE ? 0 :
9969 #if USE_NEW_CUSTOM_VALUE
9970      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9971 #else
9972      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9973 #endif
9974      action_type == CA_SET_CE_SCORE ? 0 :
9975      0);
9976
9977   int action_arg_number =
9978     (action_arg <= CA_ARG_MAX ? action_arg :
9979      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9980      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9981      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9982      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9983      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9984      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9985 #if USE_NEW_CUSTOM_VALUE
9986      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9987 #else
9988      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9989 #endif
9990      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9991      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9992      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9993      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9994      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9995      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9996      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9997      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9998      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9999      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10000      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10001      -1);
10002
10003   int action_arg_number_old =
10004     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10005      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10006      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10007      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10008      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10009      0);
10010
10011   int action_arg_number_new =
10012     getModifiedActionNumber(action_arg_number_old,
10013                             action_mode, action_arg_number,
10014                             action_arg_number_min, action_arg_number_max);
10015
10016   int trigger_player_bits =
10017     (change->actual_trigger_player >= EL_PLAYER_1 &&
10018      change->actual_trigger_player <= EL_PLAYER_4 ?
10019      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10020      PLAYER_BITS_ANY);
10021
10022   int action_arg_player_bits =
10023     (action_arg >= CA_ARG_PLAYER_1 &&
10024      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10025      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10026      PLAYER_BITS_ANY);
10027
10028   /* ---------- execute action  -------------------------------------------- */
10029
10030   switch (action_type)
10031   {
10032     case CA_NO_ACTION:
10033     {
10034       return;
10035     }
10036
10037     /* ---------- level actions  ------------------------------------------- */
10038
10039     case CA_RESTART_LEVEL:
10040     {
10041       game.restart_level = TRUE;
10042
10043       break;
10044     }
10045
10046     case CA_SHOW_ENVELOPE:
10047     {
10048       int element = getSpecialActionElement(action_arg_element,
10049                                             action_arg_number, EL_ENVELOPE_1);
10050
10051       if (IS_ENVELOPE(element))
10052         local_player->show_envelope = element;
10053
10054       break;
10055     }
10056
10057     case CA_SET_LEVEL_TIME:
10058     {
10059       if (level.time > 0)       /* only modify limited time value */
10060       {
10061         TimeLeft = action_arg_number_new;
10062
10063 #if 1
10064         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10065
10066         DisplayGameControlValues();
10067 #else
10068         DrawGameValue_Time(TimeLeft);
10069 #endif
10070
10071         if (!TimeLeft && setup.time_limit)
10072           for (i = 0; i < MAX_PLAYERS; i++)
10073             KillPlayer(&stored_player[i]);
10074       }
10075
10076       break;
10077     }
10078
10079     case CA_SET_LEVEL_SCORE:
10080     {
10081       local_player->score = action_arg_number_new;
10082
10083 #if 1
10084       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10085
10086       DisplayGameControlValues();
10087 #else
10088       DrawGameValue_Score(local_player->score);
10089 #endif
10090
10091       break;
10092     }
10093
10094     case CA_SET_LEVEL_GEMS:
10095     {
10096       local_player->gems_still_needed = action_arg_number_new;
10097
10098 #if 1
10099       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10100
10101       DisplayGameControlValues();
10102 #else
10103       DrawGameValue_Emeralds(local_player->gems_still_needed);
10104 #endif
10105
10106       break;
10107     }
10108
10109 #if !USE_PLAYER_GRAVITY
10110     case CA_SET_LEVEL_GRAVITY:
10111     {
10112       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10113                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10114                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10115                       game.gravity);
10116       break;
10117     }
10118 #endif
10119
10120     case CA_SET_LEVEL_WIND:
10121     {
10122       game.wind_direction = action_arg_direction;
10123
10124       break;
10125     }
10126
10127     /* ---------- player actions  ------------------------------------------ */
10128
10129     case CA_MOVE_PLAYER:
10130     {
10131       /* automatically move to the next field in specified direction */
10132       for (i = 0; i < MAX_PLAYERS; i++)
10133         if (trigger_player_bits & (1 << i))
10134           stored_player[i].programmed_action = action_arg_direction;
10135
10136       break;
10137     }
10138
10139     case CA_EXIT_PLAYER:
10140     {
10141       for (i = 0; i < MAX_PLAYERS; i++)
10142         if (action_arg_player_bits & (1 << i))
10143           PlayerWins(&stored_player[i]);
10144
10145       break;
10146     }
10147
10148     case CA_KILL_PLAYER:
10149     {
10150       for (i = 0; i < MAX_PLAYERS; i++)
10151         if (action_arg_player_bits & (1 << i))
10152           KillPlayer(&stored_player[i]);
10153
10154       break;
10155     }
10156
10157     case CA_SET_PLAYER_KEYS:
10158     {
10159       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10160       int element = getSpecialActionElement(action_arg_element,
10161                                             action_arg_number, EL_KEY_1);
10162
10163       if (IS_KEY(element))
10164       {
10165         for (i = 0; i < MAX_PLAYERS; i++)
10166         {
10167           if (trigger_player_bits & (1 << i))
10168           {
10169             stored_player[i].key[KEY_NR(element)] = key_state;
10170
10171             DrawGameDoorValues();
10172           }
10173         }
10174       }
10175
10176       break;
10177     }
10178
10179     case CA_SET_PLAYER_SPEED:
10180     {
10181       for (i = 0; i < MAX_PLAYERS; i++)
10182       {
10183         if (trigger_player_bits & (1 << i))
10184         {
10185           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10186
10187           if (action_arg == CA_ARG_SPEED_FASTER &&
10188               stored_player[i].cannot_move)
10189           {
10190             action_arg_number = STEPSIZE_VERY_SLOW;
10191           }
10192           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10193                    action_arg == CA_ARG_SPEED_FASTER)
10194           {
10195             action_arg_number = 2;
10196             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10197                            CA_MODE_MULTIPLY);
10198           }
10199           else if (action_arg == CA_ARG_NUMBER_RESET)
10200           {
10201             action_arg_number = level.initial_player_stepsize[i];
10202           }
10203
10204           move_stepsize =
10205             getModifiedActionNumber(move_stepsize,
10206                                     action_mode,
10207                                     action_arg_number,
10208                                     action_arg_number_min,
10209                                     action_arg_number_max);
10210
10211           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10212         }
10213       }
10214
10215       break;
10216     }
10217
10218     case CA_SET_PLAYER_SHIELD:
10219     {
10220       for (i = 0; i < MAX_PLAYERS; i++)
10221       {
10222         if (trigger_player_bits & (1 << i))
10223         {
10224           if (action_arg == CA_ARG_SHIELD_OFF)
10225           {
10226             stored_player[i].shield_normal_time_left = 0;
10227             stored_player[i].shield_deadly_time_left = 0;
10228           }
10229           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10230           {
10231             stored_player[i].shield_normal_time_left = 999999;
10232           }
10233           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10234           {
10235             stored_player[i].shield_normal_time_left = 999999;
10236             stored_player[i].shield_deadly_time_left = 999999;
10237           }
10238         }
10239       }
10240
10241       break;
10242     }
10243
10244 #if USE_PLAYER_GRAVITY
10245     case CA_SET_PLAYER_GRAVITY:
10246     {
10247       for (i = 0; i < MAX_PLAYERS; i++)
10248       {
10249         if (trigger_player_bits & (1 << i))
10250         {
10251           stored_player[i].gravity =
10252             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10253              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10254              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10255              stored_player[i].gravity);
10256         }
10257       }
10258
10259       break;
10260     }
10261 #endif
10262
10263     case CA_SET_PLAYER_ARTWORK:
10264     {
10265       for (i = 0; i < MAX_PLAYERS; i++)
10266       {
10267         if (trigger_player_bits & (1 << i))
10268         {
10269           int artwork_element = action_arg_element;
10270
10271           if (action_arg == CA_ARG_ELEMENT_RESET)
10272             artwork_element =
10273               (level.use_artwork_element[i] ? level.artwork_element[i] :
10274                stored_player[i].element_nr);
10275
10276 #if USE_GFX_RESET_PLAYER_ARTWORK
10277           if (stored_player[i].artwork_element != artwork_element)
10278             stored_player[i].Frame = 0;
10279 #endif
10280
10281           stored_player[i].artwork_element = artwork_element;
10282
10283           SetPlayerWaiting(&stored_player[i], FALSE);
10284
10285           /* set number of special actions for bored and sleeping animation */
10286           stored_player[i].num_special_action_bored =
10287             get_num_special_action(artwork_element,
10288                                    ACTION_BORING_1, ACTION_BORING_LAST);
10289           stored_player[i].num_special_action_sleeping =
10290             get_num_special_action(artwork_element,
10291                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10292         }
10293       }
10294
10295       break;
10296     }
10297
10298     /* ---------- CE actions  ---------------------------------------------- */
10299
10300     case CA_SET_CE_VALUE:
10301     {
10302 #if USE_NEW_CUSTOM_VALUE
10303       int last_ce_value = CustomValue[x][y];
10304
10305       CustomValue[x][y] = action_arg_number_new;
10306
10307       if (CustomValue[x][y] != last_ce_value)
10308       {
10309         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10310         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10311
10312         if (CustomValue[x][y] == 0)
10313         {
10314           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10315           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10316         }
10317       }
10318 #endif
10319
10320       break;
10321     }
10322
10323     case CA_SET_CE_SCORE:
10324     {
10325 #if USE_NEW_CUSTOM_VALUE
10326       int last_ce_score = ei->collect_score;
10327
10328       ei->collect_score = action_arg_number_new;
10329
10330       if (ei->collect_score != last_ce_score)
10331       {
10332         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10333         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10334
10335         if (ei->collect_score == 0)
10336         {
10337           int xx, yy;
10338
10339           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10340           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10341
10342           /*
10343             This is a very special case that seems to be a mixture between
10344             CheckElementChange() and CheckTriggeredElementChange(): while
10345             the first one only affects single elements that are triggered
10346             directly, the second one affects multiple elements in the playfield
10347             that are triggered indirectly by another element. This is a third
10348             case: Changing the CE score always affects multiple identical CEs,
10349             so every affected CE must be checked, not only the single CE for
10350             which the CE score was changed in the first place (as every instance
10351             of that CE shares the same CE score, and therefore also can change)!
10352           */
10353           SCAN_PLAYFIELD(xx, yy)
10354           {
10355             if (Feld[xx][yy] == element)
10356               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10357                                  CE_SCORE_GETS_ZERO);
10358           }
10359         }
10360       }
10361 #endif
10362
10363       break;
10364     }
10365
10366     /* ---------- engine actions  ------------------------------------------ */
10367
10368     case CA_SET_ENGINE_SCAN_MODE:
10369     {
10370       InitPlayfieldScanMode(action_arg);
10371
10372       break;
10373     }
10374
10375     default:
10376       break;
10377   }
10378 }
10379
10380 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10381 {
10382   int old_element = Feld[x][y];
10383   int new_element = GetElementFromGroupElement(element);
10384   int previous_move_direction = MovDir[x][y];
10385 #if USE_NEW_CUSTOM_VALUE
10386   int last_ce_value = CustomValue[x][y];
10387 #endif
10388   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10389   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10390   boolean add_player_onto_element = (new_element_is_player &&
10391 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10392                                      /* this breaks SnakeBite when a snake is
10393                                         halfway through a door that closes */
10394                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10395                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10396 #endif
10397                                      IS_WALKABLE(old_element));
10398
10399 #if 0
10400   /* check if element under the player changes from accessible to unaccessible
10401      (needed for special case of dropping element which then changes) */
10402   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10403       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10404   {
10405     Bang(x, y);
10406
10407     return;
10408   }
10409 #endif
10410
10411   if (!add_player_onto_element)
10412   {
10413     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10414       RemoveMovingField(x, y);
10415     else
10416       RemoveField(x, y);
10417
10418     Feld[x][y] = new_element;
10419
10420 #if !USE_GFX_RESET_GFX_ANIMATION
10421     ResetGfxAnimation(x, y);
10422     ResetRandomAnimationValue(x, y);
10423 #endif
10424
10425     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10426       MovDir[x][y] = previous_move_direction;
10427
10428 #if USE_NEW_CUSTOM_VALUE
10429     if (element_info[new_element].use_last_ce_value)
10430       CustomValue[x][y] = last_ce_value;
10431 #endif
10432
10433     InitField_WithBug1(x, y, FALSE);
10434
10435     new_element = Feld[x][y];   /* element may have changed */
10436
10437 #if USE_GFX_RESET_GFX_ANIMATION
10438     ResetGfxAnimation(x, y);
10439     ResetRandomAnimationValue(x, y);
10440 #endif
10441
10442     DrawLevelField(x, y);
10443
10444     if (GFX_CRUMBLED(new_element))
10445       DrawLevelFieldCrumbledSandNeighbours(x, y);
10446   }
10447
10448 #if 1
10449   /* check if element under the player changes from accessible to unaccessible
10450      (needed for special case of dropping element which then changes) */
10451   /* (must be checked after creating new element for walkable group elements) */
10452 #if USE_FIX_KILLED_BY_NON_WALKABLE
10453   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10454       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10455   {
10456     Bang(x, y);
10457
10458     return;
10459   }
10460 #else
10461   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10462       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10463   {
10464     Bang(x, y);
10465
10466     return;
10467   }
10468 #endif
10469 #endif
10470
10471   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10472   if (new_element_is_player)
10473     RelocatePlayer(x, y, new_element);
10474
10475   if (is_change)
10476     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10477
10478   TestIfBadThingTouchesPlayer(x, y);
10479   TestIfPlayerTouchesCustomElement(x, y);
10480   TestIfElementTouchesCustomElement(x, y);
10481 }
10482
10483 static void CreateField(int x, int y, int element)
10484 {
10485   CreateFieldExt(x, y, element, FALSE);
10486 }
10487
10488 static void CreateElementFromChange(int x, int y, int element)
10489 {
10490   element = GET_VALID_RUNTIME_ELEMENT(element);
10491
10492 #if USE_STOP_CHANGED_ELEMENTS
10493   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10494   {
10495     int old_element = Feld[x][y];
10496
10497     /* prevent changed element from moving in same engine frame
10498        unless both old and new element can either fall or move */
10499     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10500         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10501       Stop[x][y] = TRUE;
10502   }
10503 #endif
10504
10505   CreateFieldExt(x, y, element, TRUE);
10506 }
10507
10508 static boolean ChangeElement(int x, int y, int element, int page)
10509 {
10510   struct ElementInfo *ei = &element_info[element];
10511   struct ElementChangeInfo *change = &ei->change_page[page];
10512   int ce_value = CustomValue[x][y];
10513   int ce_score = ei->collect_score;
10514   int target_element;
10515   int old_element = Feld[x][y];
10516
10517   /* always use default change event to prevent running into a loop */
10518   if (ChangeEvent[x][y] == -1)
10519     ChangeEvent[x][y] = CE_DELAY;
10520
10521   if (ChangeEvent[x][y] == CE_DELAY)
10522   {
10523     /* reset actual trigger element, trigger player and action element */
10524     change->actual_trigger_element = EL_EMPTY;
10525     change->actual_trigger_player = EL_PLAYER_1;
10526     change->actual_trigger_side = CH_SIDE_NONE;
10527     change->actual_trigger_ce_value = 0;
10528     change->actual_trigger_ce_score = 0;
10529   }
10530
10531   /* do not change elements more than a specified maximum number of changes */
10532   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10533     return FALSE;
10534
10535   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10536
10537   if (change->explode)
10538   {
10539     Bang(x, y);
10540
10541     return TRUE;
10542   }
10543
10544   if (change->use_target_content)
10545   {
10546     boolean complete_replace = TRUE;
10547     boolean can_replace[3][3];
10548     int xx, yy;
10549
10550     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10551     {
10552       boolean is_empty;
10553       boolean is_walkable;
10554       boolean is_diggable;
10555       boolean is_collectible;
10556       boolean is_removable;
10557       boolean is_destructible;
10558       int ex = x + xx - 1;
10559       int ey = y + yy - 1;
10560       int content_element = change->target_content.e[xx][yy];
10561       int e;
10562
10563       can_replace[xx][yy] = TRUE;
10564
10565       if (ex == x && ey == y)   /* do not check changing element itself */
10566         continue;
10567
10568       if (content_element == EL_EMPTY_SPACE)
10569       {
10570         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10571
10572         continue;
10573       }
10574
10575       if (!IN_LEV_FIELD(ex, ey))
10576       {
10577         can_replace[xx][yy] = FALSE;
10578         complete_replace = FALSE;
10579
10580         continue;
10581       }
10582
10583       e = Feld[ex][ey];
10584
10585       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10586         e = MovingOrBlocked2Element(ex, ey);
10587
10588       is_empty = (IS_FREE(ex, ey) ||
10589                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10590
10591       is_walkable     = (is_empty || IS_WALKABLE(e));
10592       is_diggable     = (is_empty || IS_DIGGABLE(e));
10593       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10594       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10595       is_removable    = (is_diggable || is_collectible);
10596
10597       can_replace[xx][yy] =
10598         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10599           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10600           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10601           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10602           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10603           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10604          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10605
10606       if (!can_replace[xx][yy])
10607         complete_replace = FALSE;
10608     }
10609
10610     if (!change->only_if_complete || complete_replace)
10611     {
10612       boolean something_has_changed = FALSE;
10613
10614       if (change->only_if_complete && change->use_random_replace &&
10615           RND(100) < change->random_percentage)
10616         return FALSE;
10617
10618       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10619       {
10620         int ex = x + xx - 1;
10621         int ey = y + yy - 1;
10622         int content_element;
10623
10624         if (can_replace[xx][yy] && (!change->use_random_replace ||
10625                                     RND(100) < change->random_percentage))
10626         {
10627           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10628             RemoveMovingField(ex, ey);
10629
10630           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10631
10632           content_element = change->target_content.e[xx][yy];
10633           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10634                                               ce_value, ce_score);
10635
10636           CreateElementFromChange(ex, ey, target_element);
10637
10638           something_has_changed = TRUE;
10639
10640           /* for symmetry reasons, freeze newly created border elements */
10641           if (ex != x || ey != y)
10642             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10643         }
10644       }
10645
10646       if (something_has_changed)
10647       {
10648         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10649         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10650       }
10651     }
10652   }
10653   else
10654   {
10655     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10656                                         ce_value, ce_score);
10657
10658     if (element == EL_DIAGONAL_GROWING ||
10659         element == EL_DIAGONAL_SHRINKING)
10660     {
10661       target_element = Store[x][y];
10662
10663       Store[x][y] = EL_EMPTY;
10664     }
10665
10666     CreateElementFromChange(x, y, target_element);
10667
10668     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10669     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10670   }
10671
10672   /* this uses direct change before indirect change */
10673   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10674
10675   return TRUE;
10676 }
10677
10678 #if USE_NEW_DELAYED_ACTION
10679
10680 static void HandleElementChange(int x, int y, int page)
10681 {
10682   int element = MovingOrBlocked2Element(x, y);
10683   struct ElementInfo *ei = &element_info[element];
10684   struct ElementChangeInfo *change = &ei->change_page[page];
10685
10686 #ifdef DEBUG
10687   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10688       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10689   {
10690     printf("\n\n");
10691     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10692            x, y, element, element_info[element].token_name);
10693     printf("HandleElementChange(): This should never happen!\n");
10694     printf("\n\n");
10695   }
10696 #endif
10697
10698   /* this can happen with classic bombs on walkable, changing elements */
10699   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10700   {
10701 #if 0
10702     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10703       ChangeDelay[x][y] = 0;
10704 #endif
10705
10706     return;
10707   }
10708
10709   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10710   {
10711     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10712
10713     if (change->can_change)
10714     {
10715 #if 1
10716       /* !!! not clear why graphic animation should be reset at all here !!! */
10717       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10718 #if USE_GFX_RESET_WHEN_NOT_MOVING
10719       /* when a custom element is about to change (for example by change delay),
10720          do not reset graphic animation when the custom element is moving */
10721       if (!IS_MOVING(x, y))
10722 #endif
10723       {
10724         ResetGfxAnimation(x, y);
10725         ResetRandomAnimationValue(x, y);
10726       }
10727 #endif
10728
10729       if (change->pre_change_function)
10730         change->pre_change_function(x, y);
10731     }
10732   }
10733
10734   ChangeDelay[x][y]--;
10735
10736   if (ChangeDelay[x][y] != 0)           /* continue element change */
10737   {
10738     if (change->can_change)
10739     {
10740       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10741
10742       if (IS_ANIMATED(graphic))
10743         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10744
10745       if (change->change_function)
10746         change->change_function(x, y);
10747     }
10748   }
10749   else                                  /* finish element change */
10750   {
10751     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10752     {
10753       page = ChangePage[x][y];
10754       ChangePage[x][y] = -1;
10755
10756       change = &ei->change_page[page];
10757     }
10758
10759     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10760     {
10761       ChangeDelay[x][y] = 1;            /* try change after next move step */
10762       ChangePage[x][y] = page;          /* remember page to use for change */
10763
10764       return;
10765     }
10766
10767     if (change->can_change)
10768     {
10769       if (ChangeElement(x, y, element, page))
10770       {
10771         if (change->post_change_function)
10772           change->post_change_function(x, y);
10773       }
10774     }
10775
10776     if (change->has_action)
10777       ExecuteCustomElementAction(x, y, element, page);
10778   }
10779 }
10780
10781 #else
10782
10783 static void HandleElementChange(int x, int y, int page)
10784 {
10785   int element = MovingOrBlocked2Element(x, y);
10786   struct ElementInfo *ei = &element_info[element];
10787   struct ElementChangeInfo *change = &ei->change_page[page];
10788
10789 #ifdef DEBUG
10790   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10791   {
10792     printf("\n\n");
10793     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10794            x, y, element, element_info[element].token_name);
10795     printf("HandleElementChange(): This should never happen!\n");
10796     printf("\n\n");
10797   }
10798 #endif
10799
10800   /* this can happen with classic bombs on walkable, changing elements */
10801   if (!CAN_CHANGE(element))
10802   {
10803 #if 0
10804     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10805       ChangeDelay[x][y] = 0;
10806 #endif
10807
10808     return;
10809   }
10810
10811   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10812   {
10813     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10814
10815     ResetGfxAnimation(x, y);
10816     ResetRandomAnimationValue(x, y);
10817
10818     if (change->pre_change_function)
10819       change->pre_change_function(x, y);
10820   }
10821
10822   ChangeDelay[x][y]--;
10823
10824   if (ChangeDelay[x][y] != 0)           /* continue element change */
10825   {
10826     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10827
10828     if (IS_ANIMATED(graphic))
10829       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10830
10831     if (change->change_function)
10832       change->change_function(x, y);
10833   }
10834   else                                  /* finish element change */
10835   {
10836     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10837     {
10838       page = ChangePage[x][y];
10839       ChangePage[x][y] = -1;
10840
10841       change = &ei->change_page[page];
10842     }
10843
10844     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10845     {
10846       ChangeDelay[x][y] = 1;            /* try change after next move step */
10847       ChangePage[x][y] = page;          /* remember page to use for change */
10848
10849       return;
10850     }
10851
10852     if (ChangeElement(x, y, element, page))
10853     {
10854       if (change->post_change_function)
10855         change->post_change_function(x, y);
10856     }
10857   }
10858 }
10859
10860 #endif
10861
10862 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10863                                               int trigger_element,
10864                                               int trigger_event,
10865                                               int trigger_player,
10866                                               int trigger_side,
10867                                               int trigger_page)
10868 {
10869   boolean change_done_any = FALSE;
10870   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10871   int i;
10872
10873   if (!(trigger_events[trigger_element][trigger_event]))
10874     return FALSE;
10875
10876 #if 0
10877   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10878          trigger_event, recursion_loop_depth, recursion_loop_detected,
10879          recursion_loop_element, EL_NAME(recursion_loop_element));
10880 #endif
10881
10882   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10883
10884   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10885   {
10886     int element = EL_CUSTOM_START + i;
10887     boolean change_done = FALSE;
10888     int p;
10889
10890     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10891         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10892       continue;
10893
10894     for (p = 0; p < element_info[element].num_change_pages; p++)
10895     {
10896       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10897
10898       if (change->can_change_or_has_action &&
10899           change->has_event[trigger_event] &&
10900           change->trigger_side & trigger_side &&
10901           change->trigger_player & trigger_player &&
10902           change->trigger_page & trigger_page_bits &&
10903           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10904       {
10905         change->actual_trigger_element = trigger_element;
10906         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10907         change->actual_trigger_side = trigger_side;
10908         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10909         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10910
10911         if ((change->can_change && !change_done) || change->has_action)
10912         {
10913           int x, y;
10914
10915           SCAN_PLAYFIELD(x, y)
10916           {
10917             if (Feld[x][y] == element)
10918             {
10919               if (change->can_change && !change_done)
10920               {
10921                 ChangeDelay[x][y] = 1;
10922                 ChangeEvent[x][y] = trigger_event;
10923
10924                 HandleElementChange(x, y, p);
10925               }
10926 #if USE_NEW_DELAYED_ACTION
10927               else if (change->has_action)
10928               {
10929                 ExecuteCustomElementAction(x, y, element, p);
10930                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10931               }
10932 #else
10933               if (change->has_action)
10934               {
10935                 ExecuteCustomElementAction(x, y, element, p);
10936                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10937               }
10938 #endif
10939             }
10940           }
10941
10942           if (change->can_change)
10943           {
10944             change_done = TRUE;
10945             change_done_any = TRUE;
10946           }
10947         }
10948       }
10949     }
10950   }
10951
10952   RECURSION_LOOP_DETECTION_END();
10953
10954   return change_done_any;
10955 }
10956
10957 static boolean CheckElementChangeExt(int x, int y,
10958                                      int element,
10959                                      int trigger_element,
10960                                      int trigger_event,
10961                                      int trigger_player,
10962                                      int trigger_side)
10963 {
10964   boolean change_done = FALSE;
10965   int p;
10966
10967   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10968       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10969     return FALSE;
10970
10971   if (Feld[x][y] == EL_BLOCKED)
10972   {
10973     Blocked2Moving(x, y, &x, &y);
10974     element = Feld[x][y];
10975   }
10976
10977 #if 0
10978   /* check if element has already changed */
10979   if (Feld[x][y] != element)
10980     return FALSE;
10981 #else
10982   /* check if element has already changed or is about to change after moving */
10983   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10984        Feld[x][y] != element) ||
10985
10986       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10987        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10988         ChangePage[x][y] != -1)))
10989     return FALSE;
10990 #endif
10991
10992 #if 0
10993   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10994          trigger_event, recursion_loop_depth, recursion_loop_detected,
10995          recursion_loop_element, EL_NAME(recursion_loop_element));
10996 #endif
10997
10998   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10999
11000   for (p = 0; p < element_info[element].num_change_pages; p++)
11001   {
11002     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11003
11004     /* check trigger element for all events where the element that is checked
11005        for changing interacts with a directly adjacent element -- this is
11006        different to element changes that affect other elements to change on the
11007        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11008     boolean check_trigger_element =
11009       (trigger_event == CE_TOUCHING_X ||
11010        trigger_event == CE_HITTING_X ||
11011        trigger_event == CE_HIT_BY_X ||
11012 #if 1
11013        /* this one was forgotten until 3.2.3 */
11014        trigger_event == CE_DIGGING_X);
11015 #endif
11016
11017     if (change->can_change_or_has_action &&
11018         change->has_event[trigger_event] &&
11019         change->trigger_side & trigger_side &&
11020         change->trigger_player & trigger_player &&
11021         (!check_trigger_element ||
11022          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11023     {
11024       change->actual_trigger_element = trigger_element;
11025       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
11026       change->actual_trigger_side = trigger_side;
11027       change->actual_trigger_ce_value = CustomValue[x][y];
11028       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11029
11030       /* special case: trigger element not at (x,y) position for some events */
11031       if (check_trigger_element)
11032       {
11033         static struct
11034         {
11035           int dx, dy;
11036         } move_xy[] =
11037           {
11038             {  0,  0 },
11039             { -1,  0 },
11040             { +1,  0 },
11041             {  0,  0 },
11042             {  0, -1 },
11043             {  0,  0 }, { 0, 0 }, { 0, 0 },
11044             {  0, +1 }
11045           };
11046
11047         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11048         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11049
11050         change->actual_trigger_ce_value = CustomValue[xx][yy];
11051         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11052       }
11053
11054       if (change->can_change && !change_done)
11055       {
11056         ChangeDelay[x][y] = 1;
11057         ChangeEvent[x][y] = trigger_event;
11058
11059         HandleElementChange(x, y, p);
11060
11061         change_done = TRUE;
11062       }
11063 #if USE_NEW_DELAYED_ACTION
11064       else if (change->has_action)
11065       {
11066         ExecuteCustomElementAction(x, y, element, p);
11067         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11068       }
11069 #else
11070       if (change->has_action)
11071       {
11072         ExecuteCustomElementAction(x, y, element, p);
11073         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11074       }
11075 #endif
11076     }
11077   }
11078
11079   RECURSION_LOOP_DETECTION_END();
11080
11081   return change_done;
11082 }
11083
11084 static void PlayPlayerSound(struct PlayerInfo *player)
11085 {
11086   int jx = player->jx, jy = player->jy;
11087   int sound_element = player->artwork_element;
11088   int last_action = player->last_action_waiting;
11089   int action = player->action_waiting;
11090
11091   if (player->is_waiting)
11092   {
11093     if (action != last_action)
11094       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11095     else
11096       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11097   }
11098   else
11099   {
11100     if (action != last_action)
11101       StopSound(element_info[sound_element].sound[last_action]);
11102
11103     if (last_action == ACTION_SLEEPING)
11104       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11105   }
11106 }
11107
11108 static void PlayAllPlayersSound()
11109 {
11110   int i;
11111
11112   for (i = 0; i < MAX_PLAYERS; i++)
11113     if (stored_player[i].active)
11114       PlayPlayerSound(&stored_player[i]);
11115 }
11116
11117 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11118 {
11119   boolean last_waiting = player->is_waiting;
11120   int move_dir = player->MovDir;
11121
11122   player->dir_waiting = move_dir;
11123   player->last_action_waiting = player->action_waiting;
11124
11125   if (is_waiting)
11126   {
11127     if (!last_waiting)          /* not waiting -> waiting */
11128     {
11129       player->is_waiting = TRUE;
11130
11131       player->frame_counter_bored =
11132         FrameCounter +
11133         game.player_boring_delay_fixed +
11134         GetSimpleRandom(game.player_boring_delay_random);
11135       player->frame_counter_sleeping =
11136         FrameCounter +
11137         game.player_sleeping_delay_fixed +
11138         GetSimpleRandom(game.player_sleeping_delay_random);
11139
11140       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11141     }
11142
11143     if (game.player_sleeping_delay_fixed +
11144         game.player_sleeping_delay_random > 0 &&
11145         player->anim_delay_counter == 0 &&
11146         player->post_delay_counter == 0 &&
11147         FrameCounter >= player->frame_counter_sleeping)
11148       player->is_sleeping = TRUE;
11149     else if (game.player_boring_delay_fixed +
11150              game.player_boring_delay_random > 0 &&
11151              FrameCounter >= player->frame_counter_bored)
11152       player->is_bored = TRUE;
11153
11154     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11155                               player->is_bored ? ACTION_BORING :
11156                               ACTION_WAITING);
11157
11158     if (player->is_sleeping && player->use_murphy)
11159     {
11160       /* special case for sleeping Murphy when leaning against non-free tile */
11161
11162       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11163           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11164            !IS_MOVING(player->jx - 1, player->jy)))
11165         move_dir = MV_LEFT;
11166       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11167                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11168                 !IS_MOVING(player->jx + 1, player->jy)))
11169         move_dir = MV_RIGHT;
11170       else
11171         player->is_sleeping = FALSE;
11172
11173       player->dir_waiting = move_dir;
11174     }
11175
11176     if (player->is_sleeping)
11177     {
11178       if (player->num_special_action_sleeping > 0)
11179       {
11180         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11181         {
11182           int last_special_action = player->special_action_sleeping;
11183           int num_special_action = player->num_special_action_sleeping;
11184           int special_action =
11185             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11186              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11187              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11188              last_special_action + 1 : ACTION_SLEEPING);
11189           int special_graphic =
11190             el_act_dir2img(player->artwork_element, special_action, move_dir);
11191
11192           player->anim_delay_counter =
11193             graphic_info[special_graphic].anim_delay_fixed +
11194             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11195           player->post_delay_counter =
11196             graphic_info[special_graphic].post_delay_fixed +
11197             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11198
11199           player->special_action_sleeping = special_action;
11200         }
11201
11202         if (player->anim_delay_counter > 0)
11203         {
11204           player->action_waiting = player->special_action_sleeping;
11205           player->anim_delay_counter--;
11206         }
11207         else if (player->post_delay_counter > 0)
11208         {
11209           player->post_delay_counter--;
11210         }
11211       }
11212     }
11213     else if (player->is_bored)
11214     {
11215       if (player->num_special_action_bored > 0)
11216       {
11217         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11218         {
11219           int special_action =
11220             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11221           int special_graphic =
11222             el_act_dir2img(player->artwork_element, special_action, move_dir);
11223
11224           player->anim_delay_counter =
11225             graphic_info[special_graphic].anim_delay_fixed +
11226             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11227           player->post_delay_counter =
11228             graphic_info[special_graphic].post_delay_fixed +
11229             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11230
11231           player->special_action_bored = special_action;
11232         }
11233
11234         if (player->anim_delay_counter > 0)
11235         {
11236           player->action_waiting = player->special_action_bored;
11237           player->anim_delay_counter--;
11238         }
11239         else if (player->post_delay_counter > 0)
11240         {
11241           player->post_delay_counter--;
11242         }
11243       }
11244     }
11245   }
11246   else if (last_waiting)        /* waiting -> not waiting */
11247   {
11248     player->is_waiting = FALSE;
11249     player->is_bored = FALSE;
11250     player->is_sleeping = FALSE;
11251
11252     player->frame_counter_bored = -1;
11253     player->frame_counter_sleeping = -1;
11254
11255     player->anim_delay_counter = 0;
11256     player->post_delay_counter = 0;
11257
11258     player->dir_waiting = player->MovDir;
11259     player->action_waiting = ACTION_DEFAULT;
11260
11261     player->special_action_bored = ACTION_DEFAULT;
11262     player->special_action_sleeping = ACTION_DEFAULT;
11263   }
11264 }
11265
11266 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11267 {
11268   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11269   int left      = player_action & JOY_LEFT;
11270   int right     = player_action & JOY_RIGHT;
11271   int up        = player_action & JOY_UP;
11272   int down      = player_action & JOY_DOWN;
11273   int button1   = player_action & JOY_BUTTON_1;
11274   int button2   = player_action & JOY_BUTTON_2;
11275   int dx        = (left ? -1 : right ? 1 : 0);
11276   int dy        = (up   ? -1 : down  ? 1 : 0);
11277
11278   if (!player->active || tape.pausing)
11279     return 0;
11280
11281   if (player_action)
11282   {
11283     if (button1)
11284       snapped = SnapField(player, dx, dy);
11285     else
11286     {
11287       if (button2)
11288         dropped = DropElement(player);
11289
11290       moved = MovePlayer(player, dx, dy);
11291     }
11292
11293     if (tape.single_step && tape.recording && !tape.pausing)
11294     {
11295       if (button1 || (dropped && !moved))
11296       {
11297         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11298         SnapField(player, 0, 0);                /* stop snapping */
11299       }
11300     }
11301
11302     SetPlayerWaiting(player, FALSE);
11303
11304     return player_action;
11305   }
11306   else
11307   {
11308     /* no actions for this player (no input at player's configured device) */
11309
11310     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11311     SnapField(player, 0, 0);
11312     CheckGravityMovementWhenNotMoving(player);
11313
11314     if (player->MovPos == 0)
11315       SetPlayerWaiting(player, TRUE);
11316
11317     if (player->MovPos == 0)    /* needed for tape.playing */
11318       player->is_moving = FALSE;
11319
11320     player->is_dropping = FALSE;
11321     player->is_dropping_pressed = FALSE;
11322     player->drop_pressed_delay = 0;
11323
11324     return 0;
11325   }
11326 }
11327
11328 static void CheckLevelTime()
11329 {
11330   int i;
11331
11332   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11333   {
11334     if (level.native_em_level->lev->home == 0)  /* all players at home */
11335     {
11336       PlayerWins(local_player);
11337
11338       AllPlayersGone = TRUE;
11339
11340       level.native_em_level->lev->home = -1;
11341     }
11342
11343     if (level.native_em_level->ply[0]->alive == 0 &&
11344         level.native_em_level->ply[1]->alive == 0 &&
11345         level.native_em_level->ply[2]->alive == 0 &&
11346         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11347       AllPlayersGone = TRUE;
11348   }
11349
11350   if (TimeFrames >= FRAMES_PER_SECOND)
11351   {
11352     TimeFrames = 0;
11353     TapeTime++;
11354
11355     for (i = 0; i < MAX_PLAYERS; i++)
11356     {
11357       struct PlayerInfo *player = &stored_player[i];
11358
11359       if (SHIELD_ON(player))
11360       {
11361         player->shield_normal_time_left--;
11362
11363         if (player->shield_deadly_time_left > 0)
11364           player->shield_deadly_time_left--;
11365       }
11366     }
11367
11368     if (!local_player->LevelSolved && !level.use_step_counter)
11369     {
11370       TimePlayed++;
11371
11372       if (TimeLeft > 0)
11373       {
11374         TimeLeft--;
11375
11376         if (TimeLeft <= 10 && setup.time_limit)
11377           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11378
11379 #if 1
11380         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11381
11382         DisplayGameControlValues();
11383 #else
11384         DrawGameValue_Time(TimeLeft);
11385 #endif
11386
11387         if (!TimeLeft && setup.time_limit)
11388         {
11389           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11390             level.native_em_level->lev->killed_out_of_time = TRUE;
11391           else
11392             for (i = 0; i < MAX_PLAYERS; i++)
11393               KillPlayer(&stored_player[i]);
11394         }
11395       }
11396 #if 1
11397       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11398       {
11399         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11400
11401         DisplayGameControlValues();
11402       }
11403 #else
11404       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11405         DrawGameValue_Time(TimePlayed);
11406 #endif
11407
11408       level.native_em_level->lev->time =
11409         (level.time == 0 ? TimePlayed : TimeLeft);
11410     }
11411
11412     if (tape.recording || tape.playing)
11413       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11414   }
11415
11416   UpdateGameDoorValues();
11417   DrawGameDoorValues();
11418 }
11419
11420 void AdvanceFrameAndPlayerCounters(int player_nr)
11421 {
11422   int i;
11423
11424   /* advance frame counters (global frame counter and time frame counter) */
11425   FrameCounter++;
11426   TimeFrames++;
11427
11428   /* advance player counters (counters for move delay, move animation etc.) */
11429   for (i = 0; i < MAX_PLAYERS; i++)
11430   {
11431     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11432     int move_delay_value = stored_player[i].move_delay_value;
11433     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11434
11435     if (!advance_player_counters)       /* not all players may be affected */
11436       continue;
11437
11438 #if USE_NEW_PLAYER_ANIM
11439     if (move_frames == 0)       /* less than one move per game frame */
11440     {
11441       int stepsize = TILEX / move_delay_value;
11442       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11443       int count = (stored_player[i].is_moving ?
11444                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11445
11446       if (count % delay == 0)
11447         move_frames = 1;
11448     }
11449 #endif
11450
11451     stored_player[i].Frame += move_frames;
11452
11453     if (stored_player[i].MovPos != 0)
11454       stored_player[i].StepFrame += move_frames;
11455
11456     if (stored_player[i].move_delay > 0)
11457       stored_player[i].move_delay--;
11458
11459     /* due to bugs in previous versions, counter must count up, not down */
11460     if (stored_player[i].push_delay != -1)
11461       stored_player[i].push_delay++;
11462
11463     if (stored_player[i].drop_delay > 0)
11464       stored_player[i].drop_delay--;
11465
11466     if (stored_player[i].is_dropping_pressed)
11467       stored_player[i].drop_pressed_delay++;
11468   }
11469 }
11470
11471 void StartGameActions(boolean init_network_game, boolean record_tape,
11472                       long random_seed)
11473 {
11474   unsigned long new_random_seed = InitRND(random_seed);
11475
11476   if (record_tape)
11477     TapeStartRecording(new_random_seed);
11478
11479 #if defined(NETWORK_AVALIABLE)
11480   if (init_network_game)
11481   {
11482     SendToServer_StartPlaying();
11483
11484     return;
11485   }
11486 #endif
11487
11488   InitGame();
11489 }
11490
11491 void GameActions()
11492 {
11493   static unsigned long game_frame_delay = 0;
11494   unsigned long game_frame_delay_value;
11495   byte *recorded_player_action;
11496   byte summarized_player_action = 0;
11497   byte tape_action[MAX_PLAYERS];
11498   int i;
11499
11500   /* detect endless loops, caused by custom element programming */
11501   if (recursion_loop_detected && recursion_loop_depth == 0)
11502   {
11503     char *message = getStringCat3("Internal Error ! Element ",
11504                                   EL_NAME(recursion_loop_element),
11505                                   " caused endless loop ! Quit the game ?");
11506
11507     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11508           EL_NAME(recursion_loop_element));
11509
11510     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11511
11512     recursion_loop_detected = FALSE;    /* if game should be continued */
11513
11514     free(message);
11515
11516     return;
11517   }
11518
11519   if (game.restart_level)
11520     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11521
11522   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11523   {
11524     if (level.native_em_level->lev->home == 0)  /* all players at home */
11525     {
11526       PlayerWins(local_player);
11527
11528       AllPlayersGone = TRUE;
11529
11530       level.native_em_level->lev->home = -1;
11531     }
11532
11533     if (level.native_em_level->ply[0]->alive == 0 &&
11534         level.native_em_level->ply[1]->alive == 0 &&
11535         level.native_em_level->ply[2]->alive == 0 &&
11536         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11537       AllPlayersGone = TRUE;
11538   }
11539
11540   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11541     GameWon();
11542
11543   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11544     TapeStop();
11545
11546   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11547     return;
11548
11549   game_frame_delay_value =
11550     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11551
11552   if (tape.playing && tape.warp_forward && !tape.pausing)
11553     game_frame_delay_value = 0;
11554
11555   /* ---------- main game synchronization point ---------- */
11556
11557   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11558
11559   if (network_playing && !network_player_action_received)
11560   {
11561     /* try to get network player actions in time */
11562
11563 #if defined(NETWORK_AVALIABLE)
11564     /* last chance to get network player actions without main loop delay */
11565     HandleNetworking();
11566 #endif
11567
11568     /* game was quit by network peer */
11569     if (game_status != GAME_MODE_PLAYING)
11570       return;
11571
11572     if (!network_player_action_received)
11573       return;           /* failed to get network player actions in time */
11574
11575     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11576   }
11577
11578   if (tape.pausing)
11579     return;
11580
11581   /* at this point we know that we really continue executing the game */
11582
11583   network_player_action_received = FALSE;
11584
11585   /* when playing tape, read previously recorded player input from tape data */
11586   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11587
11588 #if 1
11589   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11590   if (tape.pausing)
11591     return;
11592 #endif
11593
11594   if (tape.set_centered_player)
11595   {
11596     game.centered_player_nr_next = tape.centered_player_nr_next;
11597     game.set_centered_player = TRUE;
11598   }
11599
11600   for (i = 0; i < MAX_PLAYERS; i++)
11601   {
11602     summarized_player_action |= stored_player[i].action;
11603
11604     if (!network_playing)
11605       stored_player[i].effective_action = stored_player[i].action;
11606   }
11607
11608 #if defined(NETWORK_AVALIABLE)
11609   if (network_playing)
11610     SendToServer_MovePlayer(summarized_player_action);
11611 #endif
11612
11613   if (!options.network && !setup.team_mode)
11614     local_player->effective_action = summarized_player_action;
11615
11616   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11617   {
11618     for (i = 0; i < MAX_PLAYERS; i++)
11619       stored_player[i].effective_action =
11620         (i == game.centered_player_nr ? summarized_player_action : 0);
11621   }
11622
11623   if (recorded_player_action != NULL)
11624     for (i = 0; i < MAX_PLAYERS; i++)
11625       stored_player[i].effective_action = recorded_player_action[i];
11626
11627   for (i = 0; i < MAX_PLAYERS; i++)
11628   {
11629     tape_action[i] = stored_player[i].effective_action;
11630
11631     /* (this can only happen in the R'n'D game engine) */
11632     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11633       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11634   }
11635
11636   /* only record actions from input devices, but not programmed actions */
11637   if (tape.recording)
11638     TapeRecordAction(tape_action);
11639
11640   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11641   {
11642     GameActions_EM_Main();
11643   }
11644   else
11645   {
11646     GameActions_RND();
11647   }
11648 }
11649
11650 void GameActions_EM_Main()
11651 {
11652   byte effective_action[MAX_PLAYERS];
11653   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11654   int i;
11655
11656   for (i = 0; i < MAX_PLAYERS; i++)
11657     effective_action[i] = stored_player[i].effective_action;
11658
11659   GameActions_EM(effective_action, warp_mode);
11660
11661   CheckLevelTime();
11662
11663   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11664 }
11665
11666 void GameActions_RND()
11667 {
11668   int magic_wall_x = 0, magic_wall_y = 0;
11669   int i, x, y, element, graphic;
11670
11671   InitPlayfieldScanModeVars();
11672
11673 #if USE_ONE_MORE_CHANGE_PER_FRAME
11674   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11675   {
11676     SCAN_PLAYFIELD(x, y)
11677     {
11678       ChangeCount[x][y] = 0;
11679       ChangeEvent[x][y] = -1;
11680     }
11681   }
11682 #endif
11683
11684   if (game.set_centered_player)
11685   {
11686     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11687
11688     /* switching to "all players" only possible if all players fit to screen */
11689     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11690     {
11691       game.centered_player_nr_next = game.centered_player_nr;
11692       game.set_centered_player = FALSE;
11693     }
11694
11695     /* do not switch focus to non-existing (or non-active) player */
11696     if (game.centered_player_nr_next >= 0 &&
11697         !stored_player[game.centered_player_nr_next].active)
11698     {
11699       game.centered_player_nr_next = game.centered_player_nr;
11700       game.set_centered_player = FALSE;
11701     }
11702   }
11703
11704   if (game.set_centered_player &&
11705       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11706   {
11707     int sx, sy;
11708
11709     if (game.centered_player_nr_next == -1)
11710     {
11711       setScreenCenteredToAllPlayers(&sx, &sy);
11712     }
11713     else
11714     {
11715       sx = stored_player[game.centered_player_nr_next].jx;
11716       sy = stored_player[game.centered_player_nr_next].jy;
11717     }
11718
11719     game.centered_player_nr = game.centered_player_nr_next;
11720     game.set_centered_player = FALSE;
11721
11722     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11723     DrawGameDoorValues();
11724   }
11725
11726   for (i = 0; i < MAX_PLAYERS; i++)
11727   {
11728     int actual_player_action = stored_player[i].effective_action;
11729
11730 #if 1
11731     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11732        - rnd_equinox_tetrachloride 048
11733        - rnd_equinox_tetrachloride_ii 096
11734        - rnd_emanuel_schmieg 002
11735        - doctor_sloan_ww 001, 020
11736     */
11737     if (stored_player[i].MovPos == 0)
11738       CheckGravityMovement(&stored_player[i]);
11739 #endif
11740
11741     /* overwrite programmed action with tape action */
11742     if (stored_player[i].programmed_action)
11743       actual_player_action = stored_player[i].programmed_action;
11744
11745     PlayerActions(&stored_player[i], actual_player_action);
11746
11747     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11748   }
11749
11750   ScrollScreen(NULL, SCROLL_GO_ON);
11751
11752   /* for backwards compatibility, the following code emulates a fixed bug that
11753      occured when pushing elements (causing elements that just made their last
11754      pushing step to already (if possible) make their first falling step in the
11755      same game frame, which is bad); this code is also needed to use the famous
11756      "spring push bug" which is used in older levels and might be wanted to be
11757      used also in newer levels, but in this case the buggy pushing code is only
11758      affecting the "spring" element and no other elements */
11759
11760   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11761   {
11762     for (i = 0; i < MAX_PLAYERS; i++)
11763     {
11764       struct PlayerInfo *player = &stored_player[i];
11765       int x = player->jx;
11766       int y = player->jy;
11767
11768       if (player->active && player->is_pushing && player->is_moving &&
11769           IS_MOVING(x, y) &&
11770           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11771            Feld[x][y] == EL_SPRING))
11772       {
11773         ContinueMoving(x, y);
11774
11775         /* continue moving after pushing (this is actually a bug) */
11776         if (!IS_MOVING(x, y))
11777           Stop[x][y] = FALSE;
11778       }
11779     }
11780   }
11781
11782 #if 0
11783   debug_print_timestamp(0, "start main loop profiling");
11784 #endif
11785
11786   SCAN_PLAYFIELD(x, y)
11787   {
11788     ChangeCount[x][y] = 0;
11789     ChangeEvent[x][y] = -1;
11790
11791     /* this must be handled before main playfield loop */
11792     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11793     {
11794       MovDelay[x][y]--;
11795       if (MovDelay[x][y] <= 0)
11796         RemoveField(x, y);
11797     }
11798
11799 #if USE_NEW_SNAP_DELAY
11800     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11801     {
11802       MovDelay[x][y]--;
11803       if (MovDelay[x][y] <= 0)
11804       {
11805         RemoveField(x, y);
11806         DrawLevelField(x, y);
11807
11808         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11809       }
11810     }
11811 #endif
11812
11813 #if DEBUG
11814     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11815     {
11816       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11817       printf("GameActions(): This should never happen!\n");
11818
11819       ChangePage[x][y] = -1;
11820     }
11821 #endif
11822
11823     Stop[x][y] = FALSE;
11824     if (WasJustMoving[x][y] > 0)
11825       WasJustMoving[x][y]--;
11826     if (WasJustFalling[x][y] > 0)
11827       WasJustFalling[x][y]--;
11828     if (CheckCollision[x][y] > 0)
11829       CheckCollision[x][y]--;
11830     if (CheckImpact[x][y] > 0)
11831       CheckImpact[x][y]--;
11832
11833     GfxFrame[x][y]++;
11834
11835     /* reset finished pushing action (not done in ContinueMoving() to allow
11836        continuous pushing animation for elements with zero push delay) */
11837     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11838     {
11839       ResetGfxAnimation(x, y);
11840       DrawLevelField(x, y);
11841     }
11842
11843 #if DEBUG
11844     if (IS_BLOCKED(x, y))
11845     {
11846       int oldx, oldy;
11847
11848       Blocked2Moving(x, y, &oldx, &oldy);
11849       if (!IS_MOVING(oldx, oldy))
11850       {
11851         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11852         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11853         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11854         printf("GameActions(): This should never happen!\n");
11855       }
11856     }
11857 #endif
11858   }
11859
11860 #if 0
11861   debug_print_timestamp(0, "- time for pre-main loop:");
11862 #endif
11863
11864 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11865   SCAN_PLAYFIELD(x, y)
11866   {
11867     element = Feld[x][y];
11868     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11869
11870 #if 1
11871     {
11872 #if 1
11873       int element2 = element;
11874       int graphic2 = graphic;
11875 #else
11876       int element2 = Feld[x][y];
11877       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11878 #endif
11879       int last_gfx_frame = GfxFrame[x][y];
11880
11881       if (graphic_info[graphic2].anim_global_sync)
11882         GfxFrame[x][y] = FrameCounter;
11883       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11884         GfxFrame[x][y] = CustomValue[x][y];
11885       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11886         GfxFrame[x][y] = element_info[element2].collect_score;
11887       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11888         GfxFrame[x][y] = ChangeDelay[x][y];
11889
11890       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11891         DrawLevelGraphicAnimation(x, y, graphic2);
11892     }
11893 #else
11894     ResetGfxFrame(x, y, TRUE);
11895 #endif
11896
11897 #if 1
11898     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11899         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11900       ResetRandomAnimationValue(x, y);
11901 #endif
11902
11903 #if 1
11904     SetRandomAnimationValue(x, y);
11905 #endif
11906
11907 #if 1
11908     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11909 #endif
11910   }
11911 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11912
11913 #if 0
11914   debug_print_timestamp(0, "- time for TEST loop:     -->");
11915 #endif
11916
11917   SCAN_PLAYFIELD(x, y)
11918   {
11919     element = Feld[x][y];
11920     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11921
11922     ResetGfxFrame(x, y, TRUE);
11923
11924     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11925         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11926       ResetRandomAnimationValue(x, y);
11927
11928     SetRandomAnimationValue(x, y);
11929
11930     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11931
11932     if (IS_INACTIVE(element))
11933     {
11934       if (IS_ANIMATED(graphic))
11935         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11936
11937       continue;
11938     }
11939
11940     /* this may take place after moving, so 'element' may have changed */
11941     if (IS_CHANGING(x, y) &&
11942         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11943     {
11944       int page = element_info[element].event_page_nr[CE_DELAY];
11945
11946 #if 1
11947       HandleElementChange(x, y, page);
11948 #else
11949       if (CAN_CHANGE(element))
11950         HandleElementChange(x, y, page);
11951
11952       if (HAS_ACTION(element))
11953         ExecuteCustomElementAction(x, y, element, page);
11954 #endif
11955
11956       element = Feld[x][y];
11957       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11958     }
11959
11960 #if 0   // ---------------------------------------------------------------------
11961
11962     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11963     {
11964       StartMoving(x, y);
11965
11966       element = Feld[x][y];
11967       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11968
11969       if (IS_ANIMATED(graphic) &&
11970           !IS_MOVING(x, y) &&
11971           !Stop[x][y])
11972         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11973
11974       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11975         DrawTwinkleOnField(x, y);
11976     }
11977     else if (IS_MOVING(x, y))
11978       ContinueMoving(x, y);
11979     else
11980     {
11981       switch (element)
11982       {
11983         case EL_ACID:
11984         case EL_EXIT_OPEN:
11985         case EL_EM_EXIT_OPEN:
11986         case EL_SP_EXIT_OPEN:
11987         case EL_STEEL_EXIT_OPEN:
11988         case EL_EM_STEEL_EXIT_OPEN:
11989         case EL_SP_TERMINAL:
11990         case EL_SP_TERMINAL_ACTIVE:
11991         case EL_EXTRA_TIME:
11992         case EL_SHIELD_NORMAL:
11993         case EL_SHIELD_DEADLY:
11994           if (IS_ANIMATED(graphic))
11995             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11996           break;
11997
11998         case EL_DYNAMITE_ACTIVE:
11999         case EL_EM_DYNAMITE_ACTIVE:
12000         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12001         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12002         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12003         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12004         case EL_SP_DISK_RED_ACTIVE:
12005           CheckDynamite(x, y);
12006           break;
12007
12008         case EL_AMOEBA_GROWING:
12009           AmoebeWaechst(x, y);
12010           break;
12011
12012         case EL_AMOEBA_SHRINKING:
12013           AmoebaDisappearing(x, y);
12014           break;
12015
12016 #if !USE_NEW_AMOEBA_CODE
12017         case EL_AMOEBA_WET:
12018         case EL_AMOEBA_DRY:
12019         case EL_AMOEBA_FULL:
12020         case EL_BD_AMOEBA:
12021         case EL_EMC_DRIPPER:
12022           AmoebeAbleger(x, y);
12023           break;
12024 #endif
12025
12026         case EL_GAME_OF_LIFE:
12027         case EL_BIOMAZE:
12028           Life(x, y);
12029           break;
12030
12031         case EL_EXIT_CLOSED:
12032           CheckExit(x, y);
12033           break;
12034
12035         case EL_EM_EXIT_CLOSED:
12036           CheckExitEM(x, y);
12037           break;
12038
12039         case EL_STEEL_EXIT_CLOSED:
12040           CheckExitSteel(x, y);
12041           break;
12042
12043         case EL_EM_STEEL_EXIT_CLOSED:
12044           CheckExitSteelEM(x, y);
12045           break;
12046
12047         case EL_SP_EXIT_CLOSED:
12048           CheckExitSP(x, y);
12049           break;
12050
12051         case EL_EXPANDABLE_WALL_GROWING:
12052         case EL_EXPANDABLE_STEELWALL_GROWING:
12053           MauerWaechst(x, y);
12054           break;
12055
12056         case EL_EXPANDABLE_WALL:
12057         case EL_EXPANDABLE_WALL_HORIZONTAL:
12058         case EL_EXPANDABLE_WALL_VERTICAL:
12059         case EL_EXPANDABLE_WALL_ANY:
12060         case EL_BD_EXPANDABLE_WALL:
12061           MauerAbleger(x, y);
12062           break;
12063
12064         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12065         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12066         case EL_EXPANDABLE_STEELWALL_ANY:
12067           MauerAblegerStahl(x, y);
12068           break;
12069
12070         case EL_FLAMES:
12071           CheckForDragon(x, y);
12072           break;
12073
12074         case EL_EXPLOSION:
12075           break;
12076
12077         case EL_ELEMENT_SNAPPING:
12078         case EL_DIAGONAL_SHRINKING:
12079         case EL_DIAGONAL_GROWING:
12080         {
12081           graphic =
12082             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12083
12084           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12085           break;
12086         }
12087
12088         default:
12089           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12090             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12091           break;
12092       }
12093     }
12094
12095 #else   // ---------------------------------------------------------------------
12096
12097     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12098     {
12099       StartMoving(x, y);
12100
12101       element = Feld[x][y];
12102       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12103
12104       if (IS_ANIMATED(graphic) &&
12105           !IS_MOVING(x, y) &&
12106           !Stop[x][y])
12107         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12108
12109       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12110         DrawTwinkleOnField(x, y);
12111     }
12112     else if ((element == EL_ACID ||
12113               element == EL_EXIT_OPEN ||
12114               element == EL_EM_EXIT_OPEN ||
12115               element == EL_SP_EXIT_OPEN ||
12116               element == EL_STEEL_EXIT_OPEN ||
12117               element == EL_EM_STEEL_EXIT_OPEN ||
12118               element == EL_SP_TERMINAL ||
12119               element == EL_SP_TERMINAL_ACTIVE ||
12120               element == EL_EXTRA_TIME ||
12121               element == EL_SHIELD_NORMAL ||
12122               element == EL_SHIELD_DEADLY) &&
12123              IS_ANIMATED(graphic))
12124       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12125     else if (IS_MOVING(x, y))
12126       ContinueMoving(x, y);
12127     else if (IS_ACTIVE_BOMB(element))
12128       CheckDynamite(x, y);
12129     else if (element == EL_AMOEBA_GROWING)
12130       AmoebeWaechst(x, y);
12131     else if (element == EL_AMOEBA_SHRINKING)
12132       AmoebaDisappearing(x, y);
12133
12134 #if !USE_NEW_AMOEBA_CODE
12135     else if (IS_AMOEBALIVE(element))
12136       AmoebeAbleger(x, y);
12137 #endif
12138
12139     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12140       Life(x, y);
12141     else if (element == EL_EXIT_CLOSED)
12142       CheckExit(x, y);
12143     else if (element == EL_EM_EXIT_CLOSED)
12144       CheckExitEM(x, y);
12145     else if (element == EL_STEEL_EXIT_CLOSED)
12146       CheckExitSteel(x, y);
12147     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12148       CheckExitSteelEM(x, y);
12149     else if (element == EL_SP_EXIT_CLOSED)
12150       CheckExitSP(x, y);
12151     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12152              element == EL_EXPANDABLE_STEELWALL_GROWING)
12153       MauerWaechst(x, y);
12154     else if (element == EL_EXPANDABLE_WALL ||
12155              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12156              element == EL_EXPANDABLE_WALL_VERTICAL ||
12157              element == EL_EXPANDABLE_WALL_ANY ||
12158              element == EL_BD_EXPANDABLE_WALL)
12159       MauerAbleger(x, y);
12160     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12161              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12162              element == EL_EXPANDABLE_STEELWALL_ANY)
12163       MauerAblegerStahl(x, y);
12164     else if (element == EL_FLAMES)
12165       CheckForDragon(x, y);
12166     else if (element == EL_EXPLOSION)
12167       ; /* drawing of correct explosion animation is handled separately */
12168     else if (element == EL_ELEMENT_SNAPPING ||
12169              element == EL_DIAGONAL_SHRINKING ||
12170              element == EL_DIAGONAL_GROWING)
12171     {
12172       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12173
12174       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12175     }
12176     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12177       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12178
12179 #endif  // ---------------------------------------------------------------------
12180
12181     if (IS_BELT_ACTIVE(element))
12182       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12183
12184     if (game.magic_wall_active)
12185     {
12186       int jx = local_player->jx, jy = local_player->jy;
12187
12188       /* play the element sound at the position nearest to the player */
12189       if ((element == EL_MAGIC_WALL_FULL ||
12190            element == EL_MAGIC_WALL_ACTIVE ||
12191            element == EL_MAGIC_WALL_EMPTYING ||
12192            element == EL_BD_MAGIC_WALL_FULL ||
12193            element == EL_BD_MAGIC_WALL_ACTIVE ||
12194            element == EL_BD_MAGIC_WALL_EMPTYING ||
12195            element == EL_DC_MAGIC_WALL_FULL ||
12196            element == EL_DC_MAGIC_WALL_ACTIVE ||
12197            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12198           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12199       {
12200         magic_wall_x = x;
12201         magic_wall_y = y;
12202       }
12203     }
12204   }
12205
12206 #if 0
12207   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12208 #endif
12209
12210 #if USE_NEW_AMOEBA_CODE
12211   /* new experimental amoeba growth stuff */
12212   if (!(FrameCounter % 8))
12213   {
12214     static unsigned long random = 1684108901;
12215
12216     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12217     {
12218       x = RND(lev_fieldx);
12219       y = RND(lev_fieldy);
12220       element = Feld[x][y];
12221
12222       if (!IS_PLAYER(x,y) &&
12223           (element == EL_EMPTY ||
12224            CAN_GROW_INTO(element) ||
12225            element == EL_QUICKSAND_EMPTY ||
12226            element == EL_QUICKSAND_FAST_EMPTY ||
12227            element == EL_ACID_SPLASH_LEFT ||
12228            element == EL_ACID_SPLASH_RIGHT))
12229       {
12230         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12231             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12232             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12233             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12234           Feld[x][y] = EL_AMOEBA_DROP;
12235       }
12236
12237       random = random * 129 + 1;
12238     }
12239   }
12240 #endif
12241
12242 #if 0
12243   if (game.explosions_delayed)
12244 #endif
12245   {
12246     game.explosions_delayed = FALSE;
12247
12248     SCAN_PLAYFIELD(x, y)
12249     {
12250       element = Feld[x][y];
12251
12252       if (ExplodeField[x][y])
12253         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12254       else if (element == EL_EXPLOSION)
12255         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12256
12257       ExplodeField[x][y] = EX_TYPE_NONE;
12258     }
12259
12260     game.explosions_delayed = TRUE;
12261   }
12262
12263   if (game.magic_wall_active)
12264   {
12265     if (!(game.magic_wall_time_left % 4))
12266     {
12267       int element = Feld[magic_wall_x][magic_wall_y];
12268
12269       if (element == EL_BD_MAGIC_WALL_FULL ||
12270           element == EL_BD_MAGIC_WALL_ACTIVE ||
12271           element == EL_BD_MAGIC_WALL_EMPTYING)
12272         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12273       else if (element == EL_DC_MAGIC_WALL_FULL ||
12274                element == EL_DC_MAGIC_WALL_ACTIVE ||
12275                element == EL_DC_MAGIC_WALL_EMPTYING)
12276         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12277       else
12278         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12279     }
12280
12281     if (game.magic_wall_time_left > 0)
12282     {
12283       game.magic_wall_time_left--;
12284
12285       if (!game.magic_wall_time_left)
12286       {
12287         SCAN_PLAYFIELD(x, y)
12288         {
12289           element = Feld[x][y];
12290
12291           if (element == EL_MAGIC_WALL_ACTIVE ||
12292               element == EL_MAGIC_WALL_FULL)
12293           {
12294             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12295             DrawLevelField(x, y);
12296           }
12297           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12298                    element == EL_BD_MAGIC_WALL_FULL)
12299           {
12300             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12301             DrawLevelField(x, y);
12302           }
12303           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12304                    element == EL_DC_MAGIC_WALL_FULL)
12305           {
12306             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12307             DrawLevelField(x, y);
12308           }
12309         }
12310
12311         game.magic_wall_active = FALSE;
12312       }
12313     }
12314   }
12315
12316   if (game.light_time_left > 0)
12317   {
12318     game.light_time_left--;
12319
12320     if (game.light_time_left == 0)
12321       RedrawAllLightSwitchesAndInvisibleElements();
12322   }
12323
12324   if (game.timegate_time_left > 0)
12325   {
12326     game.timegate_time_left--;
12327
12328     if (game.timegate_time_left == 0)
12329       CloseAllOpenTimegates();
12330   }
12331
12332   if (game.lenses_time_left > 0)
12333   {
12334     game.lenses_time_left--;
12335
12336     if (game.lenses_time_left == 0)
12337       RedrawAllInvisibleElementsForLenses();
12338   }
12339
12340   if (game.magnify_time_left > 0)
12341   {
12342     game.magnify_time_left--;
12343
12344     if (game.magnify_time_left == 0)
12345       RedrawAllInvisibleElementsForMagnifier();
12346   }
12347
12348   for (i = 0; i < MAX_PLAYERS; i++)
12349   {
12350     struct PlayerInfo *player = &stored_player[i];
12351
12352     if (SHIELD_ON(player))
12353     {
12354       if (player->shield_deadly_time_left)
12355         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12356       else if (player->shield_normal_time_left)
12357         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12358     }
12359   }
12360
12361   CheckLevelTime();
12362
12363   DrawAllPlayers();
12364   PlayAllPlayersSound();
12365
12366   if (options.debug)                    /* calculate frames per second */
12367   {
12368     static unsigned long fps_counter = 0;
12369     static int fps_frames = 0;
12370     unsigned long fps_delay_ms = Counter() - fps_counter;
12371
12372     fps_frames++;
12373
12374     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12375     {
12376       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12377
12378       fps_frames = 0;
12379       fps_counter = Counter();
12380     }
12381
12382     redraw_mask |= REDRAW_FPS;
12383   }
12384
12385   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12386
12387   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12388   {
12389     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12390
12391     local_player->show_envelope = 0;
12392   }
12393
12394 #if 0
12395   debug_print_timestamp(0, "stop main loop profiling ");
12396   printf("----------------------------------------------------------\n");
12397 #endif
12398
12399   /* use random number generator in every frame to make it less predictable */
12400   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12401     RND(1);
12402 }
12403
12404 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12405 {
12406   int min_x = x, min_y = y, max_x = x, max_y = y;
12407   int i;
12408
12409   for (i = 0; i < MAX_PLAYERS; i++)
12410   {
12411     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12412
12413     if (!stored_player[i].active || &stored_player[i] == player)
12414       continue;
12415
12416     min_x = MIN(min_x, jx);
12417     min_y = MIN(min_y, jy);
12418     max_x = MAX(max_x, jx);
12419     max_y = MAX(max_y, jy);
12420   }
12421
12422   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12423 }
12424
12425 static boolean AllPlayersInVisibleScreen()
12426 {
12427   int i;
12428
12429   for (i = 0; i < MAX_PLAYERS; i++)
12430   {
12431     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12432
12433     if (!stored_player[i].active)
12434       continue;
12435
12436     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12437       return FALSE;
12438   }
12439
12440   return TRUE;
12441 }
12442
12443 void ScrollLevel(int dx, int dy)
12444 {
12445 #if 1
12446   static Bitmap *bitmap_db_field2 = NULL;
12447   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12448   int x, y;
12449 #else
12450   int i, x, y;
12451 #endif
12452
12453 #if 0
12454   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12455   /* only horizontal XOR vertical scroll direction allowed */
12456   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12457     return;
12458 #endif
12459
12460 #if 1
12461   if (bitmap_db_field2 == NULL)
12462     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12463
12464   /* needed when blitting directly to same bitmap -- should not be needed with
12465      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12466   BlitBitmap(drawto_field, bitmap_db_field2,
12467              FX + TILEX * (dx == -1) - softscroll_offset,
12468              FY + TILEY * (dy == -1) - softscroll_offset,
12469              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12470              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12471              FX + TILEX * (dx == 1) - softscroll_offset,
12472              FY + TILEY * (dy == 1) - softscroll_offset);
12473   BlitBitmap(bitmap_db_field2, drawto_field,
12474              FX + TILEX * (dx == 1) - softscroll_offset,
12475              FY + TILEY * (dy == 1) - softscroll_offset,
12476              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12477              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12478              FX + TILEX * (dx == 1) - softscroll_offset,
12479              FY + TILEY * (dy == 1) - softscroll_offset);
12480
12481 #else
12482
12483 #if 1
12484   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12485   int xsize = (BX2 - BX1 + 1);
12486   int ysize = (BY2 - BY1 + 1);
12487   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12488   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12489   int step  = (start < end ? +1 : -1);
12490
12491   for (i = start; i != end; i += step)
12492   {
12493     BlitBitmap(drawto_field, drawto_field,
12494                FX + TILEX * (dx != 0 ? i + step : 0),
12495                FY + TILEY * (dy != 0 ? i + step : 0),
12496                TILEX * (dx != 0 ? 1 : xsize),
12497                TILEY * (dy != 0 ? 1 : ysize),
12498                FX + TILEX * (dx != 0 ? i : 0),
12499                FY + TILEY * (dy != 0 ? i : 0));
12500   }
12501
12502 #else
12503
12504   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12505
12506   BlitBitmap(drawto_field, drawto_field,
12507              FX + TILEX * (dx == -1) - softscroll_offset,
12508              FY + TILEY * (dy == -1) - softscroll_offset,
12509              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12510              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12511              FX + TILEX * (dx == 1) - softscroll_offset,
12512              FY + TILEY * (dy == 1) - softscroll_offset);
12513 #endif
12514 #endif
12515
12516   if (dx != 0)
12517   {
12518     x = (dx == 1 ? BX1 : BX2);
12519     for (y = BY1; y <= BY2; y++)
12520       DrawScreenField(x, y);
12521   }
12522
12523   if (dy != 0)
12524   {
12525     y = (dy == 1 ? BY1 : BY2);
12526     for (x = BX1; x <= BX2; x++)
12527       DrawScreenField(x, y);
12528   }
12529
12530   redraw_mask |= REDRAW_FIELD;
12531 }
12532
12533 static boolean canFallDown(struct PlayerInfo *player)
12534 {
12535   int jx = player->jx, jy = player->jy;
12536
12537   return (IN_LEV_FIELD(jx, jy + 1) &&
12538           (IS_FREE(jx, jy + 1) ||
12539            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12540           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12541           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12542 }
12543
12544 static boolean canPassField(int x, int y, int move_dir)
12545 {
12546   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12547   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12548   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12549   int nextx = x + dx;
12550   int nexty = y + dy;
12551   int element = Feld[x][y];
12552
12553   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12554           !CAN_MOVE(element) &&
12555           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12556           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12557           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12558 }
12559
12560 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12561 {
12562   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12563   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12564   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12565   int newx = x + dx;
12566   int newy = y + dy;
12567
12568   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12569           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12570           (IS_DIGGABLE(Feld[newx][newy]) ||
12571            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12572            canPassField(newx, newy, move_dir)));
12573 }
12574
12575 static void CheckGravityMovement(struct PlayerInfo *player)
12576 {
12577 #if USE_PLAYER_GRAVITY
12578   if (player->gravity && !player->programmed_action)
12579 #else
12580   if (game.gravity && !player->programmed_action)
12581 #endif
12582   {
12583     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12584     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12585     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12586     int jx = player->jx, jy = player->jy;
12587     boolean player_is_moving_to_valid_field =
12588       (!player_is_snapping &&
12589        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12590         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12591     boolean player_can_fall_down = canFallDown(player);
12592
12593     if (player_can_fall_down &&
12594         !player_is_moving_to_valid_field)
12595       player->programmed_action = MV_DOWN;
12596   }
12597 }
12598
12599 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12600 {
12601   return CheckGravityMovement(player);
12602
12603 #if USE_PLAYER_GRAVITY
12604   if (player->gravity && !player->programmed_action)
12605 #else
12606   if (game.gravity && !player->programmed_action)
12607 #endif
12608   {
12609     int jx = player->jx, jy = player->jy;
12610     boolean field_under_player_is_free =
12611       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12612     boolean player_is_standing_on_valid_field =
12613       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12614        (IS_WALKABLE(Feld[jx][jy]) &&
12615         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12616
12617     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12618       player->programmed_action = MV_DOWN;
12619   }
12620 }
12621
12622 /*
12623   MovePlayerOneStep()
12624   -----------------------------------------------------------------------------
12625   dx, dy:               direction (non-diagonal) to try to move the player to
12626   real_dx, real_dy:     direction as read from input device (can be diagonal)
12627 */
12628
12629 boolean MovePlayerOneStep(struct PlayerInfo *player,
12630                           int dx, int dy, int real_dx, int real_dy)
12631 {
12632   int jx = player->jx, jy = player->jy;
12633   int new_jx = jx + dx, new_jy = jy + dy;
12634 #if !USE_FIXED_DONT_RUN_INTO
12635   int element;
12636 #endif
12637   int can_move;
12638   boolean player_can_move = !player->cannot_move;
12639
12640   if (!player->active || (!dx && !dy))
12641     return MP_NO_ACTION;
12642
12643   player->MovDir = (dx < 0 ? MV_LEFT :
12644                     dx > 0 ? MV_RIGHT :
12645                     dy < 0 ? MV_UP :
12646                     dy > 0 ? MV_DOWN :  MV_NONE);
12647
12648   if (!IN_LEV_FIELD(new_jx, new_jy))
12649     return MP_NO_ACTION;
12650
12651   if (!player_can_move)
12652   {
12653     if (player->MovPos == 0)
12654     {
12655       player->is_moving = FALSE;
12656       player->is_digging = FALSE;
12657       player->is_collecting = FALSE;
12658       player->is_snapping = FALSE;
12659       player->is_pushing = FALSE;
12660     }
12661   }
12662
12663 #if 1
12664   if (!options.network && game.centered_player_nr == -1 &&
12665       !AllPlayersInSight(player, new_jx, new_jy))
12666     return MP_NO_ACTION;
12667 #else
12668   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12669     return MP_NO_ACTION;
12670 #endif
12671
12672 #if !USE_FIXED_DONT_RUN_INTO
12673   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12674
12675   /* (moved to DigField()) */
12676   if (player_can_move && DONT_RUN_INTO(element))
12677   {
12678     if (element == EL_ACID && dx == 0 && dy == 1)
12679     {
12680       SplashAcid(new_jx, new_jy);
12681       Feld[jx][jy] = EL_PLAYER_1;
12682       InitMovingField(jx, jy, MV_DOWN);
12683       Store[jx][jy] = EL_ACID;
12684       ContinueMoving(jx, jy);
12685       BuryPlayer(player);
12686     }
12687     else
12688       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12689
12690     return MP_MOVING;
12691   }
12692 #endif
12693
12694   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12695   if (can_move != MP_MOVING)
12696     return can_move;
12697
12698   /* check if DigField() has caused relocation of the player */
12699   if (player->jx != jx || player->jy != jy)
12700     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12701
12702   StorePlayer[jx][jy] = 0;
12703   player->last_jx = jx;
12704   player->last_jy = jy;
12705   player->jx = new_jx;
12706   player->jy = new_jy;
12707   StorePlayer[new_jx][new_jy] = player->element_nr;
12708
12709   if (player->move_delay_value_next != -1)
12710   {
12711     player->move_delay_value = player->move_delay_value_next;
12712     player->move_delay_value_next = -1;
12713   }
12714
12715   player->MovPos =
12716     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12717
12718   player->step_counter++;
12719
12720   PlayerVisit[jx][jy] = FrameCounter;
12721
12722 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12723   player->is_moving = TRUE;
12724 #endif
12725
12726 #if 1
12727   /* should better be called in MovePlayer(), but this breaks some tapes */
12728   ScrollPlayer(player, SCROLL_INIT);
12729 #endif
12730
12731   return MP_MOVING;
12732 }
12733
12734 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12735 {
12736   int jx = player->jx, jy = player->jy;
12737   int old_jx = jx, old_jy = jy;
12738   int moved = MP_NO_ACTION;
12739
12740   if (!player->active)
12741     return FALSE;
12742
12743   if (!dx && !dy)
12744   {
12745     if (player->MovPos == 0)
12746     {
12747       player->is_moving = FALSE;
12748       player->is_digging = FALSE;
12749       player->is_collecting = FALSE;
12750       player->is_snapping = FALSE;
12751       player->is_pushing = FALSE;
12752     }
12753
12754     return FALSE;
12755   }
12756
12757   if (player->move_delay > 0)
12758     return FALSE;
12759
12760   player->move_delay = -1;              /* set to "uninitialized" value */
12761
12762   /* store if player is automatically moved to next field */
12763   player->is_auto_moving = (player->programmed_action != MV_NONE);
12764
12765   /* remove the last programmed player action */
12766   player->programmed_action = 0;
12767
12768   if (player->MovPos)
12769   {
12770     /* should only happen if pre-1.2 tape recordings are played */
12771     /* this is only for backward compatibility */
12772
12773     int original_move_delay_value = player->move_delay_value;
12774
12775 #if DEBUG
12776     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12777            tape.counter);
12778 #endif
12779
12780     /* scroll remaining steps with finest movement resolution */
12781     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12782
12783     while (player->MovPos)
12784     {
12785       ScrollPlayer(player, SCROLL_GO_ON);
12786       ScrollScreen(NULL, SCROLL_GO_ON);
12787
12788       AdvanceFrameAndPlayerCounters(player->index_nr);
12789
12790       DrawAllPlayers();
12791       BackToFront();
12792     }
12793
12794     player->move_delay_value = original_move_delay_value;
12795   }
12796
12797   player->is_active = FALSE;
12798
12799   if (player->last_move_dir & MV_HORIZONTAL)
12800   {
12801     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12802       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12803   }
12804   else
12805   {
12806     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12807       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12808   }
12809
12810 #if USE_FIXED_BORDER_RUNNING_GFX
12811   if (!moved && !player->is_active)
12812   {
12813     player->is_moving = FALSE;
12814     player->is_digging = FALSE;
12815     player->is_collecting = FALSE;
12816     player->is_snapping = FALSE;
12817     player->is_pushing = FALSE;
12818   }
12819 #endif
12820
12821   jx = player->jx;
12822   jy = player->jy;
12823
12824 #if 1
12825   if (moved & MP_MOVING && !ScreenMovPos &&
12826       (player->index_nr == game.centered_player_nr ||
12827        game.centered_player_nr == -1))
12828 #else
12829   if (moved & MP_MOVING && !ScreenMovPos &&
12830       (player == local_player || !options.network))
12831 #endif
12832   {
12833     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12834     int offset = game.scroll_delay_value;
12835
12836     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12837     {
12838       /* actual player has left the screen -- scroll in that direction */
12839       if (jx != old_jx)         /* player has moved horizontally */
12840         scroll_x += (jx - old_jx);
12841       else                      /* player has moved vertically */
12842         scroll_y += (jy - old_jy);
12843     }
12844     else
12845     {
12846       if (jx != old_jx)         /* player has moved horizontally */
12847       {
12848         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12849             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12850           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12851
12852         /* don't scroll over playfield boundaries */
12853         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12854           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12855
12856         /* don't scroll more than one field at a time */
12857         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12858
12859         /* don't scroll against the player's moving direction */
12860         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12861             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12862           scroll_x = old_scroll_x;
12863       }
12864       else                      /* player has moved vertically */
12865       {
12866         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12867             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12868           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12869
12870         /* don't scroll over playfield boundaries */
12871         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12872           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12873
12874         /* don't scroll more than one field at a time */
12875         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12876
12877         /* don't scroll against the player's moving direction */
12878         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12879             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12880           scroll_y = old_scroll_y;
12881       }
12882     }
12883
12884     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12885     {
12886 #if 1
12887       if (!options.network && game.centered_player_nr == -1 &&
12888           !AllPlayersInVisibleScreen())
12889       {
12890         scroll_x = old_scroll_x;
12891         scroll_y = old_scroll_y;
12892       }
12893       else
12894 #else
12895       if (!options.network && !AllPlayersInVisibleScreen())
12896       {
12897         scroll_x = old_scroll_x;
12898         scroll_y = old_scroll_y;
12899       }
12900       else
12901 #endif
12902       {
12903         ScrollScreen(player, SCROLL_INIT);
12904         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12905       }
12906     }
12907   }
12908
12909   player->StepFrame = 0;
12910
12911   if (moved & MP_MOVING)
12912   {
12913     if (old_jx != jx && old_jy == jy)
12914       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12915     else if (old_jx == jx && old_jy != jy)
12916       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12917
12918     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12919
12920     player->last_move_dir = player->MovDir;
12921     player->is_moving = TRUE;
12922     player->is_snapping = FALSE;
12923     player->is_switching = FALSE;
12924     player->is_dropping = FALSE;
12925     player->is_dropping_pressed = FALSE;
12926     player->drop_pressed_delay = 0;
12927
12928 #if 0
12929     /* should better be called here than above, but this breaks some tapes */
12930     ScrollPlayer(player, SCROLL_INIT);
12931 #endif
12932   }
12933   else
12934   {
12935     CheckGravityMovementWhenNotMoving(player);
12936
12937     player->is_moving = FALSE;
12938
12939     /* at this point, the player is allowed to move, but cannot move right now
12940        (e.g. because of something blocking the way) -- ensure that the player
12941        is also allowed to move in the next frame (in old versions before 3.1.1,
12942        the player was forced to wait again for eight frames before next try) */
12943
12944     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12945       player->move_delay = 0;   /* allow direct movement in the next frame */
12946   }
12947
12948   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12949     player->move_delay = player->move_delay_value;
12950
12951   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12952   {
12953     TestIfPlayerTouchesBadThing(jx, jy);
12954     TestIfPlayerTouchesCustomElement(jx, jy);
12955   }
12956
12957   if (!player->active)
12958     RemovePlayer(player);
12959
12960   return moved;
12961 }
12962
12963 void ScrollPlayer(struct PlayerInfo *player, int mode)
12964 {
12965   int jx = player->jx, jy = player->jy;
12966   int last_jx = player->last_jx, last_jy = player->last_jy;
12967   int move_stepsize = TILEX / player->move_delay_value;
12968
12969 #if USE_NEW_PLAYER_SPEED
12970   if (!player->active)
12971     return;
12972
12973   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12974     return;
12975 #else
12976   if (!player->active || player->MovPos == 0)
12977     return;
12978 #endif
12979
12980   if (mode == SCROLL_INIT)
12981   {
12982     player->actual_frame_counter = FrameCounter;
12983     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12984
12985     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12986         Feld[last_jx][last_jy] == EL_EMPTY)
12987     {
12988       int last_field_block_delay = 0;   /* start with no blocking at all */
12989       int block_delay_adjustment = player->block_delay_adjustment;
12990
12991       /* if player blocks last field, add delay for exactly one move */
12992       if (player->block_last_field)
12993       {
12994         last_field_block_delay += player->move_delay_value;
12995
12996         /* when blocking enabled, prevent moving up despite gravity */
12997 #if USE_PLAYER_GRAVITY
12998         if (player->gravity && player->MovDir == MV_UP)
12999           block_delay_adjustment = -1;
13000 #else
13001         if (game.gravity && player->MovDir == MV_UP)
13002           block_delay_adjustment = -1;
13003 #endif
13004       }
13005
13006       /* add block delay adjustment (also possible when not blocking) */
13007       last_field_block_delay += block_delay_adjustment;
13008
13009       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13010       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13011     }
13012
13013 #if USE_NEW_PLAYER_SPEED
13014     if (player->MovPos != 0)    /* player has not yet reached destination */
13015       return;
13016 #else
13017     return;
13018 #endif
13019   }
13020   else if (!FrameReached(&player->actual_frame_counter, 1))
13021     return;
13022
13023 #if USE_NEW_PLAYER_SPEED
13024   if (player->MovPos != 0)
13025   {
13026     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13027     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13028
13029     /* before DrawPlayer() to draw correct player graphic for this case */
13030     if (player->MovPos == 0)
13031       CheckGravityMovement(player);
13032   }
13033 #else
13034   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13035   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13036
13037   /* before DrawPlayer() to draw correct player graphic for this case */
13038   if (player->MovPos == 0)
13039     CheckGravityMovement(player);
13040 #endif
13041
13042   if (player->MovPos == 0)      /* player reached destination field */
13043   {
13044     if (player->move_delay_reset_counter > 0)
13045     {
13046       player->move_delay_reset_counter--;
13047
13048       if (player->move_delay_reset_counter == 0)
13049       {
13050         /* continue with normal speed after quickly moving through gate */
13051         HALVE_PLAYER_SPEED(player);
13052
13053         /* be able to make the next move without delay */
13054         player->move_delay = 0;
13055       }
13056     }
13057
13058     player->last_jx = jx;
13059     player->last_jy = jy;
13060
13061     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13062         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13063         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13064         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13065         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13066         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13067     {
13068       DrawPlayer(player);       /* needed here only to cleanup last field */
13069       RemovePlayer(player);
13070
13071       if (local_player->friends_still_needed == 0 ||
13072           IS_SP_ELEMENT(Feld[jx][jy]))
13073         PlayerWins(player);
13074     }
13075
13076     /* this breaks one level: "machine", level 000 */
13077     {
13078       int move_direction = player->MovDir;
13079       int enter_side = MV_DIR_OPPOSITE(move_direction);
13080       int leave_side = move_direction;
13081       int old_jx = last_jx;
13082       int old_jy = last_jy;
13083       int old_element = Feld[old_jx][old_jy];
13084       int new_element = Feld[jx][jy];
13085
13086       if (IS_CUSTOM_ELEMENT(old_element))
13087         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13088                                    CE_LEFT_BY_PLAYER,
13089                                    player->index_bit, leave_side);
13090
13091       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13092                                           CE_PLAYER_LEAVES_X,
13093                                           player->index_bit, leave_side);
13094
13095       if (IS_CUSTOM_ELEMENT(new_element))
13096         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13097                                    player->index_bit, enter_side);
13098
13099       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13100                                           CE_PLAYER_ENTERS_X,
13101                                           player->index_bit, enter_side);
13102
13103       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13104                                         CE_MOVE_OF_X, move_direction);
13105     }
13106
13107     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13108     {
13109       TestIfPlayerTouchesBadThing(jx, jy);
13110       TestIfPlayerTouchesCustomElement(jx, jy);
13111
13112       /* needed because pushed element has not yet reached its destination,
13113          so it would trigger a change event at its previous field location */
13114       if (!player->is_pushing)
13115         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13116
13117       if (!player->active)
13118         RemovePlayer(player);
13119     }
13120
13121     if (!local_player->LevelSolved && level.use_step_counter)
13122     {
13123       int i;
13124
13125       TimePlayed++;
13126
13127       if (TimeLeft > 0)
13128       {
13129         TimeLeft--;
13130
13131         if (TimeLeft <= 10 && setup.time_limit)
13132           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13133
13134 #if 1
13135         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13136
13137         DisplayGameControlValues();
13138 #else
13139         DrawGameValue_Time(TimeLeft);
13140 #endif
13141
13142         if (!TimeLeft && setup.time_limit)
13143           for (i = 0; i < MAX_PLAYERS; i++)
13144             KillPlayer(&stored_player[i]);
13145       }
13146 #if 1
13147       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13148       {
13149         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13150
13151         DisplayGameControlValues();
13152       }
13153 #else
13154       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13155         DrawGameValue_Time(TimePlayed);
13156 #endif
13157     }
13158
13159     if (tape.single_step && tape.recording && !tape.pausing &&
13160         !player->programmed_action)
13161       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13162   }
13163 }
13164
13165 void ScrollScreen(struct PlayerInfo *player, int mode)
13166 {
13167   static unsigned long screen_frame_counter = 0;
13168
13169   if (mode == SCROLL_INIT)
13170   {
13171     /* set scrolling step size according to actual player's moving speed */
13172     ScrollStepSize = TILEX / player->move_delay_value;
13173
13174     screen_frame_counter = FrameCounter;
13175     ScreenMovDir = player->MovDir;
13176     ScreenMovPos = player->MovPos;
13177     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13178     return;
13179   }
13180   else if (!FrameReached(&screen_frame_counter, 1))
13181     return;
13182
13183   if (ScreenMovPos)
13184   {
13185     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13186     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13187     redraw_mask |= REDRAW_FIELD;
13188   }
13189   else
13190     ScreenMovDir = MV_NONE;
13191 }
13192
13193 void TestIfPlayerTouchesCustomElement(int x, int y)
13194 {
13195   static int xy[4][2] =
13196   {
13197     { 0, -1 },
13198     { -1, 0 },
13199     { +1, 0 },
13200     { 0, +1 }
13201   };
13202   static int trigger_sides[4][2] =
13203   {
13204     /* center side       border side */
13205     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13206     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13207     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13208     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13209   };
13210   static int touch_dir[4] =
13211   {
13212     MV_LEFT | MV_RIGHT,
13213     MV_UP   | MV_DOWN,
13214     MV_UP   | MV_DOWN,
13215     MV_LEFT | MV_RIGHT
13216   };
13217   int center_element = Feld[x][y];      /* should always be non-moving! */
13218   int i;
13219
13220   for (i = 0; i < NUM_DIRECTIONS; i++)
13221   {
13222     int xx = x + xy[i][0];
13223     int yy = y + xy[i][1];
13224     int center_side = trigger_sides[i][0];
13225     int border_side = trigger_sides[i][1];
13226     int border_element;
13227
13228     if (!IN_LEV_FIELD(xx, yy))
13229       continue;
13230
13231     if (IS_PLAYER(x, y))
13232     {
13233       struct PlayerInfo *player = PLAYERINFO(x, y);
13234
13235       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13236         border_element = Feld[xx][yy];          /* may be moving! */
13237       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13238         border_element = Feld[xx][yy];
13239       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13240         border_element = MovingOrBlocked2Element(xx, yy);
13241       else
13242         continue;               /* center and border element do not touch */
13243
13244       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13245                                  player->index_bit, border_side);
13246       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13247                                           CE_PLAYER_TOUCHES_X,
13248                                           player->index_bit, border_side);
13249     }
13250     else if (IS_PLAYER(xx, yy))
13251     {
13252       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13253
13254       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13255       {
13256         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13257           continue;             /* center and border element do not touch */
13258       }
13259
13260       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13261                                  player->index_bit, center_side);
13262       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13263                                           CE_PLAYER_TOUCHES_X,
13264                                           player->index_bit, center_side);
13265       break;
13266     }
13267   }
13268 }
13269
13270 #if USE_ELEMENT_TOUCHING_BUGFIX
13271
13272 void TestIfElementTouchesCustomElement(int x, int y)
13273 {
13274   static int xy[4][2] =
13275   {
13276     { 0, -1 },
13277     { -1, 0 },
13278     { +1, 0 },
13279     { 0, +1 }
13280   };
13281   static int trigger_sides[4][2] =
13282   {
13283     /* center side      border side */
13284     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13285     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13286     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13287     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13288   };
13289   static int touch_dir[4] =
13290   {
13291     MV_LEFT | MV_RIGHT,
13292     MV_UP   | MV_DOWN,
13293     MV_UP   | MV_DOWN,
13294     MV_LEFT | MV_RIGHT
13295   };
13296   boolean change_center_element = FALSE;
13297   int center_element = Feld[x][y];      /* should always be non-moving! */
13298   int border_element_old[NUM_DIRECTIONS];
13299   int i;
13300
13301   for (i = 0; i < NUM_DIRECTIONS; i++)
13302   {
13303     int xx = x + xy[i][0];
13304     int yy = y + xy[i][1];
13305     int border_element;
13306
13307     border_element_old[i] = -1;
13308
13309     if (!IN_LEV_FIELD(xx, yy))
13310       continue;
13311
13312     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13313       border_element = Feld[xx][yy];    /* may be moving! */
13314     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13315       border_element = Feld[xx][yy];
13316     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13317       border_element = MovingOrBlocked2Element(xx, yy);
13318     else
13319       continue;                 /* center and border element do not touch */
13320
13321     border_element_old[i] = border_element;
13322   }
13323
13324   for (i = 0; i < NUM_DIRECTIONS; i++)
13325   {
13326     int xx = x + xy[i][0];
13327     int yy = y + xy[i][1];
13328     int center_side = trigger_sides[i][0];
13329     int border_element = border_element_old[i];
13330
13331     if (border_element == -1)
13332       continue;
13333
13334     /* check for change of border element */
13335     CheckElementChangeBySide(xx, yy, border_element, center_element,
13336                              CE_TOUCHING_X, center_side);
13337   }
13338
13339   for (i = 0; i < NUM_DIRECTIONS; i++)
13340   {
13341     int border_side = trigger_sides[i][1];
13342     int border_element = border_element_old[i];
13343
13344     if (border_element == -1)
13345       continue;
13346
13347     /* check for change of center element (but change it only once) */
13348     if (!change_center_element)
13349       change_center_element =
13350         CheckElementChangeBySide(x, y, center_element, border_element,
13351                                  CE_TOUCHING_X, border_side);
13352   }
13353 }
13354
13355 #else
13356
13357 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13358 {
13359   static int xy[4][2] =
13360   {
13361     { 0, -1 },
13362     { -1, 0 },
13363     { +1, 0 },
13364     { 0, +1 }
13365   };
13366   static int trigger_sides[4][2] =
13367   {
13368     /* center side      border side */
13369     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13370     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13371     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13372     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13373   };
13374   static int touch_dir[4] =
13375   {
13376     MV_LEFT | MV_RIGHT,
13377     MV_UP   | MV_DOWN,
13378     MV_UP   | MV_DOWN,
13379     MV_LEFT | MV_RIGHT
13380   };
13381   boolean change_center_element = FALSE;
13382   int center_element = Feld[x][y];      /* should always be non-moving! */
13383   int i;
13384
13385   for (i = 0; i < NUM_DIRECTIONS; i++)
13386   {
13387     int xx = x + xy[i][0];
13388     int yy = y + xy[i][1];
13389     int center_side = trigger_sides[i][0];
13390     int border_side = trigger_sides[i][1];
13391     int border_element;
13392
13393     if (!IN_LEV_FIELD(xx, yy))
13394       continue;
13395
13396     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13397       border_element = Feld[xx][yy];    /* may be moving! */
13398     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13399       border_element = Feld[xx][yy];
13400     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13401       border_element = MovingOrBlocked2Element(xx, yy);
13402     else
13403       continue;                 /* center and border element do not touch */
13404
13405     /* check for change of center element (but change it only once) */
13406     if (!change_center_element)
13407       change_center_element =
13408         CheckElementChangeBySide(x, y, center_element, border_element,
13409                                  CE_TOUCHING_X, border_side);
13410
13411     /* check for change of border element */
13412     CheckElementChangeBySide(xx, yy, border_element, center_element,
13413                              CE_TOUCHING_X, center_side);
13414   }
13415 }
13416
13417 #endif
13418
13419 void TestIfElementHitsCustomElement(int x, int y, int direction)
13420 {
13421   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13422   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13423   int hitx = x + dx, hity = y + dy;
13424   int hitting_element = Feld[x][y];
13425   int touched_element;
13426
13427   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13428     return;
13429
13430   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13431                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13432
13433   if (IN_LEV_FIELD(hitx, hity))
13434   {
13435     int opposite_direction = MV_DIR_OPPOSITE(direction);
13436     int hitting_side = direction;
13437     int touched_side = opposite_direction;
13438     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13439                           MovDir[hitx][hity] != direction ||
13440                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13441
13442     object_hit = TRUE;
13443
13444     if (object_hit)
13445     {
13446       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13447                                CE_HITTING_X, touched_side);
13448
13449       CheckElementChangeBySide(hitx, hity, touched_element,
13450                                hitting_element, CE_HIT_BY_X, hitting_side);
13451
13452       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13453                                CE_HIT_BY_SOMETHING, opposite_direction);
13454     }
13455   }
13456
13457   /* "hitting something" is also true when hitting the playfield border */
13458   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13459                            CE_HITTING_SOMETHING, direction);
13460 }
13461
13462 #if 0
13463 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13464 {
13465   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13466   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13467   int hitx = x + dx, hity = y + dy;
13468   int hitting_element = Feld[x][y];
13469   int touched_element;
13470 #if 0
13471   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13472                         !IS_FREE(hitx, hity) &&
13473                         (!IS_MOVING(hitx, hity) ||
13474                          MovDir[hitx][hity] != direction ||
13475                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13476 #endif
13477
13478   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13479     return;
13480
13481 #if 0
13482   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13483     return;
13484 #endif
13485
13486   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13487                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13488
13489   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13490                            EP_CAN_SMASH_EVERYTHING, direction);
13491
13492   if (IN_LEV_FIELD(hitx, hity))
13493   {
13494     int opposite_direction = MV_DIR_OPPOSITE(direction);
13495     int hitting_side = direction;
13496     int touched_side = opposite_direction;
13497 #if 0
13498     int touched_element = MovingOrBlocked2Element(hitx, hity);
13499 #endif
13500 #if 1
13501     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13502                           MovDir[hitx][hity] != direction ||
13503                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13504
13505     object_hit = TRUE;
13506 #endif
13507
13508     if (object_hit)
13509     {
13510       int i;
13511
13512       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13513                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13514
13515       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13516                                CE_OTHER_IS_SMASHING, touched_side);
13517
13518       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13519                                CE_OTHER_GETS_SMASHED, hitting_side);
13520     }
13521   }
13522 }
13523 #endif
13524
13525 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13526 {
13527   int i, kill_x = -1, kill_y = -1;
13528
13529   int bad_element = -1;
13530   static int test_xy[4][2] =
13531   {
13532     { 0, -1 },
13533     { -1, 0 },
13534     { +1, 0 },
13535     { 0, +1 }
13536   };
13537   static int test_dir[4] =
13538   {
13539     MV_UP,
13540     MV_LEFT,
13541     MV_RIGHT,
13542     MV_DOWN
13543   };
13544
13545   for (i = 0; i < NUM_DIRECTIONS; i++)
13546   {
13547     int test_x, test_y, test_move_dir, test_element;
13548
13549     test_x = good_x + test_xy[i][0];
13550     test_y = good_y + test_xy[i][1];
13551
13552     if (!IN_LEV_FIELD(test_x, test_y))
13553       continue;
13554
13555     test_move_dir =
13556       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13557
13558     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13559
13560     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13561        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13562     */
13563     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13564         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13565     {
13566       kill_x = test_x;
13567       kill_y = test_y;
13568       bad_element = test_element;
13569
13570       break;
13571     }
13572   }
13573
13574   if (kill_x != -1 || kill_y != -1)
13575   {
13576     if (IS_PLAYER(good_x, good_y))
13577     {
13578       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13579
13580       if (player->shield_deadly_time_left > 0 &&
13581           !IS_INDESTRUCTIBLE(bad_element))
13582         Bang(kill_x, kill_y);
13583       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13584         KillPlayer(player);
13585     }
13586     else
13587       Bang(good_x, good_y);
13588   }
13589 }
13590
13591 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13592 {
13593   int i, kill_x = -1, kill_y = -1;
13594   int bad_element = Feld[bad_x][bad_y];
13595   static int test_xy[4][2] =
13596   {
13597     { 0, -1 },
13598     { -1, 0 },
13599     { +1, 0 },
13600     { 0, +1 }
13601   };
13602   static int touch_dir[4] =
13603   {
13604     MV_LEFT | MV_RIGHT,
13605     MV_UP   | MV_DOWN,
13606     MV_UP   | MV_DOWN,
13607     MV_LEFT | MV_RIGHT
13608   };
13609   static int test_dir[4] =
13610   {
13611     MV_UP,
13612     MV_LEFT,
13613     MV_RIGHT,
13614     MV_DOWN
13615   };
13616
13617   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13618     return;
13619
13620   for (i = 0; i < NUM_DIRECTIONS; i++)
13621   {
13622     int test_x, test_y, test_move_dir, test_element;
13623
13624     test_x = bad_x + test_xy[i][0];
13625     test_y = bad_y + test_xy[i][1];
13626     if (!IN_LEV_FIELD(test_x, test_y))
13627       continue;
13628
13629     test_move_dir =
13630       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13631
13632     test_element = Feld[test_x][test_y];
13633
13634     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13635        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13636     */
13637     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13638         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13639     {
13640       /* good thing is player or penguin that does not move away */
13641       if (IS_PLAYER(test_x, test_y))
13642       {
13643         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13644
13645         if (bad_element == EL_ROBOT && player->is_moving)
13646           continue;     /* robot does not kill player if he is moving */
13647
13648         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13649         {
13650           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13651             continue;           /* center and border element do not touch */
13652         }
13653
13654         kill_x = test_x;
13655         kill_y = test_y;
13656         break;
13657       }
13658       else if (test_element == EL_PENGUIN)
13659       {
13660         kill_x = test_x;
13661         kill_y = test_y;
13662         break;
13663       }
13664     }
13665   }
13666
13667   if (kill_x != -1 || kill_y != -1)
13668   {
13669     if (IS_PLAYER(kill_x, kill_y))
13670     {
13671       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13672
13673       if (player->shield_deadly_time_left > 0 &&
13674           !IS_INDESTRUCTIBLE(bad_element))
13675         Bang(bad_x, bad_y);
13676       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13677         KillPlayer(player);
13678     }
13679     else
13680       Bang(kill_x, kill_y);
13681   }
13682 }
13683
13684 void TestIfPlayerTouchesBadThing(int x, int y)
13685 {
13686   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13687 }
13688
13689 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13690 {
13691   TestIfGoodThingHitsBadThing(x, y, move_dir);
13692 }
13693
13694 void TestIfBadThingTouchesPlayer(int x, int y)
13695 {
13696   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13697 }
13698
13699 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13700 {
13701   TestIfBadThingHitsGoodThing(x, y, move_dir);
13702 }
13703
13704 void TestIfFriendTouchesBadThing(int x, int y)
13705 {
13706   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13707 }
13708
13709 void TestIfBadThingTouchesFriend(int x, int y)
13710 {
13711   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13712 }
13713
13714 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13715 {
13716   int i, kill_x = bad_x, kill_y = bad_y;
13717   static int xy[4][2] =
13718   {
13719     { 0, -1 },
13720     { -1, 0 },
13721     { +1, 0 },
13722     { 0, +1 }
13723   };
13724
13725   for (i = 0; i < NUM_DIRECTIONS; i++)
13726   {
13727     int x, y, element;
13728
13729     x = bad_x + xy[i][0];
13730     y = bad_y + xy[i][1];
13731     if (!IN_LEV_FIELD(x, y))
13732       continue;
13733
13734     element = Feld[x][y];
13735     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13736         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13737     {
13738       kill_x = x;
13739       kill_y = y;
13740       break;
13741     }
13742   }
13743
13744   if (kill_x != bad_x || kill_y != bad_y)
13745     Bang(bad_x, bad_y);
13746 }
13747
13748 void KillPlayer(struct PlayerInfo *player)
13749 {
13750   int jx = player->jx, jy = player->jy;
13751
13752   if (!player->active)
13753     return;
13754
13755   /* the following code was introduced to prevent an infinite loop when calling
13756      -> Bang()
13757      -> CheckTriggeredElementChangeExt()
13758      -> ExecuteCustomElementAction()
13759      -> KillPlayer()
13760      -> (infinitely repeating the above sequence of function calls)
13761      which occurs when killing the player while having a CE with the setting
13762      "kill player X when explosion of <player X>"; the solution using a new
13763      field "player->killed" was chosen for backwards compatibility, although
13764      clever use of the fields "player->active" etc. would probably also work */
13765 #if 1
13766   if (player->killed)
13767     return;
13768 #endif
13769
13770   player->killed = TRUE;
13771
13772   /* remove accessible field at the player's position */
13773   Feld[jx][jy] = EL_EMPTY;
13774
13775   /* deactivate shield (else Bang()/Explode() would not work right) */
13776   player->shield_normal_time_left = 0;
13777   player->shield_deadly_time_left = 0;
13778
13779   Bang(jx, jy);
13780   BuryPlayer(player);
13781 }
13782
13783 static void KillPlayerUnlessEnemyProtected(int x, int y)
13784 {
13785   if (!PLAYER_ENEMY_PROTECTED(x, y))
13786     KillPlayer(PLAYERINFO(x, y));
13787 }
13788
13789 static void KillPlayerUnlessExplosionProtected(int x, int y)
13790 {
13791   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13792     KillPlayer(PLAYERINFO(x, y));
13793 }
13794
13795 void BuryPlayer(struct PlayerInfo *player)
13796 {
13797   int jx = player->jx, jy = player->jy;
13798
13799   if (!player->active)
13800     return;
13801
13802   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13803   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13804
13805   player->GameOver = TRUE;
13806   RemovePlayer(player);
13807 }
13808
13809 void RemovePlayer(struct PlayerInfo *player)
13810 {
13811   int jx = player->jx, jy = player->jy;
13812   int i, found = FALSE;
13813
13814   player->present = FALSE;
13815   player->active = FALSE;
13816
13817   if (!ExplodeField[jx][jy])
13818     StorePlayer[jx][jy] = 0;
13819
13820   if (player->is_moving)
13821     DrawLevelField(player->last_jx, player->last_jy);
13822
13823   for (i = 0; i < MAX_PLAYERS; i++)
13824     if (stored_player[i].active)
13825       found = TRUE;
13826
13827   if (!found)
13828     AllPlayersGone = TRUE;
13829
13830   ExitX = ZX = jx;
13831   ExitY = ZY = jy;
13832 }
13833
13834 #if USE_NEW_SNAP_DELAY
13835 static void setFieldForSnapping(int x, int y, int element, int direction)
13836 {
13837   struct ElementInfo *ei = &element_info[element];
13838   int direction_bit = MV_DIR_TO_BIT(direction);
13839   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13840   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13841                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13842
13843   Feld[x][y] = EL_ELEMENT_SNAPPING;
13844   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13845
13846   ResetGfxAnimation(x, y);
13847
13848   GfxElement[x][y] = element;
13849   GfxAction[x][y] = action;
13850   GfxDir[x][y] = direction;
13851   GfxFrame[x][y] = -1;
13852 }
13853 #endif
13854
13855 /*
13856   =============================================================================
13857   checkDiagonalPushing()
13858   -----------------------------------------------------------------------------
13859   check if diagonal input device direction results in pushing of object
13860   (by checking if the alternative direction is walkable, diggable, ...)
13861   =============================================================================
13862 */
13863
13864 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13865                                     int x, int y, int real_dx, int real_dy)
13866 {
13867   int jx, jy, dx, dy, xx, yy;
13868
13869   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13870     return TRUE;
13871
13872   /* diagonal direction: check alternative direction */
13873   jx = player->jx;
13874   jy = player->jy;
13875   dx = x - jx;
13876   dy = y - jy;
13877   xx = jx + (dx == 0 ? real_dx : 0);
13878   yy = jy + (dy == 0 ? real_dy : 0);
13879
13880   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13881 }
13882
13883 /*
13884   =============================================================================
13885   DigField()
13886   -----------------------------------------------------------------------------
13887   x, y:                 field next to player (non-diagonal) to try to dig to
13888   real_dx, real_dy:     direction as read from input device (can be diagonal)
13889   =============================================================================
13890 */
13891
13892 int DigField(struct PlayerInfo *player,
13893              int oldx, int oldy, int x, int y,
13894              int real_dx, int real_dy, int mode)
13895 {
13896   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13897   boolean player_was_pushing = player->is_pushing;
13898   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13899   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13900   int jx = oldx, jy = oldy;
13901   int dx = x - jx, dy = y - jy;
13902   int nextx = x + dx, nexty = y + dy;
13903   int move_direction = (dx == -1 ? MV_LEFT  :
13904                         dx == +1 ? MV_RIGHT :
13905                         dy == -1 ? MV_UP    :
13906                         dy == +1 ? MV_DOWN  : MV_NONE);
13907   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13908   int dig_side = MV_DIR_OPPOSITE(move_direction);
13909   int old_element = Feld[jx][jy];
13910 #if USE_FIXED_DONT_RUN_INTO
13911   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13912 #else
13913   int element;
13914 #endif
13915   int collect_count;
13916
13917   if (is_player)                /* function can also be called by EL_PENGUIN */
13918   {
13919     if (player->MovPos == 0)
13920     {
13921       player->is_digging = FALSE;
13922       player->is_collecting = FALSE;
13923     }
13924
13925     if (player->MovPos == 0)    /* last pushing move finished */
13926       player->is_pushing = FALSE;
13927
13928     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13929     {
13930       player->is_switching = FALSE;
13931       player->push_delay = -1;
13932
13933       return MP_NO_ACTION;
13934     }
13935   }
13936
13937 #if !USE_FIXED_DONT_RUN_INTO
13938   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13939     return MP_NO_ACTION;
13940 #endif
13941
13942   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13943     old_element = Back[jx][jy];
13944
13945   /* in case of element dropped at player position, check background */
13946   else if (Back[jx][jy] != EL_EMPTY &&
13947            game.engine_version >= VERSION_IDENT(2,2,0,0))
13948     old_element = Back[jx][jy];
13949
13950   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13951     return MP_NO_ACTION;        /* field has no opening in this direction */
13952
13953   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13954     return MP_NO_ACTION;        /* field has no opening in this direction */
13955
13956 #if USE_FIXED_DONT_RUN_INTO
13957   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13958   {
13959     SplashAcid(x, y);
13960
13961     Feld[jx][jy] = player->artwork_element;
13962     InitMovingField(jx, jy, MV_DOWN);
13963     Store[jx][jy] = EL_ACID;
13964     ContinueMoving(jx, jy);
13965     BuryPlayer(player);
13966
13967     return MP_DONT_RUN_INTO;
13968   }
13969 #endif
13970
13971 #if USE_FIXED_DONT_RUN_INTO
13972   if (player_can_move && DONT_RUN_INTO(element))
13973   {
13974     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13975
13976     return MP_DONT_RUN_INTO;
13977   }
13978 #endif
13979
13980 #if USE_FIXED_DONT_RUN_INTO
13981   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13982     return MP_NO_ACTION;
13983 #endif
13984
13985 #if !USE_FIXED_DONT_RUN_INTO
13986   element = Feld[x][y];
13987 #endif
13988
13989   collect_count = element_info[element].collect_count_initial;
13990
13991   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13992     return MP_NO_ACTION;
13993
13994   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13995     player_can_move = player_can_move_or_snap;
13996
13997   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13998       game.engine_version >= VERSION_IDENT(2,2,0,0))
13999   {
14000     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14001                                player->index_bit, dig_side);
14002     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14003                                         player->index_bit, dig_side);
14004
14005     if (element == EL_DC_LANDMINE)
14006       Bang(x, y);
14007
14008     if (Feld[x][y] != element)          /* field changed by snapping */
14009       return MP_ACTION;
14010
14011     return MP_NO_ACTION;
14012   }
14013
14014 #if USE_PLAYER_GRAVITY
14015   if (player->gravity && is_player && !player->is_auto_moving &&
14016       canFallDown(player) && move_direction != MV_DOWN &&
14017       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14018     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14019 #else
14020   if (game.gravity && is_player && !player->is_auto_moving &&
14021       canFallDown(player) && move_direction != MV_DOWN &&
14022       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14023     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14024 #endif
14025
14026   if (player_can_move &&
14027       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14028   {
14029     int sound_element = SND_ELEMENT(element);
14030     int sound_action = ACTION_WALKING;
14031
14032     if (IS_RND_GATE(element))
14033     {
14034       if (!player->key[RND_GATE_NR(element)])
14035         return MP_NO_ACTION;
14036     }
14037     else if (IS_RND_GATE_GRAY(element))
14038     {
14039       if (!player->key[RND_GATE_GRAY_NR(element)])
14040         return MP_NO_ACTION;
14041     }
14042     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14043     {
14044       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14045         return MP_NO_ACTION;
14046     }
14047     else if (element == EL_EXIT_OPEN ||
14048              element == EL_EM_EXIT_OPEN ||
14049              element == EL_STEEL_EXIT_OPEN ||
14050              element == EL_EM_STEEL_EXIT_OPEN ||
14051              element == EL_SP_EXIT_OPEN ||
14052              element == EL_SP_EXIT_OPENING)
14053     {
14054       sound_action = ACTION_PASSING;    /* player is passing exit */
14055     }
14056     else if (element == EL_EMPTY)
14057     {
14058       sound_action = ACTION_MOVING;             /* nothing to walk on */
14059     }
14060
14061     /* play sound from background or player, whatever is available */
14062     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14063       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14064     else
14065       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14066   }
14067   else if (player_can_move &&
14068            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14069   {
14070     if (!ACCESS_FROM(element, opposite_direction))
14071       return MP_NO_ACTION;      /* field not accessible from this direction */
14072
14073     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14074       return MP_NO_ACTION;
14075
14076     if (IS_EM_GATE(element))
14077     {
14078       if (!player->key[EM_GATE_NR(element)])
14079         return MP_NO_ACTION;
14080     }
14081     else if (IS_EM_GATE_GRAY(element))
14082     {
14083       if (!player->key[EM_GATE_GRAY_NR(element)])
14084         return MP_NO_ACTION;
14085     }
14086     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14087     {
14088       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14089         return MP_NO_ACTION;
14090     }
14091     else if (IS_EMC_GATE(element))
14092     {
14093       if (!player->key[EMC_GATE_NR(element)])
14094         return MP_NO_ACTION;
14095     }
14096     else if (IS_EMC_GATE_GRAY(element))
14097     {
14098       if (!player->key[EMC_GATE_GRAY_NR(element)])
14099         return MP_NO_ACTION;
14100     }
14101     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14102     {
14103       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14104         return MP_NO_ACTION;
14105     }
14106     else if (element == EL_DC_GATE_WHITE ||
14107              element == EL_DC_GATE_WHITE_GRAY ||
14108              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14109     {
14110       if (player->num_white_keys == 0)
14111         return MP_NO_ACTION;
14112
14113       player->num_white_keys--;
14114     }
14115     else if (IS_SP_PORT(element))
14116     {
14117       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14118           element == EL_SP_GRAVITY_PORT_RIGHT ||
14119           element == EL_SP_GRAVITY_PORT_UP ||
14120           element == EL_SP_GRAVITY_PORT_DOWN)
14121 #if USE_PLAYER_GRAVITY
14122         player->gravity = !player->gravity;
14123 #else
14124         game.gravity = !game.gravity;
14125 #endif
14126       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14127                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14128                element == EL_SP_GRAVITY_ON_PORT_UP ||
14129                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14130 #if USE_PLAYER_GRAVITY
14131         player->gravity = TRUE;
14132 #else
14133         game.gravity = TRUE;
14134 #endif
14135       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14136                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14137                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14138                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14139 #if USE_PLAYER_GRAVITY
14140         player->gravity = FALSE;
14141 #else
14142         game.gravity = FALSE;
14143 #endif
14144     }
14145
14146     /* automatically move to the next field with double speed */
14147     player->programmed_action = move_direction;
14148
14149     if (player->move_delay_reset_counter == 0)
14150     {
14151       player->move_delay_reset_counter = 2;     /* two double speed steps */
14152
14153       DOUBLE_PLAYER_SPEED(player);
14154     }
14155
14156     PlayLevelSoundAction(x, y, ACTION_PASSING);
14157   }
14158   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14159   {
14160     RemoveField(x, y);
14161
14162     if (mode != DF_SNAP)
14163     {
14164       GfxElement[x][y] = GFX_ELEMENT(element);
14165       player->is_digging = TRUE;
14166     }
14167
14168     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14169
14170     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14171                                         player->index_bit, dig_side);
14172
14173     if (mode == DF_SNAP)
14174     {
14175 #if USE_NEW_SNAP_DELAY
14176       if (level.block_snap_field)
14177         setFieldForSnapping(x, y, element, move_direction);
14178       else
14179         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14180 #else
14181       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14182 #endif
14183
14184       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14185                                           player->index_bit, dig_side);
14186     }
14187   }
14188   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14189   {
14190     RemoveField(x, y);
14191
14192     if (is_player && mode != DF_SNAP)
14193     {
14194       GfxElement[x][y] = element;
14195       player->is_collecting = TRUE;
14196     }
14197
14198     if (element == EL_SPEED_PILL)
14199     {
14200       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14201     }
14202     else if (element == EL_EXTRA_TIME && level.time > 0)
14203     {
14204       TimeLeft += level.extra_time;
14205
14206 #if 1
14207       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14208
14209       DisplayGameControlValues();
14210 #else
14211       DrawGameValue_Time(TimeLeft);
14212 #endif
14213     }
14214     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14215     {
14216       player->shield_normal_time_left += level.shield_normal_time;
14217       if (element == EL_SHIELD_DEADLY)
14218         player->shield_deadly_time_left += level.shield_deadly_time;
14219     }
14220     else if (element == EL_DYNAMITE ||
14221              element == EL_EM_DYNAMITE ||
14222              element == EL_SP_DISK_RED)
14223     {
14224       if (player->inventory_size < MAX_INVENTORY_SIZE)
14225         player->inventory_element[player->inventory_size++] = element;
14226
14227       DrawGameDoorValues();
14228     }
14229     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14230     {
14231       player->dynabomb_count++;
14232       player->dynabombs_left++;
14233     }
14234     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14235     {
14236       player->dynabomb_size++;
14237     }
14238     else if (element == EL_DYNABOMB_INCREASE_POWER)
14239     {
14240       player->dynabomb_xl = TRUE;
14241     }
14242     else if (IS_KEY(element))
14243     {
14244       player->key[KEY_NR(element)] = TRUE;
14245
14246       DrawGameDoorValues();
14247     }
14248     else if (element == EL_DC_KEY_WHITE)
14249     {
14250       player->num_white_keys++;
14251
14252       /* display white keys? */
14253       /* DrawGameDoorValues(); */
14254     }
14255     else if (IS_ENVELOPE(element))
14256     {
14257       player->show_envelope = element;
14258     }
14259     else if (element == EL_EMC_LENSES)
14260     {
14261       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14262
14263       RedrawAllInvisibleElementsForLenses();
14264     }
14265     else if (element == EL_EMC_MAGNIFIER)
14266     {
14267       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14268
14269       RedrawAllInvisibleElementsForMagnifier();
14270     }
14271     else if (IS_DROPPABLE(element) ||
14272              IS_THROWABLE(element))     /* can be collected and dropped */
14273     {
14274       int i;
14275
14276       if (collect_count == 0)
14277         player->inventory_infinite_element = element;
14278       else
14279         for (i = 0; i < collect_count; i++)
14280           if (player->inventory_size < MAX_INVENTORY_SIZE)
14281             player->inventory_element[player->inventory_size++] = element;
14282
14283       DrawGameDoorValues();
14284     }
14285     else if (collect_count > 0)
14286     {
14287       local_player->gems_still_needed -= collect_count;
14288       if (local_player->gems_still_needed < 0)
14289         local_player->gems_still_needed = 0;
14290
14291 #if 1
14292       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14293
14294       DisplayGameControlValues();
14295 #else
14296       DrawGameValue_Emeralds(local_player->gems_still_needed);
14297 #endif
14298     }
14299
14300     RaiseScoreElement(element);
14301     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14302
14303     if (is_player)
14304       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14305                                           player->index_bit, dig_side);
14306
14307     if (mode == DF_SNAP)
14308     {
14309 #if USE_NEW_SNAP_DELAY
14310       if (level.block_snap_field)
14311         setFieldForSnapping(x, y, element, move_direction);
14312       else
14313         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14314 #else
14315       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14316 #endif
14317
14318       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14319                                           player->index_bit, dig_side);
14320     }
14321   }
14322   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14323   {
14324     if (mode == DF_SNAP && element != EL_BD_ROCK)
14325       return MP_NO_ACTION;
14326
14327     if (CAN_FALL(element) && dy)
14328       return MP_NO_ACTION;
14329
14330     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14331         !(element == EL_SPRING && level.use_spring_bug))
14332       return MP_NO_ACTION;
14333
14334     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14335         ((move_direction & MV_VERTICAL &&
14336           ((element_info[element].move_pattern & MV_LEFT &&
14337             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14338            (element_info[element].move_pattern & MV_RIGHT &&
14339             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14340          (move_direction & MV_HORIZONTAL &&
14341           ((element_info[element].move_pattern & MV_UP &&
14342             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14343            (element_info[element].move_pattern & MV_DOWN &&
14344             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14345       return MP_NO_ACTION;
14346
14347     /* do not push elements already moving away faster than player */
14348     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14349         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14350       return MP_NO_ACTION;
14351
14352     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14353     {
14354       if (player->push_delay_value == -1 || !player_was_pushing)
14355         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14356     }
14357     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14358     {
14359       if (player->push_delay_value == -1)
14360         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14361     }
14362     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14363     {
14364       if (!player->is_pushing)
14365         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14366     }
14367
14368     player->is_pushing = TRUE;
14369     player->is_active = TRUE;
14370
14371     if (!(IN_LEV_FIELD(nextx, nexty) &&
14372           (IS_FREE(nextx, nexty) ||
14373            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14374             IS_SB_ELEMENT(element)))))
14375       return MP_NO_ACTION;
14376
14377     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14378       return MP_NO_ACTION;
14379
14380     if (player->push_delay == -1)       /* new pushing; restart delay */
14381       player->push_delay = 0;
14382
14383     if (player->push_delay < player->push_delay_value &&
14384         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14385         element != EL_SPRING && element != EL_BALLOON)
14386     {
14387       /* make sure that there is no move delay before next try to push */
14388       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14389         player->move_delay = 0;
14390
14391       return MP_NO_ACTION;
14392     }
14393
14394     if (IS_SB_ELEMENT(element))
14395     {
14396       if (element == EL_SOKOBAN_FIELD_FULL)
14397       {
14398         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14399         local_player->sokobanfields_still_needed++;
14400       }
14401
14402       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14403       {
14404         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14405         local_player->sokobanfields_still_needed--;
14406       }
14407
14408       Feld[x][y] = EL_SOKOBAN_OBJECT;
14409
14410       if (Back[x][y] == Back[nextx][nexty])
14411         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14412       else if (Back[x][y] != 0)
14413         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14414                                     ACTION_EMPTYING);
14415       else
14416         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14417                                     ACTION_FILLING);
14418
14419       if (local_player->sokobanfields_still_needed == 0 &&
14420           game.emulation == EMU_SOKOBAN)
14421       {
14422         PlayerWins(player);
14423
14424         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14425       }
14426     }
14427     else
14428       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14429
14430     InitMovingField(x, y, move_direction);
14431     GfxAction[x][y] = ACTION_PUSHING;
14432
14433     if (mode == DF_SNAP)
14434       ContinueMoving(x, y);
14435     else
14436       MovPos[x][y] = (dx != 0 ? dx : dy);
14437
14438     Pushed[x][y] = TRUE;
14439     Pushed[nextx][nexty] = TRUE;
14440
14441     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14442       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14443     else
14444       player->push_delay_value = -1;    /* get new value later */
14445
14446     /* check for element change _after_ element has been pushed */
14447     if (game.use_change_when_pushing_bug)
14448     {
14449       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14450                                  player->index_bit, dig_side);
14451       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14452                                           player->index_bit, dig_side);
14453     }
14454   }
14455   else if (IS_SWITCHABLE(element))
14456   {
14457     if (PLAYER_SWITCHING(player, x, y))
14458     {
14459       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14460                                           player->index_bit, dig_side);
14461
14462       return MP_ACTION;
14463     }
14464
14465     player->is_switching = TRUE;
14466     player->switch_x = x;
14467     player->switch_y = y;
14468
14469     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14470
14471     if (element == EL_ROBOT_WHEEL)
14472     {
14473       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14474       ZX = x;
14475       ZY = y;
14476
14477       game.robot_wheel_active = TRUE;
14478
14479       DrawLevelField(x, y);
14480     }
14481     else if (element == EL_SP_TERMINAL)
14482     {
14483       int xx, yy;
14484
14485       SCAN_PLAYFIELD(xx, yy)
14486       {
14487         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14488           Bang(xx, yy);
14489         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14490           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14491       }
14492     }
14493     else if (IS_BELT_SWITCH(element))
14494     {
14495       ToggleBeltSwitch(x, y);
14496     }
14497     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14498              element == EL_SWITCHGATE_SWITCH_DOWN ||
14499              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14500              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14501     {
14502       ToggleSwitchgateSwitch(x, y);
14503     }
14504     else if (element == EL_LIGHT_SWITCH ||
14505              element == EL_LIGHT_SWITCH_ACTIVE)
14506     {
14507       ToggleLightSwitch(x, y);
14508     }
14509     else if (element == EL_TIMEGATE_SWITCH ||
14510              element == EL_DC_TIMEGATE_SWITCH)
14511     {
14512       ActivateTimegateSwitch(x, y);
14513     }
14514     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14515              element == EL_BALLOON_SWITCH_RIGHT ||
14516              element == EL_BALLOON_SWITCH_UP    ||
14517              element == EL_BALLOON_SWITCH_DOWN  ||
14518              element == EL_BALLOON_SWITCH_NONE  ||
14519              element == EL_BALLOON_SWITCH_ANY)
14520     {
14521       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14522                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14523                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14524                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14525                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14526                              move_direction);
14527     }
14528     else if (element == EL_LAMP)
14529     {
14530       Feld[x][y] = EL_LAMP_ACTIVE;
14531       local_player->lights_still_needed--;
14532
14533       ResetGfxAnimation(x, y);
14534       DrawLevelField(x, y);
14535     }
14536     else if (element == EL_TIME_ORB_FULL)
14537     {
14538       Feld[x][y] = EL_TIME_ORB_EMPTY;
14539
14540       if (level.time > 0 || level.use_time_orb_bug)
14541       {
14542         TimeLeft += level.time_orb_time;
14543
14544 #if 1
14545         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14546
14547         DisplayGameControlValues();
14548 #else
14549         DrawGameValue_Time(TimeLeft);
14550 #endif
14551       }
14552
14553       ResetGfxAnimation(x, y);
14554       DrawLevelField(x, y);
14555     }
14556     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14557              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14558     {
14559       int xx, yy;
14560
14561       game.ball_state = !game.ball_state;
14562
14563       SCAN_PLAYFIELD(xx, yy)
14564       {
14565         int e = Feld[xx][yy];
14566
14567         if (game.ball_state)
14568         {
14569           if (e == EL_EMC_MAGIC_BALL)
14570             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14571           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14572             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14573         }
14574         else
14575         {
14576           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14577             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14578           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14579             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14580         }
14581       }
14582     }
14583
14584     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14585                                         player->index_bit, dig_side);
14586
14587     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14588                                         player->index_bit, dig_side);
14589
14590     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14591                                         player->index_bit, dig_side);
14592
14593     return MP_ACTION;
14594   }
14595   else
14596   {
14597     if (!PLAYER_SWITCHING(player, x, y))
14598     {
14599       player->is_switching = TRUE;
14600       player->switch_x = x;
14601       player->switch_y = y;
14602
14603       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14604                                  player->index_bit, dig_side);
14605       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14606                                           player->index_bit, dig_side);
14607
14608       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14609                                  player->index_bit, dig_side);
14610       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14611                                           player->index_bit, dig_side);
14612     }
14613
14614     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14615                                player->index_bit, dig_side);
14616     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14617                                         player->index_bit, dig_side);
14618
14619     return MP_NO_ACTION;
14620   }
14621
14622   player->push_delay = -1;
14623
14624   if (is_player)                /* function can also be called by EL_PENGUIN */
14625   {
14626     if (Feld[x][y] != element)          /* really digged/collected something */
14627     {
14628       player->is_collecting = !player->is_digging;
14629       player->is_active = TRUE;
14630     }
14631   }
14632
14633   return MP_MOVING;
14634 }
14635
14636 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14637 {
14638   int jx = player->jx, jy = player->jy;
14639   int x = jx + dx, y = jy + dy;
14640   int snap_direction = (dx == -1 ? MV_LEFT  :
14641                         dx == +1 ? MV_RIGHT :
14642                         dy == -1 ? MV_UP    :
14643                         dy == +1 ? MV_DOWN  : MV_NONE);
14644   boolean can_continue_snapping = (level.continuous_snapping &&
14645                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14646
14647   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14648     return FALSE;
14649
14650   if (!player->active || !IN_LEV_FIELD(x, y))
14651     return FALSE;
14652
14653   if (dx && dy)
14654     return FALSE;
14655
14656   if (!dx && !dy)
14657   {
14658     if (player->MovPos == 0)
14659       player->is_pushing = FALSE;
14660
14661     player->is_snapping = FALSE;
14662
14663     if (player->MovPos == 0)
14664     {
14665       player->is_moving = FALSE;
14666       player->is_digging = FALSE;
14667       player->is_collecting = FALSE;
14668     }
14669
14670     return FALSE;
14671   }
14672
14673 #if USE_NEW_CONTINUOUS_SNAPPING
14674   /* prevent snapping with already pressed snap key when not allowed */
14675   if (player->is_snapping && !can_continue_snapping)
14676     return FALSE;
14677 #else
14678   if (player->is_snapping)
14679     return FALSE;
14680 #endif
14681
14682   player->MovDir = snap_direction;
14683
14684   if (player->MovPos == 0)
14685   {
14686     player->is_moving = FALSE;
14687     player->is_digging = FALSE;
14688     player->is_collecting = FALSE;
14689   }
14690
14691   player->is_dropping = FALSE;
14692   player->is_dropping_pressed = FALSE;
14693   player->drop_pressed_delay = 0;
14694
14695   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14696     return FALSE;
14697
14698   player->is_snapping = TRUE;
14699   player->is_active = TRUE;
14700
14701   if (player->MovPos == 0)
14702   {
14703     player->is_moving = FALSE;
14704     player->is_digging = FALSE;
14705     player->is_collecting = FALSE;
14706   }
14707
14708   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14709     DrawLevelField(player->last_jx, player->last_jy);
14710
14711   DrawLevelField(x, y);
14712
14713   return TRUE;
14714 }
14715
14716 boolean DropElement(struct PlayerInfo *player)
14717 {
14718   int old_element, new_element;
14719   int dropx = player->jx, dropy = player->jy;
14720   int drop_direction = player->MovDir;
14721   int drop_side = drop_direction;
14722 #if 1
14723   int drop_element = get_next_dropped_element(player);
14724 #else
14725   int drop_element = (player->inventory_size > 0 ?
14726                       player->inventory_element[player->inventory_size - 1] :
14727                       player->inventory_infinite_element != EL_UNDEFINED ?
14728                       player->inventory_infinite_element :
14729                       player->dynabombs_left > 0 ?
14730                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14731                       EL_UNDEFINED);
14732 #endif
14733
14734   player->is_dropping_pressed = TRUE;
14735
14736   /* do not drop an element on top of another element; when holding drop key
14737      pressed without moving, dropped element must move away before the next
14738      element can be dropped (this is especially important if the next element
14739      is dynamite, which can be placed on background for historical reasons) */
14740   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14741     return MP_ACTION;
14742
14743   if (IS_THROWABLE(drop_element))
14744   {
14745     dropx += GET_DX_FROM_DIR(drop_direction);
14746     dropy += GET_DY_FROM_DIR(drop_direction);
14747
14748     if (!IN_LEV_FIELD(dropx, dropy))
14749       return FALSE;
14750   }
14751
14752   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14753   new_element = drop_element;           /* default: no change when dropping */
14754
14755   /* check if player is active, not moving and ready to drop */
14756   if (!player->active || player->MovPos || player->drop_delay > 0)
14757     return FALSE;
14758
14759   /* check if player has anything that can be dropped */
14760   if (new_element == EL_UNDEFINED)
14761     return FALSE;
14762
14763   /* check if drop key was pressed long enough for EM style dynamite */
14764   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14765     return FALSE;
14766
14767   /* check if anything can be dropped at the current position */
14768   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14769     return FALSE;
14770
14771   /* collected custom elements can only be dropped on empty fields */
14772   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14773     return FALSE;
14774
14775   if (old_element != EL_EMPTY)
14776     Back[dropx][dropy] = old_element;   /* store old element on this field */
14777
14778   ResetGfxAnimation(dropx, dropy);
14779   ResetRandomAnimationValue(dropx, dropy);
14780
14781   if (player->inventory_size > 0 ||
14782       player->inventory_infinite_element != EL_UNDEFINED)
14783   {
14784     if (player->inventory_size > 0)
14785     {
14786       player->inventory_size--;
14787
14788       DrawGameDoorValues();
14789
14790       if (new_element == EL_DYNAMITE)
14791         new_element = EL_DYNAMITE_ACTIVE;
14792       else if (new_element == EL_EM_DYNAMITE)
14793         new_element = EL_EM_DYNAMITE_ACTIVE;
14794       else if (new_element == EL_SP_DISK_RED)
14795         new_element = EL_SP_DISK_RED_ACTIVE;
14796     }
14797
14798     Feld[dropx][dropy] = new_element;
14799
14800     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14801       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14802                           el2img(Feld[dropx][dropy]), 0);
14803
14804     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14805
14806     /* needed if previous element just changed to "empty" in the last frame */
14807     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14808
14809     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14810                                player->index_bit, drop_side);
14811     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14812                                         CE_PLAYER_DROPS_X,
14813                                         player->index_bit, drop_side);
14814
14815     TestIfElementTouchesCustomElement(dropx, dropy);
14816   }
14817   else          /* player is dropping a dyna bomb */
14818   {
14819     player->dynabombs_left--;
14820
14821     Feld[dropx][dropy] = new_element;
14822
14823     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14824       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14825                           el2img(Feld[dropx][dropy]), 0);
14826
14827     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14828   }
14829
14830   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14831     InitField_WithBug1(dropx, dropy, FALSE);
14832
14833   new_element = Feld[dropx][dropy];     /* element might have changed */
14834
14835   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14836       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14837   {
14838     int move_direction, nextx, nexty;
14839
14840     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14841       MovDir[dropx][dropy] = drop_direction;
14842
14843     move_direction = MovDir[dropx][dropy];
14844     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14845     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14846
14847     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14848
14849 #if USE_FIX_IMPACT_COLLISION
14850     /* do not cause impact style collision by dropping elements that can fall */
14851     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14852 #else
14853     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14854 #endif
14855   }
14856
14857   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14858   player->is_dropping = TRUE;
14859
14860   player->drop_pressed_delay = 0;
14861   player->is_dropping_pressed = FALSE;
14862
14863   player->drop_x = dropx;
14864   player->drop_y = dropy;
14865
14866   return TRUE;
14867 }
14868
14869 /* ------------------------------------------------------------------------- */
14870 /* game sound playing functions                                              */
14871 /* ------------------------------------------------------------------------- */
14872
14873 static int *loop_sound_frame = NULL;
14874 static int *loop_sound_volume = NULL;
14875
14876 void InitPlayLevelSound()
14877 {
14878   int num_sounds = getSoundListSize();
14879
14880   checked_free(loop_sound_frame);
14881   checked_free(loop_sound_volume);
14882
14883   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14884   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14885 }
14886
14887 static void PlayLevelSound(int x, int y, int nr)
14888 {
14889   int sx = SCREENX(x), sy = SCREENY(y);
14890   int volume, stereo_position;
14891   int max_distance = 8;
14892   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14893
14894   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14895       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14896     return;
14897
14898   if (!IN_LEV_FIELD(x, y) ||
14899       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14900       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14901     return;
14902
14903   volume = SOUND_MAX_VOLUME;
14904
14905   if (!IN_SCR_FIELD(sx, sy))
14906   {
14907     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14908     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14909
14910     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14911   }
14912
14913   stereo_position = (SOUND_MAX_LEFT +
14914                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14915                      (SCR_FIELDX + 2 * max_distance));
14916
14917   if (IS_LOOP_SOUND(nr))
14918   {
14919     /* This assures that quieter loop sounds do not overwrite louder ones,
14920        while restarting sound volume comparison with each new game frame. */
14921
14922     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14923       return;
14924
14925     loop_sound_volume[nr] = volume;
14926     loop_sound_frame[nr] = FrameCounter;
14927   }
14928
14929   PlaySoundExt(nr, volume, stereo_position, type);
14930 }
14931
14932 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14933 {
14934   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14935                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14936                  y < LEVELY(BY1) ? LEVELY(BY1) :
14937                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14938                  sound_action);
14939 }
14940
14941 static void PlayLevelSoundAction(int x, int y, int action)
14942 {
14943   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14944 }
14945
14946 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14947 {
14948   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14949
14950   if (sound_effect != SND_UNDEFINED)
14951     PlayLevelSound(x, y, sound_effect);
14952 }
14953
14954 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14955                                               int action)
14956 {
14957   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14958
14959   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14960     PlayLevelSound(x, y, sound_effect);
14961 }
14962
14963 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14964 {
14965   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14966
14967   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14968     PlayLevelSound(x, y, sound_effect);
14969 }
14970
14971 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14972 {
14973   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14974
14975   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14976     StopSound(sound_effect);
14977 }
14978
14979 static void PlayLevelMusic()
14980 {
14981   if (levelset.music[level_nr] != MUS_UNDEFINED)
14982     PlayMusic(levelset.music[level_nr]);        /* from config file */
14983   else
14984     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14985 }
14986
14987 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14988 {
14989   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14990   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14991   int x = xx - 1 - offset;
14992   int y = yy - 1 - offset;
14993
14994   switch (sample)
14995   {
14996     case SAMPLE_blank:
14997       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14998       break;
14999
15000     case SAMPLE_roll:
15001       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15002       break;
15003
15004     case SAMPLE_stone:
15005       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15006       break;
15007
15008     case SAMPLE_nut:
15009       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15010       break;
15011
15012     case SAMPLE_crack:
15013       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15014       break;
15015
15016     case SAMPLE_bug:
15017       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15018       break;
15019
15020     case SAMPLE_tank:
15021       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15022       break;
15023
15024     case SAMPLE_android_clone:
15025       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15026       break;
15027
15028     case SAMPLE_android_move:
15029       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15030       break;
15031
15032     case SAMPLE_spring:
15033       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15034       break;
15035
15036     case SAMPLE_slurp:
15037       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15038       break;
15039
15040     case SAMPLE_eater:
15041       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15042       break;
15043
15044     case SAMPLE_eater_eat:
15045       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15046       break;
15047
15048     case SAMPLE_alien:
15049       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15050       break;
15051
15052     case SAMPLE_collect:
15053       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15054       break;
15055
15056     case SAMPLE_diamond:
15057       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15058       break;
15059
15060     case SAMPLE_squash:
15061       /* !!! CHECK THIS !!! */
15062 #if 1
15063       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15064 #else
15065       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15066 #endif
15067       break;
15068
15069     case SAMPLE_wonderfall:
15070       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15071       break;
15072
15073     case SAMPLE_drip:
15074       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15075       break;
15076
15077     case SAMPLE_push:
15078       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15079       break;
15080
15081     case SAMPLE_dirt:
15082       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15083       break;
15084
15085     case SAMPLE_acid:
15086       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15087       break;
15088
15089     case SAMPLE_ball:
15090       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15091       break;
15092
15093     case SAMPLE_grow:
15094       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15095       break;
15096
15097     case SAMPLE_wonder:
15098       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15099       break;
15100
15101     case SAMPLE_door:
15102       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15103       break;
15104
15105     case SAMPLE_exit_open:
15106       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15107       break;
15108
15109     case SAMPLE_exit_leave:
15110       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15111       break;
15112
15113     case SAMPLE_dynamite:
15114       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15115       break;
15116
15117     case SAMPLE_tick:
15118       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15119       break;
15120
15121     case SAMPLE_press:
15122       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15123       break;
15124
15125     case SAMPLE_wheel:
15126       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15127       break;
15128
15129     case SAMPLE_boom:
15130       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15131       break;
15132
15133     case SAMPLE_die:
15134       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15135       break;
15136
15137     case SAMPLE_time:
15138       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15139       break;
15140
15141     default:
15142       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15143       break;
15144   }
15145 }
15146
15147 #if 0
15148 void ChangeTime(int value)
15149 {
15150   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15151
15152   *time += value;
15153
15154   /* EMC game engine uses value from time counter of RND game engine */
15155   level.native_em_level->lev->time = *time;
15156
15157   DrawGameValue_Time(*time);
15158 }
15159
15160 void RaiseScore(int value)
15161 {
15162   /* EMC game engine and RND game engine have separate score counters */
15163   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15164                 &level.native_em_level->lev->score : &local_player->score);
15165
15166   *score += value;
15167
15168   DrawGameValue_Score(*score);
15169 }
15170 #endif
15171
15172 void RaiseScore(int value)
15173 {
15174   local_player->score += value;
15175
15176 #if 1
15177   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15178
15179   DisplayGameControlValues();
15180 #else
15181   DrawGameValue_Score(local_player->score);
15182 #endif
15183 }
15184
15185 void RaiseScoreElement(int element)
15186 {
15187   switch (element)
15188   {
15189     case EL_EMERALD:
15190     case EL_BD_DIAMOND:
15191     case EL_EMERALD_YELLOW:
15192     case EL_EMERALD_RED:
15193     case EL_EMERALD_PURPLE:
15194     case EL_SP_INFOTRON:
15195       RaiseScore(level.score[SC_EMERALD]);
15196       break;
15197     case EL_DIAMOND:
15198       RaiseScore(level.score[SC_DIAMOND]);
15199       break;
15200     case EL_CRYSTAL:
15201       RaiseScore(level.score[SC_CRYSTAL]);
15202       break;
15203     case EL_PEARL:
15204       RaiseScore(level.score[SC_PEARL]);
15205       break;
15206     case EL_BUG:
15207     case EL_BD_BUTTERFLY:
15208     case EL_SP_ELECTRON:
15209       RaiseScore(level.score[SC_BUG]);
15210       break;
15211     case EL_SPACESHIP:
15212     case EL_BD_FIREFLY:
15213     case EL_SP_SNIKSNAK:
15214       RaiseScore(level.score[SC_SPACESHIP]);
15215       break;
15216     case EL_YAMYAM:
15217     case EL_DARK_YAMYAM:
15218       RaiseScore(level.score[SC_YAMYAM]);
15219       break;
15220     case EL_ROBOT:
15221       RaiseScore(level.score[SC_ROBOT]);
15222       break;
15223     case EL_PACMAN:
15224       RaiseScore(level.score[SC_PACMAN]);
15225       break;
15226     case EL_NUT:
15227       RaiseScore(level.score[SC_NUT]);
15228       break;
15229     case EL_DYNAMITE:
15230     case EL_EM_DYNAMITE:
15231     case EL_SP_DISK_RED:
15232     case EL_DYNABOMB_INCREASE_NUMBER:
15233     case EL_DYNABOMB_INCREASE_SIZE:
15234     case EL_DYNABOMB_INCREASE_POWER:
15235       RaiseScore(level.score[SC_DYNAMITE]);
15236       break;
15237     case EL_SHIELD_NORMAL:
15238     case EL_SHIELD_DEADLY:
15239       RaiseScore(level.score[SC_SHIELD]);
15240       break;
15241     case EL_EXTRA_TIME:
15242       RaiseScore(level.extra_time_score);
15243       break;
15244     case EL_KEY_1:
15245     case EL_KEY_2:
15246     case EL_KEY_3:
15247     case EL_KEY_4:
15248     case EL_EM_KEY_1:
15249     case EL_EM_KEY_2:
15250     case EL_EM_KEY_3:
15251     case EL_EM_KEY_4:
15252     case EL_EMC_KEY_5:
15253     case EL_EMC_KEY_6:
15254     case EL_EMC_KEY_7:
15255     case EL_EMC_KEY_8:
15256     case EL_DC_KEY_WHITE:
15257       RaiseScore(level.score[SC_KEY]);
15258       break;
15259     default:
15260       RaiseScore(element_info[element].collect_score);
15261       break;
15262   }
15263 }
15264
15265 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15266 {
15267   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15268   {
15269 #if defined(NETWORK_AVALIABLE)
15270     if (options.network)
15271       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15272     else
15273 #endif
15274     {
15275       if (quick_quit)
15276       {
15277 #if 1
15278
15279 #if 1
15280         FadeSkipNextFadeIn();
15281 #else
15282         fading = fading_none;
15283 #endif
15284
15285 #else
15286         OpenDoor(DOOR_CLOSE_1);
15287 #endif
15288
15289         game_status = GAME_MODE_MAIN;
15290
15291 #if 1
15292         DrawAndFadeInMainMenu(REDRAW_FIELD);
15293 #else
15294         DrawMainMenu();
15295 #endif
15296       }
15297       else
15298       {
15299 #if 0
15300         FadeOut(REDRAW_FIELD);
15301 #endif
15302
15303         game_status = GAME_MODE_MAIN;
15304
15305         DrawAndFadeInMainMenu(REDRAW_FIELD);
15306       }
15307     }
15308   }
15309   else          /* continue playing the game */
15310   {
15311     if (tape.playing && tape.deactivate_display)
15312       TapeDeactivateDisplayOff(TRUE);
15313
15314     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15315
15316     if (tape.playing && tape.deactivate_display)
15317       TapeDeactivateDisplayOn();
15318   }
15319 }
15320
15321 void RequestQuitGame(boolean ask_if_really_quit)
15322 {
15323   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15324   boolean skip_request = AllPlayersGone || quick_quit;
15325
15326   RequestQuitGameExt(skip_request, quick_quit,
15327                      "Do you really want to quit the game ?");
15328 }
15329
15330
15331 /* ------------------------------------------------------------------------- */
15332 /* random generator functions                                                */
15333 /* ------------------------------------------------------------------------- */
15334
15335 unsigned int InitEngineRandom_RND(long seed)
15336 {
15337   game.num_random_calls = 0;
15338
15339 #if 0
15340   unsigned int rnd_seed = InitEngineRandom(seed);
15341
15342   printf("::: START RND: %d\n", rnd_seed);
15343
15344   return rnd_seed;
15345 #else
15346
15347   return InitEngineRandom(seed);
15348
15349 #endif
15350
15351 }
15352
15353 unsigned int RND(int max)
15354 {
15355   if (max > 0)
15356   {
15357     game.num_random_calls++;
15358
15359     return GetEngineRandom(max);
15360   }
15361
15362   return 0;
15363 }
15364
15365
15366 /* ------------------------------------------------------------------------- */
15367 /* game engine snapshot handling functions                                   */
15368 /* ------------------------------------------------------------------------- */
15369
15370 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15371
15372 struct EngineSnapshotInfo
15373 {
15374   /* runtime values for custom element collect score */
15375   int collect_score[NUM_CUSTOM_ELEMENTS];
15376
15377   /* runtime values for group element choice position */
15378   int choice_pos[NUM_GROUP_ELEMENTS];
15379
15380   /* runtime values for belt position animations */
15381   int belt_graphic[4 * NUM_BELT_PARTS];
15382   int belt_anim_mode[4 * NUM_BELT_PARTS];
15383 };
15384
15385 struct EngineSnapshotNodeInfo
15386 {
15387   void *buffer_orig;
15388   void *buffer_copy;
15389   int size;
15390 };
15391
15392 static struct EngineSnapshotInfo engine_snapshot_rnd;
15393 static ListNode *engine_snapshot_list = NULL;
15394 static char *snapshot_level_identifier = NULL;
15395 static int snapshot_level_nr = -1;
15396
15397 void FreeEngineSnapshot()
15398 {
15399   while (engine_snapshot_list != NULL)
15400     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15401                        checked_free);
15402
15403   setString(&snapshot_level_identifier, NULL);
15404   snapshot_level_nr = -1;
15405 }
15406
15407 static void SaveEngineSnapshotValues_RND()
15408 {
15409   static int belt_base_active_element[4] =
15410   {
15411     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15412     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15413     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15414     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15415   };
15416   int i, j;
15417
15418   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15419   {
15420     int element = EL_CUSTOM_START + i;
15421
15422     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15423   }
15424
15425   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15426   {
15427     int element = EL_GROUP_START + i;
15428
15429     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15430   }
15431
15432   for (i = 0; i < 4; i++)
15433   {
15434     for (j = 0; j < NUM_BELT_PARTS; j++)
15435     {
15436       int element = belt_base_active_element[i] + j;
15437       int graphic = el2img(element);
15438       int anim_mode = graphic_info[graphic].anim_mode;
15439
15440       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15441       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15442     }
15443   }
15444 }
15445
15446 static void LoadEngineSnapshotValues_RND()
15447 {
15448   unsigned long num_random_calls = game.num_random_calls;
15449   int i, j;
15450
15451   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15452   {
15453     int element = EL_CUSTOM_START + i;
15454
15455     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15456   }
15457
15458   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15459   {
15460     int element = EL_GROUP_START + i;
15461
15462     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15463   }
15464
15465   for (i = 0; i < 4; i++)
15466   {
15467     for (j = 0; j < NUM_BELT_PARTS; j++)
15468     {
15469       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15470       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15471
15472       graphic_info[graphic].anim_mode = anim_mode;
15473     }
15474   }
15475
15476   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15477   {
15478     InitRND(tape.random_seed);
15479     for (i = 0; i < num_random_calls; i++)
15480       RND(1);
15481   }
15482
15483   if (game.num_random_calls != num_random_calls)
15484   {
15485     Error(ERR_INFO, "number of random calls out of sync");
15486     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15487     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15488     Error(ERR_EXIT, "this should not happen -- please debug");
15489   }
15490 }
15491
15492 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15493 {
15494   struct EngineSnapshotNodeInfo *bi =
15495     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15496
15497   bi->buffer_orig = buffer;
15498   bi->buffer_copy = checked_malloc(size);
15499   bi->size = size;
15500
15501   memcpy(bi->buffer_copy, buffer, size);
15502
15503   addNodeToList(&engine_snapshot_list, NULL, bi);
15504 }
15505
15506 void SaveEngineSnapshot()
15507 {
15508   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15509
15510   if (level_editor_test_game)   /* do not save snapshots from editor */
15511     return;
15512
15513   /* copy some special values to a structure better suited for the snapshot */
15514
15515   SaveEngineSnapshotValues_RND();
15516   SaveEngineSnapshotValues_EM();
15517
15518   /* save values stored in special snapshot structure */
15519
15520   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15521   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15522
15523   /* save further RND engine values */
15524
15525   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15526   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15527   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15528
15529   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15530   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15531   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15532   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15533
15534   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15535   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15536   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15537   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15538   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15539
15540   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15541   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15542   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15543
15544   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15545
15546   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15547
15548   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15549   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15550
15551   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15553   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15554   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15555   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15556   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15558   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15559   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15560   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15561   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15562   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15563   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15565   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15566   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15567   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15569
15570   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15572
15573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15576
15577   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15579
15580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15585
15586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15588
15589   /* save level identification information */
15590
15591   setString(&snapshot_level_identifier, leveldir_current->identifier);
15592   snapshot_level_nr = level_nr;
15593
15594 #if 0
15595   ListNode *node = engine_snapshot_list;
15596   int num_bytes = 0;
15597
15598   while (node != NULL)
15599   {
15600     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15601
15602     node = node->next;
15603   }
15604
15605   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15606 #endif
15607 }
15608
15609 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15610 {
15611   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15612 }
15613
15614 void LoadEngineSnapshot()
15615 {
15616   ListNode *node = engine_snapshot_list;
15617
15618   if (engine_snapshot_list == NULL)
15619     return;
15620
15621   while (node != NULL)
15622   {
15623     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15624
15625     node = node->next;
15626   }
15627
15628   /* restore special values from snapshot structure */
15629
15630   LoadEngineSnapshotValues_RND();
15631   LoadEngineSnapshotValues_EM();
15632 }
15633
15634 boolean CheckEngineSnapshot()
15635 {
15636   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15637           snapshot_level_nr == level_nr);
15638 }
15639
15640
15641 /* ---------- new game button stuff ---------------------------------------- */
15642
15643 /* graphic position values for game buttons */
15644 #define GAME_BUTTON_XSIZE       30
15645 #define GAME_BUTTON_YSIZE       30
15646 #define GAME_BUTTON_XPOS        5
15647 #define GAME_BUTTON_YPOS        215
15648 #define SOUND_BUTTON_XPOS       5
15649 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15650
15651 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15652 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15653 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15654 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15655 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15656 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15657
15658 static struct
15659 {
15660   int *x, *y;
15661   int gd_x, gd_y;
15662   int gadget_id;
15663   char *infotext;
15664 } gamebutton_info[NUM_GAME_BUTTONS] =
15665 {
15666 #if 1
15667   {
15668     &game.button.stop.x,        &game.button.stop.y,
15669     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15670     GAME_CTRL_ID_STOP,
15671     "stop game"
15672   },
15673   {
15674     &game.button.pause.x,       &game.button.pause.y,
15675     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15676     GAME_CTRL_ID_PAUSE,
15677     "pause game"
15678   },
15679   {
15680     &game.button.play.x,        &game.button.play.y,
15681     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15682     GAME_CTRL_ID_PLAY,
15683     "play game"
15684   },
15685   {
15686     &game.button.sound_music.x, &game.button.sound_music.y,
15687     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15688     SOUND_CTRL_ID_MUSIC,
15689     "background music on/off"
15690   },
15691   {
15692     &game.button.sound_loops.x, &game.button.sound_loops.y,
15693     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15694     SOUND_CTRL_ID_LOOPS,
15695     "sound loops on/off"
15696   },
15697   {
15698     &game.button.sound_simple.x,&game.button.sound_simple.y,
15699     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15700     SOUND_CTRL_ID_SIMPLE,
15701     "normal sounds on/off"
15702   }
15703 #else
15704   {
15705     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15706     GAME_CTRL_ID_STOP,
15707     "stop game"
15708   },
15709   {
15710     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15711     GAME_CTRL_ID_PAUSE,
15712     "pause game"
15713   },
15714   {
15715     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15716     GAME_CTRL_ID_PLAY,
15717     "play game"
15718   },
15719   {
15720     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15721     SOUND_CTRL_ID_MUSIC,
15722     "background music on/off"
15723   },
15724   {
15725     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15726     SOUND_CTRL_ID_LOOPS,
15727     "sound loops on/off"
15728   },
15729   {
15730     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15731     SOUND_CTRL_ID_SIMPLE,
15732     "normal sounds on/off"
15733   }
15734 #endif
15735 };
15736
15737 void CreateGameButtons()
15738 {
15739   int i;
15740
15741   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15742   {
15743     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15744     struct GadgetInfo *gi;
15745     int button_type;
15746     boolean checked;
15747     unsigned long event_mask;
15748     int x, y;
15749     int gd_xoffset, gd_yoffset;
15750     int gd_x1, gd_x2, gd_y1, gd_y2;
15751     int id = i;
15752
15753     x = DX + *gamebutton_info[i].x;
15754     y = DY + *gamebutton_info[i].y;
15755     gd_xoffset = gamebutton_info[i].gd_x;
15756     gd_yoffset = gamebutton_info[i].gd_y;
15757     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15758     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15759
15760     if (id == GAME_CTRL_ID_STOP ||
15761         id == GAME_CTRL_ID_PAUSE ||
15762         id == GAME_CTRL_ID_PLAY)
15763     {
15764       button_type = GD_TYPE_NORMAL_BUTTON;
15765       checked = FALSE;
15766       event_mask = GD_EVENT_RELEASED;
15767       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15768       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15769     }
15770     else
15771     {
15772       button_type = GD_TYPE_CHECK_BUTTON;
15773       checked =
15774         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15775          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15776          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15777       event_mask = GD_EVENT_PRESSED;
15778       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15779       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15780     }
15781
15782     gi = CreateGadget(GDI_CUSTOM_ID, id,
15783                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15784 #if 1
15785                       GDI_X, x,
15786                       GDI_Y, y,
15787 #else
15788                       GDI_X, DX + gd_xoffset,
15789                       GDI_Y, DY + gd_yoffset,
15790 #endif
15791                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15792                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15793                       GDI_TYPE, button_type,
15794                       GDI_STATE, GD_BUTTON_UNPRESSED,
15795                       GDI_CHECKED, checked,
15796                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15797                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15798                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15799                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15800                       GDI_DIRECT_DRAW, FALSE,
15801                       GDI_EVENT_MASK, event_mask,
15802                       GDI_CALLBACK_ACTION, HandleGameButtons,
15803                       GDI_END);
15804
15805     if (gi == NULL)
15806       Error(ERR_EXIT, "cannot create gadget");
15807
15808     game_gadget[id] = gi;
15809   }
15810 }
15811
15812 void FreeGameButtons()
15813 {
15814   int i;
15815
15816   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15817     FreeGadget(game_gadget[i]);
15818 }
15819
15820 static void MapGameButtons()
15821 {
15822   int i;
15823
15824   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15825     MapGadget(game_gadget[i]);
15826 }
15827
15828 void UnmapGameButtons()
15829 {
15830   int i;
15831
15832   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15833     UnmapGadget(game_gadget[i]);
15834 }
15835
15836 void RedrawGameButtons()
15837 {
15838   int i;
15839
15840   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15841     RedrawGadget(game_gadget[i]);
15842 }
15843
15844 static void HandleGameButtons(struct GadgetInfo *gi)
15845 {
15846   int id = gi->custom_id;
15847
15848   if (game_status != GAME_MODE_PLAYING)
15849     return;
15850
15851   switch (id)
15852   {
15853     case GAME_CTRL_ID_STOP:
15854       if (tape.playing)
15855         TapeStop();
15856       else
15857         RequestQuitGame(TRUE);
15858       break;
15859
15860     case GAME_CTRL_ID_PAUSE:
15861       if (options.network)
15862       {
15863 #if defined(NETWORK_AVALIABLE)
15864         if (tape.pausing)
15865           SendToServer_ContinuePlaying();
15866         else
15867           SendToServer_PausePlaying();
15868 #endif
15869       }
15870       else
15871         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15872       break;
15873
15874     case GAME_CTRL_ID_PLAY:
15875       if (tape.pausing)
15876       {
15877 #if defined(NETWORK_AVALIABLE)
15878         if (options.network)
15879           SendToServer_ContinuePlaying();
15880         else
15881 #endif
15882         {
15883           tape.pausing = FALSE;
15884           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15885         }
15886       }
15887       break;
15888
15889     case SOUND_CTRL_ID_MUSIC:
15890       if (setup.sound_music)
15891       { 
15892         setup.sound_music = FALSE;
15893         FadeMusic();
15894       }
15895       else if (audio.music_available)
15896       { 
15897         setup.sound = setup.sound_music = TRUE;
15898
15899         SetAudioMode(setup.sound);
15900
15901         PlayLevelMusic();
15902       }
15903       break;
15904
15905     case SOUND_CTRL_ID_LOOPS:
15906       if (setup.sound_loops)
15907         setup.sound_loops = FALSE;
15908       else if (audio.loops_available)
15909       {
15910         setup.sound = setup.sound_loops = TRUE;
15911         SetAudioMode(setup.sound);
15912       }
15913       break;
15914
15915     case SOUND_CTRL_ID_SIMPLE:
15916       if (setup.sound_simple)
15917         setup.sound_simple = FALSE;
15918       else if (audio.sound_available)
15919       {
15920         setup.sound = setup.sound_simple = TRUE;
15921         SetAudioMode(setup.sound);
15922       }
15923       break;
15924
15925     default:
15926       break;
15927   }
15928 }