rnd-20070901-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_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP               0
1007 #define GAME_CTRL_ID_PAUSE              1
1008 #define GAME_CTRL_ID_PLAY               2
1009 #define SOUND_CTRL_ID_MUSIC             3
1010 #define SOUND_CTRL_ID_LOOPS             4
1011 #define SOUND_CTRL_ID_SIMPLE            5
1012
1013 #define NUM_GAME_BUTTONS                6
1014
1015
1016 /* forward declaration for internal use */
1017
1018 static void CreateField(int, int, int);
1019
1020 static void ResetGfxAnimation(int, int);
1021
1022 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1023 static void AdvanceFrameAndPlayerCounters(int);
1024
1025 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1026 static boolean MovePlayer(struct PlayerInfo *, int, int);
1027 static void ScrollPlayer(struct PlayerInfo *, int);
1028 static void ScrollScreen(struct PlayerInfo *, int);
1029
1030 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1031
1032 static void InitBeltMovement(void);
1033 static void CloseAllOpenTimegates(void);
1034 static void CheckGravityMovement(struct PlayerInfo *);
1035 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1036 static void KillPlayerUnlessEnemyProtected(int, int);
1037 static void KillPlayerUnlessExplosionProtected(int, int);
1038
1039 static void TestIfPlayerTouchesCustomElement(int, int);
1040 static void TestIfElementTouchesCustomElement(int, int);
1041 static void TestIfElementHitsCustomElement(int, int, int);
1042 #if 0
1043 static void TestIfElementSmashesCustomElement(int, int, int);
1044 #endif
1045
1046 static void HandleElementChange(int, int, int);
1047 static void ExecuteCustomElementAction(int, int, int, int);
1048 static boolean ChangeElement(int, int, int, int);
1049
1050 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1051 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1052         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1053 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1054         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1055 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1056         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1057 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1058         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1059
1060 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1061 #define CheckElementChange(x, y, e, te, ev)                             \
1062         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1063 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1064         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1065 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1066         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1067
1068 static void PlayLevelSound(int, int, int);
1069 static void PlayLevelSoundNearest(int, int, int);
1070 static void PlayLevelSoundAction(int, int, int);
1071 static void PlayLevelSoundElementAction(int, int, int, int);
1072 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1073 static void PlayLevelSoundActionIfLoop(int, int, int);
1074 static void StopLevelSoundActionIfLoop(int, int, int);
1075 static void PlayLevelMusic();
1076
1077 static void MapGameButtons();
1078 static void HandleGameButtons(struct GadgetInfo *);
1079
1080 int AmoebeNachbarNr(int, int);
1081 void AmoebeUmwandeln(int, int);
1082 void ContinueMoving(int, int);
1083 void Bang(int, int);
1084 void InitMovDir(int, int);
1085 void InitAmoebaNr(int, int);
1086 int NewHiScore(void);
1087
1088 void TestIfGoodThingHitsBadThing(int, int, int);
1089 void TestIfBadThingHitsGoodThing(int, int, int);
1090 void TestIfPlayerTouchesBadThing(int, int);
1091 void TestIfPlayerRunsIntoBadThing(int, int, int);
1092 void TestIfBadThingTouchesPlayer(int, int);
1093 void TestIfBadThingRunsIntoPlayer(int, int, int);
1094 void TestIfFriendTouchesBadThing(int, int);
1095 void TestIfBadThingTouchesFriend(int, int);
1096 void TestIfBadThingTouchesOtherBadThing(int, int);
1097
1098 void KillPlayer(struct PlayerInfo *);
1099 void BuryPlayer(struct PlayerInfo *);
1100 void RemovePlayer(struct PlayerInfo *);
1101
1102 boolean SnapField(struct PlayerInfo *, int, int);
1103 boolean DropElement(struct PlayerInfo *);
1104
1105 static int getInvisibleActiveFromInvisibleElement(int);
1106 static int getInvisibleFromInvisibleActiveElement(int);
1107
1108 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1109
1110 /* for detection of endless loops, caused by custom element programming */
1111 /* (using maximal playfield width x 10 is just a rough approximation) */
1112 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1113
1114 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1115 {                                                                       \
1116   if (recursion_loop_detected)                                          \
1117     return (rc);                                                        \
1118                                                                         \
1119   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1120   {                                                                     \
1121     recursion_loop_detected = TRUE;                                     \
1122     recursion_loop_element = (e);                                       \
1123   }                                                                     \
1124                                                                         \
1125   recursion_loop_depth++;                                               \
1126 }
1127
1128 #define RECURSION_LOOP_DETECTION_END()                                  \
1129 {                                                                       \
1130   recursion_loop_depth--;                                               \
1131 }
1132
1133 static int recursion_loop_depth;
1134 static boolean recursion_loop_detected;
1135 static boolean recursion_loop_element;
1136
1137
1138 /* ------------------------------------------------------------------------- */
1139 /* definition of elements that automatically change to other elements after  */
1140 /* a specified time, eventually calling a function when changing             */
1141 /* ------------------------------------------------------------------------- */
1142
1143 /* forward declaration for changer functions */
1144 static void InitBuggyBase(int, int);
1145 static void WarnBuggyBase(int, int);
1146
1147 static void InitTrap(int, int);
1148 static void ActivateTrap(int, int);
1149 static void ChangeActiveTrap(int, int);
1150
1151 static void InitRobotWheel(int, int);
1152 static void RunRobotWheel(int, int);
1153 static void StopRobotWheel(int, int);
1154
1155 static void InitTimegateWheel(int, int);
1156 static void RunTimegateWheel(int, int);
1157
1158 static void InitMagicBallDelay(int, int);
1159 static void ActivateMagicBall(int, int);
1160
1161 struct ChangingElementInfo
1162 {
1163   int element;
1164   int target_element;
1165   int change_delay;
1166   void (*pre_change_function)(int x, int y);
1167   void (*change_function)(int x, int y);
1168   void (*post_change_function)(int x, int y);
1169 };
1170
1171 static struct ChangingElementInfo change_delay_list[] =
1172 {
1173   {
1174     EL_NUT_BREAKING,
1175     EL_EMERALD,
1176     6,
1177     NULL,
1178     NULL,
1179     NULL
1180   },
1181   {
1182     EL_PEARL_BREAKING,
1183     EL_EMPTY,
1184     8,
1185     NULL,
1186     NULL,
1187     NULL
1188   },
1189   {
1190     EL_EXIT_OPENING,
1191     EL_EXIT_OPEN,
1192     29,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_EXIT_CLOSING,
1199     EL_EXIT_CLOSED,
1200     29,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_STEEL_EXIT_OPENING,
1207     EL_STEEL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_STEEL_EXIT_CLOSING,
1215     EL_STEEL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_EM_EXIT_OPENING,
1223     EL_EM_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_EM_EXIT_CLOSING,
1231 #if 1
1232     EL_EMPTY,
1233 #else
1234     EL_EM_EXIT_CLOSED,
1235 #endif
1236     29,
1237     NULL,
1238     NULL,
1239     NULL
1240   },
1241   {
1242     EL_EM_STEEL_EXIT_OPENING,
1243     EL_EM_STEEL_EXIT_OPEN,
1244     29,
1245     NULL,
1246     NULL,
1247     NULL
1248   },
1249   {
1250     EL_EM_STEEL_EXIT_CLOSING,
1251 #if 1
1252     EL_STEELWALL,
1253 #else
1254     EL_EM_STEEL_EXIT_CLOSED,
1255 #endif
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_SP_EXIT_OPENING,
1263     EL_SP_EXIT_OPEN,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_CLOSING,
1271     EL_SP_EXIT_CLOSED,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SWITCHGATE_OPENING,
1279     EL_SWITCHGATE_OPEN,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_CLOSING,
1287     EL_SWITCHGATE_CLOSED,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_TIMEGATE_OPENING,
1295     EL_TIMEGATE_OPEN,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_CLOSING,
1303     EL_TIMEGATE_CLOSED,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309
1310   {
1311     EL_ACID_SPLASH_LEFT,
1312     EL_EMPTY,
1313     8,
1314     NULL,
1315     NULL,
1316     NULL
1317   },
1318   {
1319     EL_ACID_SPLASH_RIGHT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_SP_BUGGY_BASE,
1328     EL_SP_BUGGY_BASE_ACTIVATING,
1329     0,
1330     InitBuggyBase,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE_ACTIVATING,
1336     EL_SP_BUGGY_BASE_ACTIVE,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVE,
1344     EL_SP_BUGGY_BASE,
1345     0,
1346     InitBuggyBase,
1347     WarnBuggyBase,
1348     NULL
1349   },
1350   {
1351     EL_TRAP,
1352     EL_TRAP_ACTIVE,
1353     0,
1354     InitTrap,
1355     NULL,
1356     ActivateTrap
1357   },
1358   {
1359     EL_TRAP_ACTIVE,
1360     EL_TRAP,
1361     31,
1362     NULL,
1363     ChangeActiveTrap,
1364     NULL
1365   },
1366   {
1367     EL_ROBOT_WHEEL_ACTIVE,
1368     EL_ROBOT_WHEEL,
1369     0,
1370     InitRobotWheel,
1371     RunRobotWheel,
1372     StopRobotWheel
1373   },
1374   {
1375     EL_TIMEGATE_SWITCH_ACTIVE,
1376     EL_TIMEGATE_SWITCH,
1377     0,
1378     InitTimegateWheel,
1379     RunTimegateWheel,
1380     NULL
1381   },
1382   {
1383     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1384     EL_DC_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_EMC_MAGIC_BALL_ACTIVE,
1392     EL_EMC_MAGIC_BALL_ACTIVE,
1393     0,
1394     InitMagicBallDelay,
1395     NULL,
1396     ActivateMagicBall
1397   },
1398   {
1399     EL_EMC_SPRING_BUMPER_ACTIVE,
1400     EL_EMC_SPRING_BUMPER,
1401     8,
1402     NULL,
1403     NULL,
1404     NULL
1405   },
1406   {
1407     EL_DIAGONAL_SHRINKING,
1408     EL_UNDEFINED,
1409     0,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_GROWING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL,
1421   },
1422
1423   {
1424     EL_UNDEFINED,
1425     EL_UNDEFINED,
1426     -1,
1427     NULL,
1428     NULL,
1429     NULL
1430   }
1431 };
1432
1433 struct
1434 {
1435   int element;
1436   int push_delay_fixed, push_delay_random;
1437 }
1438 push_delay_list[] =
1439 {
1440   { EL_SPRING,                  0, 0 },
1441   { EL_BALLOON,                 0, 0 },
1442
1443   { EL_SOKOBAN_OBJECT,          2, 0 },
1444   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1445   { EL_SATELLITE,               2, 0 },
1446   { EL_SP_DISK_YELLOW,          2, 0 },
1447
1448   { EL_UNDEFINED,               0, 0 },
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int move_stepsize;
1455 }
1456 move_stepsize_list[] =
1457 {
1458   { EL_AMOEBA_DROP,             2 },
1459   { EL_AMOEBA_DROPPING,         2 },
1460   { EL_QUICKSAND_FILLING,       1 },
1461   { EL_QUICKSAND_EMPTYING,      1 },
1462   { EL_QUICKSAND_FAST_FILLING,  2 },
1463   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1464   { EL_MAGIC_WALL_FILLING,      2 },
1465   { EL_MAGIC_WALL_EMPTYING,     2 },
1466   { EL_BD_MAGIC_WALL_FILLING,   2 },
1467   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1468   { EL_DC_MAGIC_WALL_FILLING,   2 },
1469   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1470
1471   { EL_UNDEFINED,               0 },
1472 };
1473
1474 struct
1475 {
1476   int element;
1477   int count;
1478 }
1479 collect_count_list[] =
1480 {
1481   { EL_EMERALD,                 1 },
1482   { EL_BD_DIAMOND,              1 },
1483   { EL_EMERALD_YELLOW,          1 },
1484   { EL_EMERALD_RED,             1 },
1485   { EL_EMERALD_PURPLE,          1 },
1486   { EL_DIAMOND,                 3 },
1487   { EL_SP_INFOTRON,             1 },
1488   { EL_PEARL,                   5 },
1489   { EL_CRYSTAL,                 8 },
1490
1491   { EL_UNDEFINED,               0 },
1492 };
1493
1494 struct
1495 {
1496   int element;
1497   int direction;
1498 }
1499 access_direction_list[] =
1500 {
1501   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1502   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1503   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1504   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1505   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1506   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1507   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1508   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1509   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1510   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1511   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1512
1513   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1514   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1515   { EL_SP_PORT_UP,                                                   MV_DOWN },
1516   { EL_SP_PORT_DOWN,                                         MV_UP           },
1517   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1518   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1519   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1521   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1522   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1523   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1524   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1525   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1526   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1527   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1528   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1529   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1530   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1531   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1532
1533   { EL_UNDEFINED,                       MV_NONE                              }
1534 };
1535
1536 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1537
1538 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1539 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1540 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1541                                  IS_JUST_CHANGING(x, y))
1542
1543 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1544
1545 /* static variables for playfield scan mode (scanning forward or backward) */
1546 static int playfield_scan_start_x = 0;
1547 static int playfield_scan_start_y = 0;
1548 static int playfield_scan_delta_x = 1;
1549 static int playfield_scan_delta_y = 1;
1550
1551 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1552                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1553                                      (y) += playfield_scan_delta_y)     \
1554                                 for ((x) = playfield_scan_start_x;      \
1555                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1556                                      (x) += playfield_scan_delta_x)
1557
1558 #ifdef DEBUG
1559 void DEBUG_SetMaximumDynamite()
1560 {
1561   int i;
1562
1563   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1564     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1565       local_player->inventory_element[local_player->inventory_size++] =
1566         EL_DYNAMITE;
1567 }
1568 #endif
1569
1570 static void InitPlayfieldScanModeVars()
1571 {
1572   if (game.use_reverse_scan_direction)
1573   {
1574     playfield_scan_start_x = lev_fieldx - 1;
1575     playfield_scan_start_y = lev_fieldy - 1;
1576
1577     playfield_scan_delta_x = -1;
1578     playfield_scan_delta_y = -1;
1579   }
1580   else
1581   {
1582     playfield_scan_start_x = 0;
1583     playfield_scan_start_y = 0;
1584
1585     playfield_scan_delta_x = 1;
1586     playfield_scan_delta_y = 1;
1587   }
1588 }
1589
1590 static void InitPlayfieldScanMode(int mode)
1591 {
1592   game.use_reverse_scan_direction =
1593     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1594
1595   InitPlayfieldScanModeVars();
1596 }
1597
1598 static int get_move_delay_from_stepsize(int move_stepsize)
1599 {
1600   move_stepsize =
1601     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1602
1603   /* make sure that stepsize value is always a power of 2 */
1604   move_stepsize = (1 << log_2(move_stepsize));
1605
1606   return TILEX / move_stepsize;
1607 }
1608
1609 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1610                                boolean init_game)
1611 {
1612   int player_nr = player->index_nr;
1613   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1614   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1615
1616   /* do no immediately change move delay -- the player might just be moving */
1617   player->move_delay_value_next = move_delay;
1618
1619   /* information if player can move must be set separately */
1620   player->cannot_move = cannot_move;
1621
1622   if (init_game)
1623   {
1624     player->move_delay       = game.initial_move_delay[player_nr];
1625     player->move_delay_value = game.initial_move_delay_value[player_nr];
1626
1627     player->move_delay_value_next = -1;
1628
1629     player->move_delay_reset_counter = 0;
1630   }
1631 }
1632
1633 void GetPlayerConfig()
1634 {
1635   GameFrameDelay = setup.game_frame_delay;
1636
1637   if (!audio.sound_available)
1638     setup.sound_simple = FALSE;
1639
1640   if (!audio.loops_available)
1641     setup.sound_loops = FALSE;
1642
1643   if (!audio.music_available)
1644     setup.sound_music = FALSE;
1645
1646   if (!video.fullscreen_available)
1647     setup.fullscreen = FALSE;
1648
1649   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1650
1651   SetAudioMode(setup.sound);
1652   InitJoysticks();
1653 }
1654
1655 int GetElementFromGroupElement(int element)
1656 {
1657   if (IS_GROUP_ELEMENT(element))
1658   {
1659     struct ElementGroupInfo *group = element_info[element].group;
1660     int last_anim_random_frame = gfx.anim_random_frame;
1661     int element_pos;
1662
1663     if (group->choice_mode == ANIM_RANDOM)
1664       gfx.anim_random_frame = RND(group->num_elements_resolved);
1665
1666     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1667                                     group->choice_mode, 0,
1668                                     group->choice_pos);
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = last_anim_random_frame;
1672
1673     group->choice_pos++;
1674
1675     element = group->element_resolved[element_pos];
1676   }
1677
1678   return element;
1679 }
1680
1681 static void InitPlayerField(int x, int y, int element, boolean init_game)
1682 {
1683   if (element == EL_SP_MURPHY)
1684   {
1685     if (init_game)
1686     {
1687       if (stored_player[0].present)
1688       {
1689         Feld[x][y] = EL_SP_MURPHY_CLONE;
1690
1691         return;
1692       }
1693       else
1694       {
1695         stored_player[0].use_murphy = TRUE;
1696
1697         if (!level.use_artwork_element[0])
1698           stored_player[0].artwork_element = EL_SP_MURPHY;
1699       }
1700
1701       Feld[x][y] = EL_PLAYER_1;
1702     }
1703   }
1704
1705   if (init_game)
1706   {
1707     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1708     int jx = player->jx, jy = player->jy;
1709
1710     player->present = TRUE;
1711
1712     player->block_last_field = (element == EL_SP_MURPHY ?
1713                                 level.sp_block_last_field :
1714                                 level.block_last_field);
1715
1716     /* ---------- initialize player's last field block delay --------------- */
1717
1718     /* always start with reliable default value (no adjustment needed) */
1719     player->block_delay_adjustment = 0;
1720
1721     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1722     if (player->block_last_field && element == EL_SP_MURPHY)
1723       player->block_delay_adjustment = 1;
1724
1725     /* special case 2: in game engines before 3.1.1, blocking was different */
1726     if (game.use_block_last_field_bug)
1727       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1728
1729     if (!options.network || player->connected)
1730     {
1731       player->active = TRUE;
1732
1733       /* remove potentially duplicate players */
1734       if (StorePlayer[jx][jy] == Feld[x][y])
1735         StorePlayer[jx][jy] = 0;
1736
1737       StorePlayer[x][y] = Feld[x][y];
1738
1739       if (options.debug)
1740       {
1741         printf("Player %d activated.\n", player->element_nr);
1742         printf("[Local player is %d and currently %s.]\n",
1743                local_player->element_nr,
1744                local_player->active ? "active" : "not active");
1745       }
1746     }
1747
1748     Feld[x][y] = EL_EMPTY;
1749
1750     player->jx = player->last_jx = x;
1751     player->jy = player->last_jy = y;
1752   }
1753 }
1754
1755 static void InitField(int x, int y, boolean init_game)
1756 {
1757   int element = Feld[x][y];
1758
1759   switch (element)
1760   {
1761     case EL_SP_MURPHY:
1762     case EL_PLAYER_1:
1763     case EL_PLAYER_2:
1764     case EL_PLAYER_3:
1765     case EL_PLAYER_4:
1766       InitPlayerField(x, y, element, init_game);
1767       break;
1768
1769     case EL_SOKOBAN_FIELD_PLAYER:
1770       element = Feld[x][y] = EL_PLAYER_1;
1771       InitField(x, y, init_game);
1772
1773       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1774       InitField(x, y, init_game);
1775       break;
1776
1777     case EL_SOKOBAN_FIELD_EMPTY:
1778       local_player->sokobanfields_still_needed++;
1779       break;
1780
1781     case EL_STONEBLOCK:
1782       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1783         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1784       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1785         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1786       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1787         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1788       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1789         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1790       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1791         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1792       break;
1793
1794     case EL_BUG:
1795     case EL_BUG_RIGHT:
1796     case EL_BUG_UP:
1797     case EL_BUG_LEFT:
1798     case EL_BUG_DOWN:
1799     case EL_SPACESHIP:
1800     case EL_SPACESHIP_RIGHT:
1801     case EL_SPACESHIP_UP:
1802     case EL_SPACESHIP_LEFT:
1803     case EL_SPACESHIP_DOWN:
1804     case EL_BD_BUTTERFLY:
1805     case EL_BD_BUTTERFLY_RIGHT:
1806     case EL_BD_BUTTERFLY_UP:
1807     case EL_BD_BUTTERFLY_LEFT:
1808     case EL_BD_BUTTERFLY_DOWN:
1809     case EL_BD_FIREFLY:
1810     case EL_BD_FIREFLY_RIGHT:
1811     case EL_BD_FIREFLY_UP:
1812     case EL_BD_FIREFLY_LEFT:
1813     case EL_BD_FIREFLY_DOWN:
1814     case EL_PACMAN_RIGHT:
1815     case EL_PACMAN_UP:
1816     case EL_PACMAN_LEFT:
1817     case EL_PACMAN_DOWN:
1818     case EL_YAMYAM:
1819     case EL_YAMYAM_LEFT:
1820     case EL_YAMYAM_RIGHT:
1821     case EL_YAMYAM_UP:
1822     case EL_YAMYAM_DOWN:
1823     case EL_DARK_YAMYAM:
1824     case EL_ROBOT:
1825     case EL_PACMAN:
1826     case EL_SP_SNIKSNAK:
1827     case EL_SP_ELECTRON:
1828     case EL_MOLE:
1829     case EL_MOLE_LEFT:
1830     case EL_MOLE_RIGHT:
1831     case EL_MOLE_UP:
1832     case EL_MOLE_DOWN:
1833       InitMovDir(x, y);
1834       break;
1835
1836     case EL_AMOEBA_FULL:
1837     case EL_BD_AMOEBA:
1838       InitAmoebaNr(x, y);
1839       break;
1840
1841     case EL_AMOEBA_DROP:
1842       if (y == lev_fieldy - 1)
1843       {
1844         Feld[x][y] = EL_AMOEBA_GROWING;
1845         Store[x][y] = EL_AMOEBA_WET;
1846       }
1847       break;
1848
1849     case EL_DYNAMITE_ACTIVE:
1850     case EL_SP_DISK_RED_ACTIVE:
1851     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1852     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1853     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1854     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1855       MovDelay[x][y] = 96;
1856       break;
1857
1858     case EL_EM_DYNAMITE_ACTIVE:
1859       MovDelay[x][y] = 32;
1860       break;
1861
1862     case EL_LAMP:
1863       local_player->lights_still_needed++;
1864       break;
1865
1866     case EL_PENGUIN:
1867       local_player->friends_still_needed++;
1868       break;
1869
1870     case EL_PIG:
1871     case EL_DRAGON:
1872       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1873       break;
1874
1875     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1876     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1877     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1878     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1879     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1880     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1881     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1882     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1883     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1884     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1885     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1886     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1887       if (init_game)
1888       {
1889         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1890         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1891         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1892
1893         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1894         {
1895           game.belt_dir[belt_nr] = belt_dir;
1896           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1897         }
1898         else    /* more than one switch -- set it like the first switch */
1899         {
1900           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1901         }
1902       }
1903       break;
1904
1905 #if !USE_BOTH_SWITCHGATE_SWITCHES
1906     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1907       if (init_game)
1908         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1909       break;
1910
1911     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1912       if (init_game)
1913         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1914       break;
1915 #endif
1916
1917     case EL_LIGHT_SWITCH_ACTIVE:
1918       if (init_game)
1919         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1920       break;
1921
1922     case EL_INVISIBLE_STEELWALL:
1923     case EL_INVISIBLE_WALL:
1924     case EL_INVISIBLE_SAND:
1925       if (game.light_time_left > 0 ||
1926           game.lenses_time_left > 0)
1927         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1928       break;
1929
1930     case EL_EMC_MAGIC_BALL:
1931       if (game.ball_state)
1932         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL_SWITCH:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1938       break;
1939
1940     default:
1941       if (IS_CUSTOM_ELEMENT(element))
1942       {
1943         if (CAN_MOVE(element))
1944           InitMovDir(x, y);
1945
1946 #if USE_NEW_CUSTOM_VALUE
1947         if (!element_info[element].use_last_ce_value || init_game)
1948           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1949 #endif
1950       }
1951       else if (IS_GROUP_ELEMENT(element))
1952       {
1953         Feld[x][y] = GetElementFromGroupElement(element);
1954
1955         InitField(x, y, init_game);
1956       }
1957
1958       break;
1959   }
1960
1961   if (!init_game)
1962     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1963 }
1964
1965 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1966 {
1967   InitField(x, y, init_game);
1968
1969   /* not needed to call InitMovDir() -- already done by InitField()! */
1970   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971       CAN_MOVE(Feld[x][y]))
1972     InitMovDir(x, y);
1973 }
1974
1975 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1976 {
1977   int old_element = Feld[x][y];
1978
1979   InitField(x, y, init_game);
1980
1981   /* not needed to call InitMovDir() -- already done by InitField()! */
1982   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1983       CAN_MOVE(old_element) &&
1984       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1985     InitMovDir(x, y);
1986
1987   /* this case is in fact a combination of not less than three bugs:
1988      first, it calls InitMovDir() for elements that can move, although this is
1989      already done by InitField(); then, it checks the element that was at this
1990      field _before_ the call to InitField() (which can change it); lastly, it
1991      was not called for "mole with direction" elements, which were treated as
1992      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1993   */
1994 }
1995
1996 #if 1
1997
1998 static int get_key_element_from_nr(int key_nr)
1999 {
2000   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2001                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2002                           EL_EM_KEY_1 : EL_KEY_1);
2003
2004   return key_base_element + key_nr;
2005 }
2006
2007 static int get_next_dropped_element(struct PlayerInfo *player)
2008 {
2009   return (player->inventory_size > 0 ?
2010           player->inventory_element[player->inventory_size - 1] :
2011           player->inventory_infinite_element != EL_UNDEFINED ?
2012           player->inventory_infinite_element :
2013           player->dynabombs_left > 0 ?
2014           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2015           EL_UNDEFINED);
2016 }
2017
2018 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2019 {
2020   /* pos >= 0: get element from bottom of the stack;
2021      pos <  0: get element from top of the stack */
2022
2023   if (pos < 0)
2024   {
2025     int min_inventory_size = -pos;
2026     int inventory_pos = player->inventory_size - min_inventory_size;
2027     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2028
2029     return (player->inventory_size >= min_inventory_size ?
2030             player->inventory_element[inventory_pos] :
2031             player->inventory_infinite_element != EL_UNDEFINED ?
2032             player->inventory_infinite_element :
2033             player->dynabombs_left >= min_dynabombs_left ?
2034             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2035             EL_UNDEFINED);
2036   }
2037   else
2038   {
2039     int min_dynabombs_left = pos + 1;
2040     int min_inventory_size = pos + 1 - player->dynabombs_left;
2041     int inventory_pos = pos - player->dynabombs_left;
2042
2043     return (player->inventory_infinite_element != EL_UNDEFINED ?
2044             player->inventory_infinite_element :
2045             player->dynabombs_left >= min_dynabombs_left ?
2046             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2047             player->inventory_size >= min_inventory_size ?
2048             player->inventory_element[inventory_pos] :
2049             EL_UNDEFINED);
2050   }
2051 }
2052
2053 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2054 {
2055   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2056   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2057   int compare_result;
2058
2059   if (gpo1->sort_priority != gpo2->sort_priority)
2060     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2061   else
2062     compare_result = gpo1->nr - gpo2->nr;
2063
2064   return compare_result;
2065 }
2066
2067 void InitGameControlValues()
2068 {
2069   int i;
2070
2071   for (i = 0; game_panel_controls[i].nr != -1; i++)
2072   {
2073     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2074     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2075     struct TextPosInfo *pos = gpc->pos;
2076     int nr = gpc->nr;
2077     int type = gpc->type;
2078
2079     if (nr != i)
2080     {
2081       Error(ERR_INFO, "'game_panel_controls' structure corrupted");
2082       Error(ERR_EXIT, "this should not happen -- please debug");
2083     }
2084
2085     /* force update of game controls after initialization */
2086     gpc->value = gpc->last_value = -1;
2087     gpc->frame = gpc->last_frame = -1;
2088     gpc->gfx_frame = -1;
2089
2090     /* determine panel value width for later calculation of alignment */
2091     if (type == TYPE_INTEGER || type == TYPE_STRING)
2092     {
2093       pos->width = pos->size * getFontWidth(pos->font);
2094       pos->height = getFontHeight(pos->font);
2095     }
2096     else if (type == TYPE_ELEMENT)
2097     {
2098       pos->width = pos->size;
2099       pos->height = pos->size;
2100     }
2101
2102     /* fill structure for game panel draw order */
2103     gpo->nr = gpc->nr;
2104     gpo->sort_priority = pos->sort_priority;
2105   }
2106
2107   /* sort game panel controls according to sort_priority and control number */
2108   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2109         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2110 }
2111
2112 void UpdatePlayfieldElementCount()
2113 {
2114   int i, j, x, y;
2115
2116   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2117     element_info[i].element_count = 0;
2118
2119   SCAN_PLAYFIELD(x, y)
2120   {
2121     element_info[Feld[x][y]].element_count++;
2122   }
2123
2124   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2125     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2126       if (IS_IN_GROUP(j, i))
2127         element_info[EL_GROUP_START + i].element_count +=
2128           element_info[j].element_count;
2129 }
2130
2131 void UpdateGameControlValues()
2132 {
2133   int i, k;
2134   int time = (local_player->LevelSolved ?
2135               local_player->LevelSolved_CountingTime :
2136               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2137               level.native_em_level->lev->time :
2138               level.time == 0 ? TimePlayed : TimeLeft);
2139   int score = (local_player->LevelSolved ?
2140                local_player->LevelSolved_CountingScore :
2141                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142                level.native_em_level->lev->score :
2143                local_player->score);
2144   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2145               level.native_em_level->lev->required :
2146               local_player->gems_still_needed);
2147   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2148                      level.native_em_level->lev->required > 0 :
2149                      local_player->gems_still_needed > 0 ||
2150                      local_player->sokobanfields_still_needed > 0 ||
2151                      local_player->lights_still_needed > 0);
2152
2153   UpdatePlayfieldElementCount();
2154
2155   /* update game panel control values */
2156
2157   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2158   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2159
2160   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2161   for (i = 0; i < MAX_NUM_KEYS; i++)
2162     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2163   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2164   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2165
2166   if (game.centered_player_nr == -1)
2167   {
2168     for (i = 0; i < MAX_PLAYERS; i++)
2169     {
2170       for (k = 0; k < MAX_NUM_KEYS; k++)
2171       {
2172         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2173         {
2174           if (level.native_em_level->ply[i]->keys & (1 << k))
2175             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2176               get_key_element_from_nr(k);
2177         }
2178         else if (stored_player[i].key[k])
2179           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2180             get_key_element_from_nr(k);
2181       }
2182
2183       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2184         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2185           level.native_em_level->ply[i]->dynamite;
2186       else
2187         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2188           stored_player[i].inventory_size;
2189
2190       if (stored_player[i].num_white_keys > 0)
2191         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2192           EL_DC_KEY_WHITE;
2193
2194       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2195         stored_player[i].num_white_keys;
2196     }
2197   }
2198   else
2199   {
2200     int player_nr = game.centered_player_nr;
2201
2202     for (k = 0; k < MAX_NUM_KEYS; k++)
2203     {
2204       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2205       {
2206         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2207           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2208             get_key_element_from_nr(k);
2209       }
2210       else if (stored_player[player_nr].key[k])
2211         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2212           get_key_element_from_nr(k);
2213     }
2214
2215     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2216       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2217         level.native_em_level->ply[player_nr]->dynamite;
2218     else
2219       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2220         stored_player[player_nr].inventory_size;
2221
2222     if (stored_player[player_nr].num_white_keys > 0)
2223       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2224
2225     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2226       stored_player[player_nr].num_white_keys;
2227   }
2228
2229   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2230   {
2231     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2232       get_inventory_element_from_pos(local_player, i);
2233     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2234       get_inventory_element_from_pos(local_player, -i - 1);
2235   }
2236
2237   game_panel_controls[GAME_PANEL_SCORE].value = score;
2238
2239   game_panel_controls[GAME_PANEL_TIME].value = time;
2240
2241   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2242   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2243   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2244
2245   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2246     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2247      EL_EMPTY);
2248   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2249     local_player->shield_normal_time_left;
2250   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2251     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2252      EL_EMPTY);
2253   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2254     local_player->shield_deadly_time_left;
2255
2256   game_panel_controls[GAME_PANEL_EXIT].value =
2257     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2258
2259   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2260     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2261   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2262     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2263      EL_EMC_MAGIC_BALL_SWITCH);
2264
2265   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2266     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2267   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2268     game.light_time_left;
2269
2270   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2271     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2272   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2273     game.timegate_time_left;
2274
2275   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2276     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2277
2278   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2279     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2280   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2281     game.lenses_time_left;
2282
2283   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2284     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2285   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2286     game.magnify_time_left;
2287
2288   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2289     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2290      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2291      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2292      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2293      EL_BALLOON_SWITCH_NONE);
2294
2295   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2296     local_player->dynabomb_count;
2297   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2298     local_player->dynabomb_size;
2299   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2300     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2301
2302   game_panel_controls[GAME_PANEL_PENGUINS].value =
2303     local_player->friends_still_needed;
2304
2305   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2306     local_player->sokobanfields_still_needed;
2307   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2308     local_player->sokobanfields_still_needed;
2309
2310   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2311     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2312
2313   for (i = 0; i < NUM_BELTS; i++)
2314   {
2315     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2316       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2317        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2318     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2319       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2320   }
2321
2322   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2323     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2324   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2325     game.magic_wall_time_left;
2326
2327 #if USE_PLAYER_GRAVITY
2328   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2329     local_player->gravity;
2330 #else
2331   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2332 #endif
2333
2334   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2335     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2336
2337   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2338     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2339       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2340        game.panel.element[i].id : EL_UNDEFINED);
2341
2342   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2343     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2344       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2345        element_info[game.panel.element_count[i].id].element_count :
2346        EL_UNDEFINED);
2347
2348   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2349     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2350       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2351        element_info[game.panel.ce_score[i].id].collect_score : 0);
2352
2353   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2354     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2355       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2356        element_info[game.panel.ce_score_element[i].id].collect_score :
2357        EL_UNDEFINED);
2358
2359   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2360   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2361   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2362
2363   /* update game panel control frames */
2364
2365   for (i = 0; game_panel_controls[i].nr != -1; i++)
2366   {
2367     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2368
2369     if (gpc->type == TYPE_ELEMENT)
2370     {
2371       int last_anim_random_frame = gfx.anim_random_frame;
2372       int element = gpc->value;
2373       int graphic = el2panelimg(element);
2374
2375       if (gpc->value != gpc->last_value)
2376       {
2377         gpc->gfx_frame = 0;
2378         gpc->gfx_random = INIT_GFX_RANDOM();
2379       }
2380       else
2381       {
2382         gpc->gfx_frame++;
2383
2384         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2385             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2386           gpc->gfx_random = INIT_GFX_RANDOM();
2387       }
2388
2389       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2390         gfx.anim_random_frame = gpc->gfx_random;
2391
2392       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2393         gpc->gfx_frame = element_info[element].collect_score;
2394
2395       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2396                                             gpc->gfx_frame);
2397
2398       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2399         gfx.anim_random_frame = last_anim_random_frame;
2400     }
2401   }
2402 }
2403
2404 void DisplayGameControlValues()
2405 {
2406   boolean redraw_panel = FALSE;
2407   int i;
2408
2409   for (i = 0; game_panel_controls[i].nr != -1; i++)
2410   {
2411     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2412
2413     if (PANEL_DEACTIVATED(gpc->pos))
2414       continue;
2415
2416     if (gpc->value == gpc->last_value &&
2417         gpc->frame == gpc->last_frame)
2418       continue;
2419
2420     redraw_panel = TRUE;
2421   }
2422
2423   if (!redraw_panel)
2424     return;
2425
2426   /* copy default game door content to main double buffer */
2427   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2428              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2429
2430   /* redraw game control buttons */
2431 #if 1
2432   RedrawGameButtons();
2433 #else
2434   UnmapGameButtons();
2435   MapGameButtons();
2436 #endif
2437
2438   game_status = GAME_MODE_PSEUDO_PANEL;
2439
2440 #if 1
2441   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2442 #else
2443   for (i = 0; game_panel_controls[i].nr != -1; i++)
2444 #endif
2445   {
2446 #if 1
2447     int nr = game_panel_order[i].nr;
2448     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2449 #else
2450     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2451     int nr = gpc->nr;
2452 #endif
2453     struct TextPosInfo *pos = gpc->pos;
2454     int type = gpc->type;
2455     int value = gpc->value;
2456     int frame = gpc->frame;
2457 #if 0
2458     int last_value = gpc->last_value;
2459     int last_frame = gpc->last_frame;
2460 #endif
2461     int size = pos->size;
2462     int font = pos->font;
2463     boolean draw_masked = pos->draw_masked;
2464     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2465
2466     if (PANEL_DEACTIVATED(pos))
2467       continue;
2468
2469 #if 0
2470     if (value == last_value && frame == last_frame)
2471       continue;
2472 #endif
2473
2474     gpc->last_value = value;
2475     gpc->last_frame = frame;
2476
2477 #if 0
2478     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2479 #endif
2480
2481     if (type == TYPE_INTEGER)
2482     {
2483       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2484           nr == GAME_PANEL_TIME)
2485       {
2486         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2487
2488         if (use_dynamic_size)           /* use dynamic number of digits */
2489         {
2490           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2491           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2492           int size2 = size1 + 1;
2493           int font1 = pos->font;
2494           int font2 = pos->font_alt;
2495
2496           size = (value < value_change ? size1 : size2);
2497           font = (value < value_change ? font1 : font2);
2498
2499 #if 0
2500           /* clear background if value just changed its size (dynamic digits) */
2501           if ((last_value < value_change) != (value < value_change))
2502           {
2503             int width1 = size1 * getFontWidth(font1);
2504             int width2 = size2 * getFontWidth(font2);
2505             int max_width = MAX(width1, width2);
2506             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2507
2508             pos->width = max_width;
2509
2510             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2511                                        max_width, max_height);
2512           }
2513 #endif
2514         }
2515       }
2516
2517 #if 1
2518       /* correct text size if "digits" is zero or less */
2519       if (size <= 0)
2520         size = strlen(int2str(value, size));
2521
2522       /* dynamically correct text alignment */
2523       pos->width = size * getFontWidth(font);
2524 #endif
2525
2526       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2527                   int2str(value, size), font, mask_mode);
2528     }
2529     else if (type == TYPE_ELEMENT)
2530     {
2531       int element, graphic;
2532       Bitmap *src_bitmap;
2533       int src_x, src_y;
2534       int width, height;
2535       int dst_x = PANEL_XPOS(pos);
2536       int dst_y = PANEL_YPOS(pos);
2537
2538 #if 1
2539       if (value != EL_UNDEFINED && value != EL_EMPTY)
2540       {
2541         element = value;
2542         graphic = el2panelimg(value);
2543
2544         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2545
2546 #if 1
2547         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2548           size = TILESIZE;
2549 #endif
2550
2551         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2552                               &src_x, &src_y);
2553
2554         width  = graphic_info[graphic].width  * size / TILESIZE;
2555         height = graphic_info[graphic].height * size / TILESIZE;
2556
2557         if (draw_masked)
2558         {
2559           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2560                         dst_x - src_x, dst_y - src_y);
2561           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2562                            dst_x, dst_y);
2563         }
2564         else
2565         {
2566           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2567                      dst_x, dst_y);
2568         }
2569       }
2570 #else
2571       if (value == EL_UNDEFINED || value == EL_EMPTY)
2572       {
2573         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2574         graphic = el2panelimg(element);
2575
2576         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2577         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2578         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2579       }
2580       else
2581       {
2582         element = value;
2583         graphic = el2panelimg(value);
2584
2585         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2586       }
2587
2588       width  = graphic_info[graphic].width  * size / TILESIZE;
2589       height = graphic_info[graphic].height * size / TILESIZE;
2590
2591       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2592 #endif
2593     }
2594     else if (type == TYPE_STRING)
2595     {
2596       boolean active = (value != 0);
2597       char *state_normal = "off";
2598       char *state_active = "on";
2599       char *state = (active ? state_active : state_normal);
2600       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2601                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2602                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2603                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2604
2605       if (nr == GAME_PANEL_GRAVITY_STATE)
2606       {
2607         int font1 = pos->font;          /* (used for normal state) */
2608         int font2 = pos->font_alt;      /* (used for active state) */
2609 #if 0
2610         int size1 = strlen(state_normal);
2611         int size2 = strlen(state_active);
2612         int width1 = size1 * getFontWidth(font1);
2613         int width2 = size2 * getFontWidth(font2);
2614         int max_width = MAX(width1, width2);
2615         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2616
2617         pos->width = max_width;
2618
2619         /* clear background for values that may have changed its size */
2620         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2621                                    max_width, max_height);
2622 #endif
2623
2624         font = (active ? font2 : font1);
2625       }
2626
2627       if (s != NULL)
2628       {
2629         char *s_cut;
2630
2631 #if 1
2632         if (size <= 0)
2633         {
2634           /* don't truncate output if "chars" is zero or less */
2635           size = strlen(s);
2636
2637           /* dynamically correct text alignment */
2638           pos->width = size * getFontWidth(font);
2639         }
2640 #endif
2641
2642         s_cut = getStringCopyN(s, size);
2643
2644         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2645                     s_cut, font, mask_mode);
2646
2647         free(s_cut);
2648       }
2649     }
2650
2651     redraw_mask |= REDRAW_DOOR_1;
2652   }
2653
2654   game_status = GAME_MODE_PLAYING;
2655 }
2656
2657 void DrawGameValue_Emeralds(int value)
2658 {
2659   struct TextPosInfo *pos = &game.panel.gems;
2660 #if 1
2661   int font_nr = pos->font;
2662 #else
2663   int font_nr = FONT_TEXT_2;
2664 #endif
2665   int font_width = getFontWidth(font_nr);
2666   int chars = pos->size;
2667
2668 #if 1
2669   return;       /* !!! USE NEW STUFF !!! */
2670 #endif
2671
2672   if (PANEL_DEACTIVATED(pos))
2673     return;
2674
2675   pos->width = chars * font_width;
2676
2677   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2678 }
2679
2680 void DrawGameValue_Dynamite(int value)
2681 {
2682   struct TextPosInfo *pos = &game.panel.inventory_count;
2683 #if 1
2684   int font_nr = pos->font;
2685 #else
2686   int font_nr = FONT_TEXT_2;
2687 #endif
2688   int font_width = getFontWidth(font_nr);
2689   int chars = pos->size;
2690
2691 #if 1
2692   return;       /* !!! USE NEW STUFF !!! */
2693 #endif
2694
2695   if (PANEL_DEACTIVATED(pos))
2696     return;
2697
2698   pos->width = chars * font_width;
2699
2700   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2701 }
2702
2703 void DrawGameValue_Score(int value)
2704 {
2705   struct TextPosInfo *pos = &game.panel.score;
2706 #if 1
2707   int font_nr = pos->font;
2708 #else
2709   int font_nr = FONT_TEXT_2;
2710 #endif
2711   int font_width = getFontWidth(font_nr);
2712   int chars = pos->size;
2713
2714 #if 1
2715   return;       /* !!! USE NEW STUFF !!! */
2716 #endif
2717
2718   if (PANEL_DEACTIVATED(pos))
2719     return;
2720
2721   pos->width = chars * font_width;
2722
2723   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2724 }
2725
2726 void DrawGameValue_Time(int value)
2727 {
2728   struct TextPosInfo *pos = &game.panel.time;
2729   static int last_value = -1;
2730   int chars1 = 3;
2731   int chars2 = 4;
2732   int chars = pos->size;
2733 #if 1
2734   int font1_nr = pos->font;
2735   int font2_nr = pos->font_alt;
2736 #else
2737   int font1_nr = FONT_TEXT_2;
2738   int font2_nr = FONT_TEXT_1;
2739 #endif
2740   int font_nr = font1_nr;
2741   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2742
2743 #if 1
2744   return;       /* !!! USE NEW STUFF !!! */
2745 #endif
2746
2747   if (PANEL_DEACTIVATED(pos))
2748     return;
2749
2750   if (use_dynamic_chars)                /* use dynamic number of chars */
2751   {
2752     chars   = (value < 1000 ? chars1   : chars2);
2753     font_nr = (value < 1000 ? font1_nr : font2_nr);
2754   }
2755
2756   /* clear background if value just changed its size (dynamic chars only) */
2757   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2758   {
2759     int width1 = chars1 * getFontWidth(font1_nr);
2760     int width2 = chars2 * getFontWidth(font2_nr);
2761     int max_width = MAX(width1, width2);
2762     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2763
2764     pos->width = max_width;
2765
2766     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2767                                max_width, max_height);
2768   }
2769
2770   pos->width = chars * getFontWidth(font_nr);
2771
2772   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2773
2774   last_value = value;
2775 }
2776
2777 void DrawGameValue_Level(int value)
2778 {
2779   struct TextPosInfo *pos = &game.panel.level_number;
2780   int chars1 = 2;
2781   int chars2 = 3;
2782   int chars = pos->size;
2783 #if 1
2784   int font1_nr = pos->font;
2785   int font2_nr = pos->font_alt;
2786 #else
2787   int font1_nr = FONT_TEXT_2;
2788   int font2_nr = FONT_TEXT_1;
2789 #endif
2790   int font_nr = font1_nr;
2791   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2792
2793 #if 1
2794   return;       /* !!! USE NEW STUFF !!! */
2795 #endif
2796
2797   if (PANEL_DEACTIVATED(pos))
2798     return;
2799
2800   if (use_dynamic_chars)                /* use dynamic number of chars */
2801   {
2802     chars   = (level_nr < 100 ? chars1   : chars2);
2803     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2804   }
2805
2806   pos->width = chars * getFontWidth(font_nr);
2807
2808   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2809 }
2810
2811 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2812 {
2813 #if 0
2814   struct TextPosInfo *pos = &game.panel.keys;
2815 #endif
2816 #if 0
2817   int base_key_graphic = EL_KEY_1;
2818 #endif
2819   int i;
2820
2821 #if 1
2822   return;       /* !!! USE NEW STUFF !!! */
2823 #endif
2824
2825 #if 0
2826   if (PANEL_DEACTIVATED(pos))
2827     return;
2828 #endif
2829
2830 #if 0
2831   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2832     base_key_graphic = EL_EM_KEY_1;
2833 #endif
2834
2835 #if 0
2836   pos->width = 4 * MINI_TILEX;
2837 #endif
2838
2839 #if 1
2840   for (i = 0; i < MAX_NUM_KEYS; i++)
2841 #else
2842   /* currently only 4 of 8 possible keys are displayed */
2843   for (i = 0; i < STD_NUM_KEYS; i++)
2844 #endif
2845   {
2846 #if 1
2847     struct TextPosInfo *pos = &game.panel.key[i];
2848 #endif
2849     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2850     int src_y = DOOR_GFX_PAGEY1 + 123;
2851 #if 1
2852     int dst_x = PANEL_XPOS(pos);
2853     int dst_y = PANEL_YPOS(pos);
2854 #else
2855     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2856     int dst_y = PANEL_YPOS(pos);
2857 #endif
2858
2859 #if 1
2860     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2861                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2862                    EL_KEY_1) + i;
2863     int graphic = el2edimg(element);
2864 #endif
2865
2866 #if 1
2867     if (PANEL_DEACTIVATED(pos))
2868       continue;
2869 #endif
2870
2871 #if 0
2872     /* masked blit with tiles from half-size scaled bitmap does not work yet
2873        (no mask bitmap created for these sizes after loading and scaling) --
2874        solution: load without creating mask, scale, then create final mask */
2875
2876     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2877                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2878
2879     if (key[i])
2880     {
2881 #if 0
2882       int graphic = el2edimg(base_key_graphic + i);
2883 #endif
2884       Bitmap *src_bitmap;
2885       int src_x, src_y;
2886
2887       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2888
2889       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2890                     dst_x - src_x, dst_y - src_y);
2891       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2892                        dst_x, dst_y);
2893     }
2894 #else
2895 #if 1
2896     if (key[i])
2897       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2898     else
2899       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2900                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2901 #else
2902     if (key[i])
2903       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2904     else
2905       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2906                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2907 #endif
2908 #endif
2909   }
2910 }
2911
2912 #else
2913
2914 void DrawGameValue_Emeralds(int value)
2915 {
2916   int font_nr = FONT_TEXT_2;
2917   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2918
2919   if (PANEL_DEACTIVATED(game.panel.gems))
2920     return;
2921
2922   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2923 }
2924
2925 void DrawGameValue_Dynamite(int value)
2926 {
2927   int font_nr = FONT_TEXT_2;
2928   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2929
2930   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2931     return;
2932
2933   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2934 }
2935
2936 void DrawGameValue_Score(int value)
2937 {
2938   int font_nr = FONT_TEXT_2;
2939   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2940
2941   if (PANEL_DEACTIVATED(game.panel.score))
2942     return;
2943
2944   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2945 }
2946
2947 void DrawGameValue_Time(int value)
2948 {
2949   int font1_nr = FONT_TEXT_2;
2950 #if 1
2951   int font2_nr = FONT_TEXT_1;
2952 #else
2953   int font2_nr = FONT_LEVEL_NUMBER;
2954 #endif
2955   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2956   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2957
2958   if (PANEL_DEACTIVATED(game.panel.time))
2959     return;
2960
2961   /* clear background if value just changed its size */
2962   if (value == 999 || value == 1000)
2963     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2964
2965   if (value < 1000)
2966     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2967   else
2968     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2969 }
2970
2971 void DrawGameValue_Level(int value)
2972 {
2973   int font1_nr = FONT_TEXT_2;
2974 #if 1
2975   int font2_nr = FONT_TEXT_1;
2976 #else
2977   int font2_nr = FONT_LEVEL_NUMBER;
2978 #endif
2979
2980   if (PANEL_DEACTIVATED(game.panel.level))
2981     return;
2982
2983   if (level_nr < 100)
2984     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2985   else
2986     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2987 }
2988
2989 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2990 {
2991   int base_key_graphic = EL_KEY_1;
2992   int i;
2993
2994   if (PANEL_DEACTIVATED(game.panel.keys))
2995     return;
2996
2997   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2998     base_key_graphic = EL_EM_KEY_1;
2999
3000   /* currently only 4 of 8 possible keys are displayed */
3001   for (i = 0; i < STD_NUM_KEYS; i++)
3002   {
3003     int x = XX_KEYS + i * MINI_TILEX;
3004     int y = YY_KEYS;
3005
3006     if (key[i])
3007       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3008     else
3009       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3010                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3011   }
3012 }
3013
3014 #endif
3015
3016 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3017                        int key_bits)
3018 {
3019   int key[MAX_NUM_KEYS];
3020   int i;
3021
3022   /* prevent EM engine from updating time/score values parallel to GameWon() */
3023   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3024       local_player->LevelSolved)
3025     return;
3026
3027   for (i = 0; i < MAX_NUM_KEYS; i++)
3028     key[i] = key_bits & (1 << i);
3029
3030   DrawGameValue_Level(level_nr);
3031
3032   DrawGameValue_Emeralds(emeralds);
3033   DrawGameValue_Dynamite(dynamite);
3034   DrawGameValue_Score(score);
3035   DrawGameValue_Time(time);
3036
3037   DrawGameValue_Keys(key);
3038 }
3039
3040 void UpdateGameDoorValues()
3041 {
3042   UpdateGameControlValues();
3043 }
3044
3045 void DrawGameDoorValues()
3046 {
3047   DisplayGameControlValues();
3048 }
3049
3050 void DrawGameDoorValues_OLD()
3051 {
3052   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3053   int dynamite_value = 0;
3054   int score_value = (local_player->LevelSolved ? local_player->score_final :
3055                      local_player->score);
3056   int gems_value = local_player->gems_still_needed;
3057   int key_bits = 0;
3058   int i, j;
3059
3060   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3061   {
3062     DrawGameDoorValues_EM();
3063
3064     return;
3065   }
3066
3067   if (game.centered_player_nr == -1)
3068   {
3069     for (i = 0; i < MAX_PLAYERS; i++)
3070     {
3071       for (j = 0; j < MAX_NUM_KEYS; j++)
3072         if (stored_player[i].key[j])
3073           key_bits |= (1 << j);
3074
3075       dynamite_value += stored_player[i].inventory_size;
3076     }
3077   }
3078   else
3079   {
3080     int player_nr = game.centered_player_nr;
3081
3082     for (i = 0; i < MAX_NUM_KEYS; i++)
3083       if (stored_player[player_nr].key[i])
3084         key_bits |= (1 << i);
3085
3086     dynamite_value = stored_player[player_nr].inventory_size;
3087   }
3088
3089   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3090                     key_bits);
3091 }
3092
3093
3094 /*
3095   =============================================================================
3096   InitGameEngine()
3097   -----------------------------------------------------------------------------
3098   initialize game engine due to level / tape version number
3099   =============================================================================
3100 */
3101
3102 static void InitGameEngine()
3103 {
3104   int i, j, k, l, x, y;
3105
3106   /* set game engine from tape file when re-playing, else from level file */
3107   game.engine_version = (tape.playing ? tape.engine_version :
3108                          level.game_version);
3109
3110   /* ---------------------------------------------------------------------- */
3111   /* set flags for bugs and changes according to active game engine version */
3112   /* ---------------------------------------------------------------------- */
3113
3114   /*
3115     Summary of bugfix/change:
3116     Fixed handling for custom elements that change when pushed by the player.
3117
3118     Fixed/changed in version:
3119     3.1.0
3120
3121     Description:
3122     Before 3.1.0, custom elements that "change when pushing" changed directly
3123     after the player started pushing them (until then handled in "DigField()").
3124     Since 3.1.0, these custom elements are not changed until the "pushing"
3125     move of the element is finished (now handled in "ContinueMoving()").
3126
3127     Affected levels/tapes:
3128     The first condition is generally needed for all levels/tapes before version
3129     3.1.0, which might use the old behaviour before it was changed; known tapes
3130     that are affected are some tapes from the level set "Walpurgis Gardens" by
3131     Jamie Cullen.
3132     The second condition is an exception from the above case and is needed for
3133     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3134     above (including some development versions of 3.1.0), but before it was
3135     known that this change would break tapes like the above and was fixed in
3136     3.1.1, so that the changed behaviour was active although the engine version
3137     while recording maybe was before 3.1.0. There is at least one tape that is
3138     affected by this exception, which is the tape for the one-level set "Bug
3139     Machine" by Juergen Bonhagen.
3140   */
3141
3142   game.use_change_when_pushing_bug =
3143     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3144      !(tape.playing &&
3145        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3146        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3147
3148   /*
3149     Summary of bugfix/change:
3150     Fixed handling for blocking the field the player leaves when moving.
3151
3152     Fixed/changed in version:
3153     3.1.1
3154
3155     Description:
3156     Before 3.1.1, when "block last field when moving" was enabled, the field
3157     the player is leaving when moving was blocked for the time of the move,
3158     and was directly unblocked afterwards. This resulted in the last field
3159     being blocked for exactly one less than the number of frames of one player
3160     move. Additionally, even when blocking was disabled, the last field was
3161     blocked for exactly one frame.
3162     Since 3.1.1, due to changes in player movement handling, the last field
3163     is not blocked at all when blocking is disabled. When blocking is enabled,
3164     the last field is blocked for exactly the number of frames of one player
3165     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3166     last field is blocked for exactly one more than the number of frames of
3167     one player move.
3168
3169     Affected levels/tapes:
3170     (!!! yet to be determined -- probably many !!!)
3171   */
3172
3173   game.use_block_last_field_bug =
3174     (game.engine_version < VERSION_IDENT(3,1,1,0));
3175
3176   /*
3177     Summary of bugfix/change:
3178     Changed behaviour of CE changes with multiple changes per single frame.
3179
3180     Fixed/changed in version:
3181     3.2.0-6
3182
3183     Description:
3184     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3185     This resulted in race conditions where CEs seem to behave strange in some
3186     situations (where triggered CE changes were just skipped because there was
3187     already a CE change on that tile in the playfield in that engine frame).
3188     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3189     (The number of changes per frame must be limited in any case, because else
3190     it is easily possible to define CE changes that would result in an infinite
3191     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3192     should be set large enough so that it would only be reached in cases where
3193     the corresponding CE change conditions run into a loop. Therefore, it seems
3194     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3195     maximal number of change pages for custom elements.)
3196
3197     Affected levels/tapes:
3198     Probably many.
3199   */
3200
3201 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3202   game.max_num_changes_per_frame = 1;
3203 #else
3204   game.max_num_changes_per_frame =
3205     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3206 #endif
3207
3208   /* ---------------------------------------------------------------------- */
3209
3210   /* default scan direction: scan playfield from top/left to bottom/right */
3211   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3212
3213   /* dynamically adjust element properties according to game engine version */
3214   InitElementPropertiesEngine(game.engine_version);
3215
3216 #if 0
3217   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3218   printf("          tape version == %06d [%s] [file: %06d]\n",
3219          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3220          tape.file_version);
3221   printf("       => game.engine_version == %06d\n", game.engine_version);
3222 #endif
3223
3224   /* ---------- initialize player's initial move delay --------------------- */
3225
3226   /* dynamically adjust player properties according to level information */
3227   for (i = 0; i < MAX_PLAYERS; i++)
3228     game.initial_move_delay_value[i] =
3229       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3230
3231   /* dynamically adjust player properties according to game engine version */
3232   for (i = 0; i < MAX_PLAYERS; i++)
3233     game.initial_move_delay[i] =
3234       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3235        game.initial_move_delay_value[i] : 0);
3236
3237   /* ---------- initialize player's initial push delay --------------------- */
3238
3239   /* dynamically adjust player properties according to game engine version */
3240   game.initial_push_delay_value =
3241     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3242
3243   /* ---------- initialize changing elements ------------------------------- */
3244
3245   /* initialize changing elements information */
3246   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3247   {
3248     struct ElementInfo *ei = &element_info[i];
3249
3250     /* this pointer might have been changed in the level editor */
3251     ei->change = &ei->change_page[0];
3252
3253     if (!IS_CUSTOM_ELEMENT(i))
3254     {
3255       ei->change->target_element = EL_EMPTY_SPACE;
3256       ei->change->delay_fixed = 0;
3257       ei->change->delay_random = 0;
3258       ei->change->delay_frames = 1;
3259     }
3260
3261     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3262     {
3263       ei->has_change_event[j] = FALSE;
3264
3265       ei->event_page_nr[j] = 0;
3266       ei->event_page[j] = &ei->change_page[0];
3267     }
3268   }
3269
3270   /* add changing elements from pre-defined list */
3271   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3272   {
3273     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3274     struct ElementInfo *ei = &element_info[ch_delay->element];
3275
3276     ei->change->target_element       = ch_delay->target_element;
3277     ei->change->delay_fixed          = ch_delay->change_delay;
3278
3279     ei->change->pre_change_function  = ch_delay->pre_change_function;
3280     ei->change->change_function      = ch_delay->change_function;
3281     ei->change->post_change_function = ch_delay->post_change_function;
3282
3283     ei->change->can_change = TRUE;
3284     ei->change->can_change_or_has_action = TRUE;
3285
3286     ei->has_change_event[CE_DELAY] = TRUE;
3287
3288     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3289     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3290   }
3291
3292   /* ---------- initialize internal run-time variables ------------- */
3293
3294   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3295   {
3296     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3297
3298     for (j = 0; j < ei->num_change_pages; j++)
3299     {
3300       ei->change_page[j].can_change_or_has_action =
3301         (ei->change_page[j].can_change |
3302          ei->change_page[j].has_action);
3303     }
3304   }
3305
3306   /* add change events from custom element configuration */
3307   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3308   {
3309     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3310
3311     for (j = 0; j < ei->num_change_pages; j++)
3312     {
3313       if (!ei->change_page[j].can_change_or_has_action)
3314         continue;
3315
3316       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3317       {
3318         /* only add event page for the first page found with this event */
3319         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3320         {
3321           ei->has_change_event[k] = TRUE;
3322
3323           ei->event_page_nr[k] = j;
3324           ei->event_page[k] = &ei->change_page[j];
3325         }
3326       }
3327     }
3328   }
3329
3330   /* ---------- initialize run-time trigger player and element ------------- */
3331
3332   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3333   {
3334     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3335
3336     for (j = 0; j < ei->num_change_pages; j++)
3337     {
3338       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3339       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3340       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3341       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3342       ei->change_page[j].actual_trigger_ce_value = 0;
3343       ei->change_page[j].actual_trigger_ce_score = 0;
3344     }
3345   }
3346
3347   /* ---------- initialize trigger events ---------------------------------- */
3348
3349   /* initialize trigger events information */
3350   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3351     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3352       trigger_events[i][j] = FALSE;
3353
3354   /* add trigger events from element change event properties */
3355   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3356   {
3357     struct ElementInfo *ei = &element_info[i];
3358
3359     for (j = 0; j < ei->num_change_pages; j++)
3360     {
3361       if (!ei->change_page[j].can_change_or_has_action)
3362         continue;
3363
3364       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3365       {
3366         int trigger_element = ei->change_page[j].trigger_element;
3367
3368         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3369         {
3370           if (ei->change_page[j].has_event[k])
3371           {
3372             if (IS_GROUP_ELEMENT(trigger_element))
3373             {
3374               struct ElementGroupInfo *group =
3375                 element_info[trigger_element].group;
3376
3377               for (l = 0; l < group->num_elements_resolved; l++)
3378                 trigger_events[group->element_resolved[l]][k] = TRUE;
3379             }
3380             else if (trigger_element == EL_ANY_ELEMENT)
3381               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3382                 trigger_events[l][k] = TRUE;
3383             else
3384               trigger_events[trigger_element][k] = TRUE;
3385           }
3386         }
3387       }
3388     }
3389   }
3390
3391   /* ---------- initialize push delay -------------------------------------- */
3392
3393   /* initialize push delay values to default */
3394   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3395   {
3396     if (!IS_CUSTOM_ELEMENT(i))
3397     {
3398       /* set default push delay values (corrected since version 3.0.7-1) */
3399       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3400       {
3401         element_info[i].push_delay_fixed = 2;
3402         element_info[i].push_delay_random = 8;
3403       }
3404       else
3405       {
3406         element_info[i].push_delay_fixed = 8;
3407         element_info[i].push_delay_random = 8;
3408       }
3409     }
3410   }
3411
3412   /* set push delay value for certain elements from pre-defined list */
3413   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3414   {
3415     int e = push_delay_list[i].element;
3416
3417     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3418     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3419   }
3420
3421   /* set push delay value for Supaplex elements for newer engine versions */
3422   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3423   {
3424     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3425     {
3426       if (IS_SP_ELEMENT(i))
3427       {
3428         /* set SP push delay to just enough to push under a falling zonk */
3429         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3430
3431         element_info[i].push_delay_fixed  = delay;
3432         element_info[i].push_delay_random = 0;
3433       }
3434     }
3435   }
3436
3437   /* ---------- initialize move stepsize ----------------------------------- */
3438
3439   /* initialize move stepsize values to default */
3440   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3441     if (!IS_CUSTOM_ELEMENT(i))
3442       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3443
3444   /* set move stepsize value for certain elements from pre-defined list */
3445   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3446   {
3447     int e = move_stepsize_list[i].element;
3448
3449     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3450   }
3451
3452   /* ---------- initialize collect score ----------------------------------- */
3453
3454   /* initialize collect score values for custom elements from initial value */
3455   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3456     if (IS_CUSTOM_ELEMENT(i))
3457       element_info[i].collect_score = element_info[i].collect_score_initial;
3458
3459   /* ---------- initialize collect count ----------------------------------- */
3460
3461   /* initialize collect count values for non-custom elements */
3462   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3463     if (!IS_CUSTOM_ELEMENT(i))
3464       element_info[i].collect_count_initial = 0;
3465
3466   /* add collect count values for all elements from pre-defined list */
3467   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3468     element_info[collect_count_list[i].element].collect_count_initial =
3469       collect_count_list[i].count;
3470
3471   /* ---------- initialize access direction -------------------------------- */
3472
3473   /* initialize access direction values to default (access from every side) */
3474   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3475     if (!IS_CUSTOM_ELEMENT(i))
3476       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3477
3478   /* set access direction value for certain elements from pre-defined list */
3479   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3480     element_info[access_direction_list[i].element].access_direction =
3481       access_direction_list[i].direction;
3482
3483   /* ---------- initialize explosion content ------------------------------- */
3484   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3485   {
3486     if (IS_CUSTOM_ELEMENT(i))
3487       continue;
3488
3489     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3490     {
3491       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3492
3493       element_info[i].content.e[x][y] =
3494         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3495          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3496          i == EL_PLAYER_3 ? EL_EMERALD :
3497          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3498          i == EL_MOLE ? EL_EMERALD_RED :
3499          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3500          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3501          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3502          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3503          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3504          i == EL_WALL_EMERALD ? EL_EMERALD :
3505          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3506          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3507          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3508          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3509          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3510          i == EL_WALL_PEARL ? EL_PEARL :
3511          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3512          EL_EMPTY);
3513     }
3514   }
3515
3516   /* ---------- initialize recursion detection ------------------------------ */
3517   recursion_loop_depth = 0;
3518   recursion_loop_detected = FALSE;
3519   recursion_loop_element = EL_UNDEFINED;
3520
3521   /* ---------- initialize graphics engine ---------------------------------- */
3522   game.scroll_delay_value =
3523     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3524      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3525   game.scroll_delay_value =
3526     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3527 }
3528
3529 int get_num_special_action(int element, int action_first, int action_last)
3530 {
3531   int num_special_action = 0;
3532   int i, j;
3533
3534   for (i = action_first; i <= action_last; i++)
3535   {
3536     boolean found = FALSE;
3537
3538     for (j = 0; j < NUM_DIRECTIONS; j++)
3539       if (el_act_dir2img(element, i, j) !=
3540           el_act_dir2img(element, ACTION_DEFAULT, j))
3541         found = TRUE;
3542
3543     if (found)
3544       num_special_action++;
3545     else
3546       break;
3547   }
3548
3549   return num_special_action;
3550 }
3551
3552
3553 /*
3554   =============================================================================
3555   InitGame()
3556   -----------------------------------------------------------------------------
3557   initialize and start new game
3558   =============================================================================
3559 */
3560
3561 void InitGame()
3562 {
3563   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3564   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3565   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3566 #if 0
3567   boolean do_fading = (game_status == GAME_MODE_MAIN);
3568 #endif
3569   int i, j, x, y;
3570
3571   game_status = GAME_MODE_PLAYING;
3572
3573   InitGameEngine();
3574   InitGameControlValues();
3575
3576   /* don't play tapes over network */
3577   network_playing = (options.network && !tape.playing);
3578
3579   for (i = 0; i < MAX_PLAYERS; i++)
3580   {
3581     struct PlayerInfo *player = &stored_player[i];
3582
3583     player->index_nr = i;
3584     player->index_bit = (1 << i);
3585     player->element_nr = EL_PLAYER_1 + i;
3586
3587     player->present = FALSE;
3588     player->active = FALSE;
3589     player->killed = FALSE;
3590
3591     player->action = 0;
3592     player->effective_action = 0;
3593     player->programmed_action = 0;
3594
3595     player->score = 0;
3596     player->score_final = 0;
3597
3598     player->gems_still_needed = level.gems_needed;
3599     player->sokobanfields_still_needed = 0;
3600     player->lights_still_needed = 0;
3601     player->friends_still_needed = 0;
3602
3603     for (j = 0; j < MAX_NUM_KEYS; j++)
3604       player->key[j] = FALSE;
3605
3606     player->num_white_keys = 0;
3607
3608     player->dynabomb_count = 0;
3609     player->dynabomb_size = 1;
3610     player->dynabombs_left = 0;
3611     player->dynabomb_xl = FALSE;
3612
3613     player->MovDir = MV_NONE;
3614     player->MovPos = 0;
3615     player->GfxPos = 0;
3616     player->GfxDir = MV_NONE;
3617     player->GfxAction = ACTION_DEFAULT;
3618     player->Frame = 0;
3619     player->StepFrame = 0;
3620
3621     player->use_murphy = FALSE;
3622     player->artwork_element =
3623       (level.use_artwork_element[i] ? level.artwork_element[i] :
3624        player->element_nr);
3625
3626     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3627     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3628
3629     player->gravity = level.initial_player_gravity[i];
3630
3631     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3632
3633     player->actual_frame_counter = 0;
3634
3635     player->step_counter = 0;
3636
3637     player->last_move_dir = MV_NONE;
3638
3639     player->is_active = FALSE;
3640
3641     player->is_waiting = FALSE;
3642     player->is_moving = FALSE;
3643     player->is_auto_moving = FALSE;
3644     player->is_digging = FALSE;
3645     player->is_snapping = FALSE;
3646     player->is_collecting = FALSE;
3647     player->is_pushing = FALSE;
3648     player->is_switching = FALSE;
3649     player->is_dropping = FALSE;
3650     player->is_dropping_pressed = FALSE;
3651
3652     player->is_bored = FALSE;
3653     player->is_sleeping = FALSE;
3654
3655     player->frame_counter_bored = -1;
3656     player->frame_counter_sleeping = -1;
3657
3658     player->anim_delay_counter = 0;
3659     player->post_delay_counter = 0;
3660
3661     player->dir_waiting = MV_NONE;
3662     player->action_waiting = ACTION_DEFAULT;
3663     player->last_action_waiting = ACTION_DEFAULT;
3664     player->special_action_bored = ACTION_DEFAULT;
3665     player->special_action_sleeping = ACTION_DEFAULT;
3666
3667     player->switch_x = -1;
3668     player->switch_y = -1;
3669
3670     player->drop_x = -1;
3671     player->drop_y = -1;
3672
3673     player->show_envelope = 0;
3674
3675     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3676
3677     player->push_delay       = -1;      /* initialized when pushing starts */
3678     player->push_delay_value = game.initial_push_delay_value;
3679
3680     player->drop_delay = 0;
3681     player->drop_pressed_delay = 0;
3682
3683     player->last_jx = -1;
3684     player->last_jy = -1;
3685     player->jx = -1;
3686     player->jy = -1;
3687
3688     player->shield_normal_time_left = 0;
3689     player->shield_deadly_time_left = 0;
3690
3691     player->inventory_infinite_element = EL_UNDEFINED;
3692     player->inventory_size = 0;
3693
3694     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3695     SnapField(player, 0, 0);
3696
3697     player->LevelSolved = FALSE;
3698     player->GameOver = FALSE;
3699
3700     player->LevelSolved_GameWon = FALSE;
3701     player->LevelSolved_GameEnd = FALSE;
3702     player->LevelSolved_PanelOff = FALSE;
3703     player->LevelSolved_SaveTape = FALSE;
3704     player->LevelSolved_SaveScore = FALSE;
3705     player->LevelSolved_CountingTime = 0;
3706     player->LevelSolved_CountingScore = 0;
3707   }
3708
3709   network_player_action_received = FALSE;
3710
3711 #if defined(NETWORK_AVALIABLE)
3712   /* initial null action */
3713   if (network_playing)
3714     SendToServer_MovePlayer(MV_NONE);
3715 #endif
3716
3717   ZX = ZY = -1;
3718   ExitX = ExitY = -1;
3719
3720   FrameCounter = 0;
3721   TimeFrames = 0;
3722   TimePlayed = 0;
3723   TimeLeft = level.time;
3724   TapeTime = 0;
3725
3726   ScreenMovDir = MV_NONE;
3727   ScreenMovPos = 0;
3728   ScreenGfxPos = 0;
3729
3730   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3731
3732   AllPlayersGone = FALSE;
3733
3734   game.yamyam_content_nr = 0;
3735   game.robot_wheel_active = FALSE;
3736   game.magic_wall_active = FALSE;
3737   game.magic_wall_time_left = 0;
3738   game.light_time_left = 0;
3739   game.timegate_time_left = 0;
3740   game.switchgate_pos = 0;
3741   game.wind_direction = level.wind_direction_initial;
3742
3743 #if !USE_PLAYER_GRAVITY
3744   game.gravity = FALSE;
3745   game.explosions_delayed = TRUE;
3746 #endif
3747
3748   game.lenses_time_left = 0;
3749   game.magnify_time_left = 0;
3750
3751   game.ball_state = level.ball_state_initial;
3752   game.ball_content_nr = 0;
3753
3754   game.envelope_active = FALSE;
3755
3756   /* set focus to local player for network games, else to all players */
3757   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3758   game.centered_player_nr_next = game.centered_player_nr;
3759   game.set_centered_player = FALSE;
3760
3761   if (network_playing && tape.recording)
3762   {
3763     /* store client dependent player focus when recording network games */
3764     tape.centered_player_nr_next = game.centered_player_nr_next;
3765     tape.set_centered_player = TRUE;
3766   }
3767
3768   for (i = 0; i < NUM_BELTS; i++)
3769   {
3770     game.belt_dir[i] = MV_NONE;
3771     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3772   }
3773
3774   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3775     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3776
3777   SCAN_PLAYFIELD(x, y)
3778   {
3779     Feld[x][y] = level.field[x][y];
3780     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3781     ChangeDelay[x][y] = 0;
3782     ChangePage[x][y] = -1;
3783 #if USE_NEW_CUSTOM_VALUE
3784     CustomValue[x][y] = 0;              /* initialized in InitField() */
3785 #endif
3786     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3787     AmoebaNr[x][y] = 0;
3788     WasJustMoving[x][y] = 0;
3789     WasJustFalling[x][y] = 0;
3790     CheckCollision[x][y] = 0;
3791     CheckImpact[x][y] = 0;
3792     Stop[x][y] = FALSE;
3793     Pushed[x][y] = FALSE;
3794
3795     ChangeCount[x][y] = 0;
3796     ChangeEvent[x][y] = -1;
3797
3798     ExplodePhase[x][y] = 0;
3799     ExplodeDelay[x][y] = 0;
3800     ExplodeField[x][y] = EX_TYPE_NONE;
3801
3802     RunnerVisit[x][y] = 0;
3803     PlayerVisit[x][y] = 0;
3804
3805     GfxFrame[x][y] = 0;
3806     GfxRandom[x][y] = INIT_GFX_RANDOM();
3807     GfxElement[x][y] = EL_UNDEFINED;
3808     GfxAction[x][y] = ACTION_DEFAULT;
3809     GfxDir[x][y] = MV_NONE;
3810   }
3811
3812   SCAN_PLAYFIELD(x, y)
3813   {
3814     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3815       emulate_bd = FALSE;
3816     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3817       emulate_sb = FALSE;
3818     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3819       emulate_sp = FALSE;
3820
3821     InitField(x, y, TRUE);
3822
3823     ResetGfxAnimation(x, y);
3824   }
3825
3826   InitBeltMovement();
3827
3828   for (i = 0; i < MAX_PLAYERS; i++)
3829   {
3830     struct PlayerInfo *player = &stored_player[i];
3831
3832     /* set number of special actions for bored and sleeping animation */
3833     player->num_special_action_bored =
3834       get_num_special_action(player->artwork_element,
3835                              ACTION_BORING_1, ACTION_BORING_LAST);
3836     player->num_special_action_sleeping =
3837       get_num_special_action(player->artwork_element,
3838                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3839   }
3840
3841   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3842                     emulate_sb ? EMU_SOKOBAN :
3843                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3844
3845 #if USE_NEW_ALL_SLIPPERY
3846   /* initialize type of slippery elements */
3847   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3848   {
3849     if (!IS_CUSTOM_ELEMENT(i))
3850     {
3851       /* default: elements slip down either to the left or right randomly */
3852       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3853
3854       /* SP style elements prefer to slip down on the left side */
3855       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3856         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3857
3858       /* BD style elements prefer to slip down on the left side */
3859       if (game.emulation == EMU_BOULDERDASH)
3860         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3861     }
3862   }
3863 #endif
3864
3865   /* initialize explosion and ignition delay */
3866   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3867   {
3868     if (!IS_CUSTOM_ELEMENT(i))
3869     {
3870       int num_phase = 8;
3871       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3872                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3873                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3874       int last_phase = (num_phase + 1) * delay;
3875       int half_phase = (num_phase / 2) * delay;
3876
3877       element_info[i].explosion_delay = last_phase - 1;
3878       element_info[i].ignition_delay = half_phase;
3879
3880       if (i == EL_BLACK_ORB)
3881         element_info[i].ignition_delay = 1;
3882     }
3883
3884 #if 0
3885     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3886       element_info[i].explosion_delay = 1;
3887
3888     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3889       element_info[i].ignition_delay = 1;
3890 #endif
3891   }
3892
3893   /* correct non-moving belts to start moving left */
3894   for (i = 0; i < NUM_BELTS; i++)
3895     if (game.belt_dir[i] == MV_NONE)
3896       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3897
3898   /* check if any connected player was not found in playfield */
3899   for (i = 0; i < MAX_PLAYERS; i++)
3900   {
3901     struct PlayerInfo *player = &stored_player[i];
3902
3903     if (player->connected && !player->present)
3904     {
3905       for (j = 0; j < MAX_PLAYERS; j++)
3906       {
3907         struct PlayerInfo *some_player = &stored_player[j];
3908         int jx = some_player->jx, jy = some_player->jy;
3909
3910         /* assign first free player found that is present in the playfield */
3911         if (some_player->present && !some_player->connected)
3912         {
3913           player->present = TRUE;
3914           player->active = TRUE;
3915
3916           some_player->present = FALSE;
3917           some_player->active = FALSE;
3918
3919           player->artwork_element = some_player->artwork_element;
3920
3921           player->block_last_field       = some_player->block_last_field;
3922           player->block_delay_adjustment = some_player->block_delay_adjustment;
3923
3924           StorePlayer[jx][jy] = player->element_nr;
3925           player->jx = player->last_jx = jx;
3926           player->jy = player->last_jy = jy;
3927
3928           break;
3929         }
3930       }
3931     }
3932   }
3933
3934   if (tape.playing)
3935   {
3936     /* when playing a tape, eliminate all players who do not participate */
3937
3938     for (i = 0; i < MAX_PLAYERS; i++)
3939     {
3940       if (stored_player[i].active && !tape.player_participates[i])
3941       {
3942         struct PlayerInfo *player = &stored_player[i];
3943         int jx = player->jx, jy = player->jy;
3944
3945         player->active = FALSE;
3946         StorePlayer[jx][jy] = 0;
3947         Feld[jx][jy] = EL_EMPTY;
3948       }
3949     }
3950   }
3951   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3952   {
3953     /* when in single player mode, eliminate all but the first active player */
3954
3955     for (i = 0; i < MAX_PLAYERS; i++)
3956     {
3957       if (stored_player[i].active)
3958       {
3959         for (j = i + 1; j < MAX_PLAYERS; j++)
3960         {
3961           if (stored_player[j].active)
3962           {
3963             struct PlayerInfo *player = &stored_player[j];
3964             int jx = player->jx, jy = player->jy;
3965
3966             player->active = FALSE;
3967             player->present = FALSE;
3968
3969             StorePlayer[jx][jy] = 0;
3970             Feld[jx][jy] = EL_EMPTY;
3971           }
3972         }
3973       }
3974     }
3975   }
3976
3977   /* when recording the game, store which players take part in the game */
3978   if (tape.recording)
3979   {
3980     for (i = 0; i < MAX_PLAYERS; i++)
3981       if (stored_player[i].active)
3982         tape.player_participates[i] = TRUE;
3983   }
3984
3985   if (options.debug)
3986   {
3987     for (i = 0; i < MAX_PLAYERS; i++)
3988     {
3989       struct PlayerInfo *player = &stored_player[i];
3990
3991       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3992              i+1,
3993              player->present,
3994              player->connected,
3995              player->active);
3996       if (local_player == player)
3997         printf("Player  %d is local player.\n", i+1);
3998     }
3999   }
4000
4001   if (BorderElement == EL_EMPTY)
4002   {
4003     SBX_Left = 0;
4004     SBX_Right = lev_fieldx - SCR_FIELDX;
4005     SBY_Upper = 0;
4006     SBY_Lower = lev_fieldy - SCR_FIELDY;
4007   }
4008   else
4009   {
4010     SBX_Left = -1;
4011     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4012     SBY_Upper = -1;
4013     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4014   }
4015
4016   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4017     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4018
4019   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4020     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4021
4022   /* if local player not found, look for custom element that might create
4023      the player (make some assumptions about the right custom element) */
4024   if (!local_player->present)
4025   {
4026     int start_x = 0, start_y = 0;
4027     int found_rating = 0;
4028     int found_element = EL_UNDEFINED;
4029     int player_nr = local_player->index_nr;
4030
4031     SCAN_PLAYFIELD(x, y)
4032     {
4033       int element = Feld[x][y];
4034       int content;
4035       int xx, yy;
4036       boolean is_player;
4037
4038       if (level.use_start_element[player_nr] &&
4039           level.start_element[player_nr] == element &&
4040           found_rating < 4)
4041       {
4042         start_x = x;
4043         start_y = y;
4044
4045         found_rating = 4;
4046         found_element = element;
4047       }
4048
4049       if (!IS_CUSTOM_ELEMENT(element))
4050         continue;
4051
4052       if (CAN_CHANGE(element))
4053       {
4054         for (i = 0; i < element_info[element].num_change_pages; i++)
4055         {
4056           /* check for player created from custom element as single target */
4057           content = element_info[element].change_page[i].target_element;
4058           is_player = ELEM_IS_PLAYER(content);
4059
4060           if (is_player && (found_rating < 3 ||
4061                             (found_rating == 3 && element < found_element)))
4062           {
4063             start_x = x;
4064             start_y = y;
4065
4066             found_rating = 3;
4067             found_element = element;
4068           }
4069         }
4070       }
4071
4072       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4073       {
4074         /* check for player created from custom element as explosion content */
4075         content = element_info[element].content.e[xx][yy];
4076         is_player = ELEM_IS_PLAYER(content);
4077
4078         if (is_player && (found_rating < 2 ||
4079                           (found_rating == 2 && element < found_element)))
4080         {
4081           start_x = x + xx - 1;
4082           start_y = y + yy - 1;
4083
4084           found_rating = 2;
4085           found_element = element;
4086         }
4087
4088         if (!CAN_CHANGE(element))
4089           continue;
4090
4091         for (i = 0; i < element_info[element].num_change_pages; i++)
4092         {
4093           /* check for player created from custom element as extended target */
4094           content =
4095             element_info[element].change_page[i].target_content.e[xx][yy];
4096
4097           is_player = ELEM_IS_PLAYER(content);
4098
4099           if (is_player && (found_rating < 1 ||
4100                             (found_rating == 1 && element < found_element)))
4101           {
4102             start_x = x + xx - 1;
4103             start_y = y + yy - 1;
4104
4105             found_rating = 1;
4106             found_element = element;
4107           }
4108         }
4109       }
4110     }
4111
4112     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4113                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4114                 start_x - MIDPOSX);
4115
4116     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4117                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4118                 start_y - MIDPOSY);
4119   }
4120   else
4121   {
4122     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4123                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4124                 local_player->jx - MIDPOSX);
4125
4126     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4127                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4128                 local_player->jy - MIDPOSY);
4129   }
4130
4131 #if 0
4132   /* do not use PLAYING mask for fading out from main screen */
4133   game_status = GAME_MODE_MAIN;
4134 #endif
4135
4136   StopAnimation();
4137
4138   if (!game.restart_level)
4139     CloseDoor(DOOR_CLOSE_1);
4140
4141 #if 1
4142   if (level_editor_test_game)
4143     FadeSkipNextFadeIn();
4144   else
4145     FadeSetEnterScreen();
4146 #else
4147   if (level_editor_test_game)
4148     fading = fading_none;
4149   else
4150     fading = menu.destination;
4151 #endif
4152
4153 #if 1
4154   FadeOut(REDRAW_FIELD);
4155 #else
4156   if (do_fading)
4157     FadeOut(REDRAW_FIELD);
4158 #endif
4159
4160 #if 0
4161   game_status = GAME_MODE_PLAYING;
4162 #endif
4163
4164   /* !!! FIX THIS (START) !!! */
4165   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4166   {
4167     InitGameEngine_EM();
4168
4169     /* blit playfield from scroll buffer to normal back buffer for fading in */
4170     BlitScreenToBitmap_EM(backbuffer);
4171   }
4172   else
4173   {
4174     DrawLevel();
4175     DrawAllPlayers();
4176
4177     /* after drawing the level, correct some elements */
4178     if (game.timegate_time_left == 0)
4179       CloseAllOpenTimegates();
4180
4181     /* blit playfield from scroll buffer to normal back buffer for fading in */
4182     if (setup.soft_scrolling)
4183       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4184
4185     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4186   }
4187   /* !!! FIX THIS (END) !!! */
4188
4189 #if 1
4190   FadeIn(REDRAW_FIELD);
4191 #else
4192   if (do_fading)
4193     FadeIn(REDRAW_FIELD);
4194
4195   BackToFront();
4196 #endif
4197
4198   if (!game.restart_level)
4199   {
4200     /* copy default game door content to main double buffer */
4201     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4202                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4203   }
4204
4205   SetPanelBackground();
4206   SetDrawBackgroundMask(REDRAW_DOOR_1);
4207
4208   UpdateGameDoorValues();
4209   DrawGameDoorValues();
4210
4211   if (!game.restart_level)
4212   {
4213     UnmapGameButtons();
4214     UnmapTapeButtons();
4215     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4216     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4217     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4218     MapGameButtons();
4219     MapTapeButtons();
4220
4221     /* copy actual game door content to door double buffer for OpenDoor() */
4222     BlitBitmap(drawto, bitmap_db_door,
4223                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4224
4225     OpenDoor(DOOR_OPEN_ALL);
4226
4227     PlaySound(SND_GAME_STARTING);
4228
4229     if (setup.sound_music)
4230       PlayLevelMusic();
4231
4232     KeyboardAutoRepeatOffUnlessAutoplay();
4233
4234     if (options.debug)
4235     {
4236       for (i = 0; i < MAX_PLAYERS; i++)
4237         printf("Player %d %sactive.\n",
4238                i + 1, (stored_player[i].active ? "" : "not "));
4239     }
4240   }
4241
4242 #if 1
4243   UnmapAllGadgets();
4244
4245   MapGameButtons();
4246   MapTapeButtons();
4247 #endif
4248
4249   game.restart_level = FALSE;
4250 }
4251
4252 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4253 {
4254   /* this is used for non-R'n'D game engines to update certain engine values */
4255
4256   /* needed to determine if sounds are played within the visible screen area */
4257   scroll_x = actual_scroll_x;
4258   scroll_y = actual_scroll_y;
4259 }
4260
4261 void InitMovDir(int x, int y)
4262 {
4263   int i, element = Feld[x][y];
4264   static int xy[4][2] =
4265   {
4266     {  0, +1 },
4267     { +1,  0 },
4268     {  0, -1 },
4269     { -1,  0 }
4270   };
4271   static int direction[3][4] =
4272   {
4273     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4274     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4275     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4276   };
4277
4278   switch (element)
4279   {
4280     case EL_BUG_RIGHT:
4281     case EL_BUG_UP:
4282     case EL_BUG_LEFT:
4283     case EL_BUG_DOWN:
4284       Feld[x][y] = EL_BUG;
4285       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4286       break;
4287
4288     case EL_SPACESHIP_RIGHT:
4289     case EL_SPACESHIP_UP:
4290     case EL_SPACESHIP_LEFT:
4291     case EL_SPACESHIP_DOWN:
4292       Feld[x][y] = EL_SPACESHIP;
4293       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4294       break;
4295
4296     case EL_BD_BUTTERFLY_RIGHT:
4297     case EL_BD_BUTTERFLY_UP:
4298     case EL_BD_BUTTERFLY_LEFT:
4299     case EL_BD_BUTTERFLY_DOWN:
4300       Feld[x][y] = EL_BD_BUTTERFLY;
4301       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4302       break;
4303
4304     case EL_BD_FIREFLY_RIGHT:
4305     case EL_BD_FIREFLY_UP:
4306     case EL_BD_FIREFLY_LEFT:
4307     case EL_BD_FIREFLY_DOWN:
4308       Feld[x][y] = EL_BD_FIREFLY;
4309       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4310       break;
4311
4312     case EL_PACMAN_RIGHT:
4313     case EL_PACMAN_UP:
4314     case EL_PACMAN_LEFT:
4315     case EL_PACMAN_DOWN:
4316       Feld[x][y] = EL_PACMAN;
4317       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4318       break;
4319
4320     case EL_YAMYAM_LEFT:
4321     case EL_YAMYAM_RIGHT:
4322     case EL_YAMYAM_UP:
4323     case EL_YAMYAM_DOWN:
4324       Feld[x][y] = EL_YAMYAM;
4325       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4326       break;
4327
4328     case EL_SP_SNIKSNAK:
4329       MovDir[x][y] = MV_UP;
4330       break;
4331
4332     case EL_SP_ELECTRON:
4333       MovDir[x][y] = MV_LEFT;
4334       break;
4335
4336     case EL_MOLE_LEFT:
4337     case EL_MOLE_RIGHT:
4338     case EL_MOLE_UP:
4339     case EL_MOLE_DOWN:
4340       Feld[x][y] = EL_MOLE;
4341       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4342       break;
4343
4344     default:
4345       if (IS_CUSTOM_ELEMENT(element))
4346       {
4347         struct ElementInfo *ei = &element_info[element];
4348         int move_direction_initial = ei->move_direction_initial;
4349         int move_pattern = ei->move_pattern;
4350
4351         if (move_direction_initial == MV_START_PREVIOUS)
4352         {
4353           if (MovDir[x][y] != MV_NONE)
4354             return;
4355
4356           move_direction_initial = MV_START_AUTOMATIC;
4357         }
4358
4359         if (move_direction_initial == MV_START_RANDOM)
4360           MovDir[x][y] = 1 << RND(4);
4361         else if (move_direction_initial & MV_ANY_DIRECTION)
4362           MovDir[x][y] = move_direction_initial;
4363         else if (move_pattern == MV_ALL_DIRECTIONS ||
4364                  move_pattern == MV_TURNING_LEFT ||
4365                  move_pattern == MV_TURNING_RIGHT ||
4366                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4367                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4368                  move_pattern == MV_TURNING_RANDOM)
4369           MovDir[x][y] = 1 << RND(4);
4370         else if (move_pattern == MV_HORIZONTAL)
4371           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4372         else if (move_pattern == MV_VERTICAL)
4373           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4374         else if (move_pattern & MV_ANY_DIRECTION)
4375           MovDir[x][y] = element_info[element].move_pattern;
4376         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4377                  move_pattern == MV_ALONG_RIGHT_SIDE)
4378         {
4379           /* use random direction as default start direction */
4380           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4381             MovDir[x][y] = 1 << RND(4);
4382
4383           for (i = 0; i < NUM_DIRECTIONS; i++)
4384           {
4385             int x1 = x + xy[i][0];
4386             int y1 = y + xy[i][1];
4387
4388             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4389             {
4390               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4391                 MovDir[x][y] = direction[0][i];
4392               else
4393                 MovDir[x][y] = direction[1][i];
4394
4395               break;
4396             }
4397           }
4398         }                
4399       }
4400       else
4401       {
4402         MovDir[x][y] = 1 << RND(4);
4403
4404         if (element != EL_BUG &&
4405             element != EL_SPACESHIP &&
4406             element != EL_BD_BUTTERFLY &&
4407             element != EL_BD_FIREFLY)
4408           break;
4409
4410         for (i = 0; i < NUM_DIRECTIONS; i++)
4411         {
4412           int x1 = x + xy[i][0];
4413           int y1 = y + xy[i][1];
4414
4415           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4416           {
4417             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4418             {
4419               MovDir[x][y] = direction[0][i];
4420               break;
4421             }
4422             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4423                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4424             {
4425               MovDir[x][y] = direction[1][i];
4426               break;
4427             }
4428           }
4429         }
4430       }
4431       break;
4432   }
4433
4434   GfxDir[x][y] = MovDir[x][y];
4435 }
4436
4437 void InitAmoebaNr(int x, int y)
4438 {
4439   int i;
4440   int group_nr = AmoebeNachbarNr(x, y);
4441
4442   if (group_nr == 0)
4443   {
4444     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4445     {
4446       if (AmoebaCnt[i] == 0)
4447       {
4448         group_nr = i;
4449         break;
4450       }
4451     }
4452   }
4453
4454   AmoebaNr[x][y] = group_nr;
4455   AmoebaCnt[group_nr]++;
4456   AmoebaCnt2[group_nr]++;
4457 }
4458
4459 static void PlayerWins(struct PlayerInfo *player)
4460 {
4461   player->LevelSolved = TRUE;
4462   player->GameOver = TRUE;
4463
4464   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4465                          level.native_em_level->lev->score : player->score);
4466
4467   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4468   player->LevelSolved_CountingScore = player->score_final;
4469 }
4470
4471 void GameWon()
4472 {
4473   static int time, time_final;
4474   static int score, score_final;
4475   static int game_over_delay_1 = 0;
4476   static int game_over_delay_2 = 0;
4477   int game_over_delay_value_1 = 50;
4478   int game_over_delay_value_2 = 50;
4479
4480   if (!local_player->LevelSolved_GameWon)
4481   {
4482     int i;
4483
4484     /* do not start end game actions before the player stops moving (to exit) */
4485     if (local_player->MovPos)
4486       return;
4487
4488     local_player->LevelSolved_GameWon = TRUE;
4489     local_player->LevelSolved_SaveTape = tape.recording;
4490     local_player->LevelSolved_SaveScore = !tape.playing;
4491
4492     if (tape.auto_play)         /* tape might already be stopped here */
4493       tape.auto_play_level_solved = TRUE;
4494
4495 #if 1
4496     TapeStop();
4497 #endif
4498
4499     game_over_delay_1 = game_over_delay_value_1;
4500     game_over_delay_2 = game_over_delay_value_2;
4501
4502     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4503     score = score_final = local_player->score_final;
4504
4505     if (TimeLeft > 0)
4506     {
4507       time_final = 0;
4508       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4509     }
4510     else if (level.time == 0 && TimePlayed < 999)
4511     {
4512       time_final = 999;
4513       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4514     }
4515
4516     local_player->score_final = score_final;
4517
4518     if (level_editor_test_game)
4519     {
4520       time = time_final;
4521       score = score_final;
4522
4523 #if 1
4524       local_player->LevelSolved_CountingTime = time;
4525       local_player->LevelSolved_CountingScore = score;
4526
4527       game_panel_controls[GAME_PANEL_TIME].value = time;
4528       game_panel_controls[GAME_PANEL_SCORE].value = score;
4529
4530       DisplayGameControlValues();
4531 #else
4532       DrawGameValue_Time(time);
4533       DrawGameValue_Score(score);
4534 #endif
4535     }
4536
4537     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4538     {
4539       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4540       {
4541         /* close exit door after last player */
4542         if ((AllPlayersGone &&
4543              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4544               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4545               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4546             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4547             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4548         {
4549           int element = Feld[ExitX][ExitY];
4550
4551 #if 0
4552           if (element == EL_EM_EXIT_OPEN ||
4553               element == EL_EM_STEEL_EXIT_OPEN)
4554           {
4555             Bang(ExitX, ExitY);
4556           }
4557           else
4558 #endif
4559           {
4560             Feld[ExitX][ExitY] =
4561               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4562                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4563                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4564                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4565                EL_EM_STEEL_EXIT_CLOSING);
4566
4567             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4568           }
4569         }
4570
4571         /* player disappears */
4572         DrawLevelField(ExitX, ExitY);
4573       }
4574
4575       for (i = 0; i < MAX_PLAYERS; i++)
4576       {
4577         struct PlayerInfo *player = &stored_player[i];
4578
4579         if (player->present)
4580         {
4581           RemovePlayer(player);
4582
4583           /* player disappears */
4584           DrawLevelField(player->jx, player->jy);
4585         }
4586       }
4587     }
4588
4589     PlaySound(SND_GAME_WINNING);
4590   }
4591
4592   if (game_over_delay_1 > 0)
4593   {
4594     game_over_delay_1--;
4595
4596     return;
4597   }
4598
4599   if (time != time_final)
4600   {
4601     int time_to_go = ABS(time_final - time);
4602     int time_count_dir = (time < time_final ? +1 : -1);
4603     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4604
4605     time  += time_count_steps * time_count_dir;
4606     score += time_count_steps * level.score[SC_TIME_BONUS];
4607
4608 #if 1
4609     local_player->LevelSolved_CountingTime = time;
4610     local_player->LevelSolved_CountingScore = score;
4611
4612     game_panel_controls[GAME_PANEL_TIME].value = time;
4613     game_panel_controls[GAME_PANEL_SCORE].value = score;
4614
4615     DisplayGameControlValues();
4616 #else
4617     DrawGameValue_Time(time);
4618     DrawGameValue_Score(score);
4619 #endif
4620
4621     if (time == time_final)
4622       StopSound(SND_GAME_LEVELTIME_BONUS);
4623     else if (setup.sound_loops)
4624       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4625     else
4626       PlaySound(SND_GAME_LEVELTIME_BONUS);
4627
4628     return;
4629   }
4630
4631   local_player->LevelSolved_PanelOff = TRUE;
4632
4633   if (game_over_delay_2 > 0)
4634   {
4635     game_over_delay_2--;
4636
4637     return;
4638   }
4639
4640 #if 1
4641   GameEnd();
4642 #endif
4643 }
4644
4645 void GameEnd()
4646 {
4647   int hi_pos;
4648   boolean raise_level = FALSE;
4649
4650   local_player->LevelSolved_GameEnd = TRUE;
4651
4652   CloseDoor(DOOR_CLOSE_1);
4653
4654   if (local_player->LevelSolved_SaveTape)
4655   {
4656 #if 0
4657     TapeStop();
4658 #endif
4659
4660 #if 1
4661     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4662 #else
4663     SaveTape(tape.level_nr);            /* ask to save tape */
4664 #endif
4665   }
4666
4667   if (level_editor_test_game)
4668   {
4669     game_status = GAME_MODE_MAIN;
4670
4671 #if 1
4672     DrawAndFadeInMainMenu(REDRAW_FIELD);
4673 #else
4674     DrawMainMenu();
4675 #endif
4676
4677     return;
4678   }
4679
4680   if (!local_player->LevelSolved_SaveScore)
4681   {
4682 #if 1
4683     FadeOut(REDRAW_FIELD);
4684 #endif
4685
4686     game_status = GAME_MODE_MAIN;
4687
4688     DrawAndFadeInMainMenu(REDRAW_FIELD);
4689
4690     return;
4691   }
4692
4693   if (level_nr == leveldir_current->handicap_level)
4694   {
4695     leveldir_current->handicap_level++;
4696     SaveLevelSetup_SeriesInfo();
4697   }
4698
4699   if (level_nr < leveldir_current->last_level)
4700     raise_level = TRUE;                 /* advance to next level */
4701
4702   if ((hi_pos = NewHiScore()) >= 0) 
4703   {
4704     game_status = GAME_MODE_SCORES;
4705
4706     DrawHallOfFame(hi_pos);
4707
4708     if (raise_level)
4709     {
4710       level_nr++;
4711       TapeErase();
4712     }
4713   }
4714   else
4715   {
4716 #if 1
4717     FadeOut(REDRAW_FIELD);
4718 #endif
4719
4720     game_status = GAME_MODE_MAIN;
4721
4722     if (raise_level)
4723     {
4724       level_nr++;
4725       TapeErase();
4726     }
4727
4728     DrawAndFadeInMainMenu(REDRAW_FIELD);
4729   }
4730 }
4731
4732 int NewHiScore()
4733 {
4734   int k, l;
4735   int position = -1;
4736
4737   LoadScore(level_nr);
4738
4739   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4740       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4741     return -1;
4742
4743   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4744   {
4745     if (local_player->score_final > highscore[k].Score)
4746     {
4747       /* player has made it to the hall of fame */
4748
4749       if (k < MAX_SCORE_ENTRIES - 1)
4750       {
4751         int m = MAX_SCORE_ENTRIES - 1;
4752
4753 #ifdef ONE_PER_NAME
4754         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4755           if (strEqual(setup.player_name, highscore[l].Name))
4756             m = l;
4757         if (m == k)     /* player's new highscore overwrites his old one */
4758           goto put_into_list;
4759 #endif
4760
4761         for (l = m; l > k; l--)
4762         {
4763           strcpy(highscore[l].Name, highscore[l - 1].Name);
4764           highscore[l].Score = highscore[l - 1].Score;
4765         }
4766       }
4767
4768 #ifdef ONE_PER_NAME
4769       put_into_list:
4770 #endif
4771       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4772       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4773       highscore[k].Score = local_player->score_final; 
4774       position = k;
4775       break;
4776     }
4777
4778 #ifdef ONE_PER_NAME
4779     else if (!strncmp(setup.player_name, highscore[k].Name,
4780                       MAX_PLAYER_NAME_LEN))
4781       break;    /* player already there with a higher score */
4782 #endif
4783
4784   }
4785
4786   if (position >= 0) 
4787     SaveScore(level_nr);
4788
4789   return position;
4790 }
4791
4792 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4793 {
4794   int element = Feld[x][y];
4795   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4796   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4797   int horiz_move = (dx != 0);
4798   int sign = (horiz_move ? dx : dy);
4799   int step = sign * element_info[element].move_stepsize;
4800
4801   /* special values for move stepsize for spring and things on conveyor belt */
4802   if (horiz_move)
4803   {
4804     if (CAN_FALL(element) &&
4805         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4806       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4807     else if (element == EL_SPRING)
4808       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4809   }
4810
4811   return step;
4812 }
4813
4814 inline static int getElementMoveStepsize(int x, int y)
4815 {
4816   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4817 }
4818
4819 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4820 {
4821   if (player->GfxAction != action || player->GfxDir != dir)
4822   {
4823 #if 0
4824     printf("Player frame reset! (%d => %d, %d => %d)\n",
4825            player->GfxAction, action, player->GfxDir, dir);
4826 #endif
4827
4828     player->GfxAction = action;
4829     player->GfxDir = dir;
4830     player->Frame = 0;
4831     player->StepFrame = 0;
4832   }
4833 }
4834
4835 #if USE_GFX_RESET_GFX_ANIMATION
4836 static void ResetGfxFrame(int x, int y, boolean redraw)
4837 {
4838   int element = Feld[x][y];
4839   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4840   int last_gfx_frame = GfxFrame[x][y];
4841
4842   if (graphic_info[graphic].anim_global_sync)
4843     GfxFrame[x][y] = FrameCounter;
4844   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4845     GfxFrame[x][y] = CustomValue[x][y];
4846   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4847     GfxFrame[x][y] = element_info[element].collect_score;
4848   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4849     GfxFrame[x][y] = ChangeDelay[x][y];
4850
4851   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4852     DrawLevelGraphicAnimation(x, y, graphic);
4853 }
4854 #endif
4855
4856 static void ResetGfxAnimation(int x, int y)
4857 {
4858   GfxAction[x][y] = ACTION_DEFAULT;
4859   GfxDir[x][y] = MovDir[x][y];
4860   GfxFrame[x][y] = 0;
4861
4862 #if USE_GFX_RESET_GFX_ANIMATION
4863   ResetGfxFrame(x, y, FALSE);
4864 #endif
4865 }
4866
4867 static void ResetRandomAnimationValue(int x, int y)
4868 {
4869   GfxRandom[x][y] = INIT_GFX_RANDOM();
4870 }
4871
4872 void InitMovingField(int x, int y, int direction)
4873 {
4874   int element = Feld[x][y];
4875   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4876   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4877   int newx = x + dx;
4878   int newy = y + dy;
4879   boolean is_moving_before, is_moving_after;
4880 #if 0
4881   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4882 #endif
4883
4884   /* check if element was/is moving or being moved before/after mode change */
4885 #if 1
4886 #if 1
4887   is_moving_before = (WasJustMoving[x][y] != 0);
4888 #else
4889   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4890   is_moving_before = WasJustMoving[x][y];
4891 #endif
4892 #else
4893   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4894 #endif
4895   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4896
4897   /* reset animation only for moving elements which change direction of moving
4898      or which just started or stopped moving
4899      (else CEs with property "can move" / "not moving" are reset each frame) */
4900 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4901 #if 1
4902   if (is_moving_before != is_moving_after ||
4903       direction != MovDir[x][y])
4904     ResetGfxAnimation(x, y);
4905 #else
4906   if ((is_moving_before || is_moving_after) && !continues_moving)
4907     ResetGfxAnimation(x, y);
4908 #endif
4909 #else
4910   if (!continues_moving)
4911     ResetGfxAnimation(x, y);
4912 #endif
4913
4914   MovDir[x][y] = direction;
4915   GfxDir[x][y] = direction;
4916
4917 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4918   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4919                      direction == MV_DOWN && CAN_FALL(element) ?
4920                      ACTION_FALLING : ACTION_MOVING);
4921 #else
4922   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4923                      ACTION_FALLING : ACTION_MOVING);
4924 #endif
4925
4926   /* this is needed for CEs with property "can move" / "not moving" */
4927
4928   if (is_moving_after)
4929   {
4930     if (Feld[newx][newy] == EL_EMPTY)
4931       Feld[newx][newy] = EL_BLOCKED;
4932
4933     MovDir[newx][newy] = MovDir[x][y];
4934
4935 #if USE_NEW_CUSTOM_VALUE
4936     CustomValue[newx][newy] = CustomValue[x][y];
4937 #endif
4938
4939     GfxFrame[newx][newy] = GfxFrame[x][y];
4940     GfxRandom[newx][newy] = GfxRandom[x][y];
4941     GfxAction[newx][newy] = GfxAction[x][y];
4942     GfxDir[newx][newy] = GfxDir[x][y];
4943   }
4944 }
4945
4946 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4947 {
4948   int direction = MovDir[x][y];
4949   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4950   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4951
4952   *goes_to_x = newx;
4953   *goes_to_y = newy;
4954 }
4955
4956 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4957 {
4958   int oldx = x, oldy = y;
4959   int direction = MovDir[x][y];
4960
4961   if (direction == MV_LEFT)
4962     oldx++;
4963   else if (direction == MV_RIGHT)
4964     oldx--;
4965   else if (direction == MV_UP)
4966     oldy++;
4967   else if (direction == MV_DOWN)
4968     oldy--;
4969
4970   *comes_from_x = oldx;
4971   *comes_from_y = oldy;
4972 }
4973
4974 int MovingOrBlocked2Element(int x, int y)
4975 {
4976   int element = Feld[x][y];
4977
4978   if (element == EL_BLOCKED)
4979   {
4980     int oldx, oldy;
4981
4982     Blocked2Moving(x, y, &oldx, &oldy);
4983     return Feld[oldx][oldy];
4984   }
4985   else
4986     return element;
4987 }
4988
4989 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4990 {
4991   /* like MovingOrBlocked2Element(), but if element is moving
4992      and (x,y) is the field the moving element is just leaving,
4993      return EL_BLOCKED instead of the element value */
4994   int element = Feld[x][y];
4995
4996   if (IS_MOVING(x, y))
4997   {
4998     if (element == EL_BLOCKED)
4999     {
5000       int oldx, oldy;
5001
5002       Blocked2Moving(x, y, &oldx, &oldy);
5003       return Feld[oldx][oldy];
5004     }
5005     else
5006       return EL_BLOCKED;
5007   }
5008   else
5009     return element;
5010 }
5011
5012 static void RemoveField(int x, int y)
5013 {
5014   Feld[x][y] = EL_EMPTY;
5015
5016   MovPos[x][y] = 0;
5017   MovDir[x][y] = 0;
5018   MovDelay[x][y] = 0;
5019
5020 #if USE_NEW_CUSTOM_VALUE
5021   CustomValue[x][y] = 0;
5022 #endif
5023
5024   AmoebaNr[x][y] = 0;
5025   ChangeDelay[x][y] = 0;
5026   ChangePage[x][y] = -1;
5027   Pushed[x][y] = FALSE;
5028
5029 #if 0
5030   ExplodeField[x][y] = EX_TYPE_NONE;
5031 #endif
5032
5033   GfxElement[x][y] = EL_UNDEFINED;
5034   GfxAction[x][y] = ACTION_DEFAULT;
5035   GfxDir[x][y] = MV_NONE;
5036 }
5037
5038 void RemoveMovingField(int x, int y)
5039 {
5040   int oldx = x, oldy = y, newx = x, newy = y;
5041   int element = Feld[x][y];
5042   int next_element = EL_UNDEFINED;
5043
5044   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5045     return;
5046
5047   if (IS_MOVING(x, y))
5048   {
5049     Moving2Blocked(x, y, &newx, &newy);
5050
5051     if (Feld[newx][newy] != EL_BLOCKED)
5052     {
5053       /* element is moving, but target field is not free (blocked), but
5054          already occupied by something different (example: acid pool);
5055          in this case, only remove the moving field, but not the target */
5056
5057       RemoveField(oldx, oldy);
5058
5059       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5060
5061       DrawLevelField(oldx, oldy);
5062
5063       return;
5064     }
5065   }
5066   else if (element == EL_BLOCKED)
5067   {
5068     Blocked2Moving(x, y, &oldx, &oldy);
5069     if (!IS_MOVING(oldx, oldy))
5070       return;
5071   }
5072
5073   if (element == EL_BLOCKED &&
5074       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5075        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5076        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5077        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5078        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5079        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5080     next_element = get_next_element(Feld[oldx][oldy]);
5081
5082   RemoveField(oldx, oldy);
5083   RemoveField(newx, newy);
5084
5085   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5086
5087   if (next_element != EL_UNDEFINED)
5088     Feld[oldx][oldy] = next_element;
5089
5090   DrawLevelField(oldx, oldy);
5091   DrawLevelField(newx, newy);
5092 }
5093
5094 void DrawDynamite(int x, int y)
5095 {
5096   int sx = SCREENX(x), sy = SCREENY(y);
5097   int graphic = el2img(Feld[x][y]);
5098   int frame;
5099
5100   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5101     return;
5102
5103   if (IS_WALKABLE_INSIDE(Back[x][y]))
5104     return;
5105
5106   if (Back[x][y])
5107     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5108   else if (Store[x][y])
5109     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5110
5111   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5112
5113   if (Back[x][y] || Store[x][y])
5114     DrawGraphicThruMask(sx, sy, graphic, frame);
5115   else
5116     DrawGraphic(sx, sy, graphic, frame);
5117 }
5118
5119 void CheckDynamite(int x, int y)
5120 {
5121   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5122   {
5123     MovDelay[x][y]--;
5124
5125     if (MovDelay[x][y] != 0)
5126     {
5127       DrawDynamite(x, y);
5128       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5129
5130       return;
5131     }
5132   }
5133
5134   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5135
5136   Bang(x, y);
5137 }
5138
5139 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5140 {
5141   boolean num_checked_players = 0;
5142   int i;
5143
5144   for (i = 0; i < MAX_PLAYERS; i++)
5145   {
5146     if (stored_player[i].active)
5147     {
5148       int sx = stored_player[i].jx;
5149       int sy = stored_player[i].jy;
5150
5151       if (num_checked_players == 0)
5152       {
5153         *sx1 = *sx2 = sx;
5154         *sy1 = *sy2 = sy;
5155       }
5156       else
5157       {
5158         *sx1 = MIN(*sx1, sx);
5159         *sy1 = MIN(*sy1, sy);
5160         *sx2 = MAX(*sx2, sx);
5161         *sy2 = MAX(*sy2, sy);
5162       }
5163
5164       num_checked_players++;
5165     }
5166   }
5167 }
5168
5169 static boolean checkIfAllPlayersFitToScreen_RND()
5170 {
5171   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5172
5173   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5174
5175   return (sx2 - sx1 < SCR_FIELDX &&
5176           sy2 - sy1 < SCR_FIELDY);
5177 }
5178
5179 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5180 {
5181   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5182
5183   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5184
5185   *sx = (sx1 + sx2) / 2;
5186   *sy = (sy1 + sy2) / 2;
5187 }
5188
5189 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5190                         boolean center_screen, boolean quick_relocation)
5191 {
5192   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5193   boolean no_delay = (tape.warp_forward);
5194   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5195   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5196
5197   if (quick_relocation)
5198   {
5199     int offset = game.scroll_delay_value;
5200
5201     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5202     {
5203       if (!level.shifted_relocation || center_screen)
5204       {
5205         /* quick relocation (without scrolling), with centering of screen */
5206
5207         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5208                     x > SBX_Right + MIDPOSX ? SBX_Right :
5209                     x - MIDPOSX);
5210
5211         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5212                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5213                     y - MIDPOSY);
5214       }
5215       else
5216       {
5217         /* quick relocation (without scrolling), but do not center screen */
5218
5219         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5220                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5221                                old_x - MIDPOSX);
5222
5223         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5224                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5225                                old_y - MIDPOSY);
5226
5227         int offset_x = x + (scroll_x - center_scroll_x);
5228         int offset_y = y + (scroll_y - center_scroll_y);
5229
5230         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5231                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5232                     offset_x - MIDPOSX);
5233
5234         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5235                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5236                     offset_y - MIDPOSY);
5237       }
5238     }
5239     else
5240     {
5241       /* quick relocation (without scrolling), inside visible screen area */
5242
5243       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5244           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5245         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5246
5247       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5248           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5249         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5250
5251       /* don't scroll over playfield boundaries */
5252       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5253         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5254
5255       /* don't scroll over playfield boundaries */
5256       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5257         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5258     }
5259
5260     RedrawPlayfield(TRUE, 0,0,0,0);
5261   }
5262   else
5263   {
5264 #if 1
5265     int scroll_xx, scroll_yy;
5266
5267     if (!level.shifted_relocation || center_screen)
5268     {
5269       /* visible relocation (with scrolling), with centering of screen */
5270
5271       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5272                    x > SBX_Right + MIDPOSX ? SBX_Right :
5273                    x - MIDPOSX);
5274
5275       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5276                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5277                    y - MIDPOSY);
5278     }
5279     else
5280     {
5281       /* visible relocation (with scrolling), but do not center screen */
5282
5283       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5284                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5285                              old_x - MIDPOSX);
5286
5287       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5288                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5289                              old_y - MIDPOSY);
5290
5291       int offset_x = x + (scroll_x - center_scroll_x);
5292       int offset_y = y + (scroll_y - center_scroll_y);
5293
5294       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5295                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5296                    offset_x - MIDPOSX);
5297
5298       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5299                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5300                    offset_y - MIDPOSY);
5301     }
5302
5303 #else
5304
5305     /* visible relocation (with scrolling), with centering of screen */
5306
5307     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5308                      x > SBX_Right + MIDPOSX ? SBX_Right :
5309                      x - MIDPOSX);
5310
5311     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5312                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5313                      y - MIDPOSY);
5314 #endif
5315
5316     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5317
5318     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5319     {
5320       int dx = 0, dy = 0;
5321       int fx = FX, fy = FY;
5322
5323       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5324       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5325
5326       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5327         break;
5328
5329       scroll_x -= dx;
5330       scroll_y -= dy;
5331
5332       fx += dx * TILEX / 2;
5333       fy += dy * TILEY / 2;
5334
5335       ScrollLevel(dx, dy);
5336       DrawAllPlayers();
5337
5338       /* scroll in two steps of half tile size to make things smoother */
5339       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5340       FlushDisplay();
5341       Delay(wait_delay_value);
5342
5343       /* scroll second step to align at full tile size */
5344       BackToFront();
5345       Delay(wait_delay_value);
5346     }
5347
5348     DrawAllPlayers();
5349     BackToFront();
5350     Delay(wait_delay_value);
5351   }
5352 }
5353
5354 void RelocatePlayer(int jx, int jy, int el_player_raw)
5355 {
5356   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5357   int player_nr = GET_PLAYER_NR(el_player);
5358   struct PlayerInfo *player = &stored_player[player_nr];
5359   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5360   boolean no_delay = (tape.warp_forward);
5361   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5362   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5363   int old_jx = player->jx;
5364   int old_jy = player->jy;
5365   int old_element = Feld[old_jx][old_jy];
5366   int element = Feld[jx][jy];
5367   boolean player_relocated = (old_jx != jx || old_jy != jy);
5368
5369   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5370   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5371   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5372   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5373   int leave_side_horiz = move_dir_horiz;
5374   int leave_side_vert  = move_dir_vert;
5375   int enter_side = enter_side_horiz | enter_side_vert;
5376   int leave_side = leave_side_horiz | leave_side_vert;
5377
5378   if (player->GameOver)         /* do not reanimate dead player */
5379     return;
5380
5381   if (!player_relocated)        /* no need to relocate the player */
5382     return;
5383
5384   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5385   {
5386     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5387     DrawLevelField(jx, jy);
5388   }
5389
5390   if (player->present)
5391   {
5392     while (player->MovPos)
5393     {
5394       ScrollPlayer(player, SCROLL_GO_ON);
5395       ScrollScreen(NULL, SCROLL_GO_ON);
5396
5397       AdvanceFrameAndPlayerCounters(player->index_nr);
5398
5399       DrawPlayer(player);
5400
5401       BackToFront();
5402       Delay(wait_delay_value);
5403     }
5404
5405     DrawPlayer(player);         /* needed here only to cleanup last field */
5406     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5407
5408     player->is_moving = FALSE;
5409   }
5410
5411   if (IS_CUSTOM_ELEMENT(old_element))
5412     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5413                                CE_LEFT_BY_PLAYER,
5414                                player->index_bit, leave_side);
5415
5416   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5417                                       CE_PLAYER_LEAVES_X,
5418                                       player->index_bit, leave_side);
5419
5420   Feld[jx][jy] = el_player;
5421   InitPlayerField(jx, jy, el_player, TRUE);
5422
5423   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5424   {
5425     Feld[jx][jy] = element;
5426     InitField(jx, jy, FALSE);
5427   }
5428
5429   /* only visually relocate centered player */
5430   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5431                      FALSE, level.instant_relocation);
5432
5433   TestIfPlayerTouchesBadThing(jx, jy);
5434   TestIfPlayerTouchesCustomElement(jx, jy);
5435
5436   if (IS_CUSTOM_ELEMENT(element))
5437     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5438                                player->index_bit, enter_side);
5439
5440   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5441                                       player->index_bit, enter_side);
5442 }
5443
5444 void Explode(int ex, int ey, int phase, int mode)
5445 {
5446   int x, y;
5447   int last_phase;
5448   int border_element;
5449
5450   /* !!! eliminate this variable !!! */
5451   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5452
5453   if (game.explosions_delayed)
5454   {
5455     ExplodeField[ex][ey] = mode;
5456     return;
5457   }
5458
5459   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5460   {
5461     int center_element = Feld[ex][ey];
5462     int artwork_element, explosion_element;     /* set these values later */
5463
5464 #if 0
5465     /* --- This is only really needed (and now handled) in "Impact()". --- */
5466     /* do not explode moving elements that left the explode field in time */
5467     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5468         center_element == EL_EMPTY &&
5469         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5470       return;
5471 #endif
5472
5473 #if 0
5474     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5475     if (mode == EX_TYPE_NORMAL ||
5476         mode == EX_TYPE_CENTER ||
5477         mode == EX_TYPE_CROSS)
5478       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5479 #endif
5480
5481     /* remove things displayed in background while burning dynamite */
5482     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5483       Back[ex][ey] = 0;
5484
5485     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5486     {
5487       /* put moving element to center field (and let it explode there) */
5488       center_element = MovingOrBlocked2Element(ex, ey);
5489       RemoveMovingField(ex, ey);
5490       Feld[ex][ey] = center_element;
5491     }
5492
5493     /* now "center_element" is finally determined -- set related values now */
5494     artwork_element = center_element;           /* for custom player artwork */
5495     explosion_element = center_element;         /* for custom player artwork */
5496
5497     if (IS_PLAYER(ex, ey))
5498     {
5499       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5500
5501       artwork_element = stored_player[player_nr].artwork_element;
5502
5503       if (level.use_explosion_element[player_nr])
5504       {
5505         explosion_element = level.explosion_element[player_nr];
5506         artwork_element = explosion_element;
5507       }
5508     }
5509
5510 #if 1
5511     if (mode == EX_TYPE_NORMAL ||
5512         mode == EX_TYPE_CENTER ||
5513         mode == EX_TYPE_CROSS)
5514       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5515 #endif
5516
5517     last_phase = element_info[explosion_element].explosion_delay + 1;
5518
5519     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5520     {
5521       int xx = x - ex + 1;
5522       int yy = y - ey + 1;
5523       int element;
5524
5525       if (!IN_LEV_FIELD(x, y) ||
5526           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5527           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5528         continue;
5529
5530       element = Feld[x][y];
5531
5532       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5533       {
5534         element = MovingOrBlocked2Element(x, y);
5535
5536         if (!IS_EXPLOSION_PROOF(element))
5537           RemoveMovingField(x, y);
5538       }
5539
5540       /* indestructible elements can only explode in center (but not flames) */
5541       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5542                                            mode == EX_TYPE_BORDER)) ||
5543           element == EL_FLAMES)
5544         continue;
5545
5546       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5547          behaviour, for example when touching a yamyam that explodes to rocks
5548          with active deadly shield, a rock is created under the player !!! */
5549       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5550 #if 0
5551       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5552           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5553            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5554 #else
5555       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5556 #endif
5557       {
5558         if (IS_ACTIVE_BOMB(element))
5559         {
5560           /* re-activate things under the bomb like gate or penguin */
5561           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5562           Back[x][y] = 0;
5563         }
5564
5565         continue;
5566       }
5567
5568       /* save walkable background elements while explosion on same tile */
5569       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5570           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5571         Back[x][y] = element;
5572
5573       /* ignite explodable elements reached by other explosion */
5574       if (element == EL_EXPLOSION)
5575         element = Store2[x][y];
5576
5577       if (AmoebaNr[x][y] &&
5578           (element == EL_AMOEBA_FULL ||
5579            element == EL_BD_AMOEBA ||
5580            element == EL_AMOEBA_GROWING))
5581       {
5582         AmoebaCnt[AmoebaNr[x][y]]--;
5583         AmoebaCnt2[AmoebaNr[x][y]]--;
5584       }
5585
5586       RemoveField(x, y);
5587
5588       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5589       {
5590         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5591
5592         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5593
5594         if (PLAYERINFO(ex, ey)->use_murphy)
5595           Store[x][y] = EL_EMPTY;
5596       }
5597
5598       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5599          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5600       else if (ELEM_IS_PLAYER(center_element))
5601         Store[x][y] = EL_EMPTY;
5602       else if (center_element == EL_YAMYAM)
5603         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5604       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5605         Store[x][y] = element_info[center_element].content.e[xx][yy];
5606 #if 1
5607       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5608          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5609          otherwise) -- FIX THIS !!! */
5610       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5611         Store[x][y] = element_info[element].content.e[1][1];
5612 #else
5613       else if (!CAN_EXPLODE(element))
5614         Store[x][y] = element_info[element].content.e[1][1];
5615 #endif
5616       else
5617         Store[x][y] = EL_EMPTY;
5618
5619       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5620           center_element == EL_AMOEBA_TO_DIAMOND)
5621         Store2[x][y] = element;
5622
5623       Feld[x][y] = EL_EXPLOSION;
5624       GfxElement[x][y] = artwork_element;
5625
5626       ExplodePhase[x][y] = 1;
5627       ExplodeDelay[x][y] = last_phase;
5628
5629       Stop[x][y] = TRUE;
5630     }
5631
5632     if (center_element == EL_YAMYAM)
5633       game.yamyam_content_nr =
5634         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5635
5636     return;
5637   }
5638
5639   if (Stop[ex][ey])
5640     return;
5641
5642   x = ex;
5643   y = ey;
5644
5645   if (phase == 1)
5646     GfxFrame[x][y] = 0;         /* restart explosion animation */
5647
5648   last_phase = ExplodeDelay[x][y];
5649
5650   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5651
5652 #ifdef DEBUG
5653
5654   /* activate this even in non-DEBUG version until cause for crash in
5655      getGraphicAnimationFrame() (see below) is found and eliminated */
5656
5657 #endif
5658 #if 1
5659
5660 #if 1
5661   /* this can happen if the player leaves an explosion just in time */
5662   if (GfxElement[x][y] == EL_UNDEFINED)
5663     GfxElement[x][y] = EL_EMPTY;
5664 #else
5665   if (GfxElement[x][y] == EL_UNDEFINED)
5666   {
5667     printf("\n\n");
5668     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5669     printf("Explode(): This should never happen!\n");
5670     printf("\n\n");
5671
5672     GfxElement[x][y] = EL_EMPTY;
5673   }
5674 #endif
5675
5676 #endif
5677
5678   border_element = Store2[x][y];
5679   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5680     border_element = StorePlayer[x][y];
5681
5682   if (phase == element_info[border_element].ignition_delay ||
5683       phase == last_phase)
5684   {
5685     boolean border_explosion = FALSE;
5686
5687     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5688         !PLAYER_EXPLOSION_PROTECTED(x, y))
5689     {
5690       KillPlayerUnlessExplosionProtected(x, y);
5691       border_explosion = TRUE;
5692     }
5693     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5694     {
5695       Feld[x][y] = Store2[x][y];
5696       Store2[x][y] = 0;
5697       Bang(x, y);
5698       border_explosion = TRUE;
5699     }
5700     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5701     {
5702       AmoebeUmwandeln(x, y);
5703       Store2[x][y] = 0;
5704       border_explosion = TRUE;
5705     }
5706
5707     /* if an element just explodes due to another explosion (chain-reaction),
5708        do not immediately end the new explosion when it was the last frame of
5709        the explosion (as it would be done in the following "if"-statement!) */
5710     if (border_explosion && phase == last_phase)
5711       return;
5712   }
5713
5714   if (phase == last_phase)
5715   {
5716     int element;
5717
5718     element = Feld[x][y] = Store[x][y];
5719     Store[x][y] = Store2[x][y] = 0;
5720     GfxElement[x][y] = EL_UNDEFINED;
5721
5722     /* player can escape from explosions and might therefore be still alive */
5723     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5724         element <= EL_PLAYER_IS_EXPLODING_4)
5725     {
5726       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5727       int explosion_element = EL_PLAYER_1 + player_nr;
5728       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5729       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5730
5731       if (level.use_explosion_element[player_nr])
5732         explosion_element = level.explosion_element[player_nr];
5733
5734       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5735                     element_info[explosion_element].content.e[xx][yy]);
5736     }
5737
5738     /* restore probably existing indestructible background element */
5739     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5740       element = Feld[x][y] = Back[x][y];
5741     Back[x][y] = 0;
5742
5743     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5744     GfxDir[x][y] = MV_NONE;
5745     ChangeDelay[x][y] = 0;
5746     ChangePage[x][y] = -1;
5747
5748 #if USE_NEW_CUSTOM_VALUE
5749     CustomValue[x][y] = 0;
5750 #endif
5751
5752     InitField_WithBug2(x, y, FALSE);
5753
5754     DrawLevelField(x, y);
5755
5756     TestIfElementTouchesCustomElement(x, y);
5757
5758     if (GFX_CRUMBLED(element))
5759       DrawLevelFieldCrumbledSandNeighbours(x, y);
5760
5761     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5762       StorePlayer[x][y] = 0;
5763
5764     if (ELEM_IS_PLAYER(element))
5765       RelocatePlayer(x, y, element);
5766   }
5767   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5768   {
5769     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5770     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5771
5772     if (phase == delay)
5773       DrawLevelFieldCrumbledSand(x, y);
5774
5775     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5776     {
5777       DrawLevelElement(x, y, Back[x][y]);
5778       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5779     }
5780     else if (IS_WALKABLE_UNDER(Back[x][y]))
5781     {
5782       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5783       DrawLevelElementThruMask(x, y, Back[x][y]);
5784     }
5785     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5786       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5787   }
5788 }
5789
5790 void DynaExplode(int ex, int ey)
5791 {
5792   int i, j;
5793   int dynabomb_element = Feld[ex][ey];
5794   int dynabomb_size = 1;
5795   boolean dynabomb_xl = FALSE;
5796   struct PlayerInfo *player;
5797   static int xy[4][2] =
5798   {
5799     { 0, -1 },
5800     { -1, 0 },
5801     { +1, 0 },
5802     { 0, +1 }
5803   };
5804
5805   if (IS_ACTIVE_BOMB(dynabomb_element))
5806   {
5807     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5808     dynabomb_size = player->dynabomb_size;
5809     dynabomb_xl = player->dynabomb_xl;
5810     player->dynabombs_left++;
5811   }
5812
5813   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5814
5815   for (i = 0; i < NUM_DIRECTIONS; i++)
5816   {
5817     for (j = 1; j <= dynabomb_size; j++)
5818     {
5819       int x = ex + j * xy[i][0];
5820       int y = ey + j * xy[i][1];
5821       int element;
5822
5823       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5824         break;
5825
5826       element = Feld[x][y];
5827
5828       /* do not restart explosions of fields with active bombs */
5829       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5830         continue;
5831
5832       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5833
5834       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5835           !IS_DIGGABLE(element) && !dynabomb_xl)
5836         break;
5837     }
5838   }
5839 }
5840
5841 void Bang(int x, int y)
5842 {
5843   int element = MovingOrBlocked2Element(x, y);
5844   int explosion_type = EX_TYPE_NORMAL;
5845
5846   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5847   {
5848     struct PlayerInfo *player = PLAYERINFO(x, y);
5849
5850     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5851                             player->element_nr);
5852
5853     if (level.use_explosion_element[player->index_nr])
5854     {
5855       int explosion_element = level.explosion_element[player->index_nr];
5856
5857       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5858         explosion_type = EX_TYPE_CROSS;
5859       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5860         explosion_type = EX_TYPE_CENTER;
5861     }
5862   }
5863
5864   switch (element)
5865   {
5866     case EL_BUG:
5867     case EL_SPACESHIP:
5868     case EL_BD_BUTTERFLY:
5869     case EL_BD_FIREFLY:
5870     case EL_YAMYAM:
5871     case EL_DARK_YAMYAM:
5872     case EL_ROBOT:
5873     case EL_PACMAN:
5874     case EL_MOLE:
5875       RaiseScoreElement(element);
5876       break;
5877
5878     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5879     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5880     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5881     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5882     case EL_DYNABOMB_INCREASE_NUMBER:
5883     case EL_DYNABOMB_INCREASE_SIZE:
5884     case EL_DYNABOMB_INCREASE_POWER:
5885       explosion_type = EX_TYPE_DYNA;
5886       break;
5887
5888     case EL_DC_LANDMINE:
5889 #if 0
5890     case EL_EM_EXIT_OPEN:
5891     case EL_EM_STEEL_EXIT_OPEN:
5892 #endif
5893       explosion_type = EX_TYPE_CENTER;
5894       break;
5895
5896     case EL_PENGUIN:
5897     case EL_LAMP:
5898     case EL_LAMP_ACTIVE:
5899     case EL_AMOEBA_TO_DIAMOND:
5900       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5901         explosion_type = EX_TYPE_CENTER;
5902       break;
5903
5904     default:
5905       if (element_info[element].explosion_type == EXPLODES_CROSS)
5906         explosion_type = EX_TYPE_CROSS;
5907       else if (element_info[element].explosion_type == EXPLODES_1X1)
5908         explosion_type = EX_TYPE_CENTER;
5909       break;
5910   }
5911
5912   if (explosion_type == EX_TYPE_DYNA)
5913     DynaExplode(x, y);
5914   else
5915     Explode(x, y, EX_PHASE_START, explosion_type);
5916
5917   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5918 }
5919
5920 void SplashAcid(int x, int y)
5921 {
5922   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5923       (!IN_LEV_FIELD(x - 1, y - 2) ||
5924        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5925     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5926
5927   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5928       (!IN_LEV_FIELD(x + 1, y - 2) ||
5929        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5930     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5931
5932   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5933 }
5934
5935 static void InitBeltMovement()
5936 {
5937   static int belt_base_element[4] =
5938   {
5939     EL_CONVEYOR_BELT_1_LEFT,
5940     EL_CONVEYOR_BELT_2_LEFT,
5941     EL_CONVEYOR_BELT_3_LEFT,
5942     EL_CONVEYOR_BELT_4_LEFT
5943   };
5944   static int belt_base_active_element[4] =
5945   {
5946     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5947     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5948     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5949     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5950   };
5951
5952   int x, y, i, j;
5953
5954   /* set frame order for belt animation graphic according to belt direction */
5955   for (i = 0; i < NUM_BELTS; i++)
5956   {
5957     int belt_nr = i;
5958
5959     for (j = 0; j < NUM_BELT_PARTS; j++)
5960     {
5961       int element = belt_base_active_element[belt_nr] + j;
5962       int graphic_1 = el2img(element);
5963       int graphic_2 = el2panelimg(element);
5964
5965       if (game.belt_dir[i] == MV_LEFT)
5966       {
5967         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5968         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5969       }
5970       else
5971       {
5972         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5973         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5974       }
5975     }
5976   }
5977
5978   SCAN_PLAYFIELD(x, y)
5979   {
5980     int element = Feld[x][y];
5981
5982     for (i = 0; i < NUM_BELTS; i++)
5983     {
5984       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5985       {
5986         int e_belt_nr = getBeltNrFromBeltElement(element);
5987         int belt_nr = i;
5988
5989         if (e_belt_nr == belt_nr)
5990         {
5991           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5992
5993           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5994         }
5995       }
5996     }
5997   }
5998 }
5999
6000 static void ToggleBeltSwitch(int x, int y)
6001 {
6002   static int belt_base_element[4] =
6003   {
6004     EL_CONVEYOR_BELT_1_LEFT,
6005     EL_CONVEYOR_BELT_2_LEFT,
6006     EL_CONVEYOR_BELT_3_LEFT,
6007     EL_CONVEYOR_BELT_4_LEFT
6008   };
6009   static int belt_base_active_element[4] =
6010   {
6011     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6012     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6013     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6014     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6015   };
6016   static int belt_base_switch_element[4] =
6017   {
6018     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6019     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6020     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6021     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6022   };
6023   static int belt_move_dir[4] =
6024   {
6025     MV_LEFT,
6026     MV_NONE,
6027     MV_RIGHT,
6028     MV_NONE,
6029   };
6030
6031   int element = Feld[x][y];
6032   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6033   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6034   int belt_dir = belt_move_dir[belt_dir_nr];
6035   int xx, yy, i;
6036
6037   if (!IS_BELT_SWITCH(element))
6038     return;
6039
6040   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6041   game.belt_dir[belt_nr] = belt_dir;
6042
6043   if (belt_dir_nr == 3)
6044     belt_dir_nr = 1;
6045
6046   /* set frame order for belt animation graphic according to belt direction */
6047   for (i = 0; i < NUM_BELT_PARTS; i++)
6048   {
6049     int element = belt_base_active_element[belt_nr] + i;
6050     int graphic_1 = el2img(element);
6051     int graphic_2 = el2panelimg(element);
6052
6053     if (belt_dir == MV_LEFT)
6054     {
6055       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6056       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6057     }
6058     else
6059     {
6060       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6061       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6062     }
6063   }
6064
6065   SCAN_PLAYFIELD(xx, yy)
6066   {
6067     int element = Feld[xx][yy];
6068
6069     if (IS_BELT_SWITCH(element))
6070     {
6071       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6072
6073       if (e_belt_nr == belt_nr)
6074       {
6075         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6076         DrawLevelField(xx, yy);
6077       }
6078     }
6079     else if (IS_BELT(element) && belt_dir != MV_NONE)
6080     {
6081       int e_belt_nr = getBeltNrFromBeltElement(element);
6082
6083       if (e_belt_nr == belt_nr)
6084       {
6085         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6086
6087         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6088         DrawLevelField(xx, yy);
6089       }
6090     }
6091     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6092     {
6093       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6094
6095       if (e_belt_nr == belt_nr)
6096       {
6097         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6098
6099         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6100         DrawLevelField(xx, yy);
6101       }
6102     }
6103   }
6104 }
6105
6106 static void ToggleSwitchgateSwitch(int x, int y)
6107 {
6108   int xx, yy;
6109
6110   game.switchgate_pos = !game.switchgate_pos;
6111
6112   SCAN_PLAYFIELD(xx, yy)
6113   {
6114     int element = Feld[xx][yy];
6115
6116 #if !USE_BOTH_SWITCHGATE_SWITCHES
6117     if (element == EL_SWITCHGATE_SWITCH_UP ||
6118         element == EL_SWITCHGATE_SWITCH_DOWN)
6119     {
6120       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6121       DrawLevelField(xx, yy);
6122     }
6123     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6124              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6125     {
6126       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6127       DrawLevelField(xx, yy);
6128     }
6129 #else
6130     if (element == EL_SWITCHGATE_SWITCH_UP)
6131     {
6132       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6133       DrawLevelField(xx, yy);
6134     }
6135     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6136     {
6137       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6138       DrawLevelField(xx, yy);
6139     }
6140     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6141     {
6142       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6143       DrawLevelField(xx, yy);
6144     }
6145     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6146     {
6147       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6148       DrawLevelField(xx, yy);
6149     }
6150 #endif
6151     else if (element == EL_SWITCHGATE_OPEN ||
6152              element == EL_SWITCHGATE_OPENING)
6153     {
6154       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6155
6156       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6157     }
6158     else if (element == EL_SWITCHGATE_CLOSED ||
6159              element == EL_SWITCHGATE_CLOSING)
6160     {
6161       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6162
6163       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6164     }
6165   }
6166 }
6167
6168 static int getInvisibleActiveFromInvisibleElement(int element)
6169 {
6170   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6171           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6172           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6173           element);
6174 }
6175
6176 static int getInvisibleFromInvisibleActiveElement(int element)
6177 {
6178   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6179           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6180           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6181           element);
6182 }
6183
6184 static void RedrawAllLightSwitchesAndInvisibleElements()
6185 {
6186   int x, y;
6187
6188   SCAN_PLAYFIELD(x, y)
6189   {
6190     int element = Feld[x][y];
6191
6192     if (element == EL_LIGHT_SWITCH &&
6193         game.light_time_left > 0)
6194     {
6195       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6196       DrawLevelField(x, y);
6197     }
6198     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6199              game.light_time_left == 0)
6200     {
6201       Feld[x][y] = EL_LIGHT_SWITCH;
6202       DrawLevelField(x, y);
6203     }
6204     else if (element == EL_EMC_DRIPPER &&
6205              game.light_time_left > 0)
6206     {
6207       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6208       DrawLevelField(x, y);
6209     }
6210     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6211              game.light_time_left == 0)
6212     {
6213       Feld[x][y] = EL_EMC_DRIPPER;
6214       DrawLevelField(x, y);
6215     }
6216     else if (element == EL_INVISIBLE_STEELWALL ||
6217              element == EL_INVISIBLE_WALL ||
6218              element == EL_INVISIBLE_SAND)
6219     {
6220       if (game.light_time_left > 0)
6221         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6222
6223       DrawLevelField(x, y);
6224
6225       /* uncrumble neighbour fields, if needed */
6226       if (element == EL_INVISIBLE_SAND)
6227         DrawLevelFieldCrumbledSandNeighbours(x, y);
6228     }
6229     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6230              element == EL_INVISIBLE_WALL_ACTIVE ||
6231              element == EL_INVISIBLE_SAND_ACTIVE)
6232     {
6233       if (game.light_time_left == 0)
6234         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6235
6236       DrawLevelField(x, y);
6237
6238       /* re-crumble neighbour fields, if needed */
6239       if (element == EL_INVISIBLE_SAND)
6240         DrawLevelFieldCrumbledSandNeighbours(x, y);
6241     }
6242   }
6243 }
6244
6245 static void RedrawAllInvisibleElementsForLenses()
6246 {
6247   int x, y;
6248
6249   SCAN_PLAYFIELD(x, y)
6250   {
6251     int element = Feld[x][y];
6252
6253     if (element == EL_EMC_DRIPPER &&
6254         game.lenses_time_left > 0)
6255     {
6256       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6257       DrawLevelField(x, y);
6258     }
6259     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6260              game.lenses_time_left == 0)
6261     {
6262       Feld[x][y] = EL_EMC_DRIPPER;
6263       DrawLevelField(x, y);
6264     }
6265     else if (element == EL_INVISIBLE_STEELWALL ||
6266              element == EL_INVISIBLE_WALL ||
6267              element == EL_INVISIBLE_SAND)
6268     {
6269       if (game.lenses_time_left > 0)
6270         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6271
6272       DrawLevelField(x, y);
6273
6274       /* uncrumble neighbour fields, if needed */
6275       if (element == EL_INVISIBLE_SAND)
6276         DrawLevelFieldCrumbledSandNeighbours(x, y);
6277     }
6278     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6279              element == EL_INVISIBLE_WALL_ACTIVE ||
6280              element == EL_INVISIBLE_SAND_ACTIVE)
6281     {
6282       if (game.lenses_time_left == 0)
6283         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6284
6285       DrawLevelField(x, y);
6286
6287       /* re-crumble neighbour fields, if needed */
6288       if (element == EL_INVISIBLE_SAND)
6289         DrawLevelFieldCrumbledSandNeighbours(x, y);
6290     }
6291   }
6292 }
6293
6294 static void RedrawAllInvisibleElementsForMagnifier()
6295 {
6296   int x, y;
6297
6298   SCAN_PLAYFIELD(x, y)
6299   {
6300     int element = Feld[x][y];
6301
6302     if (element == EL_EMC_FAKE_GRASS &&
6303         game.magnify_time_left > 0)
6304     {
6305       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6306       DrawLevelField(x, y);
6307     }
6308     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6309              game.magnify_time_left == 0)
6310     {
6311       Feld[x][y] = EL_EMC_FAKE_GRASS;
6312       DrawLevelField(x, y);
6313     }
6314     else if (IS_GATE_GRAY(element) &&
6315              game.magnify_time_left > 0)
6316     {
6317       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6318                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6319                     IS_EM_GATE_GRAY(element) ?
6320                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6321                     IS_EMC_GATE_GRAY(element) ?
6322                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6323                     element);
6324       DrawLevelField(x, y);
6325     }
6326     else if (IS_GATE_GRAY_ACTIVE(element) &&
6327              game.magnify_time_left == 0)
6328     {
6329       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6330                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6331                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6332                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6333                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6334                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6335                     element);
6336       DrawLevelField(x, y);
6337     }
6338   }
6339 }
6340
6341 static void ToggleLightSwitch(int x, int y)
6342 {
6343   int element = Feld[x][y];
6344
6345   game.light_time_left =
6346     (element == EL_LIGHT_SWITCH ?
6347      level.time_light * FRAMES_PER_SECOND : 0);
6348
6349   RedrawAllLightSwitchesAndInvisibleElements();
6350 }
6351
6352 static void ActivateTimegateSwitch(int x, int y)
6353 {
6354   int xx, yy;
6355
6356   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6357
6358   SCAN_PLAYFIELD(xx, yy)
6359   {
6360     int element = Feld[xx][yy];
6361
6362     if (element == EL_TIMEGATE_CLOSED ||
6363         element == EL_TIMEGATE_CLOSING)
6364     {
6365       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6366       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6367     }
6368
6369     /*
6370     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6371     {
6372       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6373       DrawLevelField(xx, yy);
6374     }
6375     */
6376
6377   }
6378
6379 #if 1
6380   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6381                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6382 #else
6383   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6384 #endif
6385 }
6386
6387 void Impact(int x, int y)
6388 {
6389   boolean last_line = (y == lev_fieldy - 1);
6390   boolean object_hit = FALSE;
6391   boolean impact = (last_line || object_hit);
6392   int element = Feld[x][y];
6393   int smashed = EL_STEELWALL;
6394
6395   if (!last_line)       /* check if element below was hit */
6396   {
6397     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6398       return;
6399
6400     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6401                                          MovDir[x][y + 1] != MV_DOWN ||
6402                                          MovPos[x][y + 1] <= TILEY / 2));
6403
6404     /* do not smash moving elements that left the smashed field in time */
6405     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6406         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6407       object_hit = FALSE;
6408
6409 #if USE_QUICKSAND_IMPACT_BUGFIX
6410     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6411     {
6412       RemoveMovingField(x, y + 1);
6413       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6414       Feld[x][y + 2] = EL_ROCK;
6415       DrawLevelField(x, y + 2);
6416
6417       object_hit = TRUE;
6418     }
6419
6420     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6421     {
6422       RemoveMovingField(x, y + 1);
6423       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6424       Feld[x][y + 2] = EL_ROCK;
6425       DrawLevelField(x, y + 2);
6426
6427       object_hit = TRUE;
6428     }
6429 #endif
6430
6431     if (object_hit)
6432       smashed = MovingOrBlocked2Element(x, y + 1);
6433
6434     impact = (last_line || object_hit);
6435   }
6436
6437   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6438   {
6439     SplashAcid(x, y + 1);
6440     return;
6441   }
6442
6443   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6444   /* only reset graphic animation if graphic really changes after impact */
6445   if (impact &&
6446       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6447   {
6448     ResetGfxAnimation(x, y);
6449     DrawLevelField(x, y);
6450   }
6451
6452   if (impact && CAN_EXPLODE_IMPACT(element))
6453   {
6454     Bang(x, y);
6455     return;
6456   }
6457   else if (impact && element == EL_PEARL &&
6458            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6459   {
6460     ResetGfxAnimation(x, y);
6461
6462     Feld[x][y] = EL_PEARL_BREAKING;
6463     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6464     return;
6465   }
6466   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6467   {
6468     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6469
6470     return;
6471   }
6472
6473   if (impact && element == EL_AMOEBA_DROP)
6474   {
6475     if (object_hit && IS_PLAYER(x, y + 1))
6476       KillPlayerUnlessEnemyProtected(x, y + 1);
6477     else if (object_hit && smashed == EL_PENGUIN)
6478       Bang(x, y + 1);
6479     else
6480     {
6481       Feld[x][y] = EL_AMOEBA_GROWING;
6482       Store[x][y] = EL_AMOEBA_WET;
6483
6484       ResetRandomAnimationValue(x, y);
6485     }
6486     return;
6487   }
6488
6489   if (object_hit)               /* check which object was hit */
6490   {
6491     if ((CAN_PASS_MAGIC_WALL(element) && 
6492          (smashed == EL_MAGIC_WALL ||
6493           smashed == EL_BD_MAGIC_WALL)) ||
6494         (CAN_PASS_DC_MAGIC_WALL(element) &&
6495          smashed == EL_DC_MAGIC_WALL))
6496     {
6497       int xx, yy;
6498       int activated_magic_wall =
6499         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6500          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6501          EL_DC_MAGIC_WALL_ACTIVE);
6502
6503       /* activate magic wall / mill */
6504       SCAN_PLAYFIELD(xx, yy)
6505       {
6506         if (Feld[xx][yy] == smashed)
6507           Feld[xx][yy] = activated_magic_wall;
6508       }
6509
6510       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6511       game.magic_wall_active = TRUE;
6512
6513       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6514                             SND_MAGIC_WALL_ACTIVATING :
6515                             smashed == EL_BD_MAGIC_WALL ?
6516                             SND_BD_MAGIC_WALL_ACTIVATING :
6517                             SND_DC_MAGIC_WALL_ACTIVATING));
6518     }
6519
6520     if (IS_PLAYER(x, y + 1))
6521     {
6522       if (CAN_SMASH_PLAYER(element))
6523       {
6524         KillPlayerUnlessEnemyProtected(x, y + 1);
6525         return;
6526       }
6527     }
6528     else if (smashed == EL_PENGUIN)
6529     {
6530       if (CAN_SMASH_PLAYER(element))
6531       {
6532         Bang(x, y + 1);
6533         return;
6534       }
6535     }
6536     else if (element == EL_BD_DIAMOND)
6537     {
6538       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6539       {
6540         Bang(x, y + 1);
6541         return;
6542       }
6543     }
6544     else if (((element == EL_SP_INFOTRON ||
6545                element == EL_SP_ZONK) &&
6546               (smashed == EL_SP_SNIKSNAK ||
6547                smashed == EL_SP_ELECTRON ||
6548                smashed == EL_SP_DISK_ORANGE)) ||
6549              (element == EL_SP_INFOTRON &&
6550               smashed == EL_SP_DISK_YELLOW))
6551     {
6552       Bang(x, y + 1);
6553       return;
6554     }
6555     else if (CAN_SMASH_EVERYTHING(element))
6556     {
6557       if (IS_CLASSIC_ENEMY(smashed) ||
6558           CAN_EXPLODE_SMASHED(smashed))
6559       {
6560         Bang(x, y + 1);
6561         return;
6562       }
6563       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6564       {
6565         if (smashed == EL_LAMP ||
6566             smashed == EL_LAMP_ACTIVE)
6567         {
6568           Bang(x, y + 1);
6569           return;
6570         }
6571         else if (smashed == EL_NUT)
6572         {
6573           Feld[x][y + 1] = EL_NUT_BREAKING;
6574           PlayLevelSound(x, y, SND_NUT_BREAKING);
6575           RaiseScoreElement(EL_NUT);
6576           return;
6577         }
6578         else if (smashed == EL_PEARL)
6579         {
6580           ResetGfxAnimation(x, y);
6581
6582           Feld[x][y + 1] = EL_PEARL_BREAKING;
6583           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6584           return;
6585         }
6586         else if (smashed == EL_DIAMOND)
6587         {
6588           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6589           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6590           return;
6591         }
6592         else if (IS_BELT_SWITCH(smashed))
6593         {
6594           ToggleBeltSwitch(x, y + 1);
6595         }
6596         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6597                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6598                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6599                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6600         {
6601           ToggleSwitchgateSwitch(x, y + 1);
6602         }
6603         else if (smashed == EL_LIGHT_SWITCH ||
6604                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6605         {
6606           ToggleLightSwitch(x, y + 1);
6607         }
6608         else
6609         {
6610 #if 0
6611           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6612 #endif
6613
6614           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6615
6616           CheckElementChangeBySide(x, y + 1, smashed, element,
6617                                    CE_SWITCHED, CH_SIDE_TOP);
6618           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6619                                             CH_SIDE_TOP);
6620         }
6621       }
6622       else
6623       {
6624         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6625       }
6626     }
6627   }
6628
6629   /* play sound of magic wall / mill */
6630   if (!last_line &&
6631       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6632        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6633        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6634   {
6635     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6636       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6637     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6638       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6639     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6640       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6641
6642     return;
6643   }
6644
6645   /* play sound of object that hits the ground */
6646   if (last_line || object_hit)
6647     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6648 }
6649
6650 inline static void TurnRoundExt(int x, int y)
6651 {
6652   static struct
6653   {
6654     int dx, dy;
6655   } move_xy[] =
6656   {
6657     {  0,  0 },
6658     { -1,  0 },
6659     { +1,  0 },
6660     {  0,  0 },
6661     {  0, -1 },
6662     {  0,  0 }, { 0, 0 }, { 0, 0 },
6663     {  0, +1 }
6664   };
6665   static struct
6666   {
6667     int left, right, back;
6668   } turn[] =
6669   {
6670     { 0,        0,              0        },
6671     { MV_DOWN,  MV_UP,          MV_RIGHT },
6672     { MV_UP,    MV_DOWN,        MV_LEFT  },
6673     { 0,        0,              0        },
6674     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6675     { 0,        0,              0        },
6676     { 0,        0,              0        },
6677     { 0,        0,              0        },
6678     { MV_RIGHT, MV_LEFT,        MV_UP    }
6679   };
6680
6681   int element = Feld[x][y];
6682   int move_pattern = element_info[element].move_pattern;
6683
6684   int old_move_dir = MovDir[x][y];
6685   int left_dir  = turn[old_move_dir].left;
6686   int right_dir = turn[old_move_dir].right;
6687   int back_dir  = turn[old_move_dir].back;
6688
6689   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6690   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6691   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6692   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6693
6694   int left_x  = x + left_dx,  left_y  = y + left_dy;
6695   int right_x = x + right_dx, right_y = y + right_dy;
6696   int move_x  = x + move_dx,  move_y  = y + move_dy;
6697
6698   int xx, yy;
6699
6700   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6701   {
6702     TestIfBadThingTouchesOtherBadThing(x, y);
6703
6704     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6705       MovDir[x][y] = right_dir;
6706     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6707       MovDir[x][y] = left_dir;
6708
6709     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6710       MovDelay[x][y] = 9;
6711     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6712       MovDelay[x][y] = 1;
6713   }
6714   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6715   {
6716     TestIfBadThingTouchesOtherBadThing(x, y);
6717
6718     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6719       MovDir[x][y] = left_dir;
6720     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6721       MovDir[x][y] = right_dir;
6722
6723     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6724       MovDelay[x][y] = 9;
6725     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6726       MovDelay[x][y] = 1;
6727   }
6728   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6729   {
6730     TestIfBadThingTouchesOtherBadThing(x, y);
6731
6732     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6733       MovDir[x][y] = left_dir;
6734     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6735       MovDir[x][y] = right_dir;
6736
6737     if (MovDir[x][y] != old_move_dir)
6738       MovDelay[x][y] = 9;
6739   }
6740   else if (element == EL_YAMYAM)
6741   {
6742     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6743     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6744
6745     if (can_turn_left && can_turn_right)
6746       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6747     else if (can_turn_left)
6748       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6749     else if (can_turn_right)
6750       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6751     else
6752       MovDir[x][y] = back_dir;
6753
6754     MovDelay[x][y] = 16 + 16 * RND(3);
6755   }
6756   else if (element == EL_DARK_YAMYAM)
6757   {
6758     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6759                                                          left_x, left_y);
6760     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6761                                                          right_x, right_y);
6762
6763     if (can_turn_left && can_turn_right)
6764       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6765     else if (can_turn_left)
6766       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6767     else if (can_turn_right)
6768       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6769     else
6770       MovDir[x][y] = back_dir;
6771
6772     MovDelay[x][y] = 16 + 16 * RND(3);
6773   }
6774   else if (element == EL_PACMAN)
6775   {
6776     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6777     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6778
6779     if (can_turn_left && can_turn_right)
6780       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6781     else if (can_turn_left)
6782       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6783     else if (can_turn_right)
6784       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6785     else
6786       MovDir[x][y] = back_dir;
6787
6788     MovDelay[x][y] = 6 + RND(40);
6789   }
6790   else if (element == EL_PIG)
6791   {
6792     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6793     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6794     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6795     boolean should_turn_left, should_turn_right, should_move_on;
6796     int rnd_value = 24;
6797     int rnd = RND(rnd_value);
6798
6799     should_turn_left = (can_turn_left &&
6800                         (!can_move_on ||
6801                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6802                                                    y + back_dy + left_dy)));
6803     should_turn_right = (can_turn_right &&
6804                          (!can_move_on ||
6805                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6806                                                     y + back_dy + right_dy)));
6807     should_move_on = (can_move_on &&
6808                       (!can_turn_left ||
6809                        !can_turn_right ||
6810                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6811                                                  y + move_dy + left_dy) ||
6812                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6813                                                  y + move_dy + right_dy)));
6814
6815     if (should_turn_left || should_turn_right || should_move_on)
6816     {
6817       if (should_turn_left && should_turn_right && should_move_on)
6818         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6819                         rnd < 2 * rnd_value / 3 ? right_dir :
6820                         old_move_dir);
6821       else if (should_turn_left && should_turn_right)
6822         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6823       else if (should_turn_left && should_move_on)
6824         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6825       else if (should_turn_right && should_move_on)
6826         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6827       else if (should_turn_left)
6828         MovDir[x][y] = left_dir;
6829       else if (should_turn_right)
6830         MovDir[x][y] = right_dir;
6831       else if (should_move_on)
6832         MovDir[x][y] = old_move_dir;
6833     }
6834     else if (can_move_on && rnd > rnd_value / 8)
6835       MovDir[x][y] = old_move_dir;
6836     else if (can_turn_left && can_turn_right)
6837       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6838     else if (can_turn_left && rnd > rnd_value / 8)
6839       MovDir[x][y] = left_dir;
6840     else if (can_turn_right && rnd > rnd_value/8)
6841       MovDir[x][y] = right_dir;
6842     else
6843       MovDir[x][y] = back_dir;
6844
6845     xx = x + move_xy[MovDir[x][y]].dx;
6846     yy = y + move_xy[MovDir[x][y]].dy;
6847
6848     if (!IN_LEV_FIELD(xx, yy) ||
6849         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6850       MovDir[x][y] = old_move_dir;
6851
6852     MovDelay[x][y] = 0;
6853   }
6854   else if (element == EL_DRAGON)
6855   {
6856     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6857     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6858     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6859     int rnd_value = 24;
6860     int rnd = RND(rnd_value);
6861
6862     if (can_move_on && rnd > rnd_value / 8)
6863       MovDir[x][y] = old_move_dir;
6864     else if (can_turn_left && can_turn_right)
6865       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6866     else if (can_turn_left && rnd > rnd_value / 8)
6867       MovDir[x][y] = left_dir;
6868     else if (can_turn_right && rnd > rnd_value / 8)
6869       MovDir[x][y] = right_dir;
6870     else
6871       MovDir[x][y] = back_dir;
6872
6873     xx = x + move_xy[MovDir[x][y]].dx;
6874     yy = y + move_xy[MovDir[x][y]].dy;
6875
6876     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6877       MovDir[x][y] = old_move_dir;
6878
6879     MovDelay[x][y] = 0;
6880   }
6881   else if (element == EL_MOLE)
6882   {
6883     boolean can_move_on =
6884       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6885                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6886                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6887     if (!can_move_on)
6888     {
6889       boolean can_turn_left =
6890         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6891                               IS_AMOEBOID(Feld[left_x][left_y])));
6892
6893       boolean can_turn_right =
6894         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6895                               IS_AMOEBOID(Feld[right_x][right_y])));
6896
6897       if (can_turn_left && can_turn_right)
6898         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6899       else if (can_turn_left)
6900         MovDir[x][y] = left_dir;
6901       else
6902         MovDir[x][y] = right_dir;
6903     }
6904
6905     if (MovDir[x][y] != old_move_dir)
6906       MovDelay[x][y] = 9;
6907   }
6908   else if (element == EL_BALLOON)
6909   {
6910     MovDir[x][y] = game.wind_direction;
6911     MovDelay[x][y] = 0;
6912   }
6913   else if (element == EL_SPRING)
6914   {
6915 #if USE_NEW_SPRING_BUMPER
6916     if (MovDir[x][y] & MV_HORIZONTAL)
6917     {
6918       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6919           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6920       {
6921         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6922         ResetGfxAnimation(move_x, move_y);
6923         DrawLevelField(move_x, move_y);
6924
6925         MovDir[x][y] = back_dir;
6926       }
6927       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6928                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6929         MovDir[x][y] = MV_NONE;
6930     }
6931 #else
6932     if (MovDir[x][y] & MV_HORIZONTAL &&
6933         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6934          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6935       MovDir[x][y] = MV_NONE;
6936 #endif
6937
6938     MovDelay[x][y] = 0;
6939   }
6940   else if (element == EL_ROBOT ||
6941            element == EL_SATELLITE ||
6942            element == EL_PENGUIN ||
6943            element == EL_EMC_ANDROID)
6944   {
6945     int attr_x = -1, attr_y = -1;
6946
6947     if (AllPlayersGone)
6948     {
6949       attr_x = ExitX;
6950       attr_y = ExitY;
6951     }
6952     else
6953     {
6954       int i;
6955
6956       for (i = 0; i < MAX_PLAYERS; i++)
6957       {
6958         struct PlayerInfo *player = &stored_player[i];
6959         int jx = player->jx, jy = player->jy;
6960
6961         if (!player->active)
6962           continue;
6963
6964         if (attr_x == -1 ||
6965             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6966         {
6967           attr_x = jx;
6968           attr_y = jy;
6969         }
6970       }
6971     }
6972
6973     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6974         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6975          game.engine_version < VERSION_IDENT(3,1,0,0)))
6976     {
6977       attr_x = ZX;
6978       attr_y = ZY;
6979     }
6980
6981     if (element == EL_PENGUIN)
6982     {
6983       int i;
6984       static int xy[4][2] =
6985       {
6986         { 0, -1 },
6987         { -1, 0 },
6988         { +1, 0 },
6989         { 0, +1 }
6990       };
6991
6992       for (i = 0; i < NUM_DIRECTIONS; i++)
6993       {
6994         int ex = x + xy[i][0];
6995         int ey = y + xy[i][1];
6996
6997         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6998                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6999                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7000                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7001         {
7002           attr_x = ex;
7003           attr_y = ey;
7004           break;
7005         }
7006       }
7007     }
7008
7009     MovDir[x][y] = MV_NONE;
7010     if (attr_x < x)
7011       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7012     else if (attr_x > x)
7013       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7014     if (attr_y < y)
7015       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7016     else if (attr_y > y)
7017       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7018
7019     if (element == EL_ROBOT)
7020     {
7021       int newx, newy;
7022
7023       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7024         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7025       Moving2Blocked(x, y, &newx, &newy);
7026
7027       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7028         MovDelay[x][y] = 8 + 8 * !RND(3);
7029       else
7030         MovDelay[x][y] = 16;
7031     }
7032     else if (element == EL_PENGUIN)
7033     {
7034       int newx, newy;
7035
7036       MovDelay[x][y] = 1;
7037
7038       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7039       {
7040         boolean first_horiz = RND(2);
7041         int new_move_dir = MovDir[x][y];
7042
7043         MovDir[x][y] =
7044           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7045         Moving2Blocked(x, y, &newx, &newy);
7046
7047         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7048           return;
7049
7050         MovDir[x][y] =
7051           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7052         Moving2Blocked(x, y, &newx, &newy);
7053
7054         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7055           return;
7056
7057         MovDir[x][y] = old_move_dir;
7058         return;
7059       }
7060     }
7061     else if (element == EL_SATELLITE)
7062     {
7063       int newx, newy;
7064
7065       MovDelay[x][y] = 1;
7066
7067       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7068       {
7069         boolean first_horiz = RND(2);
7070         int new_move_dir = MovDir[x][y];
7071
7072         MovDir[x][y] =
7073           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7074         Moving2Blocked(x, y, &newx, &newy);
7075
7076         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7077           return;
7078
7079         MovDir[x][y] =
7080           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7081         Moving2Blocked(x, y, &newx, &newy);
7082
7083         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7084           return;
7085
7086         MovDir[x][y] = old_move_dir;
7087         return;
7088       }
7089     }
7090     else if (element == EL_EMC_ANDROID)
7091     {
7092       static int check_pos[16] =
7093       {
7094         -1,             /*  0 => (invalid)          */
7095         7,              /*  1 => MV_LEFT            */
7096         3,              /*  2 => MV_RIGHT           */
7097         -1,             /*  3 => (invalid)          */
7098         1,              /*  4 =>            MV_UP   */
7099         0,              /*  5 => MV_LEFT  | MV_UP   */
7100         2,              /*  6 => MV_RIGHT | MV_UP   */
7101         -1,             /*  7 => (invalid)          */
7102         5,              /*  8 =>            MV_DOWN */
7103         6,              /*  9 => MV_LEFT  | MV_DOWN */
7104         4,              /* 10 => MV_RIGHT | MV_DOWN */
7105         -1,             /* 11 => (invalid)          */
7106         -1,             /* 12 => (invalid)          */
7107         -1,             /* 13 => (invalid)          */
7108         -1,             /* 14 => (invalid)          */
7109         -1,             /* 15 => (invalid)          */
7110       };
7111       static struct
7112       {
7113         int dx, dy;
7114         int dir;
7115       } check_xy[8] =
7116       {
7117         { -1, -1,       MV_LEFT  | MV_UP   },
7118         {  0, -1,                  MV_UP   },
7119         { +1, -1,       MV_RIGHT | MV_UP   },
7120         { +1,  0,       MV_RIGHT           },
7121         { +1, +1,       MV_RIGHT | MV_DOWN },
7122         {  0, +1,                  MV_DOWN },
7123         { -1, +1,       MV_LEFT  | MV_DOWN },
7124         { -1,  0,       MV_LEFT            },
7125       };
7126       int start_pos, check_order;
7127       boolean can_clone = FALSE;
7128       int i;
7129
7130       /* check if there is any free field around current position */
7131       for (i = 0; i < 8; i++)
7132       {
7133         int newx = x + check_xy[i].dx;
7134         int newy = y + check_xy[i].dy;
7135
7136         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7137         {
7138           can_clone = TRUE;
7139
7140           break;
7141         }
7142       }
7143
7144       if (can_clone)            /* randomly find an element to clone */
7145       {
7146         can_clone = FALSE;
7147
7148         start_pos = check_pos[RND(8)];
7149         check_order = (RND(2) ? -1 : +1);
7150
7151         for (i = 0; i < 8; i++)
7152         {
7153           int pos_raw = start_pos + i * check_order;
7154           int pos = (pos_raw + 8) % 8;
7155           int newx = x + check_xy[pos].dx;
7156           int newy = y + check_xy[pos].dy;
7157
7158           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7159           {
7160             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7161             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7162
7163             Store[x][y] = Feld[newx][newy];
7164
7165             can_clone = TRUE;
7166
7167             break;
7168           }
7169         }
7170       }
7171
7172       if (can_clone)            /* randomly find a direction to move */
7173       {
7174         can_clone = FALSE;
7175
7176         start_pos = check_pos[RND(8)];
7177         check_order = (RND(2) ? -1 : +1);
7178
7179         for (i = 0; i < 8; i++)
7180         {
7181           int pos_raw = start_pos + i * check_order;
7182           int pos = (pos_raw + 8) % 8;
7183           int newx = x + check_xy[pos].dx;
7184           int newy = y + check_xy[pos].dy;
7185           int new_move_dir = check_xy[pos].dir;
7186
7187           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7188           {
7189             MovDir[x][y] = new_move_dir;
7190             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7191
7192             can_clone = TRUE;
7193
7194             break;
7195           }
7196         }
7197       }
7198
7199       if (can_clone)            /* cloning and moving successful */
7200         return;
7201
7202       /* cannot clone -- try to move towards player */
7203
7204       start_pos = check_pos[MovDir[x][y] & 0x0f];
7205       check_order = (RND(2) ? -1 : +1);
7206
7207       for (i = 0; i < 3; i++)
7208       {
7209         /* first check start_pos, then previous/next or (next/previous) pos */
7210         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7211         int pos = (pos_raw + 8) % 8;
7212         int newx = x + check_xy[pos].dx;
7213         int newy = y + check_xy[pos].dy;
7214         int new_move_dir = check_xy[pos].dir;
7215
7216         if (IS_PLAYER(newx, newy))
7217           break;
7218
7219         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7220         {
7221           MovDir[x][y] = new_move_dir;
7222           MovDelay[x][y] = level.android_move_time * 8 + 1;
7223
7224           break;
7225         }
7226       }
7227     }
7228   }
7229   else if (move_pattern == MV_TURNING_LEFT ||
7230            move_pattern == MV_TURNING_RIGHT ||
7231            move_pattern == MV_TURNING_LEFT_RIGHT ||
7232            move_pattern == MV_TURNING_RIGHT_LEFT ||
7233            move_pattern == MV_TURNING_RANDOM ||
7234            move_pattern == MV_ALL_DIRECTIONS)
7235   {
7236     boolean can_turn_left =
7237       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7238     boolean can_turn_right =
7239       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7240
7241     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7242       return;
7243
7244     if (move_pattern == MV_TURNING_LEFT)
7245       MovDir[x][y] = left_dir;
7246     else if (move_pattern == MV_TURNING_RIGHT)
7247       MovDir[x][y] = right_dir;
7248     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7249       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7250     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7251       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7252     else if (move_pattern == MV_TURNING_RANDOM)
7253       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7254                       can_turn_right && !can_turn_left ? right_dir :
7255                       RND(2) ? left_dir : right_dir);
7256     else if (can_turn_left && can_turn_right)
7257       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7258     else if (can_turn_left)
7259       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7260     else if (can_turn_right)
7261       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7262     else
7263       MovDir[x][y] = back_dir;
7264
7265     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7266   }
7267   else if (move_pattern == MV_HORIZONTAL ||
7268            move_pattern == MV_VERTICAL)
7269   {
7270     if (move_pattern & old_move_dir)
7271       MovDir[x][y] = back_dir;
7272     else if (move_pattern == MV_HORIZONTAL)
7273       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7274     else if (move_pattern == MV_VERTICAL)
7275       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7276
7277     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7278   }
7279   else if (move_pattern & MV_ANY_DIRECTION)
7280   {
7281     MovDir[x][y] = move_pattern;
7282     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7283   }
7284   else if (move_pattern & MV_WIND_DIRECTION)
7285   {
7286     MovDir[x][y] = game.wind_direction;
7287     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7288   }
7289   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7290   {
7291     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7292       MovDir[x][y] = left_dir;
7293     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7294       MovDir[x][y] = right_dir;
7295
7296     if (MovDir[x][y] != old_move_dir)
7297       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7298   }
7299   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7300   {
7301     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7302       MovDir[x][y] = right_dir;
7303     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7304       MovDir[x][y] = left_dir;
7305
7306     if (MovDir[x][y] != old_move_dir)
7307       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7308   }
7309   else if (move_pattern == MV_TOWARDS_PLAYER ||
7310            move_pattern == MV_AWAY_FROM_PLAYER)
7311   {
7312     int attr_x = -1, attr_y = -1;
7313     int newx, newy;
7314     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7315
7316     if (AllPlayersGone)
7317     {
7318       attr_x = ExitX;
7319       attr_y = ExitY;
7320     }
7321     else
7322     {
7323       int i;
7324
7325       for (i = 0; i < MAX_PLAYERS; i++)
7326       {
7327         struct PlayerInfo *player = &stored_player[i];
7328         int jx = player->jx, jy = player->jy;
7329
7330         if (!player->active)
7331           continue;
7332
7333         if (attr_x == -1 ||
7334             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7335         {
7336           attr_x = jx;
7337           attr_y = jy;
7338         }
7339       }
7340     }
7341
7342     MovDir[x][y] = MV_NONE;
7343     if (attr_x < x)
7344       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7345     else if (attr_x > x)
7346       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7347     if (attr_y < y)
7348       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7349     else if (attr_y > y)
7350       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7351
7352     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7353
7354     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7355     {
7356       boolean first_horiz = RND(2);
7357       int new_move_dir = MovDir[x][y];
7358
7359       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7360       {
7361         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7362         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7363
7364         return;
7365       }
7366
7367       MovDir[x][y] =
7368         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7369       Moving2Blocked(x, y, &newx, &newy);
7370
7371       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7372         return;
7373
7374       MovDir[x][y] =
7375         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7376       Moving2Blocked(x, y, &newx, &newy);
7377
7378       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7379         return;
7380
7381       MovDir[x][y] = old_move_dir;
7382     }
7383   }
7384   else if (move_pattern == MV_WHEN_PUSHED ||
7385            move_pattern == MV_WHEN_DROPPED)
7386   {
7387     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7388       MovDir[x][y] = MV_NONE;
7389
7390     MovDelay[x][y] = 0;
7391   }
7392   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7393   {
7394     static int test_xy[7][2] =
7395     {
7396       { 0, -1 },
7397       { -1, 0 },
7398       { +1, 0 },
7399       { 0, +1 },
7400       { 0, -1 },
7401       { -1, 0 },
7402       { +1, 0 },
7403     };
7404     static int test_dir[7] =
7405     {
7406       MV_UP,
7407       MV_LEFT,
7408       MV_RIGHT,
7409       MV_DOWN,
7410       MV_UP,
7411       MV_LEFT,
7412       MV_RIGHT,
7413     };
7414     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7415     int move_preference = -1000000;     /* start with very low preference */
7416     int new_move_dir = MV_NONE;
7417     int start_test = RND(4);
7418     int i;
7419
7420     for (i = 0; i < NUM_DIRECTIONS; i++)
7421     {
7422       int move_dir = test_dir[start_test + i];
7423       int move_dir_preference;
7424
7425       xx = x + test_xy[start_test + i][0];
7426       yy = y + test_xy[start_test + i][1];
7427
7428       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7429           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7430       {
7431         new_move_dir = move_dir;
7432
7433         break;
7434       }
7435
7436       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7437         continue;
7438
7439       move_dir_preference = -1 * RunnerVisit[xx][yy];
7440       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7441         move_dir_preference = PlayerVisit[xx][yy];
7442
7443       if (move_dir_preference > move_preference)
7444       {
7445         /* prefer field that has not been visited for the longest time */
7446         move_preference = move_dir_preference;
7447         new_move_dir = move_dir;
7448       }
7449       else if (move_dir_preference == move_preference &&
7450                move_dir == old_move_dir)
7451       {
7452         /* prefer last direction when all directions are preferred equally */
7453         move_preference = move_dir_preference;
7454         new_move_dir = move_dir;
7455       }
7456     }
7457
7458     MovDir[x][y] = new_move_dir;
7459     if (old_move_dir != new_move_dir)
7460       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7461   }
7462 }
7463
7464 static void TurnRound(int x, int y)
7465 {
7466   int direction = MovDir[x][y];
7467
7468   TurnRoundExt(x, y);
7469
7470   GfxDir[x][y] = MovDir[x][y];
7471
7472   if (direction != MovDir[x][y])
7473     GfxFrame[x][y] = 0;
7474
7475   if (MovDelay[x][y])
7476     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7477
7478   ResetGfxFrame(x, y, FALSE);
7479 }
7480
7481 static boolean JustBeingPushed(int x, int y)
7482 {
7483   int i;
7484
7485   for (i = 0; i < MAX_PLAYERS; i++)
7486   {
7487     struct PlayerInfo *player = &stored_player[i];
7488
7489     if (player->active && player->is_pushing && player->MovPos)
7490     {
7491       int next_jx = player->jx + (player->jx - player->last_jx);
7492       int next_jy = player->jy + (player->jy - player->last_jy);
7493
7494       if (x == next_jx && y == next_jy)
7495         return TRUE;
7496     }
7497   }
7498
7499   return FALSE;
7500 }
7501
7502 void StartMoving(int x, int y)
7503 {
7504   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7505   int element = Feld[x][y];
7506
7507   if (Stop[x][y])
7508     return;
7509
7510   if (MovDelay[x][y] == 0)
7511     GfxAction[x][y] = ACTION_DEFAULT;
7512
7513   if (CAN_FALL(element) && y < lev_fieldy - 1)
7514   {
7515     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7516         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7517       if (JustBeingPushed(x, y))
7518         return;
7519
7520     if (element == EL_QUICKSAND_FULL)
7521     {
7522       if (IS_FREE(x, y + 1))
7523       {
7524         InitMovingField(x, y, MV_DOWN);
7525         started_moving = TRUE;
7526
7527         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7528 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7529         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7530           Store[x][y] = EL_ROCK;
7531 #else
7532         Store[x][y] = EL_ROCK;
7533 #endif
7534
7535         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7536       }
7537       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7538       {
7539         if (!MovDelay[x][y])
7540           MovDelay[x][y] = TILEY + 1;
7541
7542         if (MovDelay[x][y])
7543         {
7544           MovDelay[x][y]--;
7545           if (MovDelay[x][y])
7546             return;
7547         }
7548
7549         Feld[x][y] = EL_QUICKSAND_EMPTY;
7550         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7551         Store[x][y + 1] = Store[x][y];
7552         Store[x][y] = 0;
7553
7554         PlayLevelSoundAction(x, y, ACTION_FILLING);
7555       }
7556     }
7557     else if (element == EL_QUICKSAND_FAST_FULL)
7558     {
7559       if (IS_FREE(x, y + 1))
7560       {
7561         InitMovingField(x, y, MV_DOWN);
7562         started_moving = TRUE;
7563
7564         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7565 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7566         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7567           Store[x][y] = EL_ROCK;
7568 #else
7569         Store[x][y] = EL_ROCK;
7570 #endif
7571
7572         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7573       }
7574       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7575       {
7576         if (!MovDelay[x][y])
7577           MovDelay[x][y] = TILEY + 1;
7578
7579         if (MovDelay[x][y])
7580         {
7581           MovDelay[x][y]--;
7582           if (MovDelay[x][y])
7583             return;
7584         }
7585
7586         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7587         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7588         Store[x][y + 1] = Store[x][y];
7589         Store[x][y] = 0;
7590
7591         PlayLevelSoundAction(x, y, ACTION_FILLING);
7592       }
7593     }
7594     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7595              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7596     {
7597       InitMovingField(x, y, MV_DOWN);
7598       started_moving = TRUE;
7599
7600       Feld[x][y] = EL_QUICKSAND_FILLING;
7601       Store[x][y] = element;
7602
7603       PlayLevelSoundAction(x, y, ACTION_FILLING);
7604     }
7605     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7606              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7607     {
7608       InitMovingField(x, y, MV_DOWN);
7609       started_moving = TRUE;
7610
7611       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7612       Store[x][y] = element;
7613
7614       PlayLevelSoundAction(x, y, ACTION_FILLING);
7615     }
7616     else if (element == EL_MAGIC_WALL_FULL)
7617     {
7618       if (IS_FREE(x, y + 1))
7619       {
7620         InitMovingField(x, y, MV_DOWN);
7621         started_moving = TRUE;
7622
7623         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7624         Store[x][y] = EL_CHANGED(Store[x][y]);
7625       }
7626       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7627       {
7628         if (!MovDelay[x][y])
7629           MovDelay[x][y] = TILEY/4 + 1;
7630
7631         if (MovDelay[x][y])
7632         {
7633           MovDelay[x][y]--;
7634           if (MovDelay[x][y])
7635             return;
7636         }
7637
7638         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7639         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7640         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7641         Store[x][y] = 0;
7642       }
7643     }
7644     else if (element == EL_BD_MAGIC_WALL_FULL)
7645     {
7646       if (IS_FREE(x, y + 1))
7647       {
7648         InitMovingField(x, y, MV_DOWN);
7649         started_moving = TRUE;
7650
7651         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7652         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7653       }
7654       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7655       {
7656         if (!MovDelay[x][y])
7657           MovDelay[x][y] = TILEY/4 + 1;
7658
7659         if (MovDelay[x][y])
7660         {
7661           MovDelay[x][y]--;
7662           if (MovDelay[x][y])
7663             return;
7664         }
7665
7666         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7667         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7668         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7669         Store[x][y] = 0;
7670       }
7671     }
7672     else if (element == EL_DC_MAGIC_WALL_FULL)
7673     {
7674       if (IS_FREE(x, y + 1))
7675       {
7676         InitMovingField(x, y, MV_DOWN);
7677         started_moving = TRUE;
7678
7679         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7680         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7681       }
7682       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7683       {
7684         if (!MovDelay[x][y])
7685           MovDelay[x][y] = TILEY/4 + 1;
7686
7687         if (MovDelay[x][y])
7688         {
7689           MovDelay[x][y]--;
7690           if (MovDelay[x][y])
7691             return;
7692         }
7693
7694         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7695         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7696         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7697         Store[x][y] = 0;
7698       }
7699     }
7700     else if ((CAN_PASS_MAGIC_WALL(element) &&
7701               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7702                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7703              (CAN_PASS_DC_MAGIC_WALL(element) &&
7704               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7705
7706     {
7707       InitMovingField(x, y, MV_DOWN);
7708       started_moving = TRUE;
7709
7710       Feld[x][y] =
7711         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7712          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7713          EL_DC_MAGIC_WALL_FILLING);
7714       Store[x][y] = element;
7715     }
7716     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7717     {
7718       SplashAcid(x, y + 1);
7719
7720       InitMovingField(x, y, MV_DOWN);
7721       started_moving = TRUE;
7722
7723       Store[x][y] = EL_ACID;
7724     }
7725     else if (
7726 #if USE_FIX_IMPACT_COLLISION
7727              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7728               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7729 #else
7730              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7731               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7732 #endif
7733              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7734               CAN_FALL(element) && WasJustFalling[x][y] &&
7735               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7736
7737              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7738               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7739               (Feld[x][y + 1] == EL_BLOCKED)))
7740     {
7741       /* this is needed for a special case not covered by calling "Impact()"
7742          from "ContinueMoving()": if an element moves to a tile directly below
7743          another element which was just falling on that tile (which was empty
7744          in the previous frame), the falling element above would just stop
7745          instead of smashing the element below (in previous version, the above
7746          element was just checked for "moving" instead of "falling", resulting
7747          in incorrect smashes caused by horizontal movement of the above
7748          element; also, the case of the player being the element to smash was
7749          simply not covered here... :-/ ) */
7750
7751       CheckCollision[x][y] = 0;
7752       CheckImpact[x][y] = 0;
7753
7754       Impact(x, y);
7755     }
7756     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7757     {
7758       if (MovDir[x][y] == MV_NONE)
7759       {
7760         InitMovingField(x, y, MV_DOWN);
7761         started_moving = TRUE;
7762       }
7763     }
7764     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7765     {
7766       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7767         MovDir[x][y] = MV_DOWN;
7768
7769       InitMovingField(x, y, MV_DOWN);
7770       started_moving = TRUE;
7771     }
7772     else if (element == EL_AMOEBA_DROP)
7773     {
7774       Feld[x][y] = EL_AMOEBA_GROWING;
7775       Store[x][y] = EL_AMOEBA_WET;
7776     }
7777     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7778               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7779              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7780              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7781     {
7782       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7783                                 (IS_FREE(x - 1, y + 1) ||
7784                                  Feld[x - 1][y + 1] == EL_ACID));
7785       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7786                                 (IS_FREE(x + 1, y + 1) ||
7787                                  Feld[x + 1][y + 1] == EL_ACID));
7788       boolean can_fall_any  = (can_fall_left || can_fall_right);
7789       boolean can_fall_both = (can_fall_left && can_fall_right);
7790       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7791
7792 #if USE_NEW_ALL_SLIPPERY
7793       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7794       {
7795         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7796           can_fall_right = FALSE;
7797         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7798           can_fall_left = FALSE;
7799         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7800           can_fall_right = FALSE;
7801         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7802           can_fall_left = FALSE;
7803
7804         can_fall_any  = (can_fall_left || can_fall_right);
7805         can_fall_both = FALSE;
7806       }
7807 #else
7808       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7809       {
7810         if (slippery_type == SLIPPERY_ONLY_LEFT)
7811           can_fall_right = FALSE;
7812         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7813           can_fall_left = FALSE;
7814         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7815           can_fall_right = FALSE;
7816         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7817           can_fall_left = FALSE;
7818
7819         can_fall_any  = (can_fall_left || can_fall_right);
7820         can_fall_both = (can_fall_left && can_fall_right);
7821       }
7822 #endif
7823
7824 #if USE_NEW_ALL_SLIPPERY
7825 #else
7826 #if USE_NEW_SP_SLIPPERY
7827       /* !!! better use the same properties as for custom elements here !!! */
7828       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7829                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7830       {
7831         can_fall_right = FALSE;         /* slip down on left side */
7832         can_fall_both = FALSE;
7833       }
7834 #endif
7835 #endif
7836
7837 #if USE_NEW_ALL_SLIPPERY
7838       if (can_fall_both)
7839       {
7840         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7841           can_fall_right = FALSE;       /* slip down on left side */
7842         else
7843           can_fall_left = !(can_fall_right = RND(2));
7844
7845         can_fall_both = FALSE;
7846       }
7847 #else
7848       if (can_fall_both)
7849       {
7850         if (game.emulation == EMU_BOULDERDASH ||
7851             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7852           can_fall_right = FALSE;       /* slip down on left side */
7853         else
7854           can_fall_left = !(can_fall_right = RND(2));
7855
7856         can_fall_both = FALSE;
7857       }
7858 #endif
7859
7860       if (can_fall_any)
7861       {
7862         /* if not determined otherwise, prefer left side for slipping down */
7863         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7864         started_moving = TRUE;
7865       }
7866     }
7867 #if 0
7868     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7869 #else
7870     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7871 #endif
7872     {
7873       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7874       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7875       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7876       int belt_dir = game.belt_dir[belt_nr];
7877
7878       if ((belt_dir == MV_LEFT  && left_is_free) ||
7879           (belt_dir == MV_RIGHT && right_is_free))
7880       {
7881         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7882
7883         InitMovingField(x, y, belt_dir);
7884         started_moving = TRUE;
7885
7886         Pushed[x][y] = TRUE;
7887         Pushed[nextx][y] = TRUE;
7888
7889         GfxAction[x][y] = ACTION_DEFAULT;
7890       }
7891       else
7892       {
7893         MovDir[x][y] = 0;       /* if element was moving, stop it */
7894       }
7895     }
7896   }
7897
7898   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7899 #if 0
7900   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7901 #else
7902   if (CAN_MOVE(element) && !started_moving)
7903 #endif
7904   {
7905     int move_pattern = element_info[element].move_pattern;
7906     int newx, newy;
7907
7908 #if 0
7909 #if DEBUG
7910     if (MovDir[x][y] == MV_NONE)
7911     {
7912       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7913              x, y, element, element_info[element].token_name);
7914       printf("StartMoving(): This should never happen!\n");
7915     }
7916 #endif
7917 #endif
7918
7919     Moving2Blocked(x, y, &newx, &newy);
7920
7921     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7922       return;
7923
7924     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7925         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7926     {
7927       WasJustMoving[x][y] = 0;
7928       CheckCollision[x][y] = 0;
7929
7930       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7931
7932       if (Feld[x][y] != element)        /* element has changed */
7933         return;
7934     }
7935
7936     if (!MovDelay[x][y])        /* start new movement phase */
7937     {
7938       /* all objects that can change their move direction after each step
7939          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7940
7941       if (element != EL_YAMYAM &&
7942           element != EL_DARK_YAMYAM &&
7943           element != EL_PACMAN &&
7944           !(move_pattern & MV_ANY_DIRECTION) &&
7945           move_pattern != MV_TURNING_LEFT &&
7946           move_pattern != MV_TURNING_RIGHT &&
7947           move_pattern != MV_TURNING_LEFT_RIGHT &&
7948           move_pattern != MV_TURNING_RIGHT_LEFT &&
7949           move_pattern != MV_TURNING_RANDOM)
7950       {
7951         TurnRound(x, y);
7952
7953         if (MovDelay[x][y] && (element == EL_BUG ||
7954                                element == EL_SPACESHIP ||
7955                                element == EL_SP_SNIKSNAK ||
7956                                element == EL_SP_ELECTRON ||
7957                                element == EL_MOLE))
7958           DrawLevelField(x, y);
7959       }
7960     }
7961
7962     if (MovDelay[x][y])         /* wait some time before next movement */
7963     {
7964       MovDelay[x][y]--;
7965
7966       if (element == EL_ROBOT ||
7967           element == EL_YAMYAM ||
7968           element == EL_DARK_YAMYAM)
7969       {
7970         DrawLevelElementAnimationIfNeeded(x, y, element);
7971         PlayLevelSoundAction(x, y, ACTION_WAITING);
7972       }
7973       else if (element == EL_SP_ELECTRON)
7974         DrawLevelElementAnimationIfNeeded(x, y, element);
7975       else if (element == EL_DRAGON)
7976       {
7977         int i;
7978         int dir = MovDir[x][y];
7979         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7980         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7981         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7982                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7983                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7984                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7985         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7986
7987         GfxAction[x][y] = ACTION_ATTACKING;
7988
7989         if (IS_PLAYER(x, y))
7990           DrawPlayerField(x, y);
7991         else
7992           DrawLevelField(x, y);
7993
7994         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7995
7996         for (i = 1; i <= 3; i++)
7997         {
7998           int xx = x + i * dx;
7999           int yy = y + i * dy;
8000           int sx = SCREENX(xx);
8001           int sy = SCREENY(yy);
8002           int flame_graphic = graphic + (i - 1);
8003
8004           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8005             break;
8006
8007           if (MovDelay[x][y])
8008           {
8009             int flamed = MovingOrBlocked2Element(xx, yy);
8010
8011             /* !!! */
8012 #if 0
8013             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8014               Bang(xx, yy);
8015             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8016               RemoveMovingField(xx, yy);
8017             else
8018               RemoveField(xx, yy);
8019 #else
8020             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8021               Bang(xx, yy);
8022             else
8023               RemoveMovingField(xx, yy);
8024 #endif
8025
8026             ChangeDelay[xx][yy] = 0;
8027
8028             Feld[xx][yy] = EL_FLAMES;
8029
8030             if (IN_SCR_FIELD(sx, sy))
8031             {
8032               DrawLevelFieldCrumbledSand(xx, yy);
8033               DrawGraphic(sx, sy, flame_graphic, frame);
8034             }
8035           }
8036           else
8037           {
8038             if (Feld[xx][yy] == EL_FLAMES)
8039               Feld[xx][yy] = EL_EMPTY;
8040             DrawLevelField(xx, yy);
8041           }
8042         }
8043       }
8044
8045       if (MovDelay[x][y])       /* element still has to wait some time */
8046       {
8047         PlayLevelSoundAction(x, y, ACTION_WAITING);
8048
8049         return;
8050       }
8051     }
8052
8053     /* now make next step */
8054
8055     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8056
8057     if (DONT_COLLIDE_WITH(element) &&
8058         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8059         !PLAYER_ENEMY_PROTECTED(newx, newy))
8060     {
8061       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8062
8063       return;
8064     }
8065
8066     else if (CAN_MOVE_INTO_ACID(element) &&
8067              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8068              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8069              (MovDir[x][y] == MV_DOWN ||
8070               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8071     {
8072       SplashAcid(newx, newy);
8073       Store[x][y] = EL_ACID;
8074     }
8075     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8076     {
8077       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8078           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8079           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8080           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8081       {
8082         RemoveField(x, y);
8083         DrawLevelField(x, y);
8084
8085         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8086         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8087           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8088
8089         local_player->friends_still_needed--;
8090         if (!local_player->friends_still_needed &&
8091             !local_player->GameOver && AllPlayersGone)
8092           PlayerWins(local_player);
8093
8094         return;
8095       }
8096       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8097       {
8098         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8099           DrawLevelField(newx, newy);
8100         else
8101           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8102       }
8103       else if (!IS_FREE(newx, newy))
8104       {
8105         GfxAction[x][y] = ACTION_WAITING;
8106
8107         if (IS_PLAYER(x, y))
8108           DrawPlayerField(x, y);
8109         else
8110           DrawLevelField(x, y);
8111
8112         return;
8113       }
8114     }
8115     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8116     {
8117       if (IS_FOOD_PIG(Feld[newx][newy]))
8118       {
8119         if (IS_MOVING(newx, newy))
8120           RemoveMovingField(newx, newy);
8121         else
8122         {
8123           Feld[newx][newy] = EL_EMPTY;
8124           DrawLevelField(newx, newy);
8125         }
8126
8127         PlayLevelSound(x, y, SND_PIG_DIGGING);
8128       }
8129       else if (!IS_FREE(newx, newy))
8130       {
8131         if (IS_PLAYER(x, y))
8132           DrawPlayerField(x, y);
8133         else
8134           DrawLevelField(x, y);
8135
8136         return;
8137       }
8138     }
8139     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8140     {
8141       if (Store[x][y] != EL_EMPTY)
8142       {
8143         boolean can_clone = FALSE;
8144         int xx, yy;
8145
8146         /* check if element to clone is still there */
8147         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8148         {
8149           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8150           {
8151             can_clone = TRUE;
8152
8153             break;
8154           }
8155         }
8156
8157         /* cannot clone or target field not free anymore -- do not clone */
8158         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8159           Store[x][y] = EL_EMPTY;
8160       }
8161
8162       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8163       {
8164         if (IS_MV_DIAGONAL(MovDir[x][y]))
8165         {
8166           int diagonal_move_dir = MovDir[x][y];
8167           int stored = Store[x][y];
8168           int change_delay = 8;
8169           int graphic;
8170
8171           /* android is moving diagonally */
8172
8173           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8174
8175           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8176           GfxElement[x][y] = EL_EMC_ANDROID;
8177           GfxAction[x][y] = ACTION_SHRINKING;
8178           GfxDir[x][y] = diagonal_move_dir;
8179           ChangeDelay[x][y] = change_delay;
8180
8181           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8182                                    GfxDir[x][y]);
8183
8184           DrawLevelGraphicAnimation(x, y, graphic);
8185           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8186
8187           if (Feld[newx][newy] == EL_ACID)
8188           {
8189             SplashAcid(newx, newy);
8190
8191             return;
8192           }
8193
8194           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8195
8196           Store[newx][newy] = EL_EMC_ANDROID;
8197           GfxElement[newx][newy] = EL_EMC_ANDROID;
8198           GfxAction[newx][newy] = ACTION_GROWING;
8199           GfxDir[newx][newy] = diagonal_move_dir;
8200           ChangeDelay[newx][newy] = change_delay;
8201
8202           graphic = el_act_dir2img(GfxElement[newx][newy],
8203                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8204
8205           DrawLevelGraphicAnimation(newx, newy, graphic);
8206           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8207
8208           return;
8209         }
8210         else
8211         {
8212           Feld[newx][newy] = EL_EMPTY;
8213           DrawLevelField(newx, newy);
8214
8215           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8216         }
8217       }
8218       else if (!IS_FREE(newx, newy))
8219       {
8220 #if 0
8221         if (IS_PLAYER(x, y))
8222           DrawPlayerField(x, y);
8223         else
8224           DrawLevelField(x, y);
8225 #endif
8226
8227         return;
8228       }
8229     }
8230     else if (IS_CUSTOM_ELEMENT(element) &&
8231              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8232     {
8233       int new_element = Feld[newx][newy];
8234
8235       if (!IS_FREE(newx, newy))
8236       {
8237         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8238                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8239                       ACTION_BREAKING);
8240
8241         /* no element can dig solid indestructible elements */
8242         if (IS_INDESTRUCTIBLE(new_element) &&
8243             !IS_DIGGABLE(new_element) &&
8244             !IS_COLLECTIBLE(new_element))
8245           return;
8246
8247         if (AmoebaNr[newx][newy] &&
8248             (new_element == EL_AMOEBA_FULL ||
8249              new_element == EL_BD_AMOEBA ||
8250              new_element == EL_AMOEBA_GROWING))
8251         {
8252           AmoebaCnt[AmoebaNr[newx][newy]]--;
8253           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8254         }
8255
8256         if (IS_MOVING(newx, newy))
8257           RemoveMovingField(newx, newy);
8258         else
8259         {
8260           RemoveField(newx, newy);
8261           DrawLevelField(newx, newy);
8262         }
8263
8264         /* if digged element was about to explode, prevent the explosion */
8265         ExplodeField[newx][newy] = EX_TYPE_NONE;
8266
8267         PlayLevelSoundAction(x, y, action);
8268       }
8269
8270       Store[newx][newy] = EL_EMPTY;
8271 #if 1
8272       /* this makes it possible to leave the removed element again */
8273       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8274         Store[newx][newy] = new_element;
8275 #else
8276       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8277       {
8278         int move_leave_element = element_info[element].move_leave_element;
8279
8280         /* this makes it possible to leave the removed element again */
8281         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8282                              new_element : move_leave_element);
8283       }
8284 #endif
8285
8286       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8287       {
8288         RunnerVisit[x][y] = FrameCounter;
8289         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8290       }
8291     }
8292     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8293     {
8294       if (!IS_FREE(newx, newy))
8295       {
8296         if (IS_PLAYER(x, y))
8297           DrawPlayerField(x, y);
8298         else
8299           DrawLevelField(x, y);
8300
8301         return;
8302       }
8303       else
8304       {
8305         boolean wanna_flame = !RND(10);
8306         int dx = newx - x, dy = newy - y;
8307         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8308         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8309         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8310                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8311         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8312                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8313
8314         if ((wanna_flame ||
8315              IS_CLASSIC_ENEMY(element1) ||
8316              IS_CLASSIC_ENEMY(element2)) &&
8317             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8318             element1 != EL_FLAMES && element2 != EL_FLAMES)
8319         {
8320           ResetGfxAnimation(x, y);
8321           GfxAction[x][y] = ACTION_ATTACKING;
8322
8323           if (IS_PLAYER(x, y))
8324             DrawPlayerField(x, y);
8325           else
8326             DrawLevelField(x, y);
8327
8328           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8329
8330           MovDelay[x][y] = 50;
8331
8332           /* !!! */
8333 #if 0
8334           RemoveField(newx, newy);
8335 #endif
8336           Feld[newx][newy] = EL_FLAMES;
8337           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8338           {
8339 #if 0
8340             RemoveField(newx1, newy1);
8341 #endif
8342             Feld[newx1][newy1] = EL_FLAMES;
8343           }
8344           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8345           {
8346 #if 0
8347             RemoveField(newx2, newy2);
8348 #endif
8349             Feld[newx2][newy2] = EL_FLAMES;
8350           }
8351
8352           return;
8353         }
8354       }
8355     }
8356     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8357              Feld[newx][newy] == EL_DIAMOND)
8358     {
8359       if (IS_MOVING(newx, newy))
8360         RemoveMovingField(newx, newy);
8361       else
8362       {
8363         Feld[newx][newy] = EL_EMPTY;
8364         DrawLevelField(newx, newy);
8365       }
8366
8367       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8368     }
8369     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8370              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8371     {
8372       if (AmoebaNr[newx][newy])
8373       {
8374         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8375         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8376             Feld[newx][newy] == EL_BD_AMOEBA)
8377           AmoebaCnt[AmoebaNr[newx][newy]]--;
8378       }
8379
8380 #if 0
8381       /* !!! test !!! */
8382       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8383       {
8384         RemoveMovingField(newx, newy);
8385       }
8386 #else
8387       if (IS_MOVING(newx, newy))
8388       {
8389         RemoveMovingField(newx, newy);
8390       }
8391 #endif
8392       else
8393       {
8394         Feld[newx][newy] = EL_EMPTY;
8395         DrawLevelField(newx, newy);
8396       }
8397
8398       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8399     }
8400     else if ((element == EL_PACMAN || element == EL_MOLE)
8401              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8402     {
8403       if (AmoebaNr[newx][newy])
8404       {
8405         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8406         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8407             Feld[newx][newy] == EL_BD_AMOEBA)
8408           AmoebaCnt[AmoebaNr[newx][newy]]--;
8409       }
8410
8411       if (element == EL_MOLE)
8412       {
8413         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8414         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8415
8416         ResetGfxAnimation(x, y);
8417         GfxAction[x][y] = ACTION_DIGGING;
8418         DrawLevelField(x, y);
8419
8420         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8421
8422         return;                         /* wait for shrinking amoeba */
8423       }
8424       else      /* element == EL_PACMAN */
8425       {
8426         Feld[newx][newy] = EL_EMPTY;
8427         DrawLevelField(newx, newy);
8428         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8429       }
8430     }
8431     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8432              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8433               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8434     {
8435       /* wait for shrinking amoeba to completely disappear */
8436       return;
8437     }
8438     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8439     {
8440       /* object was running against a wall */
8441
8442       TurnRound(x, y);
8443
8444 #if 0
8445       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8446       if (move_pattern & MV_ANY_DIRECTION &&
8447           move_pattern == MovDir[x][y])
8448       {
8449         int blocking_element =
8450           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8451
8452         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8453                                  MovDir[x][y]);
8454
8455         element = Feld[x][y];   /* element might have changed */
8456       }
8457 #endif
8458
8459       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8460         DrawLevelElementAnimation(x, y, element);
8461
8462       if (DONT_TOUCH(element))
8463         TestIfBadThingTouchesPlayer(x, y);
8464
8465       return;
8466     }
8467
8468     InitMovingField(x, y, MovDir[x][y]);
8469
8470     PlayLevelSoundAction(x, y, ACTION_MOVING);
8471   }
8472
8473   if (MovDir[x][y])
8474     ContinueMoving(x, y);
8475 }
8476
8477 void ContinueMoving(int x, int y)
8478 {
8479   int element = Feld[x][y];
8480   struct ElementInfo *ei = &element_info[element];
8481   int direction = MovDir[x][y];
8482   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8483   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8484   int newx = x + dx, newy = y + dy;
8485   int stored = Store[x][y];
8486   int stored_new = Store[newx][newy];
8487   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8488   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8489   boolean last_line = (newy == lev_fieldy - 1);
8490
8491   MovPos[x][y] += getElementMoveStepsize(x, y);
8492
8493   if (pushed_by_player) /* special case: moving object pushed by player */
8494     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8495
8496   if (ABS(MovPos[x][y]) < TILEX)
8497   {
8498 #if 0
8499     int ee = Feld[x][y];
8500     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8501     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8502
8503     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8504            x, y, ABS(MovPos[x][y]),
8505            ee, gg, ff,
8506            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8507 #endif
8508
8509     DrawLevelField(x, y);
8510
8511     return;     /* element is still moving */
8512   }
8513
8514   /* element reached destination field */
8515
8516   Feld[x][y] = EL_EMPTY;
8517   Feld[newx][newy] = element;
8518   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8519
8520   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8521   {
8522     element = Feld[newx][newy] = EL_ACID;
8523   }
8524   else if (element == EL_MOLE)
8525   {
8526     Feld[x][y] = EL_SAND;
8527
8528     DrawLevelFieldCrumbledSandNeighbours(x, y);
8529   }
8530   else if (element == EL_QUICKSAND_FILLING)
8531   {
8532     element = Feld[newx][newy] = get_next_element(element);
8533     Store[newx][newy] = Store[x][y];
8534   }
8535   else if (element == EL_QUICKSAND_EMPTYING)
8536   {
8537     Feld[x][y] = get_next_element(element);
8538     element = Feld[newx][newy] = Store[x][y];
8539   }
8540   else if (element == EL_QUICKSAND_FAST_FILLING)
8541   {
8542     element = Feld[newx][newy] = get_next_element(element);
8543     Store[newx][newy] = Store[x][y];
8544   }
8545   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8546   {
8547     Feld[x][y] = get_next_element(element);
8548     element = Feld[newx][newy] = Store[x][y];
8549   }
8550   else if (element == EL_MAGIC_WALL_FILLING)
8551   {
8552     element = Feld[newx][newy] = get_next_element(element);
8553     if (!game.magic_wall_active)
8554       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8555     Store[newx][newy] = Store[x][y];
8556   }
8557   else if (element == EL_MAGIC_WALL_EMPTYING)
8558   {
8559     Feld[x][y] = get_next_element(element);
8560     if (!game.magic_wall_active)
8561       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8562     element = Feld[newx][newy] = Store[x][y];
8563
8564 #if USE_NEW_CUSTOM_VALUE
8565     InitField(newx, newy, FALSE);
8566 #endif
8567   }
8568   else if (element == EL_BD_MAGIC_WALL_FILLING)
8569   {
8570     element = Feld[newx][newy] = get_next_element(element);
8571     if (!game.magic_wall_active)
8572       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8573     Store[newx][newy] = Store[x][y];
8574   }
8575   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8576   {
8577     Feld[x][y] = get_next_element(element);
8578     if (!game.magic_wall_active)
8579       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8580     element = Feld[newx][newy] = Store[x][y];
8581
8582 #if USE_NEW_CUSTOM_VALUE
8583     InitField(newx, newy, FALSE);
8584 #endif
8585   }
8586   else if (element == EL_DC_MAGIC_WALL_FILLING)
8587   {
8588     element = Feld[newx][newy] = get_next_element(element);
8589     if (!game.magic_wall_active)
8590       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8591     Store[newx][newy] = Store[x][y];
8592   }
8593   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8594   {
8595     Feld[x][y] = get_next_element(element);
8596     if (!game.magic_wall_active)
8597       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8598     element = Feld[newx][newy] = Store[x][y];
8599
8600 #if USE_NEW_CUSTOM_VALUE
8601     InitField(newx, newy, FALSE);
8602 #endif
8603   }
8604   else if (element == EL_AMOEBA_DROPPING)
8605   {
8606     Feld[x][y] = get_next_element(element);
8607     element = Feld[newx][newy] = Store[x][y];
8608   }
8609   else if (element == EL_SOKOBAN_OBJECT)
8610   {
8611     if (Back[x][y])
8612       Feld[x][y] = Back[x][y];
8613
8614     if (Back[newx][newy])
8615       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8616
8617     Back[x][y] = Back[newx][newy] = 0;
8618   }
8619
8620   Store[x][y] = EL_EMPTY;
8621   MovPos[x][y] = 0;
8622   MovDir[x][y] = 0;
8623   MovDelay[x][y] = 0;
8624
8625   MovDelay[newx][newy] = 0;
8626
8627   if (CAN_CHANGE_OR_HAS_ACTION(element))
8628   {
8629     /* copy element change control values to new field */
8630     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8631     ChangePage[newx][newy]  = ChangePage[x][y];
8632     ChangeCount[newx][newy] = ChangeCount[x][y];
8633     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8634   }
8635
8636 #if USE_NEW_CUSTOM_VALUE
8637     CustomValue[newx][newy] = CustomValue[x][y];
8638 #endif
8639
8640   ChangeDelay[x][y] = 0;
8641   ChangePage[x][y] = -1;
8642   ChangeCount[x][y] = 0;
8643   ChangeEvent[x][y] = -1;
8644
8645 #if USE_NEW_CUSTOM_VALUE
8646   CustomValue[x][y] = 0;
8647 #endif
8648
8649   /* copy animation control values to new field */
8650   GfxFrame[newx][newy]  = GfxFrame[x][y];
8651   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8652   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8653   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8654
8655   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8656
8657   /* some elements can leave other elements behind after moving */
8658 #if 1
8659   if (ei->move_leave_element != EL_EMPTY &&
8660       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8661       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8662 #else
8663   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8664       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8665       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8666 #endif
8667   {
8668     int move_leave_element = ei->move_leave_element;
8669
8670 #if 1
8671 #if 1
8672     /* this makes it possible to leave the removed element again */
8673     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8674       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8675 #else
8676     /* this makes it possible to leave the removed element again */
8677     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8678       move_leave_element = stored;
8679 #endif
8680 #else
8681     /* this makes it possible to leave the removed element again */
8682     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8683         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8684       move_leave_element = stored;
8685 #endif
8686
8687     Feld[x][y] = move_leave_element;
8688
8689     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8690       MovDir[x][y] = direction;
8691
8692     InitField(x, y, FALSE);
8693
8694     if (GFX_CRUMBLED(Feld[x][y]))
8695       DrawLevelFieldCrumbledSandNeighbours(x, y);
8696
8697     if (ELEM_IS_PLAYER(move_leave_element))
8698       RelocatePlayer(x, y, move_leave_element);
8699   }
8700
8701   /* do this after checking for left-behind element */
8702   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8703
8704   if (!CAN_MOVE(element) ||
8705       (CAN_FALL(element) && direction == MV_DOWN &&
8706        (element == EL_SPRING ||
8707         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8708         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8709     GfxDir[x][y] = MovDir[newx][newy] = 0;
8710
8711   DrawLevelField(x, y);
8712   DrawLevelField(newx, newy);
8713
8714   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8715
8716   /* prevent pushed element from moving on in pushed direction */
8717   if (pushed_by_player && CAN_MOVE(element) &&
8718       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8719       !(element_info[element].move_pattern & direction))
8720     TurnRound(newx, newy);
8721
8722   /* prevent elements on conveyor belt from moving on in last direction */
8723   if (pushed_by_conveyor && CAN_FALL(element) &&
8724       direction & MV_HORIZONTAL)
8725     MovDir[newx][newy] = 0;
8726
8727   if (!pushed_by_player)
8728   {
8729     int nextx = newx + dx, nexty = newy + dy;
8730     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8731
8732     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8733
8734     if (CAN_FALL(element) && direction == MV_DOWN)
8735       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8736
8737     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8738       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8739
8740 #if USE_FIX_IMPACT_COLLISION
8741     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8742       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8743 #endif
8744   }
8745
8746   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8747   {
8748     TestIfBadThingTouchesPlayer(newx, newy);
8749     TestIfBadThingTouchesFriend(newx, newy);
8750
8751     if (!IS_CUSTOM_ELEMENT(element))
8752       TestIfBadThingTouchesOtherBadThing(newx, newy);
8753   }
8754   else if (element == EL_PENGUIN)
8755     TestIfFriendTouchesBadThing(newx, newy);
8756
8757   /* give the player one last chance (one more frame) to move away */
8758   if (CAN_FALL(element) && direction == MV_DOWN &&
8759       (last_line || (!IS_FREE(x, newy + 1) &&
8760                      (!IS_PLAYER(x, newy + 1) ||
8761                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8762     Impact(x, newy);
8763
8764   if (pushed_by_player && !game.use_change_when_pushing_bug)
8765   {
8766     int push_side = MV_DIR_OPPOSITE(direction);
8767     struct PlayerInfo *player = PLAYERINFO(x, y);
8768
8769     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8770                                player->index_bit, push_side);
8771     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8772                                         player->index_bit, push_side);
8773   }
8774
8775   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8776     MovDelay[newx][newy] = 1;
8777
8778   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8779
8780   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8781
8782 #if 0
8783   if (ChangePage[newx][newy] != -1)             /* delayed change */
8784   {
8785     int page = ChangePage[newx][newy];
8786     struct ElementChangeInfo *change = &ei->change_page[page];
8787
8788     ChangePage[newx][newy] = -1;
8789
8790     if (change->can_change)
8791     {
8792       if (ChangeElement(newx, newy, element, page))
8793       {
8794         if (change->post_change_function)
8795           change->post_change_function(newx, newy);
8796       }
8797     }
8798
8799     if (change->has_action)
8800       ExecuteCustomElementAction(newx, newy, element, page);
8801   }
8802 #endif
8803
8804   TestIfElementHitsCustomElement(newx, newy, direction);
8805   TestIfPlayerTouchesCustomElement(newx, newy);
8806   TestIfElementTouchesCustomElement(newx, newy);
8807
8808   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8809       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8810     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8811                              MV_DIR_OPPOSITE(direction));
8812 }
8813
8814 int AmoebeNachbarNr(int ax, int ay)
8815 {
8816   int i;
8817   int element = Feld[ax][ay];
8818   int group_nr = 0;
8819   static int xy[4][2] =
8820   {
8821     { 0, -1 },
8822     { -1, 0 },
8823     { +1, 0 },
8824     { 0, +1 }
8825   };
8826
8827   for (i = 0; i < NUM_DIRECTIONS; i++)
8828   {
8829     int x = ax + xy[i][0];
8830     int y = ay + xy[i][1];
8831
8832     if (!IN_LEV_FIELD(x, y))
8833       continue;
8834
8835     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8836       group_nr = AmoebaNr[x][y];
8837   }
8838
8839   return group_nr;
8840 }
8841
8842 void AmoebenVereinigen(int ax, int ay)
8843 {
8844   int i, x, y, xx, yy;
8845   int new_group_nr = AmoebaNr[ax][ay];
8846   static int xy[4][2] =
8847   {
8848     { 0, -1 },
8849     { -1, 0 },
8850     { +1, 0 },
8851     { 0, +1 }
8852   };
8853
8854   if (new_group_nr == 0)
8855     return;
8856
8857   for (i = 0; i < NUM_DIRECTIONS; i++)
8858   {
8859     x = ax + xy[i][0];
8860     y = ay + xy[i][1];
8861
8862     if (!IN_LEV_FIELD(x, y))
8863       continue;
8864
8865     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8866          Feld[x][y] == EL_BD_AMOEBA ||
8867          Feld[x][y] == EL_AMOEBA_DEAD) &&
8868         AmoebaNr[x][y] != new_group_nr)
8869     {
8870       int old_group_nr = AmoebaNr[x][y];
8871
8872       if (old_group_nr == 0)
8873         return;
8874
8875       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8876       AmoebaCnt[old_group_nr] = 0;
8877       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8878       AmoebaCnt2[old_group_nr] = 0;
8879
8880       SCAN_PLAYFIELD(xx, yy)
8881       {
8882         if (AmoebaNr[xx][yy] == old_group_nr)
8883           AmoebaNr[xx][yy] = new_group_nr;
8884       }
8885     }
8886   }
8887 }
8888
8889 void AmoebeUmwandeln(int ax, int ay)
8890 {
8891   int i, x, y;
8892
8893   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8894   {
8895     int group_nr = AmoebaNr[ax][ay];
8896
8897 #ifdef DEBUG
8898     if (group_nr == 0)
8899     {
8900       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8901       printf("AmoebeUmwandeln(): This should never happen!\n");
8902       return;
8903     }
8904 #endif
8905
8906     SCAN_PLAYFIELD(x, y)
8907     {
8908       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8909       {
8910         AmoebaNr[x][y] = 0;
8911         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8912       }
8913     }
8914
8915     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8916                             SND_AMOEBA_TURNING_TO_GEM :
8917                             SND_AMOEBA_TURNING_TO_ROCK));
8918     Bang(ax, ay);
8919   }
8920   else
8921   {
8922     static int xy[4][2] =
8923     {
8924       { 0, -1 },
8925       { -1, 0 },
8926       { +1, 0 },
8927       { 0, +1 }
8928     };
8929
8930     for (i = 0; i < NUM_DIRECTIONS; i++)
8931     {
8932       x = ax + xy[i][0];
8933       y = ay + xy[i][1];
8934
8935       if (!IN_LEV_FIELD(x, y))
8936         continue;
8937
8938       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8939       {
8940         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8941                               SND_AMOEBA_TURNING_TO_GEM :
8942                               SND_AMOEBA_TURNING_TO_ROCK));
8943         Bang(x, y);
8944       }
8945     }
8946   }
8947 }
8948
8949 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8950 {
8951   int x, y;
8952   int group_nr = AmoebaNr[ax][ay];
8953   boolean done = FALSE;
8954
8955 #ifdef DEBUG
8956   if (group_nr == 0)
8957   {
8958     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8959     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8960     return;
8961   }
8962 #endif
8963
8964   SCAN_PLAYFIELD(x, y)
8965   {
8966     if (AmoebaNr[x][y] == group_nr &&
8967         (Feld[x][y] == EL_AMOEBA_DEAD ||
8968          Feld[x][y] == EL_BD_AMOEBA ||
8969          Feld[x][y] == EL_AMOEBA_GROWING))
8970     {
8971       AmoebaNr[x][y] = 0;
8972       Feld[x][y] = new_element;
8973       InitField(x, y, FALSE);
8974       DrawLevelField(x, y);
8975       done = TRUE;
8976     }
8977   }
8978
8979   if (done)
8980     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8981                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8982                             SND_BD_AMOEBA_TURNING_TO_GEM));
8983 }
8984
8985 void AmoebeWaechst(int x, int y)
8986 {
8987   static unsigned long sound_delay = 0;
8988   static unsigned long sound_delay_value = 0;
8989
8990   if (!MovDelay[x][y])          /* start new growing cycle */
8991   {
8992     MovDelay[x][y] = 7;
8993
8994     if (DelayReached(&sound_delay, sound_delay_value))
8995     {
8996       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8997       sound_delay_value = 30;
8998     }
8999   }
9000
9001   if (MovDelay[x][y])           /* wait some time before growing bigger */
9002   {
9003     MovDelay[x][y]--;
9004     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9005     {
9006       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9007                                            6 - MovDelay[x][y]);
9008
9009       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9010     }
9011
9012     if (!MovDelay[x][y])
9013     {
9014       Feld[x][y] = Store[x][y];
9015       Store[x][y] = 0;
9016       DrawLevelField(x, y);
9017     }
9018   }
9019 }
9020
9021 void AmoebaDisappearing(int x, int y)
9022 {
9023   static unsigned long sound_delay = 0;
9024   static unsigned long sound_delay_value = 0;
9025
9026   if (!MovDelay[x][y])          /* start new shrinking cycle */
9027   {
9028     MovDelay[x][y] = 7;
9029
9030     if (DelayReached(&sound_delay, sound_delay_value))
9031       sound_delay_value = 30;
9032   }
9033
9034   if (MovDelay[x][y])           /* wait some time before shrinking */
9035   {
9036     MovDelay[x][y]--;
9037     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9038     {
9039       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9040                                            6 - MovDelay[x][y]);
9041
9042       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9043     }
9044
9045     if (!MovDelay[x][y])
9046     {
9047       Feld[x][y] = EL_EMPTY;
9048       DrawLevelField(x, y);
9049
9050       /* don't let mole enter this field in this cycle;
9051          (give priority to objects falling to this field from above) */
9052       Stop[x][y] = TRUE;
9053     }
9054   }
9055 }
9056
9057 void AmoebeAbleger(int ax, int ay)
9058 {
9059   int i;
9060   int element = Feld[ax][ay];
9061   int graphic = el2img(element);
9062   int newax = ax, neway = ay;
9063   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9064   static int xy[4][2] =
9065   {
9066     { 0, -1 },
9067     { -1, 0 },
9068     { +1, 0 },
9069     { 0, +1 }
9070   };
9071
9072   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9073   {
9074     Feld[ax][ay] = EL_AMOEBA_DEAD;
9075     DrawLevelField(ax, ay);
9076     return;
9077   }
9078
9079   if (IS_ANIMATED(graphic))
9080     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9081
9082   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9083     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9084
9085   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9086   {
9087     MovDelay[ax][ay]--;
9088     if (MovDelay[ax][ay])
9089       return;
9090   }
9091
9092   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9093   {
9094     int start = RND(4);
9095     int x = ax + xy[start][0];
9096     int y = ay + xy[start][1];
9097
9098     if (!IN_LEV_FIELD(x, y))
9099       return;
9100
9101     if (IS_FREE(x, y) ||
9102         CAN_GROW_INTO(Feld[x][y]) ||
9103         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9104         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9105     {
9106       newax = x;
9107       neway = y;
9108     }
9109
9110     if (newax == ax && neway == ay)
9111       return;
9112   }
9113   else                          /* normal or "filled" (BD style) amoeba */
9114   {
9115     int start = RND(4);
9116     boolean waiting_for_player = FALSE;
9117
9118     for (i = 0; i < NUM_DIRECTIONS; i++)
9119     {
9120       int j = (start + i) % 4;
9121       int x = ax + xy[j][0];
9122       int y = ay + xy[j][1];
9123
9124       if (!IN_LEV_FIELD(x, y))
9125         continue;
9126
9127       if (IS_FREE(x, y) ||
9128           CAN_GROW_INTO(Feld[x][y]) ||
9129           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9130           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9131       {
9132         newax = x;
9133         neway = y;
9134         break;
9135       }
9136       else if (IS_PLAYER(x, y))
9137         waiting_for_player = TRUE;
9138     }
9139
9140     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9141     {
9142       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9143       {
9144         Feld[ax][ay] = EL_AMOEBA_DEAD;
9145         DrawLevelField(ax, ay);
9146         AmoebaCnt[AmoebaNr[ax][ay]]--;
9147
9148         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9149         {
9150           if (element == EL_AMOEBA_FULL)
9151             AmoebeUmwandeln(ax, ay);
9152           else if (element == EL_BD_AMOEBA)
9153             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9154         }
9155       }
9156       return;
9157     }
9158     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9159     {
9160       /* amoeba gets larger by growing in some direction */
9161
9162       int new_group_nr = AmoebaNr[ax][ay];
9163
9164 #ifdef DEBUG
9165   if (new_group_nr == 0)
9166   {
9167     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9168     printf("AmoebeAbleger(): This should never happen!\n");
9169     return;
9170   }
9171 #endif
9172
9173       AmoebaNr[newax][neway] = new_group_nr;
9174       AmoebaCnt[new_group_nr]++;
9175       AmoebaCnt2[new_group_nr]++;
9176
9177       /* if amoeba touches other amoeba(s) after growing, unify them */
9178       AmoebenVereinigen(newax, neway);
9179
9180       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9181       {
9182         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9183         return;
9184       }
9185     }
9186   }
9187
9188   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9189       (neway == lev_fieldy - 1 && newax != ax))
9190   {
9191     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9192     Store[newax][neway] = element;
9193   }
9194   else if (neway == ay || element == EL_EMC_DRIPPER)
9195   {
9196     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9197
9198     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9199   }
9200   else
9201   {
9202     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9203     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9204     Store[ax][ay] = EL_AMOEBA_DROP;
9205     ContinueMoving(ax, ay);
9206     return;
9207   }
9208
9209   DrawLevelField(newax, neway);
9210 }
9211
9212 void Life(int ax, int ay)
9213 {
9214   int x1, y1, x2, y2;
9215   int life_time = 40;
9216   int element = Feld[ax][ay];
9217   int graphic = el2img(element);
9218   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9219                          level.biomaze);
9220   boolean changed = FALSE;
9221
9222   if (IS_ANIMATED(graphic))
9223     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9224
9225   if (Stop[ax][ay])
9226     return;
9227
9228   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9229     MovDelay[ax][ay] = life_time;
9230
9231   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9232   {
9233     MovDelay[ax][ay]--;
9234     if (MovDelay[ax][ay])
9235       return;
9236   }
9237
9238   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9239   {
9240     int xx = ax+x1, yy = ay+y1;
9241     int nachbarn = 0;
9242
9243     if (!IN_LEV_FIELD(xx, yy))
9244       continue;
9245
9246     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9247     {
9248       int x = xx+x2, y = yy+y2;
9249
9250       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9251         continue;
9252
9253       if (((Feld[x][y] == element ||
9254             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9255            !Stop[x][y]) ||
9256           (IS_FREE(x, y) && Stop[x][y]))
9257         nachbarn++;
9258     }
9259
9260     if (xx == ax && yy == ay)           /* field in the middle */
9261     {
9262       if (nachbarn < life_parameter[0] ||
9263           nachbarn > life_parameter[1])
9264       {
9265         Feld[xx][yy] = EL_EMPTY;
9266         if (!Stop[xx][yy])
9267           DrawLevelField(xx, yy);
9268         Stop[xx][yy] = TRUE;
9269         changed = TRUE;
9270       }
9271     }
9272     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9273     {                                   /* free border field */
9274       if (nachbarn >= life_parameter[2] &&
9275           nachbarn <= life_parameter[3])
9276       {
9277         Feld[xx][yy] = element;
9278         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9279         if (!Stop[xx][yy])
9280           DrawLevelField(xx, yy);
9281         Stop[xx][yy] = TRUE;
9282         changed = TRUE;
9283       }
9284     }
9285   }
9286
9287   if (changed)
9288     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9289                    SND_GAME_OF_LIFE_GROWING);
9290 }
9291
9292 static void InitRobotWheel(int x, int y)
9293 {
9294   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9295 }
9296
9297 static void RunRobotWheel(int x, int y)
9298 {
9299   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9300 }
9301
9302 static void StopRobotWheel(int x, int y)
9303 {
9304   if (ZX == x && ZY == y)
9305   {
9306     ZX = ZY = -1;
9307
9308     game.robot_wheel_active = FALSE;
9309   }
9310 }
9311
9312 static void InitTimegateWheel(int x, int y)
9313 {
9314   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9315 }
9316
9317 static void RunTimegateWheel(int x, int y)
9318 {
9319   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9320 }
9321
9322 static void InitMagicBallDelay(int x, int y)
9323 {
9324 #if 1
9325   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9326 #else
9327   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9328 #endif
9329 }
9330
9331 static void ActivateMagicBall(int bx, int by)
9332 {
9333   int x, y;
9334
9335   if (level.ball_random)
9336   {
9337     int pos_border = RND(8);    /* select one of the eight border elements */
9338     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9339     int xx = pos_content % 3;
9340     int yy = pos_content / 3;
9341
9342     x = bx - 1 + xx;
9343     y = by - 1 + yy;
9344
9345     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9346       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9347   }
9348   else
9349   {
9350     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9351     {
9352       int xx = x - bx + 1;
9353       int yy = y - by + 1;
9354
9355       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9356         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9357     }
9358   }
9359
9360   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9361 }
9362
9363 void CheckExit(int x, int y)
9364 {
9365   if (local_player->gems_still_needed > 0 ||
9366       local_player->sokobanfields_still_needed > 0 ||
9367       local_player->lights_still_needed > 0)
9368   {
9369     int element = Feld[x][y];
9370     int graphic = el2img(element);
9371
9372     if (IS_ANIMATED(graphic))
9373       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9374
9375     return;
9376   }
9377
9378   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9379     return;
9380
9381   Feld[x][y] = EL_EXIT_OPENING;
9382
9383   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9384 }
9385
9386 void CheckExitEM(int x, int y)
9387 {
9388   if (local_player->gems_still_needed > 0 ||
9389       local_player->sokobanfields_still_needed > 0 ||
9390       local_player->lights_still_needed > 0)
9391   {
9392     int element = Feld[x][y];
9393     int graphic = el2img(element);
9394
9395     if (IS_ANIMATED(graphic))
9396       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9397
9398     return;
9399   }
9400
9401   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9402     return;
9403
9404   Feld[x][y] = EL_EM_EXIT_OPENING;
9405
9406   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9407 }
9408
9409 void CheckExitSteel(int x, int y)
9410 {
9411   if (local_player->gems_still_needed > 0 ||
9412       local_player->sokobanfields_still_needed > 0 ||
9413       local_player->lights_still_needed > 0)
9414   {
9415     int element = Feld[x][y];
9416     int graphic = el2img(element);
9417
9418     if (IS_ANIMATED(graphic))
9419       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9420
9421     return;
9422   }
9423
9424   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9425     return;
9426
9427   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9428
9429   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9430 }
9431
9432 void CheckExitSteelEM(int x, int y)
9433 {
9434   if (local_player->gems_still_needed > 0 ||
9435       local_player->sokobanfields_still_needed > 0 ||
9436       local_player->lights_still_needed > 0)
9437   {
9438     int element = Feld[x][y];
9439     int graphic = el2img(element);
9440
9441     if (IS_ANIMATED(graphic))
9442       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9443
9444     return;
9445   }
9446
9447   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9448     return;
9449
9450   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9451
9452   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9453 }
9454
9455 void CheckExitSP(int x, int y)
9456 {
9457   if (local_player->gems_still_needed > 0)
9458   {
9459     int element = Feld[x][y];
9460     int graphic = el2img(element);
9461
9462     if (IS_ANIMATED(graphic))
9463       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9464
9465     return;
9466   }
9467
9468   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9469     return;
9470
9471   Feld[x][y] = EL_SP_EXIT_OPENING;
9472
9473   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9474 }
9475
9476 static void CloseAllOpenTimegates()
9477 {
9478   int x, y;
9479
9480   SCAN_PLAYFIELD(x, y)
9481   {
9482     int element = Feld[x][y];
9483
9484     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9485     {
9486       Feld[x][y] = EL_TIMEGATE_CLOSING;
9487
9488       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9489     }
9490   }
9491 }
9492
9493 void DrawTwinkleOnField(int x, int y)
9494 {
9495   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9496     return;
9497
9498   if (Feld[x][y] == EL_BD_DIAMOND)
9499     return;
9500
9501   if (MovDelay[x][y] == 0)      /* next animation frame */
9502     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9503
9504   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9505   {
9506     MovDelay[x][y]--;
9507
9508     if (setup.direct_draw && MovDelay[x][y])
9509       SetDrawtoField(DRAW_BUFFERED);
9510
9511     DrawLevelElementAnimation(x, y, Feld[x][y]);
9512
9513     if (MovDelay[x][y] != 0)
9514     {
9515       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9516                                            10 - MovDelay[x][y]);
9517
9518       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9519
9520       if (setup.direct_draw)
9521       {
9522         int dest_x, dest_y;
9523
9524         dest_x = FX + SCREENX(x) * TILEX;
9525         dest_y = FY + SCREENY(y) * TILEY;
9526
9527         BlitBitmap(drawto_field, window,
9528                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9529         SetDrawtoField(DRAW_DIRECT);
9530       }
9531     }
9532   }
9533 }
9534
9535 void MauerWaechst(int x, int y)
9536 {
9537   int delay = 6;
9538
9539   if (!MovDelay[x][y])          /* next animation frame */
9540     MovDelay[x][y] = 3 * delay;
9541
9542   if (MovDelay[x][y])           /* wait some time before next frame */
9543   {
9544     MovDelay[x][y]--;
9545
9546     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9547     {
9548       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9549       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9550
9551       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9552     }
9553
9554     if (!MovDelay[x][y])
9555     {
9556       if (MovDir[x][y] == MV_LEFT)
9557       {
9558         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9559           DrawLevelField(x - 1, y);
9560       }
9561       else if (MovDir[x][y] == MV_RIGHT)
9562       {
9563         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9564           DrawLevelField(x + 1, y);
9565       }
9566       else if (MovDir[x][y] == MV_UP)
9567       {
9568         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9569           DrawLevelField(x, y - 1);
9570       }
9571       else
9572       {
9573         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9574           DrawLevelField(x, y + 1);
9575       }
9576
9577       Feld[x][y] = Store[x][y];
9578       Store[x][y] = 0;
9579       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9580       DrawLevelField(x, y);
9581     }
9582   }
9583 }
9584
9585 void MauerAbleger(int ax, int ay)
9586 {
9587   int element = Feld[ax][ay];
9588   int graphic = el2img(element);
9589   boolean oben_frei = FALSE, unten_frei = FALSE;
9590   boolean links_frei = FALSE, rechts_frei = FALSE;
9591   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9592   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9593   boolean new_wall = FALSE;
9594
9595   if (IS_ANIMATED(graphic))
9596     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9597
9598   if (!MovDelay[ax][ay])        /* start building new wall */
9599     MovDelay[ax][ay] = 6;
9600
9601   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9602   {
9603     MovDelay[ax][ay]--;
9604     if (MovDelay[ax][ay])
9605       return;
9606   }
9607
9608   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9609     oben_frei = TRUE;
9610   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9611     unten_frei = TRUE;
9612   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9613     links_frei = TRUE;
9614   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9615     rechts_frei = TRUE;
9616
9617   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9618       element == EL_EXPANDABLE_WALL_ANY)
9619   {
9620     if (oben_frei)
9621     {
9622       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9623       Store[ax][ay-1] = element;
9624       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9625       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9626         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9627                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9628       new_wall = TRUE;
9629     }
9630     if (unten_frei)
9631     {
9632       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9633       Store[ax][ay+1] = element;
9634       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9635       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9636         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9637                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9638       new_wall = TRUE;
9639     }
9640   }
9641
9642   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9643       element == EL_EXPANDABLE_WALL_ANY ||
9644       element == EL_EXPANDABLE_WALL ||
9645       element == EL_BD_EXPANDABLE_WALL)
9646   {
9647     if (links_frei)
9648     {
9649       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9650       Store[ax-1][ay] = element;
9651       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9652       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9653         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9654                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9655       new_wall = TRUE;
9656     }
9657
9658     if (rechts_frei)
9659     {
9660       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9661       Store[ax+1][ay] = element;
9662       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9663       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9664         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9665                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9666       new_wall = TRUE;
9667     }
9668   }
9669
9670   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9671     DrawLevelField(ax, ay);
9672
9673   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9674     oben_massiv = TRUE;
9675   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9676     unten_massiv = TRUE;
9677   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9678     links_massiv = TRUE;
9679   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9680     rechts_massiv = TRUE;
9681
9682   if (((oben_massiv && unten_massiv) ||
9683        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9684        element == EL_EXPANDABLE_WALL) &&
9685       ((links_massiv && rechts_massiv) ||
9686        element == EL_EXPANDABLE_WALL_VERTICAL))
9687     Feld[ax][ay] = EL_WALL;
9688
9689   if (new_wall)
9690     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9691 }
9692
9693 void MauerAblegerStahl(int ax, int ay)
9694 {
9695   int element = Feld[ax][ay];
9696   int graphic = el2img(element);
9697   boolean oben_frei = FALSE, unten_frei = FALSE;
9698   boolean links_frei = FALSE, rechts_frei = FALSE;
9699   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9700   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9701   boolean new_wall = FALSE;
9702
9703   if (IS_ANIMATED(graphic))
9704     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9705
9706   if (!MovDelay[ax][ay])        /* start building new wall */
9707     MovDelay[ax][ay] = 6;
9708
9709   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9710   {
9711     MovDelay[ax][ay]--;
9712     if (MovDelay[ax][ay])
9713       return;
9714   }
9715
9716   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9717     oben_frei = TRUE;
9718   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9719     unten_frei = TRUE;
9720   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9721     links_frei = TRUE;
9722   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9723     rechts_frei = TRUE;
9724
9725   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9726       element == EL_EXPANDABLE_STEELWALL_ANY)
9727   {
9728     if (oben_frei)
9729     {
9730       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9731       Store[ax][ay-1] = element;
9732       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9733       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9734         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9735                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9736       new_wall = TRUE;
9737     }
9738     if (unten_frei)
9739     {
9740       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9741       Store[ax][ay+1] = element;
9742       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9743       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9744         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9745                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9746       new_wall = TRUE;
9747     }
9748   }
9749
9750   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9751       element == EL_EXPANDABLE_STEELWALL_ANY)
9752   {
9753     if (links_frei)
9754     {
9755       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9756       Store[ax-1][ay] = element;
9757       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9758       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9759         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9760                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9761       new_wall = TRUE;
9762     }
9763
9764     if (rechts_frei)
9765     {
9766       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9767       Store[ax+1][ay] = element;
9768       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9769       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9770         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9771                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9772       new_wall = TRUE;
9773     }
9774   }
9775
9776   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9777     oben_massiv = TRUE;
9778   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9779     unten_massiv = TRUE;
9780   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9781     links_massiv = TRUE;
9782   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9783     rechts_massiv = TRUE;
9784
9785   if (((oben_massiv && unten_massiv) ||
9786        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9787       ((links_massiv && rechts_massiv) ||
9788        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9789     Feld[ax][ay] = EL_WALL;
9790
9791   if (new_wall)
9792     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9793 }
9794
9795 void CheckForDragon(int x, int y)
9796 {
9797   int i, j;
9798   boolean dragon_found = FALSE;
9799   static int xy[4][2] =
9800   {
9801     { 0, -1 },
9802     { -1, 0 },
9803     { +1, 0 },
9804     { 0, +1 }
9805   };
9806
9807   for (i = 0; i < NUM_DIRECTIONS; i++)
9808   {
9809     for (j = 0; j < 4; j++)
9810     {
9811       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9812
9813       if (IN_LEV_FIELD(xx, yy) &&
9814           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9815       {
9816         if (Feld[xx][yy] == EL_DRAGON)
9817           dragon_found = TRUE;
9818       }
9819       else
9820         break;
9821     }
9822   }
9823
9824   if (!dragon_found)
9825   {
9826     for (i = 0; i < NUM_DIRECTIONS; i++)
9827     {
9828       for (j = 0; j < 3; j++)
9829       {
9830         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9831   
9832         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9833         {
9834           Feld[xx][yy] = EL_EMPTY;
9835           DrawLevelField(xx, yy);
9836         }
9837         else
9838           break;
9839       }
9840     }
9841   }
9842 }
9843
9844 static void InitBuggyBase(int x, int y)
9845 {
9846   int element = Feld[x][y];
9847   int activating_delay = FRAMES_PER_SECOND / 4;
9848
9849   ChangeDelay[x][y] =
9850     (element == EL_SP_BUGGY_BASE ?
9851      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9852      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9853      activating_delay :
9854      element == EL_SP_BUGGY_BASE_ACTIVE ?
9855      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9856 }
9857
9858 static void WarnBuggyBase(int x, int y)
9859 {
9860   int i;
9861   static int xy[4][2] =
9862   {
9863     { 0, -1 },
9864     { -1, 0 },
9865     { +1, 0 },
9866     { 0, +1 }
9867   };
9868
9869   for (i = 0; i < NUM_DIRECTIONS; i++)
9870   {
9871     int xx = x + xy[i][0];
9872     int yy = y + xy[i][1];
9873
9874     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9875     {
9876       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9877
9878       break;
9879     }
9880   }
9881 }
9882
9883 static void InitTrap(int x, int y)
9884 {
9885   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9886 }
9887
9888 static void ActivateTrap(int x, int y)
9889 {
9890   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9891 }
9892
9893 static void ChangeActiveTrap(int x, int y)
9894 {
9895   int graphic = IMG_TRAP_ACTIVE;
9896
9897   /* if new animation frame was drawn, correct crumbled sand border */
9898   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9899     DrawLevelFieldCrumbledSand(x, y);
9900 }
9901
9902 static int getSpecialActionElement(int element, int number, int base_element)
9903 {
9904   return (element != EL_EMPTY ? element :
9905           number != -1 ? base_element + number - 1 :
9906           EL_EMPTY);
9907 }
9908
9909 static int getModifiedActionNumber(int value_old, int operator, int operand,
9910                                    int value_min, int value_max)
9911 {
9912   int value_new = (operator == CA_MODE_SET      ? operand :
9913                    operator == CA_MODE_ADD      ? value_old + operand :
9914                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9915                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9916                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9917                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9918                    value_old);
9919
9920   return (value_new < value_min ? value_min :
9921           value_new > value_max ? value_max :
9922           value_new);
9923 }
9924
9925 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9926 {
9927   struct ElementInfo *ei = &element_info[element];
9928   struct ElementChangeInfo *change = &ei->change_page[page];
9929   int target_element = change->target_element;
9930   int action_type = change->action_type;
9931   int action_mode = change->action_mode;
9932   int action_arg = change->action_arg;
9933   int i;
9934
9935   if (!change->has_action)
9936     return;
9937
9938   /* ---------- determine action paramater values -------------------------- */
9939
9940   int level_time_value =
9941     (level.time > 0 ? TimeLeft :
9942      TimePlayed);
9943
9944   int action_arg_element =
9945     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9946      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9947      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9948      EL_EMPTY);
9949
9950   int action_arg_direction =
9951     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9952      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9953      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9954      change->actual_trigger_side :
9955      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9956      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9957      MV_NONE);
9958
9959   int action_arg_number_min =
9960     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9961      CA_ARG_MIN);
9962
9963   int action_arg_number_max =
9964     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9965      action_type == CA_SET_LEVEL_GEMS ? 999 :
9966      action_type == CA_SET_LEVEL_TIME ? 9999 :
9967      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9968      action_type == CA_SET_CE_VALUE ? 9999 :
9969      action_type == CA_SET_CE_SCORE ? 9999 :
9970      CA_ARG_MAX);
9971
9972   int action_arg_number_reset =
9973     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9974      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9975      action_type == CA_SET_LEVEL_TIME ? level.time :
9976      action_type == CA_SET_LEVEL_SCORE ? 0 :
9977 #if USE_NEW_CUSTOM_VALUE
9978      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9979 #else
9980      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9981 #endif
9982      action_type == CA_SET_CE_SCORE ? 0 :
9983      0);
9984
9985   int action_arg_number =
9986     (action_arg <= CA_ARG_MAX ? action_arg :
9987      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9988      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9989      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9990      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9991      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9992      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9993 #if USE_NEW_CUSTOM_VALUE
9994      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9995 #else
9996      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9997 #endif
9998      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9999      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10000      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10001      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10002      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10003      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10004      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10005      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10006      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10007      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10008      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10009      -1);
10010
10011   int action_arg_number_old =
10012     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10013      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10014      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10015      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10016      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10017      0);
10018
10019   int action_arg_number_new =
10020     getModifiedActionNumber(action_arg_number_old,
10021                             action_mode, action_arg_number,
10022                             action_arg_number_min, action_arg_number_max);
10023
10024 #if 1
10025   int trigger_player_bits = change->actual_trigger_player_bits;
10026 #else
10027   int trigger_player_bits =
10028     (change->actual_trigger_player >= EL_PLAYER_1 &&
10029      change->actual_trigger_player <= EL_PLAYER_4 ?
10030      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10031      PLAYER_BITS_ANY);
10032 #endif
10033
10034   int action_arg_player_bits =
10035     (action_arg >= CA_ARG_PLAYER_1 &&
10036      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10037      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10038      PLAYER_BITS_ANY);
10039
10040   /* ---------- execute action  -------------------------------------------- */
10041
10042   switch (action_type)
10043   {
10044     case CA_NO_ACTION:
10045     {
10046       return;
10047     }
10048
10049     /* ---------- level actions  ------------------------------------------- */
10050
10051     case CA_RESTART_LEVEL:
10052     {
10053       game.restart_level = TRUE;
10054
10055       break;
10056     }
10057
10058     case CA_SHOW_ENVELOPE:
10059     {
10060       int element = getSpecialActionElement(action_arg_element,
10061                                             action_arg_number, EL_ENVELOPE_1);
10062
10063       if (IS_ENVELOPE(element))
10064         local_player->show_envelope = element;
10065
10066       break;
10067     }
10068
10069     case CA_SET_LEVEL_TIME:
10070     {
10071       if (level.time > 0)       /* only modify limited time value */
10072       {
10073         TimeLeft = action_arg_number_new;
10074
10075 #if 1
10076         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10077
10078         DisplayGameControlValues();
10079 #else
10080         DrawGameValue_Time(TimeLeft);
10081 #endif
10082
10083         if (!TimeLeft && setup.time_limit)
10084           for (i = 0; i < MAX_PLAYERS; i++)
10085             KillPlayer(&stored_player[i]);
10086       }
10087
10088       break;
10089     }
10090
10091     case CA_SET_LEVEL_SCORE:
10092     {
10093       local_player->score = action_arg_number_new;
10094
10095 #if 1
10096       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10097
10098       DisplayGameControlValues();
10099 #else
10100       DrawGameValue_Score(local_player->score);
10101 #endif
10102
10103       break;
10104     }
10105
10106     case CA_SET_LEVEL_GEMS:
10107     {
10108       local_player->gems_still_needed = action_arg_number_new;
10109
10110 #if 1
10111       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10112
10113       DisplayGameControlValues();
10114 #else
10115       DrawGameValue_Emeralds(local_player->gems_still_needed);
10116 #endif
10117
10118       break;
10119     }
10120
10121 #if !USE_PLAYER_GRAVITY
10122     case CA_SET_LEVEL_GRAVITY:
10123     {
10124       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10125                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10126                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10127                       game.gravity);
10128       break;
10129     }
10130 #endif
10131
10132     case CA_SET_LEVEL_WIND:
10133     {
10134       game.wind_direction = action_arg_direction;
10135
10136       break;
10137     }
10138
10139     /* ---------- player actions  ------------------------------------------ */
10140
10141     case CA_MOVE_PLAYER:
10142     {
10143       /* automatically move to the next field in specified direction */
10144       for (i = 0; i < MAX_PLAYERS; i++)
10145         if (trigger_player_bits & (1 << i))
10146           stored_player[i].programmed_action = action_arg_direction;
10147
10148       break;
10149     }
10150
10151     case CA_EXIT_PLAYER:
10152     {
10153       for (i = 0; i < MAX_PLAYERS; i++)
10154         if (action_arg_player_bits & (1 << i))
10155           PlayerWins(&stored_player[i]);
10156
10157       break;
10158     }
10159
10160     case CA_KILL_PLAYER:
10161     {
10162       for (i = 0; i < MAX_PLAYERS; i++)
10163         if (action_arg_player_bits & (1 << i))
10164           KillPlayer(&stored_player[i]);
10165
10166       break;
10167     }
10168
10169     case CA_SET_PLAYER_KEYS:
10170     {
10171       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10172       int element = getSpecialActionElement(action_arg_element,
10173                                             action_arg_number, EL_KEY_1);
10174
10175       if (IS_KEY(element))
10176       {
10177         for (i = 0; i < MAX_PLAYERS; i++)
10178         {
10179           if (trigger_player_bits & (1 << i))
10180           {
10181             stored_player[i].key[KEY_NR(element)] = key_state;
10182
10183             DrawGameDoorValues();
10184           }
10185         }
10186       }
10187
10188       break;
10189     }
10190
10191     case CA_SET_PLAYER_SPEED:
10192     {
10193       for (i = 0; i < MAX_PLAYERS; i++)
10194       {
10195         if (trigger_player_bits & (1 << i))
10196         {
10197           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10198
10199           if (action_arg == CA_ARG_SPEED_FASTER &&
10200               stored_player[i].cannot_move)
10201           {
10202             action_arg_number = STEPSIZE_VERY_SLOW;
10203           }
10204           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10205                    action_arg == CA_ARG_SPEED_FASTER)
10206           {
10207             action_arg_number = 2;
10208             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10209                            CA_MODE_MULTIPLY);
10210           }
10211           else if (action_arg == CA_ARG_NUMBER_RESET)
10212           {
10213             action_arg_number = level.initial_player_stepsize[i];
10214           }
10215
10216           move_stepsize =
10217             getModifiedActionNumber(move_stepsize,
10218                                     action_mode,
10219                                     action_arg_number,
10220                                     action_arg_number_min,
10221                                     action_arg_number_max);
10222
10223           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10224         }
10225       }
10226
10227       break;
10228     }
10229
10230     case CA_SET_PLAYER_SHIELD:
10231     {
10232       for (i = 0; i < MAX_PLAYERS; i++)
10233       {
10234         if (trigger_player_bits & (1 << i))
10235         {
10236           if (action_arg == CA_ARG_SHIELD_OFF)
10237           {
10238             stored_player[i].shield_normal_time_left = 0;
10239             stored_player[i].shield_deadly_time_left = 0;
10240           }
10241           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10242           {
10243             stored_player[i].shield_normal_time_left = 999999;
10244           }
10245           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10246           {
10247             stored_player[i].shield_normal_time_left = 999999;
10248             stored_player[i].shield_deadly_time_left = 999999;
10249           }
10250         }
10251       }
10252
10253       break;
10254     }
10255
10256 #if USE_PLAYER_GRAVITY
10257     case CA_SET_PLAYER_GRAVITY:
10258     {
10259       for (i = 0; i < MAX_PLAYERS; i++)
10260       {
10261         if (trigger_player_bits & (1 << i))
10262         {
10263           stored_player[i].gravity =
10264             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10265              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10266              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10267              stored_player[i].gravity);
10268         }
10269       }
10270
10271       break;
10272     }
10273 #endif
10274
10275     case CA_SET_PLAYER_ARTWORK:
10276     {
10277       for (i = 0; i < MAX_PLAYERS; i++)
10278       {
10279         if (trigger_player_bits & (1 << i))
10280         {
10281           int artwork_element = action_arg_element;
10282
10283           if (action_arg == CA_ARG_ELEMENT_RESET)
10284             artwork_element =
10285               (level.use_artwork_element[i] ? level.artwork_element[i] :
10286                stored_player[i].element_nr);
10287
10288 #if USE_GFX_RESET_PLAYER_ARTWORK
10289           if (stored_player[i].artwork_element != artwork_element)
10290             stored_player[i].Frame = 0;
10291 #endif
10292
10293           stored_player[i].artwork_element = artwork_element;
10294
10295           SetPlayerWaiting(&stored_player[i], FALSE);
10296
10297           /* set number of special actions for bored and sleeping animation */
10298           stored_player[i].num_special_action_bored =
10299             get_num_special_action(artwork_element,
10300                                    ACTION_BORING_1, ACTION_BORING_LAST);
10301           stored_player[i].num_special_action_sleeping =
10302             get_num_special_action(artwork_element,
10303                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10304         }
10305       }
10306
10307       break;
10308     }
10309
10310     /* ---------- CE actions  ---------------------------------------------- */
10311
10312     case CA_SET_CE_VALUE:
10313     {
10314 #if USE_NEW_CUSTOM_VALUE
10315       int last_ce_value = CustomValue[x][y];
10316
10317       CustomValue[x][y] = action_arg_number_new;
10318
10319       if (CustomValue[x][y] != last_ce_value)
10320       {
10321         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10322         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10323
10324         if (CustomValue[x][y] == 0)
10325         {
10326           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10327           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10328         }
10329       }
10330 #endif
10331
10332       break;
10333     }
10334
10335     case CA_SET_CE_SCORE:
10336     {
10337 #if USE_NEW_CUSTOM_VALUE
10338       int last_ce_score = ei->collect_score;
10339
10340       ei->collect_score = action_arg_number_new;
10341
10342       if (ei->collect_score != last_ce_score)
10343       {
10344         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10345         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10346
10347         if (ei->collect_score == 0)
10348         {
10349           int xx, yy;
10350
10351           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10352           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10353
10354           /*
10355             This is a very special case that seems to be a mixture between
10356             CheckElementChange() and CheckTriggeredElementChange(): while
10357             the first one only affects single elements that are triggered
10358             directly, the second one affects multiple elements in the playfield
10359             that are triggered indirectly by another element. This is a third
10360             case: Changing the CE score always affects multiple identical CEs,
10361             so every affected CE must be checked, not only the single CE for
10362             which the CE score was changed in the first place (as every instance
10363             of that CE shares the same CE score, and therefore also can change)!
10364           */
10365           SCAN_PLAYFIELD(xx, yy)
10366           {
10367             if (Feld[xx][yy] == element)
10368               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10369                                  CE_SCORE_GETS_ZERO);
10370           }
10371         }
10372       }
10373 #endif
10374
10375       break;
10376     }
10377
10378     /* ---------- engine actions  ------------------------------------------ */
10379
10380     case CA_SET_ENGINE_SCAN_MODE:
10381     {
10382       InitPlayfieldScanMode(action_arg);
10383
10384       break;
10385     }
10386
10387     default:
10388       break;
10389   }
10390 }
10391
10392 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10393 {
10394   int old_element = Feld[x][y];
10395   int new_element = GetElementFromGroupElement(element);
10396   int previous_move_direction = MovDir[x][y];
10397 #if USE_NEW_CUSTOM_VALUE
10398   int last_ce_value = CustomValue[x][y];
10399 #endif
10400   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10401   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10402   boolean add_player_onto_element = (new_element_is_player &&
10403 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10404                                      /* this breaks SnakeBite when a snake is
10405                                         halfway through a door that closes */
10406                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10407                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10408 #endif
10409                                      IS_WALKABLE(old_element));
10410
10411 #if 0
10412   /* check if element under the player changes from accessible to unaccessible
10413      (needed for special case of dropping element which then changes) */
10414   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10415       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10416   {
10417     Bang(x, y);
10418
10419     return;
10420   }
10421 #endif
10422
10423   if (!add_player_onto_element)
10424   {
10425     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10426       RemoveMovingField(x, y);
10427     else
10428       RemoveField(x, y);
10429
10430     Feld[x][y] = new_element;
10431
10432 #if !USE_GFX_RESET_GFX_ANIMATION
10433     ResetGfxAnimation(x, y);
10434     ResetRandomAnimationValue(x, y);
10435 #endif
10436
10437     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10438       MovDir[x][y] = previous_move_direction;
10439
10440 #if USE_NEW_CUSTOM_VALUE
10441     if (element_info[new_element].use_last_ce_value)
10442       CustomValue[x][y] = last_ce_value;
10443 #endif
10444
10445     InitField_WithBug1(x, y, FALSE);
10446
10447     new_element = Feld[x][y];   /* element may have changed */
10448
10449 #if USE_GFX_RESET_GFX_ANIMATION
10450     ResetGfxAnimation(x, y);
10451     ResetRandomAnimationValue(x, y);
10452 #endif
10453
10454     DrawLevelField(x, y);
10455
10456     if (GFX_CRUMBLED(new_element))
10457       DrawLevelFieldCrumbledSandNeighbours(x, y);
10458   }
10459
10460 #if 1
10461   /* check if element under the player changes from accessible to unaccessible
10462      (needed for special case of dropping element which then changes) */
10463   /* (must be checked after creating new element for walkable group elements) */
10464 #if USE_FIX_KILLED_BY_NON_WALKABLE
10465   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10466       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10467   {
10468     Bang(x, y);
10469
10470     return;
10471   }
10472 #else
10473   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10474       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10475   {
10476     Bang(x, y);
10477
10478     return;
10479   }
10480 #endif
10481 #endif
10482
10483   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10484   if (new_element_is_player)
10485     RelocatePlayer(x, y, new_element);
10486
10487   if (is_change)
10488     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10489
10490   TestIfBadThingTouchesPlayer(x, y);
10491   TestIfPlayerTouchesCustomElement(x, y);
10492   TestIfElementTouchesCustomElement(x, y);
10493 }
10494
10495 static void CreateField(int x, int y, int element)
10496 {
10497   CreateFieldExt(x, y, element, FALSE);
10498 }
10499
10500 static void CreateElementFromChange(int x, int y, int element)
10501 {
10502   element = GET_VALID_RUNTIME_ELEMENT(element);
10503
10504 #if USE_STOP_CHANGED_ELEMENTS
10505   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10506   {
10507     int old_element = Feld[x][y];
10508
10509     /* prevent changed element from moving in same engine frame
10510        unless both old and new element can either fall or move */
10511     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10512         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10513       Stop[x][y] = TRUE;
10514   }
10515 #endif
10516
10517   CreateFieldExt(x, y, element, TRUE);
10518 }
10519
10520 static boolean ChangeElement(int x, int y, int element, int page)
10521 {
10522   struct ElementInfo *ei = &element_info[element];
10523   struct ElementChangeInfo *change = &ei->change_page[page];
10524   int ce_value = CustomValue[x][y];
10525   int ce_score = ei->collect_score;
10526   int target_element;
10527   int old_element = Feld[x][y];
10528
10529   /* always use default change event to prevent running into a loop */
10530   if (ChangeEvent[x][y] == -1)
10531     ChangeEvent[x][y] = CE_DELAY;
10532
10533   if (ChangeEvent[x][y] == CE_DELAY)
10534   {
10535     /* reset actual trigger element, trigger player and action element */
10536     change->actual_trigger_element = EL_EMPTY;
10537     change->actual_trigger_player = EL_PLAYER_1;
10538     change->actual_trigger_player_bits = CH_PLAYER_1;
10539     change->actual_trigger_side = CH_SIDE_NONE;
10540     change->actual_trigger_ce_value = 0;
10541     change->actual_trigger_ce_score = 0;
10542   }
10543
10544   /* do not change elements more than a specified maximum number of changes */
10545   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10546     return FALSE;
10547
10548   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10549
10550   if (change->explode)
10551   {
10552     Bang(x, y);
10553
10554     return TRUE;
10555   }
10556
10557   if (change->use_target_content)
10558   {
10559     boolean complete_replace = TRUE;
10560     boolean can_replace[3][3];
10561     int xx, yy;
10562
10563     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10564     {
10565       boolean is_empty;
10566       boolean is_walkable;
10567       boolean is_diggable;
10568       boolean is_collectible;
10569       boolean is_removable;
10570       boolean is_destructible;
10571       int ex = x + xx - 1;
10572       int ey = y + yy - 1;
10573       int content_element = change->target_content.e[xx][yy];
10574       int e;
10575
10576       can_replace[xx][yy] = TRUE;
10577
10578       if (ex == x && ey == y)   /* do not check changing element itself */
10579         continue;
10580
10581       if (content_element == EL_EMPTY_SPACE)
10582       {
10583         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10584
10585         continue;
10586       }
10587
10588       if (!IN_LEV_FIELD(ex, ey))
10589       {
10590         can_replace[xx][yy] = FALSE;
10591         complete_replace = FALSE;
10592
10593         continue;
10594       }
10595
10596       e = Feld[ex][ey];
10597
10598       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10599         e = MovingOrBlocked2Element(ex, ey);
10600
10601       is_empty = (IS_FREE(ex, ey) ||
10602                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10603
10604       is_walkable     = (is_empty || IS_WALKABLE(e));
10605       is_diggable     = (is_empty || IS_DIGGABLE(e));
10606       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10607       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10608       is_removable    = (is_diggable || is_collectible);
10609
10610       can_replace[xx][yy] =
10611         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10612           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10613           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10614           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10615           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10616           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10617          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10618
10619       if (!can_replace[xx][yy])
10620         complete_replace = FALSE;
10621     }
10622
10623     if (!change->only_if_complete || complete_replace)
10624     {
10625       boolean something_has_changed = FALSE;
10626
10627       if (change->only_if_complete && change->use_random_replace &&
10628           RND(100) < change->random_percentage)
10629         return FALSE;
10630
10631       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10632       {
10633         int ex = x + xx - 1;
10634         int ey = y + yy - 1;
10635         int content_element;
10636
10637         if (can_replace[xx][yy] && (!change->use_random_replace ||
10638                                     RND(100) < change->random_percentage))
10639         {
10640           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10641             RemoveMovingField(ex, ey);
10642
10643           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10644
10645           content_element = change->target_content.e[xx][yy];
10646           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10647                                               ce_value, ce_score);
10648
10649           CreateElementFromChange(ex, ey, target_element);
10650
10651           something_has_changed = TRUE;
10652
10653           /* for symmetry reasons, freeze newly created border elements */
10654           if (ex != x || ey != y)
10655             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10656         }
10657       }
10658
10659       if (something_has_changed)
10660       {
10661         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10662         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10663       }
10664     }
10665   }
10666   else
10667   {
10668     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10669                                         ce_value, ce_score);
10670
10671     if (element == EL_DIAGONAL_GROWING ||
10672         element == EL_DIAGONAL_SHRINKING)
10673     {
10674       target_element = Store[x][y];
10675
10676       Store[x][y] = EL_EMPTY;
10677     }
10678
10679     CreateElementFromChange(x, y, target_element);
10680
10681     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10682     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10683   }
10684
10685   /* this uses direct change before indirect change */
10686   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10687
10688   return TRUE;
10689 }
10690
10691 #if USE_NEW_DELAYED_ACTION
10692
10693 static void HandleElementChange(int x, int y, int page)
10694 {
10695   int element = MovingOrBlocked2Element(x, y);
10696   struct ElementInfo *ei = &element_info[element];
10697   struct ElementChangeInfo *change = &ei->change_page[page];
10698
10699 #ifdef DEBUG
10700   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10701       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10702   {
10703     printf("\n\n");
10704     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10705            x, y, element, element_info[element].token_name);
10706     printf("HandleElementChange(): This should never happen!\n");
10707     printf("\n\n");
10708   }
10709 #endif
10710
10711   /* this can happen with classic bombs on walkable, changing elements */
10712   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10713   {
10714 #if 0
10715     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10716       ChangeDelay[x][y] = 0;
10717 #endif
10718
10719     return;
10720   }
10721
10722   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10723   {
10724     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10725
10726     if (change->can_change)
10727     {
10728 #if 1
10729       /* !!! not clear why graphic animation should be reset at all here !!! */
10730       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10731 #if USE_GFX_RESET_WHEN_NOT_MOVING
10732       /* when a custom element is about to change (for example by change delay),
10733          do not reset graphic animation when the custom element is moving */
10734       if (!IS_MOVING(x, y))
10735 #endif
10736       {
10737         ResetGfxAnimation(x, y);
10738         ResetRandomAnimationValue(x, y);
10739       }
10740 #endif
10741
10742       if (change->pre_change_function)
10743         change->pre_change_function(x, y);
10744     }
10745   }
10746
10747   ChangeDelay[x][y]--;
10748
10749   if (ChangeDelay[x][y] != 0)           /* continue element change */
10750   {
10751     if (change->can_change)
10752     {
10753       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10754
10755       if (IS_ANIMATED(graphic))
10756         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10757
10758       if (change->change_function)
10759         change->change_function(x, y);
10760     }
10761   }
10762   else                                  /* finish element change */
10763   {
10764     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10765     {
10766       page = ChangePage[x][y];
10767       ChangePage[x][y] = -1;
10768
10769       change = &ei->change_page[page];
10770     }
10771
10772     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10773     {
10774       ChangeDelay[x][y] = 1;            /* try change after next move step */
10775       ChangePage[x][y] = page;          /* remember page to use for change */
10776
10777       return;
10778     }
10779
10780     if (change->can_change)
10781     {
10782       if (ChangeElement(x, y, element, page))
10783       {
10784         if (change->post_change_function)
10785           change->post_change_function(x, y);
10786       }
10787     }
10788
10789     if (change->has_action)
10790       ExecuteCustomElementAction(x, y, element, page);
10791   }
10792 }
10793
10794 #else
10795
10796 static void HandleElementChange(int x, int y, int page)
10797 {
10798   int element = MovingOrBlocked2Element(x, y);
10799   struct ElementInfo *ei = &element_info[element];
10800   struct ElementChangeInfo *change = &ei->change_page[page];
10801
10802 #ifdef DEBUG
10803   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10804   {
10805     printf("\n\n");
10806     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10807            x, y, element, element_info[element].token_name);
10808     printf("HandleElementChange(): This should never happen!\n");
10809     printf("\n\n");
10810   }
10811 #endif
10812
10813   /* this can happen with classic bombs on walkable, changing elements */
10814   if (!CAN_CHANGE(element))
10815   {
10816 #if 0
10817     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10818       ChangeDelay[x][y] = 0;
10819 #endif
10820
10821     return;
10822   }
10823
10824   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10825   {
10826     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10827
10828     ResetGfxAnimation(x, y);
10829     ResetRandomAnimationValue(x, y);
10830
10831     if (change->pre_change_function)
10832       change->pre_change_function(x, y);
10833   }
10834
10835   ChangeDelay[x][y]--;
10836
10837   if (ChangeDelay[x][y] != 0)           /* continue element change */
10838   {
10839     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10840
10841     if (IS_ANIMATED(graphic))
10842       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10843
10844     if (change->change_function)
10845       change->change_function(x, y);
10846   }
10847   else                                  /* finish element change */
10848   {
10849     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10850     {
10851       page = ChangePage[x][y];
10852       ChangePage[x][y] = -1;
10853
10854       change = &ei->change_page[page];
10855     }
10856
10857     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10858     {
10859       ChangeDelay[x][y] = 1;            /* try change after next move step */
10860       ChangePage[x][y] = page;          /* remember page to use for change */
10861
10862       return;
10863     }
10864
10865     if (ChangeElement(x, y, element, page))
10866     {
10867       if (change->post_change_function)
10868         change->post_change_function(x, y);
10869     }
10870   }
10871 }
10872
10873 #endif
10874
10875 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10876                                               int trigger_element,
10877                                               int trigger_event,
10878                                               int trigger_player,
10879                                               int trigger_side,
10880                                               int trigger_page)
10881 {
10882   boolean change_done_any = FALSE;
10883   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10884   int i;
10885
10886   if (!(trigger_events[trigger_element][trigger_event]))
10887     return FALSE;
10888
10889 #if 0
10890   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10891          trigger_event, recursion_loop_depth, recursion_loop_detected,
10892          recursion_loop_element, EL_NAME(recursion_loop_element));
10893 #endif
10894
10895   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10896
10897   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10898   {
10899     int element = EL_CUSTOM_START + i;
10900     boolean change_done = FALSE;
10901     int p;
10902
10903     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10904         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10905       continue;
10906
10907     for (p = 0; p < element_info[element].num_change_pages; p++)
10908     {
10909       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10910
10911       if (change->can_change_or_has_action &&
10912           change->has_event[trigger_event] &&
10913           change->trigger_side & trigger_side &&
10914           change->trigger_player & trigger_player &&
10915           change->trigger_page & trigger_page_bits &&
10916           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10917       {
10918         change->actual_trigger_element = trigger_element;
10919         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10920         change->actual_trigger_player_bits = trigger_player;
10921         change->actual_trigger_side = trigger_side;
10922         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10923         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10924
10925         if ((change->can_change && !change_done) || change->has_action)
10926         {
10927           int x, y;
10928
10929           SCAN_PLAYFIELD(x, y)
10930           {
10931             if (Feld[x][y] == element)
10932             {
10933               if (change->can_change && !change_done)
10934               {
10935                 ChangeDelay[x][y] = 1;
10936                 ChangeEvent[x][y] = trigger_event;
10937
10938                 HandleElementChange(x, y, p);
10939               }
10940 #if USE_NEW_DELAYED_ACTION
10941               else if (change->has_action)
10942               {
10943                 ExecuteCustomElementAction(x, y, element, p);
10944                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10945               }
10946 #else
10947               if (change->has_action)
10948               {
10949                 ExecuteCustomElementAction(x, y, element, p);
10950                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10951               }
10952 #endif
10953             }
10954           }
10955
10956           if (change->can_change)
10957           {
10958             change_done = TRUE;
10959             change_done_any = TRUE;
10960           }
10961         }
10962       }
10963     }
10964   }
10965
10966   RECURSION_LOOP_DETECTION_END();
10967
10968   return change_done_any;
10969 }
10970
10971 static boolean CheckElementChangeExt(int x, int y,
10972                                      int element,
10973                                      int trigger_element,
10974                                      int trigger_event,
10975                                      int trigger_player,
10976                                      int trigger_side)
10977 {
10978   boolean change_done = FALSE;
10979   int p;
10980
10981   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10982       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10983     return FALSE;
10984
10985   if (Feld[x][y] == EL_BLOCKED)
10986   {
10987     Blocked2Moving(x, y, &x, &y);
10988     element = Feld[x][y];
10989   }
10990
10991 #if 0
10992   /* check if element has already changed */
10993   if (Feld[x][y] != element)
10994     return FALSE;
10995 #else
10996   /* check if element has already changed or is about to change after moving */
10997   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10998        Feld[x][y] != element) ||
10999
11000       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11001        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11002         ChangePage[x][y] != -1)))
11003     return FALSE;
11004 #endif
11005
11006 #if 0
11007   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11008          trigger_event, recursion_loop_depth, recursion_loop_detected,
11009          recursion_loop_element, EL_NAME(recursion_loop_element));
11010 #endif
11011
11012   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11013
11014   for (p = 0; p < element_info[element].num_change_pages; p++)
11015   {
11016     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11017
11018     /* check trigger element for all events where the element that is checked
11019        for changing interacts with a directly adjacent element -- this is
11020        different to element changes that affect other elements to change on the
11021        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11022     boolean check_trigger_element =
11023       (trigger_event == CE_TOUCHING_X ||
11024        trigger_event == CE_HITTING_X ||
11025        trigger_event == CE_HIT_BY_X ||
11026 #if 1
11027        /* this one was forgotten until 3.2.3 */
11028        trigger_event == CE_DIGGING_X);
11029 #endif
11030
11031     if (change->can_change_or_has_action &&
11032         change->has_event[trigger_event] &&
11033         change->trigger_side & trigger_side &&
11034         change->trigger_player & trigger_player &&
11035         (!check_trigger_element ||
11036          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11037     {
11038       change->actual_trigger_element = trigger_element;
11039       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11040       change->actual_trigger_player_bits = trigger_player;
11041       change->actual_trigger_side = trigger_side;
11042       change->actual_trigger_ce_value = CustomValue[x][y];
11043       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11044
11045       /* special case: trigger element not at (x,y) position for some events */
11046       if (check_trigger_element)
11047       {
11048         static struct
11049         {
11050           int dx, dy;
11051         } move_xy[] =
11052           {
11053             {  0,  0 },
11054             { -1,  0 },
11055             { +1,  0 },
11056             {  0,  0 },
11057             {  0, -1 },
11058             {  0,  0 }, { 0, 0 }, { 0, 0 },
11059             {  0, +1 }
11060           };
11061
11062         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11063         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11064
11065         change->actual_trigger_ce_value = CustomValue[xx][yy];
11066         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11067       }
11068
11069       if (change->can_change && !change_done)
11070       {
11071         ChangeDelay[x][y] = 1;
11072         ChangeEvent[x][y] = trigger_event;
11073
11074         HandleElementChange(x, y, p);
11075
11076         change_done = TRUE;
11077       }
11078 #if USE_NEW_DELAYED_ACTION
11079       else if (change->has_action)
11080       {
11081         ExecuteCustomElementAction(x, y, element, p);
11082         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11083       }
11084 #else
11085       if (change->has_action)
11086       {
11087         ExecuteCustomElementAction(x, y, element, p);
11088         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11089       }
11090 #endif
11091     }
11092   }
11093
11094   RECURSION_LOOP_DETECTION_END();
11095
11096   return change_done;
11097 }
11098
11099 static void PlayPlayerSound(struct PlayerInfo *player)
11100 {
11101   int jx = player->jx, jy = player->jy;
11102   int sound_element = player->artwork_element;
11103   int last_action = player->last_action_waiting;
11104   int action = player->action_waiting;
11105
11106   if (player->is_waiting)
11107   {
11108     if (action != last_action)
11109       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11110     else
11111       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11112   }
11113   else
11114   {
11115     if (action != last_action)
11116       StopSound(element_info[sound_element].sound[last_action]);
11117
11118     if (last_action == ACTION_SLEEPING)
11119       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11120   }
11121 }
11122
11123 static void PlayAllPlayersSound()
11124 {
11125   int i;
11126
11127   for (i = 0; i < MAX_PLAYERS; i++)
11128     if (stored_player[i].active)
11129       PlayPlayerSound(&stored_player[i]);
11130 }
11131
11132 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11133 {
11134   boolean last_waiting = player->is_waiting;
11135   int move_dir = player->MovDir;
11136
11137   player->dir_waiting = move_dir;
11138   player->last_action_waiting = player->action_waiting;
11139
11140   if (is_waiting)
11141   {
11142     if (!last_waiting)          /* not waiting -> waiting */
11143     {
11144       player->is_waiting = TRUE;
11145
11146       player->frame_counter_bored =
11147         FrameCounter +
11148         game.player_boring_delay_fixed +
11149         GetSimpleRandom(game.player_boring_delay_random);
11150       player->frame_counter_sleeping =
11151         FrameCounter +
11152         game.player_sleeping_delay_fixed +
11153         GetSimpleRandom(game.player_sleeping_delay_random);
11154
11155       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11156     }
11157
11158     if (game.player_sleeping_delay_fixed +
11159         game.player_sleeping_delay_random > 0 &&
11160         player->anim_delay_counter == 0 &&
11161         player->post_delay_counter == 0 &&
11162         FrameCounter >= player->frame_counter_sleeping)
11163       player->is_sleeping = TRUE;
11164     else if (game.player_boring_delay_fixed +
11165              game.player_boring_delay_random > 0 &&
11166              FrameCounter >= player->frame_counter_bored)
11167       player->is_bored = TRUE;
11168
11169     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11170                               player->is_bored ? ACTION_BORING :
11171                               ACTION_WAITING);
11172
11173     if (player->is_sleeping && player->use_murphy)
11174     {
11175       /* special case for sleeping Murphy when leaning against non-free tile */
11176
11177       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11178           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11179            !IS_MOVING(player->jx - 1, player->jy)))
11180         move_dir = MV_LEFT;
11181       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11182                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11183                 !IS_MOVING(player->jx + 1, player->jy)))
11184         move_dir = MV_RIGHT;
11185       else
11186         player->is_sleeping = FALSE;
11187
11188       player->dir_waiting = move_dir;
11189     }
11190
11191     if (player->is_sleeping)
11192     {
11193       if (player->num_special_action_sleeping > 0)
11194       {
11195         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11196         {
11197           int last_special_action = player->special_action_sleeping;
11198           int num_special_action = player->num_special_action_sleeping;
11199           int special_action =
11200             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11201              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11202              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11203              last_special_action + 1 : ACTION_SLEEPING);
11204           int special_graphic =
11205             el_act_dir2img(player->artwork_element, special_action, move_dir);
11206
11207           player->anim_delay_counter =
11208             graphic_info[special_graphic].anim_delay_fixed +
11209             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11210           player->post_delay_counter =
11211             graphic_info[special_graphic].post_delay_fixed +
11212             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11213
11214           player->special_action_sleeping = special_action;
11215         }
11216
11217         if (player->anim_delay_counter > 0)
11218         {
11219           player->action_waiting = player->special_action_sleeping;
11220           player->anim_delay_counter--;
11221         }
11222         else if (player->post_delay_counter > 0)
11223         {
11224           player->post_delay_counter--;
11225         }
11226       }
11227     }
11228     else if (player->is_bored)
11229     {
11230       if (player->num_special_action_bored > 0)
11231       {
11232         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11233         {
11234           int special_action =
11235             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11236           int special_graphic =
11237             el_act_dir2img(player->artwork_element, special_action, move_dir);
11238
11239           player->anim_delay_counter =
11240             graphic_info[special_graphic].anim_delay_fixed +
11241             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11242           player->post_delay_counter =
11243             graphic_info[special_graphic].post_delay_fixed +
11244             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11245
11246           player->special_action_bored = special_action;
11247         }
11248
11249         if (player->anim_delay_counter > 0)
11250         {
11251           player->action_waiting = player->special_action_bored;
11252           player->anim_delay_counter--;
11253         }
11254         else if (player->post_delay_counter > 0)
11255         {
11256           player->post_delay_counter--;
11257         }
11258       }
11259     }
11260   }
11261   else if (last_waiting)        /* waiting -> not waiting */
11262   {
11263     player->is_waiting = FALSE;
11264     player->is_bored = FALSE;
11265     player->is_sleeping = FALSE;
11266
11267     player->frame_counter_bored = -1;
11268     player->frame_counter_sleeping = -1;
11269
11270     player->anim_delay_counter = 0;
11271     player->post_delay_counter = 0;
11272
11273     player->dir_waiting = player->MovDir;
11274     player->action_waiting = ACTION_DEFAULT;
11275
11276     player->special_action_bored = ACTION_DEFAULT;
11277     player->special_action_sleeping = ACTION_DEFAULT;
11278   }
11279 }
11280
11281 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11282 {
11283   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11284   int left      = player_action & JOY_LEFT;
11285   int right     = player_action & JOY_RIGHT;
11286   int up        = player_action & JOY_UP;
11287   int down      = player_action & JOY_DOWN;
11288   int button1   = player_action & JOY_BUTTON_1;
11289   int button2   = player_action & JOY_BUTTON_2;
11290   int dx        = (left ? -1 : right ? 1 : 0);
11291   int dy        = (up   ? -1 : down  ? 1 : 0);
11292
11293   if (!player->active || tape.pausing)
11294     return 0;
11295
11296   if (player_action)
11297   {
11298     if (button1)
11299       snapped = SnapField(player, dx, dy);
11300     else
11301     {
11302       if (button2)
11303         dropped = DropElement(player);
11304
11305       moved = MovePlayer(player, dx, dy);
11306     }
11307
11308     if (tape.single_step && tape.recording && !tape.pausing)
11309     {
11310       if (button1 || (dropped && !moved))
11311       {
11312         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11313         SnapField(player, 0, 0);                /* stop snapping */
11314       }
11315     }
11316
11317     SetPlayerWaiting(player, FALSE);
11318
11319     return player_action;
11320   }
11321   else
11322   {
11323     /* no actions for this player (no input at player's configured device) */
11324
11325     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11326     SnapField(player, 0, 0);
11327     CheckGravityMovementWhenNotMoving(player);
11328
11329     if (player->MovPos == 0)
11330       SetPlayerWaiting(player, TRUE);
11331
11332     if (player->MovPos == 0)    /* needed for tape.playing */
11333       player->is_moving = FALSE;
11334
11335     player->is_dropping = FALSE;
11336     player->is_dropping_pressed = FALSE;
11337     player->drop_pressed_delay = 0;
11338
11339     return 0;
11340   }
11341 }
11342
11343 static void CheckLevelTime()
11344 {
11345   int i;
11346
11347   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11348   {
11349     if (level.native_em_level->lev->home == 0)  /* all players at home */
11350     {
11351       PlayerWins(local_player);
11352
11353       AllPlayersGone = TRUE;
11354
11355       level.native_em_level->lev->home = -1;
11356     }
11357
11358     if (level.native_em_level->ply[0]->alive == 0 &&
11359         level.native_em_level->ply[1]->alive == 0 &&
11360         level.native_em_level->ply[2]->alive == 0 &&
11361         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11362       AllPlayersGone = TRUE;
11363   }
11364
11365   if (TimeFrames >= FRAMES_PER_SECOND)
11366   {
11367     TimeFrames = 0;
11368     TapeTime++;
11369
11370     for (i = 0; i < MAX_PLAYERS; i++)
11371     {
11372       struct PlayerInfo *player = &stored_player[i];
11373
11374       if (SHIELD_ON(player))
11375       {
11376         player->shield_normal_time_left--;
11377
11378         if (player->shield_deadly_time_left > 0)
11379           player->shield_deadly_time_left--;
11380       }
11381     }
11382
11383     if (!local_player->LevelSolved && !level.use_step_counter)
11384     {
11385       TimePlayed++;
11386
11387       if (TimeLeft > 0)
11388       {
11389         TimeLeft--;
11390
11391         if (TimeLeft <= 10 && setup.time_limit)
11392           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11393
11394 #if 1
11395         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11396
11397         DisplayGameControlValues();
11398 #else
11399         DrawGameValue_Time(TimeLeft);
11400 #endif
11401
11402         if (!TimeLeft && setup.time_limit)
11403         {
11404           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11405             level.native_em_level->lev->killed_out_of_time = TRUE;
11406           else
11407             for (i = 0; i < MAX_PLAYERS; i++)
11408               KillPlayer(&stored_player[i]);
11409         }
11410       }
11411 #if 1
11412       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11413       {
11414         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11415
11416         DisplayGameControlValues();
11417       }
11418 #else
11419       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11420         DrawGameValue_Time(TimePlayed);
11421 #endif
11422
11423       level.native_em_level->lev->time =
11424         (level.time == 0 ? TimePlayed : TimeLeft);
11425     }
11426
11427     if (tape.recording || tape.playing)
11428       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11429   }
11430
11431   UpdateGameDoorValues();
11432   DrawGameDoorValues();
11433 }
11434
11435 void AdvanceFrameAndPlayerCounters(int player_nr)
11436 {
11437   int i;
11438
11439   /* advance frame counters (global frame counter and time frame counter) */
11440   FrameCounter++;
11441   TimeFrames++;
11442
11443   /* advance player counters (counters for move delay, move animation etc.) */
11444   for (i = 0; i < MAX_PLAYERS; i++)
11445   {
11446     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11447     int move_delay_value = stored_player[i].move_delay_value;
11448     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11449
11450     if (!advance_player_counters)       /* not all players may be affected */
11451       continue;
11452
11453 #if USE_NEW_PLAYER_ANIM
11454     if (move_frames == 0)       /* less than one move per game frame */
11455     {
11456       int stepsize = TILEX / move_delay_value;
11457       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11458       int count = (stored_player[i].is_moving ?
11459                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11460
11461       if (count % delay == 0)
11462         move_frames = 1;
11463     }
11464 #endif
11465
11466     stored_player[i].Frame += move_frames;
11467
11468     if (stored_player[i].MovPos != 0)
11469       stored_player[i].StepFrame += move_frames;
11470
11471     if (stored_player[i].move_delay > 0)
11472       stored_player[i].move_delay--;
11473
11474     /* due to bugs in previous versions, counter must count up, not down */
11475     if (stored_player[i].push_delay != -1)
11476       stored_player[i].push_delay++;
11477
11478     if (stored_player[i].drop_delay > 0)
11479       stored_player[i].drop_delay--;
11480
11481     if (stored_player[i].is_dropping_pressed)
11482       stored_player[i].drop_pressed_delay++;
11483   }
11484 }
11485
11486 void StartGameActions(boolean init_network_game, boolean record_tape,
11487                       long random_seed)
11488 {
11489   unsigned long new_random_seed = InitRND(random_seed);
11490
11491   if (record_tape)
11492     TapeStartRecording(new_random_seed);
11493
11494 #if defined(NETWORK_AVALIABLE)
11495   if (init_network_game)
11496   {
11497     SendToServer_StartPlaying();
11498
11499     return;
11500   }
11501 #endif
11502
11503   InitGame();
11504 }
11505
11506 void GameActions()
11507 {
11508   static unsigned long game_frame_delay = 0;
11509   unsigned long game_frame_delay_value;
11510   byte *recorded_player_action;
11511   byte summarized_player_action = 0;
11512   byte tape_action[MAX_PLAYERS];
11513   int i;
11514
11515   /* detect endless loops, caused by custom element programming */
11516   if (recursion_loop_detected && recursion_loop_depth == 0)
11517   {
11518     char *message = getStringCat3("Internal Error ! Element ",
11519                                   EL_NAME(recursion_loop_element),
11520                                   " caused endless loop ! Quit the game ?");
11521
11522     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11523           EL_NAME(recursion_loop_element));
11524
11525     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11526
11527     recursion_loop_detected = FALSE;    /* if game should be continued */
11528
11529     free(message);
11530
11531     return;
11532   }
11533
11534   if (game.restart_level)
11535     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11536
11537   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11538   {
11539     if (level.native_em_level->lev->home == 0)  /* all players at home */
11540     {
11541       PlayerWins(local_player);
11542
11543       AllPlayersGone = TRUE;
11544
11545       level.native_em_level->lev->home = -1;
11546     }
11547
11548     if (level.native_em_level->ply[0]->alive == 0 &&
11549         level.native_em_level->ply[1]->alive == 0 &&
11550         level.native_em_level->ply[2]->alive == 0 &&
11551         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11552       AllPlayersGone = TRUE;
11553   }
11554
11555   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11556     GameWon();
11557
11558   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11559     TapeStop();
11560
11561   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11562     return;
11563
11564   game_frame_delay_value =
11565     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11566
11567   if (tape.playing && tape.warp_forward && !tape.pausing)
11568     game_frame_delay_value = 0;
11569
11570   /* ---------- main game synchronization point ---------- */
11571
11572   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11573
11574   if (network_playing && !network_player_action_received)
11575   {
11576     /* try to get network player actions in time */
11577
11578 #if defined(NETWORK_AVALIABLE)
11579     /* last chance to get network player actions without main loop delay */
11580     HandleNetworking();
11581 #endif
11582
11583     /* game was quit by network peer */
11584     if (game_status != GAME_MODE_PLAYING)
11585       return;
11586
11587     if (!network_player_action_received)
11588       return;           /* failed to get network player actions in time */
11589
11590     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11591   }
11592
11593   if (tape.pausing)
11594     return;
11595
11596   /* at this point we know that we really continue executing the game */
11597
11598   network_player_action_received = FALSE;
11599
11600   /* when playing tape, read previously recorded player input from tape data */
11601   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11602
11603 #if 1
11604   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11605   if (tape.pausing)
11606     return;
11607 #endif
11608
11609   if (tape.set_centered_player)
11610   {
11611     game.centered_player_nr_next = tape.centered_player_nr_next;
11612     game.set_centered_player = TRUE;
11613   }
11614
11615   for (i = 0; i < MAX_PLAYERS; i++)
11616   {
11617     summarized_player_action |= stored_player[i].action;
11618
11619     if (!network_playing)
11620       stored_player[i].effective_action = stored_player[i].action;
11621   }
11622
11623 #if defined(NETWORK_AVALIABLE)
11624   if (network_playing)
11625     SendToServer_MovePlayer(summarized_player_action);
11626 #endif
11627
11628   if (!options.network && !setup.team_mode)
11629     local_player->effective_action = summarized_player_action;
11630
11631   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11632   {
11633     for (i = 0; i < MAX_PLAYERS; i++)
11634       stored_player[i].effective_action =
11635         (i == game.centered_player_nr ? summarized_player_action : 0);
11636   }
11637
11638   if (recorded_player_action != NULL)
11639     for (i = 0; i < MAX_PLAYERS; i++)
11640       stored_player[i].effective_action = recorded_player_action[i];
11641
11642   for (i = 0; i < MAX_PLAYERS; i++)
11643   {
11644     tape_action[i] = stored_player[i].effective_action;
11645
11646     /* (this can only happen in the R'n'D game engine) */
11647     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11648       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11649   }
11650
11651   /* only record actions from input devices, but not programmed actions */
11652   if (tape.recording)
11653     TapeRecordAction(tape_action);
11654
11655   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11656   {
11657     GameActions_EM_Main();
11658   }
11659   else
11660   {
11661     GameActions_RND();
11662   }
11663 }
11664
11665 void GameActions_EM_Main()
11666 {
11667   byte effective_action[MAX_PLAYERS];
11668   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11669   int i;
11670
11671   for (i = 0; i < MAX_PLAYERS; i++)
11672     effective_action[i] = stored_player[i].effective_action;
11673
11674   GameActions_EM(effective_action, warp_mode);
11675
11676   CheckLevelTime();
11677
11678   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11679 }
11680
11681 void GameActions_RND()
11682 {
11683   int magic_wall_x = 0, magic_wall_y = 0;
11684   int i, x, y, element, graphic;
11685
11686   InitPlayfieldScanModeVars();
11687
11688 #if USE_ONE_MORE_CHANGE_PER_FRAME
11689   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11690   {
11691     SCAN_PLAYFIELD(x, y)
11692     {
11693       ChangeCount[x][y] = 0;
11694       ChangeEvent[x][y] = -1;
11695     }
11696   }
11697 #endif
11698
11699   if (game.set_centered_player)
11700   {
11701     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11702
11703     /* switching to "all players" only possible if all players fit to screen */
11704     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11705     {
11706       game.centered_player_nr_next = game.centered_player_nr;
11707       game.set_centered_player = FALSE;
11708     }
11709
11710     /* do not switch focus to non-existing (or non-active) player */
11711     if (game.centered_player_nr_next >= 0 &&
11712         !stored_player[game.centered_player_nr_next].active)
11713     {
11714       game.centered_player_nr_next = game.centered_player_nr;
11715       game.set_centered_player = FALSE;
11716     }
11717   }
11718
11719   if (game.set_centered_player &&
11720       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11721   {
11722     int sx, sy;
11723
11724     if (game.centered_player_nr_next == -1)
11725     {
11726       setScreenCenteredToAllPlayers(&sx, &sy);
11727     }
11728     else
11729     {
11730       sx = stored_player[game.centered_player_nr_next].jx;
11731       sy = stored_player[game.centered_player_nr_next].jy;
11732     }
11733
11734     game.centered_player_nr = game.centered_player_nr_next;
11735     game.set_centered_player = FALSE;
11736
11737     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11738     DrawGameDoorValues();
11739   }
11740
11741   for (i = 0; i < MAX_PLAYERS; i++)
11742   {
11743     int actual_player_action = stored_player[i].effective_action;
11744
11745 #if 1
11746     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11747        - rnd_equinox_tetrachloride 048
11748        - rnd_equinox_tetrachloride_ii 096
11749        - rnd_emanuel_schmieg 002
11750        - doctor_sloan_ww 001, 020
11751     */
11752     if (stored_player[i].MovPos == 0)
11753       CheckGravityMovement(&stored_player[i]);
11754 #endif
11755
11756     /* overwrite programmed action with tape action */
11757     if (stored_player[i].programmed_action)
11758       actual_player_action = stored_player[i].programmed_action;
11759
11760     PlayerActions(&stored_player[i], actual_player_action);
11761
11762     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11763   }
11764
11765   ScrollScreen(NULL, SCROLL_GO_ON);
11766
11767   /* for backwards compatibility, the following code emulates a fixed bug that
11768      occured when pushing elements (causing elements that just made their last
11769      pushing step to already (if possible) make their first falling step in the
11770      same game frame, which is bad); this code is also needed to use the famous
11771      "spring push bug" which is used in older levels and might be wanted to be
11772      used also in newer levels, but in this case the buggy pushing code is only
11773      affecting the "spring" element and no other elements */
11774
11775   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11776   {
11777     for (i = 0; i < MAX_PLAYERS; i++)
11778     {
11779       struct PlayerInfo *player = &stored_player[i];
11780       int x = player->jx;
11781       int y = player->jy;
11782
11783       if (player->active && player->is_pushing && player->is_moving &&
11784           IS_MOVING(x, y) &&
11785           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11786            Feld[x][y] == EL_SPRING))
11787       {
11788         ContinueMoving(x, y);
11789
11790         /* continue moving after pushing (this is actually a bug) */
11791         if (!IS_MOVING(x, y))
11792           Stop[x][y] = FALSE;
11793       }
11794     }
11795   }
11796
11797 #if 0
11798   debug_print_timestamp(0, "start main loop profiling");
11799 #endif
11800
11801   SCAN_PLAYFIELD(x, y)
11802   {
11803     ChangeCount[x][y] = 0;
11804     ChangeEvent[x][y] = -1;
11805
11806     /* this must be handled before main playfield loop */
11807     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11808     {
11809       MovDelay[x][y]--;
11810       if (MovDelay[x][y] <= 0)
11811         RemoveField(x, y);
11812     }
11813
11814 #if USE_NEW_SNAP_DELAY
11815     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11816     {
11817       MovDelay[x][y]--;
11818       if (MovDelay[x][y] <= 0)
11819       {
11820         RemoveField(x, y);
11821         DrawLevelField(x, y);
11822
11823         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11824       }
11825     }
11826 #endif
11827
11828 #if DEBUG
11829     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11830     {
11831       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11832       printf("GameActions(): This should never happen!\n");
11833
11834       ChangePage[x][y] = -1;
11835     }
11836 #endif
11837
11838     Stop[x][y] = FALSE;
11839     if (WasJustMoving[x][y] > 0)
11840       WasJustMoving[x][y]--;
11841     if (WasJustFalling[x][y] > 0)
11842       WasJustFalling[x][y]--;
11843     if (CheckCollision[x][y] > 0)
11844       CheckCollision[x][y]--;
11845     if (CheckImpact[x][y] > 0)
11846       CheckImpact[x][y]--;
11847
11848     GfxFrame[x][y]++;
11849
11850     /* reset finished pushing action (not done in ContinueMoving() to allow
11851        continuous pushing animation for elements with zero push delay) */
11852     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11853     {
11854       ResetGfxAnimation(x, y);
11855       DrawLevelField(x, y);
11856     }
11857
11858 #if DEBUG
11859     if (IS_BLOCKED(x, y))
11860     {
11861       int oldx, oldy;
11862
11863       Blocked2Moving(x, y, &oldx, &oldy);
11864       if (!IS_MOVING(oldx, oldy))
11865       {
11866         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11867         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11868         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11869         printf("GameActions(): This should never happen!\n");
11870       }
11871     }
11872 #endif
11873   }
11874
11875 #if 0
11876   debug_print_timestamp(0, "- time for pre-main loop:");
11877 #endif
11878
11879 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11880   SCAN_PLAYFIELD(x, y)
11881   {
11882     element = Feld[x][y];
11883     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11884
11885 #if 1
11886     {
11887 #if 1
11888       int element2 = element;
11889       int graphic2 = graphic;
11890 #else
11891       int element2 = Feld[x][y];
11892       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11893 #endif
11894       int last_gfx_frame = GfxFrame[x][y];
11895
11896       if (graphic_info[graphic2].anim_global_sync)
11897         GfxFrame[x][y] = FrameCounter;
11898       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11899         GfxFrame[x][y] = CustomValue[x][y];
11900       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11901         GfxFrame[x][y] = element_info[element2].collect_score;
11902       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11903         GfxFrame[x][y] = ChangeDelay[x][y];
11904
11905       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11906         DrawLevelGraphicAnimation(x, y, graphic2);
11907     }
11908 #else
11909     ResetGfxFrame(x, y, TRUE);
11910 #endif
11911
11912 #if 1
11913     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11914         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11915       ResetRandomAnimationValue(x, y);
11916 #endif
11917
11918 #if 1
11919     SetRandomAnimationValue(x, y);
11920 #endif
11921
11922 #if 1
11923     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11924 #endif
11925   }
11926 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11927
11928 #if 0
11929   debug_print_timestamp(0, "- time for TEST loop:     -->");
11930 #endif
11931
11932   SCAN_PLAYFIELD(x, y)
11933   {
11934     element = Feld[x][y];
11935     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11936
11937     ResetGfxFrame(x, y, TRUE);
11938
11939     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11940         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11941       ResetRandomAnimationValue(x, y);
11942
11943     SetRandomAnimationValue(x, y);
11944
11945     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11946
11947     if (IS_INACTIVE(element))
11948     {
11949       if (IS_ANIMATED(graphic))
11950         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11951
11952       continue;
11953     }
11954
11955     /* this may take place after moving, so 'element' may have changed */
11956     if (IS_CHANGING(x, y) &&
11957         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11958     {
11959       int page = element_info[element].event_page_nr[CE_DELAY];
11960
11961 #if 1
11962       HandleElementChange(x, y, page);
11963 #else
11964       if (CAN_CHANGE(element))
11965         HandleElementChange(x, y, page);
11966
11967       if (HAS_ACTION(element))
11968         ExecuteCustomElementAction(x, y, element, page);
11969 #endif
11970
11971       element = Feld[x][y];
11972       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11973     }
11974
11975 #if 0   // ---------------------------------------------------------------------
11976
11977     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11978     {
11979       StartMoving(x, y);
11980
11981       element = Feld[x][y];
11982       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11983
11984       if (IS_ANIMATED(graphic) &&
11985           !IS_MOVING(x, y) &&
11986           !Stop[x][y])
11987         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11988
11989       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11990         DrawTwinkleOnField(x, y);
11991     }
11992     else if (IS_MOVING(x, y))
11993       ContinueMoving(x, y);
11994     else
11995     {
11996       switch (element)
11997       {
11998         case EL_ACID:
11999         case EL_EXIT_OPEN:
12000         case EL_EM_EXIT_OPEN:
12001         case EL_SP_EXIT_OPEN:
12002         case EL_STEEL_EXIT_OPEN:
12003         case EL_EM_STEEL_EXIT_OPEN:
12004         case EL_SP_TERMINAL:
12005         case EL_SP_TERMINAL_ACTIVE:
12006         case EL_EXTRA_TIME:
12007         case EL_SHIELD_NORMAL:
12008         case EL_SHIELD_DEADLY:
12009           if (IS_ANIMATED(graphic))
12010             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12011           break;
12012
12013         case EL_DYNAMITE_ACTIVE:
12014         case EL_EM_DYNAMITE_ACTIVE:
12015         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12016         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12017         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12018         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12019         case EL_SP_DISK_RED_ACTIVE:
12020           CheckDynamite(x, y);
12021           break;
12022
12023         case EL_AMOEBA_GROWING:
12024           AmoebeWaechst(x, y);
12025           break;
12026
12027         case EL_AMOEBA_SHRINKING:
12028           AmoebaDisappearing(x, y);
12029           break;
12030
12031 #if !USE_NEW_AMOEBA_CODE
12032         case EL_AMOEBA_WET:
12033         case EL_AMOEBA_DRY:
12034         case EL_AMOEBA_FULL:
12035         case EL_BD_AMOEBA:
12036         case EL_EMC_DRIPPER:
12037           AmoebeAbleger(x, y);
12038           break;
12039 #endif
12040
12041         case EL_GAME_OF_LIFE:
12042         case EL_BIOMAZE:
12043           Life(x, y);
12044           break;
12045
12046         case EL_EXIT_CLOSED:
12047           CheckExit(x, y);
12048           break;
12049
12050         case EL_EM_EXIT_CLOSED:
12051           CheckExitEM(x, y);
12052           break;
12053
12054         case EL_STEEL_EXIT_CLOSED:
12055           CheckExitSteel(x, y);
12056           break;
12057
12058         case EL_EM_STEEL_EXIT_CLOSED:
12059           CheckExitSteelEM(x, y);
12060           break;
12061
12062         case EL_SP_EXIT_CLOSED:
12063           CheckExitSP(x, y);
12064           break;
12065
12066         case EL_EXPANDABLE_WALL_GROWING:
12067         case EL_EXPANDABLE_STEELWALL_GROWING:
12068           MauerWaechst(x, y);
12069           break;
12070
12071         case EL_EXPANDABLE_WALL:
12072         case EL_EXPANDABLE_WALL_HORIZONTAL:
12073         case EL_EXPANDABLE_WALL_VERTICAL:
12074         case EL_EXPANDABLE_WALL_ANY:
12075         case EL_BD_EXPANDABLE_WALL:
12076           MauerAbleger(x, y);
12077           break;
12078
12079         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12080         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12081         case EL_EXPANDABLE_STEELWALL_ANY:
12082           MauerAblegerStahl(x, y);
12083           break;
12084
12085         case EL_FLAMES:
12086           CheckForDragon(x, y);
12087           break;
12088
12089         case EL_EXPLOSION:
12090           break;
12091
12092         case EL_ELEMENT_SNAPPING:
12093         case EL_DIAGONAL_SHRINKING:
12094         case EL_DIAGONAL_GROWING:
12095         {
12096           graphic =
12097             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12098
12099           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12100           break;
12101         }
12102
12103         default:
12104           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12105             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12106           break;
12107       }
12108     }
12109
12110 #else   // ---------------------------------------------------------------------
12111
12112     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12113     {
12114       StartMoving(x, y);
12115
12116       element = Feld[x][y];
12117       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12118
12119       if (IS_ANIMATED(graphic) &&
12120           !IS_MOVING(x, y) &&
12121           !Stop[x][y])
12122         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12123
12124       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12125         DrawTwinkleOnField(x, y);
12126     }
12127     else if ((element == EL_ACID ||
12128               element == EL_EXIT_OPEN ||
12129               element == EL_EM_EXIT_OPEN ||
12130               element == EL_SP_EXIT_OPEN ||
12131               element == EL_STEEL_EXIT_OPEN ||
12132               element == EL_EM_STEEL_EXIT_OPEN ||
12133               element == EL_SP_TERMINAL ||
12134               element == EL_SP_TERMINAL_ACTIVE ||
12135               element == EL_EXTRA_TIME ||
12136               element == EL_SHIELD_NORMAL ||
12137               element == EL_SHIELD_DEADLY) &&
12138              IS_ANIMATED(graphic))
12139       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12140     else if (IS_MOVING(x, y))
12141       ContinueMoving(x, y);
12142     else if (IS_ACTIVE_BOMB(element))
12143       CheckDynamite(x, y);
12144     else if (element == EL_AMOEBA_GROWING)
12145       AmoebeWaechst(x, y);
12146     else if (element == EL_AMOEBA_SHRINKING)
12147       AmoebaDisappearing(x, y);
12148
12149 #if !USE_NEW_AMOEBA_CODE
12150     else if (IS_AMOEBALIVE(element))
12151       AmoebeAbleger(x, y);
12152 #endif
12153
12154     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12155       Life(x, y);
12156     else if (element == EL_EXIT_CLOSED)
12157       CheckExit(x, y);
12158     else if (element == EL_EM_EXIT_CLOSED)
12159       CheckExitEM(x, y);
12160     else if (element == EL_STEEL_EXIT_CLOSED)
12161       CheckExitSteel(x, y);
12162     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12163       CheckExitSteelEM(x, y);
12164     else if (element == EL_SP_EXIT_CLOSED)
12165       CheckExitSP(x, y);
12166     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12167              element == EL_EXPANDABLE_STEELWALL_GROWING)
12168       MauerWaechst(x, y);
12169     else if (element == EL_EXPANDABLE_WALL ||
12170              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12171              element == EL_EXPANDABLE_WALL_VERTICAL ||
12172              element == EL_EXPANDABLE_WALL_ANY ||
12173              element == EL_BD_EXPANDABLE_WALL)
12174       MauerAbleger(x, y);
12175     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12176              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12177              element == EL_EXPANDABLE_STEELWALL_ANY)
12178       MauerAblegerStahl(x, y);
12179     else if (element == EL_FLAMES)
12180       CheckForDragon(x, y);
12181     else if (element == EL_EXPLOSION)
12182       ; /* drawing of correct explosion animation is handled separately */
12183     else if (element == EL_ELEMENT_SNAPPING ||
12184              element == EL_DIAGONAL_SHRINKING ||
12185              element == EL_DIAGONAL_GROWING)
12186     {
12187       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12188
12189       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12190     }
12191     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12192       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12193
12194 #endif  // ---------------------------------------------------------------------
12195
12196     if (IS_BELT_ACTIVE(element))
12197       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12198
12199     if (game.magic_wall_active)
12200     {
12201       int jx = local_player->jx, jy = local_player->jy;
12202
12203       /* play the element sound at the position nearest to the player */
12204       if ((element == EL_MAGIC_WALL_FULL ||
12205            element == EL_MAGIC_WALL_ACTIVE ||
12206            element == EL_MAGIC_WALL_EMPTYING ||
12207            element == EL_BD_MAGIC_WALL_FULL ||
12208            element == EL_BD_MAGIC_WALL_ACTIVE ||
12209            element == EL_BD_MAGIC_WALL_EMPTYING ||
12210            element == EL_DC_MAGIC_WALL_FULL ||
12211            element == EL_DC_MAGIC_WALL_ACTIVE ||
12212            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12213           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12214       {
12215         magic_wall_x = x;
12216         magic_wall_y = y;
12217       }
12218     }
12219   }
12220
12221 #if 0
12222   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12223 #endif
12224
12225 #if USE_NEW_AMOEBA_CODE
12226   /* new experimental amoeba growth stuff */
12227   if (!(FrameCounter % 8))
12228   {
12229     static unsigned long random = 1684108901;
12230
12231     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12232     {
12233       x = RND(lev_fieldx);
12234       y = RND(lev_fieldy);
12235       element = Feld[x][y];
12236
12237       if (!IS_PLAYER(x,y) &&
12238           (element == EL_EMPTY ||
12239            CAN_GROW_INTO(element) ||
12240            element == EL_QUICKSAND_EMPTY ||
12241            element == EL_QUICKSAND_FAST_EMPTY ||
12242            element == EL_ACID_SPLASH_LEFT ||
12243            element == EL_ACID_SPLASH_RIGHT))
12244       {
12245         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12246             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12247             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12248             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12249           Feld[x][y] = EL_AMOEBA_DROP;
12250       }
12251
12252       random = random * 129 + 1;
12253     }
12254   }
12255 #endif
12256
12257 #if 0
12258   if (game.explosions_delayed)
12259 #endif
12260   {
12261     game.explosions_delayed = FALSE;
12262
12263     SCAN_PLAYFIELD(x, y)
12264     {
12265       element = Feld[x][y];
12266
12267       if (ExplodeField[x][y])
12268         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12269       else if (element == EL_EXPLOSION)
12270         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12271
12272       ExplodeField[x][y] = EX_TYPE_NONE;
12273     }
12274
12275     game.explosions_delayed = TRUE;
12276   }
12277
12278   if (game.magic_wall_active)
12279   {
12280     if (!(game.magic_wall_time_left % 4))
12281     {
12282       int element = Feld[magic_wall_x][magic_wall_y];
12283
12284       if (element == EL_BD_MAGIC_WALL_FULL ||
12285           element == EL_BD_MAGIC_WALL_ACTIVE ||
12286           element == EL_BD_MAGIC_WALL_EMPTYING)
12287         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12288       else if (element == EL_DC_MAGIC_WALL_FULL ||
12289                element == EL_DC_MAGIC_WALL_ACTIVE ||
12290                element == EL_DC_MAGIC_WALL_EMPTYING)
12291         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12292       else
12293         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12294     }
12295
12296     if (game.magic_wall_time_left > 0)
12297     {
12298       game.magic_wall_time_left--;
12299
12300       if (!game.magic_wall_time_left)
12301       {
12302         SCAN_PLAYFIELD(x, y)
12303         {
12304           element = Feld[x][y];
12305
12306           if (element == EL_MAGIC_WALL_ACTIVE ||
12307               element == EL_MAGIC_WALL_FULL)
12308           {
12309             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12310             DrawLevelField(x, y);
12311           }
12312           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12313                    element == EL_BD_MAGIC_WALL_FULL)
12314           {
12315             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12316             DrawLevelField(x, y);
12317           }
12318           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12319                    element == EL_DC_MAGIC_WALL_FULL)
12320           {
12321             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12322             DrawLevelField(x, y);
12323           }
12324         }
12325
12326         game.magic_wall_active = FALSE;
12327       }
12328     }
12329   }
12330
12331   if (game.light_time_left > 0)
12332   {
12333     game.light_time_left--;
12334
12335     if (game.light_time_left == 0)
12336       RedrawAllLightSwitchesAndInvisibleElements();
12337   }
12338
12339   if (game.timegate_time_left > 0)
12340   {
12341     game.timegate_time_left--;
12342
12343     if (game.timegate_time_left == 0)
12344       CloseAllOpenTimegates();
12345   }
12346
12347   if (game.lenses_time_left > 0)
12348   {
12349     game.lenses_time_left--;
12350
12351     if (game.lenses_time_left == 0)
12352       RedrawAllInvisibleElementsForLenses();
12353   }
12354
12355   if (game.magnify_time_left > 0)
12356   {
12357     game.magnify_time_left--;
12358
12359     if (game.magnify_time_left == 0)
12360       RedrawAllInvisibleElementsForMagnifier();
12361   }
12362
12363   for (i = 0; i < MAX_PLAYERS; i++)
12364   {
12365     struct PlayerInfo *player = &stored_player[i];
12366
12367     if (SHIELD_ON(player))
12368     {
12369       if (player->shield_deadly_time_left)
12370         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12371       else if (player->shield_normal_time_left)
12372         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12373     }
12374   }
12375
12376   CheckLevelTime();
12377
12378   DrawAllPlayers();
12379   PlayAllPlayersSound();
12380
12381   if (options.debug)                    /* calculate frames per second */
12382   {
12383     static unsigned long fps_counter = 0;
12384     static int fps_frames = 0;
12385     unsigned long fps_delay_ms = Counter() - fps_counter;
12386
12387     fps_frames++;
12388
12389     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12390     {
12391       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12392
12393       fps_frames = 0;
12394       fps_counter = Counter();
12395     }
12396
12397     redraw_mask |= REDRAW_FPS;
12398   }
12399
12400   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12401
12402   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12403   {
12404     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12405
12406     local_player->show_envelope = 0;
12407   }
12408
12409 #if 0
12410   debug_print_timestamp(0, "stop main loop profiling ");
12411   printf("----------------------------------------------------------\n");
12412 #endif
12413
12414   /* use random number generator in every frame to make it less predictable */
12415   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12416     RND(1);
12417 }
12418
12419 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12420 {
12421   int min_x = x, min_y = y, max_x = x, max_y = y;
12422   int i;
12423
12424   for (i = 0; i < MAX_PLAYERS; i++)
12425   {
12426     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12427
12428     if (!stored_player[i].active || &stored_player[i] == player)
12429       continue;
12430
12431     min_x = MIN(min_x, jx);
12432     min_y = MIN(min_y, jy);
12433     max_x = MAX(max_x, jx);
12434     max_y = MAX(max_y, jy);
12435   }
12436
12437   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12438 }
12439
12440 static boolean AllPlayersInVisibleScreen()
12441 {
12442   int i;
12443
12444   for (i = 0; i < MAX_PLAYERS; i++)
12445   {
12446     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12447
12448     if (!stored_player[i].active)
12449       continue;
12450
12451     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12452       return FALSE;
12453   }
12454
12455   return TRUE;
12456 }
12457
12458 void ScrollLevel(int dx, int dy)
12459 {
12460 #if 1
12461   static Bitmap *bitmap_db_field2 = NULL;
12462   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12463   int x, y;
12464 #else
12465   int i, x, y;
12466 #endif
12467
12468 #if 0
12469   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12470   /* only horizontal XOR vertical scroll direction allowed */
12471   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12472     return;
12473 #endif
12474
12475 #if 1
12476   if (bitmap_db_field2 == NULL)
12477     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12478
12479   /* needed when blitting directly to same bitmap -- should not be needed with
12480      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12481   BlitBitmap(drawto_field, bitmap_db_field2,
12482              FX + TILEX * (dx == -1) - softscroll_offset,
12483              FY + TILEY * (dy == -1) - softscroll_offset,
12484              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12485              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12486              FX + TILEX * (dx == 1) - softscroll_offset,
12487              FY + TILEY * (dy == 1) - softscroll_offset);
12488   BlitBitmap(bitmap_db_field2, drawto_field,
12489              FX + TILEX * (dx == 1) - softscroll_offset,
12490              FY + TILEY * (dy == 1) - softscroll_offset,
12491              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12492              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12493              FX + TILEX * (dx == 1) - softscroll_offset,
12494              FY + TILEY * (dy == 1) - softscroll_offset);
12495
12496 #else
12497
12498 #if 1
12499   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12500   int xsize = (BX2 - BX1 + 1);
12501   int ysize = (BY2 - BY1 + 1);
12502   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12503   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12504   int step  = (start < end ? +1 : -1);
12505
12506   for (i = start; i != end; i += step)
12507   {
12508     BlitBitmap(drawto_field, drawto_field,
12509                FX + TILEX * (dx != 0 ? i + step : 0),
12510                FY + TILEY * (dy != 0 ? i + step : 0),
12511                TILEX * (dx != 0 ? 1 : xsize),
12512                TILEY * (dy != 0 ? 1 : ysize),
12513                FX + TILEX * (dx != 0 ? i : 0),
12514                FY + TILEY * (dy != 0 ? i : 0));
12515   }
12516
12517 #else
12518
12519   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12520
12521   BlitBitmap(drawto_field, drawto_field,
12522              FX + TILEX * (dx == -1) - softscroll_offset,
12523              FY + TILEY * (dy == -1) - softscroll_offset,
12524              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12525              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12526              FX + TILEX * (dx == 1) - softscroll_offset,
12527              FY + TILEY * (dy == 1) - softscroll_offset);
12528 #endif
12529 #endif
12530
12531   if (dx != 0)
12532   {
12533     x = (dx == 1 ? BX1 : BX2);
12534     for (y = BY1; y <= BY2; y++)
12535       DrawScreenField(x, y);
12536   }
12537
12538   if (dy != 0)
12539   {
12540     y = (dy == 1 ? BY1 : BY2);
12541     for (x = BX1; x <= BX2; x++)
12542       DrawScreenField(x, y);
12543   }
12544
12545   redraw_mask |= REDRAW_FIELD;
12546 }
12547
12548 static boolean canFallDown(struct PlayerInfo *player)
12549 {
12550   int jx = player->jx, jy = player->jy;
12551
12552   return (IN_LEV_FIELD(jx, jy + 1) &&
12553           (IS_FREE(jx, jy + 1) ||
12554            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12555           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12556           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12557 }
12558
12559 static boolean canPassField(int x, int y, int move_dir)
12560 {
12561   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12562   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12563   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12564   int nextx = x + dx;
12565   int nexty = y + dy;
12566   int element = Feld[x][y];
12567
12568   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12569           !CAN_MOVE(element) &&
12570           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12571           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12572           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12573 }
12574
12575 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12576 {
12577   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12578   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12579   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12580   int newx = x + dx;
12581   int newy = y + dy;
12582
12583   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12584           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12585           (IS_DIGGABLE(Feld[newx][newy]) ||
12586            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12587            canPassField(newx, newy, move_dir)));
12588 }
12589
12590 static void CheckGravityMovement(struct PlayerInfo *player)
12591 {
12592 #if USE_PLAYER_GRAVITY
12593   if (player->gravity && !player->programmed_action)
12594 #else
12595   if (game.gravity && !player->programmed_action)
12596 #endif
12597   {
12598     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12599     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12600     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12601     int jx = player->jx, jy = player->jy;
12602     boolean player_is_moving_to_valid_field =
12603       (!player_is_snapping &&
12604        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12605         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12606     boolean player_can_fall_down = canFallDown(player);
12607
12608     if (player_can_fall_down &&
12609         !player_is_moving_to_valid_field)
12610       player->programmed_action = MV_DOWN;
12611   }
12612 }
12613
12614 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12615 {
12616   return CheckGravityMovement(player);
12617
12618 #if USE_PLAYER_GRAVITY
12619   if (player->gravity && !player->programmed_action)
12620 #else
12621   if (game.gravity && !player->programmed_action)
12622 #endif
12623   {
12624     int jx = player->jx, jy = player->jy;
12625     boolean field_under_player_is_free =
12626       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12627     boolean player_is_standing_on_valid_field =
12628       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12629        (IS_WALKABLE(Feld[jx][jy]) &&
12630         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12631
12632     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12633       player->programmed_action = MV_DOWN;
12634   }
12635 }
12636
12637 /*
12638   MovePlayerOneStep()
12639   -----------------------------------------------------------------------------
12640   dx, dy:               direction (non-diagonal) to try to move the player to
12641   real_dx, real_dy:     direction as read from input device (can be diagonal)
12642 */
12643
12644 boolean MovePlayerOneStep(struct PlayerInfo *player,
12645                           int dx, int dy, int real_dx, int real_dy)
12646 {
12647   int jx = player->jx, jy = player->jy;
12648   int new_jx = jx + dx, new_jy = jy + dy;
12649 #if !USE_FIXED_DONT_RUN_INTO
12650   int element;
12651 #endif
12652   int can_move;
12653   boolean player_can_move = !player->cannot_move;
12654
12655   if (!player->active || (!dx && !dy))
12656     return MP_NO_ACTION;
12657
12658   player->MovDir = (dx < 0 ? MV_LEFT :
12659                     dx > 0 ? MV_RIGHT :
12660                     dy < 0 ? MV_UP :
12661                     dy > 0 ? MV_DOWN :  MV_NONE);
12662
12663   if (!IN_LEV_FIELD(new_jx, new_jy))
12664     return MP_NO_ACTION;
12665
12666   if (!player_can_move)
12667   {
12668     if (player->MovPos == 0)
12669     {
12670       player->is_moving = FALSE;
12671       player->is_digging = FALSE;
12672       player->is_collecting = FALSE;
12673       player->is_snapping = FALSE;
12674       player->is_pushing = FALSE;
12675     }
12676   }
12677
12678 #if 1
12679   if (!options.network && game.centered_player_nr == -1 &&
12680       !AllPlayersInSight(player, new_jx, new_jy))
12681     return MP_NO_ACTION;
12682 #else
12683   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12684     return MP_NO_ACTION;
12685 #endif
12686
12687 #if !USE_FIXED_DONT_RUN_INTO
12688   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12689
12690   /* (moved to DigField()) */
12691   if (player_can_move && DONT_RUN_INTO(element))
12692   {
12693     if (element == EL_ACID && dx == 0 && dy == 1)
12694     {
12695       SplashAcid(new_jx, new_jy);
12696       Feld[jx][jy] = EL_PLAYER_1;
12697       InitMovingField(jx, jy, MV_DOWN);
12698       Store[jx][jy] = EL_ACID;
12699       ContinueMoving(jx, jy);
12700       BuryPlayer(player);
12701     }
12702     else
12703       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12704
12705     return MP_MOVING;
12706   }
12707 #endif
12708
12709   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12710   if (can_move != MP_MOVING)
12711     return can_move;
12712
12713   /* check if DigField() has caused relocation of the player */
12714   if (player->jx != jx || player->jy != jy)
12715     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12716
12717   StorePlayer[jx][jy] = 0;
12718   player->last_jx = jx;
12719   player->last_jy = jy;
12720   player->jx = new_jx;
12721   player->jy = new_jy;
12722   StorePlayer[new_jx][new_jy] = player->element_nr;
12723
12724   if (player->move_delay_value_next != -1)
12725   {
12726     player->move_delay_value = player->move_delay_value_next;
12727     player->move_delay_value_next = -1;
12728   }
12729
12730   player->MovPos =
12731     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12732
12733   player->step_counter++;
12734
12735   PlayerVisit[jx][jy] = FrameCounter;
12736
12737 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12738   player->is_moving = TRUE;
12739 #endif
12740
12741 #if 1
12742   /* should better be called in MovePlayer(), but this breaks some tapes */
12743   ScrollPlayer(player, SCROLL_INIT);
12744 #endif
12745
12746   return MP_MOVING;
12747 }
12748
12749 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12750 {
12751   int jx = player->jx, jy = player->jy;
12752   int old_jx = jx, old_jy = jy;
12753   int moved = MP_NO_ACTION;
12754
12755   if (!player->active)
12756     return FALSE;
12757
12758   if (!dx && !dy)
12759   {
12760     if (player->MovPos == 0)
12761     {
12762       player->is_moving = FALSE;
12763       player->is_digging = FALSE;
12764       player->is_collecting = FALSE;
12765       player->is_snapping = FALSE;
12766       player->is_pushing = FALSE;
12767     }
12768
12769     return FALSE;
12770   }
12771
12772   if (player->move_delay > 0)
12773     return FALSE;
12774
12775   player->move_delay = -1;              /* set to "uninitialized" value */
12776
12777   /* store if player is automatically moved to next field */
12778   player->is_auto_moving = (player->programmed_action != MV_NONE);
12779
12780   /* remove the last programmed player action */
12781   player->programmed_action = 0;
12782
12783   if (player->MovPos)
12784   {
12785     /* should only happen if pre-1.2 tape recordings are played */
12786     /* this is only for backward compatibility */
12787
12788     int original_move_delay_value = player->move_delay_value;
12789
12790 #if DEBUG
12791     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12792            tape.counter);
12793 #endif
12794
12795     /* scroll remaining steps with finest movement resolution */
12796     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12797
12798     while (player->MovPos)
12799     {
12800       ScrollPlayer(player, SCROLL_GO_ON);
12801       ScrollScreen(NULL, SCROLL_GO_ON);
12802
12803       AdvanceFrameAndPlayerCounters(player->index_nr);
12804
12805       DrawAllPlayers();
12806       BackToFront();
12807     }
12808
12809     player->move_delay_value = original_move_delay_value;
12810   }
12811
12812   player->is_active = FALSE;
12813
12814   if (player->last_move_dir & MV_HORIZONTAL)
12815   {
12816     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12817       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12818   }
12819   else
12820   {
12821     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12822       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12823   }
12824
12825 #if USE_FIXED_BORDER_RUNNING_GFX
12826   if (!moved && !player->is_active)
12827   {
12828     player->is_moving = FALSE;
12829     player->is_digging = FALSE;
12830     player->is_collecting = FALSE;
12831     player->is_snapping = FALSE;
12832     player->is_pushing = FALSE;
12833   }
12834 #endif
12835
12836   jx = player->jx;
12837   jy = player->jy;
12838
12839 #if 1
12840   if (moved & MP_MOVING && !ScreenMovPos &&
12841       (player->index_nr == game.centered_player_nr ||
12842        game.centered_player_nr == -1))
12843 #else
12844   if (moved & MP_MOVING && !ScreenMovPos &&
12845       (player == local_player || !options.network))
12846 #endif
12847   {
12848     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12849     int offset = game.scroll_delay_value;
12850
12851     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12852     {
12853       /* actual player has left the screen -- scroll in that direction */
12854       if (jx != old_jx)         /* player has moved horizontally */
12855         scroll_x += (jx - old_jx);
12856       else                      /* player has moved vertically */
12857         scroll_y += (jy - old_jy);
12858     }
12859     else
12860     {
12861       if (jx != old_jx)         /* player has moved horizontally */
12862       {
12863         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12864             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12865           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12866
12867         /* don't scroll over playfield boundaries */
12868         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12869           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12870
12871         /* don't scroll more than one field at a time */
12872         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12873
12874         /* don't scroll against the player's moving direction */
12875         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12876             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12877           scroll_x = old_scroll_x;
12878       }
12879       else                      /* player has moved vertically */
12880       {
12881         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12882             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12883           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12884
12885         /* don't scroll over playfield boundaries */
12886         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12887           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12888
12889         /* don't scroll more than one field at a time */
12890         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12891
12892         /* don't scroll against the player's moving direction */
12893         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12894             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12895           scroll_y = old_scroll_y;
12896       }
12897     }
12898
12899     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12900     {
12901 #if 1
12902       if (!options.network && game.centered_player_nr == -1 &&
12903           !AllPlayersInVisibleScreen())
12904       {
12905         scroll_x = old_scroll_x;
12906         scroll_y = old_scroll_y;
12907       }
12908       else
12909 #else
12910       if (!options.network && !AllPlayersInVisibleScreen())
12911       {
12912         scroll_x = old_scroll_x;
12913         scroll_y = old_scroll_y;
12914       }
12915       else
12916 #endif
12917       {
12918         ScrollScreen(player, SCROLL_INIT);
12919         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12920       }
12921     }
12922   }
12923
12924   player->StepFrame = 0;
12925
12926   if (moved & MP_MOVING)
12927   {
12928     if (old_jx != jx && old_jy == jy)
12929       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12930     else if (old_jx == jx && old_jy != jy)
12931       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12932
12933     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12934
12935     player->last_move_dir = player->MovDir;
12936     player->is_moving = TRUE;
12937     player->is_snapping = FALSE;
12938     player->is_switching = FALSE;
12939     player->is_dropping = FALSE;
12940     player->is_dropping_pressed = FALSE;
12941     player->drop_pressed_delay = 0;
12942
12943 #if 0
12944     /* should better be called here than above, but this breaks some tapes */
12945     ScrollPlayer(player, SCROLL_INIT);
12946 #endif
12947   }
12948   else
12949   {
12950     CheckGravityMovementWhenNotMoving(player);
12951
12952     player->is_moving = FALSE;
12953
12954     /* at this point, the player is allowed to move, but cannot move right now
12955        (e.g. because of something blocking the way) -- ensure that the player
12956        is also allowed to move in the next frame (in old versions before 3.1.1,
12957        the player was forced to wait again for eight frames before next try) */
12958
12959     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12960       player->move_delay = 0;   /* allow direct movement in the next frame */
12961   }
12962
12963   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12964     player->move_delay = player->move_delay_value;
12965
12966   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12967   {
12968     TestIfPlayerTouchesBadThing(jx, jy);
12969     TestIfPlayerTouchesCustomElement(jx, jy);
12970   }
12971
12972   if (!player->active)
12973     RemovePlayer(player);
12974
12975   return moved;
12976 }
12977
12978 void ScrollPlayer(struct PlayerInfo *player, int mode)
12979 {
12980   int jx = player->jx, jy = player->jy;
12981   int last_jx = player->last_jx, last_jy = player->last_jy;
12982   int move_stepsize = TILEX / player->move_delay_value;
12983
12984 #if USE_NEW_PLAYER_SPEED
12985   if (!player->active)
12986     return;
12987
12988   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12989     return;
12990 #else
12991   if (!player->active || player->MovPos == 0)
12992     return;
12993 #endif
12994
12995   if (mode == SCROLL_INIT)
12996   {
12997     player->actual_frame_counter = FrameCounter;
12998     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12999
13000     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13001         Feld[last_jx][last_jy] == EL_EMPTY)
13002     {
13003       int last_field_block_delay = 0;   /* start with no blocking at all */
13004       int block_delay_adjustment = player->block_delay_adjustment;
13005
13006       /* if player blocks last field, add delay for exactly one move */
13007       if (player->block_last_field)
13008       {
13009         last_field_block_delay += player->move_delay_value;
13010
13011         /* when blocking enabled, prevent moving up despite gravity */
13012 #if USE_PLAYER_GRAVITY
13013         if (player->gravity && player->MovDir == MV_UP)
13014           block_delay_adjustment = -1;
13015 #else
13016         if (game.gravity && player->MovDir == MV_UP)
13017           block_delay_adjustment = -1;
13018 #endif
13019       }
13020
13021       /* add block delay adjustment (also possible when not blocking) */
13022       last_field_block_delay += block_delay_adjustment;
13023
13024       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13025       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13026     }
13027
13028 #if USE_NEW_PLAYER_SPEED
13029     if (player->MovPos != 0)    /* player has not yet reached destination */
13030       return;
13031 #else
13032     return;
13033 #endif
13034   }
13035   else if (!FrameReached(&player->actual_frame_counter, 1))
13036     return;
13037
13038 #if USE_NEW_PLAYER_SPEED
13039   if (player->MovPos != 0)
13040   {
13041     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13042     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13043
13044     /* before DrawPlayer() to draw correct player graphic for this case */
13045     if (player->MovPos == 0)
13046       CheckGravityMovement(player);
13047   }
13048 #else
13049   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13050   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13051
13052   /* before DrawPlayer() to draw correct player graphic for this case */
13053   if (player->MovPos == 0)
13054     CheckGravityMovement(player);
13055 #endif
13056
13057   if (player->MovPos == 0)      /* player reached destination field */
13058   {
13059     if (player->move_delay_reset_counter > 0)
13060     {
13061       player->move_delay_reset_counter--;
13062
13063       if (player->move_delay_reset_counter == 0)
13064       {
13065         /* continue with normal speed after quickly moving through gate */
13066         HALVE_PLAYER_SPEED(player);
13067
13068         /* be able to make the next move without delay */
13069         player->move_delay = 0;
13070       }
13071     }
13072
13073     player->last_jx = jx;
13074     player->last_jy = jy;
13075
13076     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13077         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13078         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13079         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13080         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13081         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13082     {
13083       DrawPlayer(player);       /* needed here only to cleanup last field */
13084       RemovePlayer(player);
13085
13086       if (local_player->friends_still_needed == 0 ||
13087           IS_SP_ELEMENT(Feld[jx][jy]))
13088         PlayerWins(player);
13089     }
13090
13091     /* this breaks one level: "machine", level 000 */
13092     {
13093       int move_direction = player->MovDir;
13094       int enter_side = MV_DIR_OPPOSITE(move_direction);
13095       int leave_side = move_direction;
13096       int old_jx = last_jx;
13097       int old_jy = last_jy;
13098       int old_element = Feld[old_jx][old_jy];
13099       int new_element = Feld[jx][jy];
13100
13101       if (IS_CUSTOM_ELEMENT(old_element))
13102         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13103                                    CE_LEFT_BY_PLAYER,
13104                                    player->index_bit, leave_side);
13105
13106       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13107                                           CE_PLAYER_LEAVES_X,
13108                                           player->index_bit, leave_side);
13109
13110       if (IS_CUSTOM_ELEMENT(new_element))
13111         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13112                                    player->index_bit, enter_side);
13113
13114       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13115                                           CE_PLAYER_ENTERS_X,
13116                                           player->index_bit, enter_side);
13117
13118       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13119                                         CE_MOVE_OF_X, move_direction);
13120     }
13121
13122     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13123     {
13124       TestIfPlayerTouchesBadThing(jx, jy);
13125       TestIfPlayerTouchesCustomElement(jx, jy);
13126
13127       /* needed because pushed element has not yet reached its destination,
13128          so it would trigger a change event at its previous field location */
13129       if (!player->is_pushing)
13130         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13131
13132       if (!player->active)
13133         RemovePlayer(player);
13134     }
13135
13136     if (!local_player->LevelSolved && level.use_step_counter)
13137     {
13138       int i;
13139
13140       TimePlayed++;
13141
13142       if (TimeLeft > 0)
13143       {
13144         TimeLeft--;
13145
13146         if (TimeLeft <= 10 && setup.time_limit)
13147           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13148
13149 #if 1
13150         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13151
13152         DisplayGameControlValues();
13153 #else
13154         DrawGameValue_Time(TimeLeft);
13155 #endif
13156
13157         if (!TimeLeft && setup.time_limit)
13158           for (i = 0; i < MAX_PLAYERS; i++)
13159             KillPlayer(&stored_player[i]);
13160       }
13161 #if 1
13162       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13163       {
13164         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13165
13166         DisplayGameControlValues();
13167       }
13168 #else
13169       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13170         DrawGameValue_Time(TimePlayed);
13171 #endif
13172     }
13173
13174     if (tape.single_step && tape.recording && !tape.pausing &&
13175         !player->programmed_action)
13176       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13177   }
13178 }
13179
13180 void ScrollScreen(struct PlayerInfo *player, int mode)
13181 {
13182   static unsigned long screen_frame_counter = 0;
13183
13184   if (mode == SCROLL_INIT)
13185   {
13186     /* set scrolling step size according to actual player's moving speed */
13187     ScrollStepSize = TILEX / player->move_delay_value;
13188
13189     screen_frame_counter = FrameCounter;
13190     ScreenMovDir = player->MovDir;
13191     ScreenMovPos = player->MovPos;
13192     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13193     return;
13194   }
13195   else if (!FrameReached(&screen_frame_counter, 1))
13196     return;
13197
13198   if (ScreenMovPos)
13199   {
13200     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13201     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13202     redraw_mask |= REDRAW_FIELD;
13203   }
13204   else
13205     ScreenMovDir = MV_NONE;
13206 }
13207
13208 void TestIfPlayerTouchesCustomElement(int x, int y)
13209 {
13210   static int xy[4][2] =
13211   {
13212     { 0, -1 },
13213     { -1, 0 },
13214     { +1, 0 },
13215     { 0, +1 }
13216   };
13217   static int trigger_sides[4][2] =
13218   {
13219     /* center side       border side */
13220     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13221     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13222     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13223     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13224   };
13225   static int touch_dir[4] =
13226   {
13227     MV_LEFT | MV_RIGHT,
13228     MV_UP   | MV_DOWN,
13229     MV_UP   | MV_DOWN,
13230     MV_LEFT | MV_RIGHT
13231   };
13232   int center_element = Feld[x][y];      /* should always be non-moving! */
13233   int i;
13234
13235   for (i = 0; i < NUM_DIRECTIONS; i++)
13236   {
13237     int xx = x + xy[i][0];
13238     int yy = y + xy[i][1];
13239     int center_side = trigger_sides[i][0];
13240     int border_side = trigger_sides[i][1];
13241     int border_element;
13242
13243     if (!IN_LEV_FIELD(xx, yy))
13244       continue;
13245
13246     if (IS_PLAYER(x, y))
13247     {
13248       struct PlayerInfo *player = PLAYERINFO(x, y);
13249
13250       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13251         border_element = Feld[xx][yy];          /* may be moving! */
13252       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13253         border_element = Feld[xx][yy];
13254       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13255         border_element = MovingOrBlocked2Element(xx, yy);
13256       else
13257         continue;               /* center and border element do not touch */
13258
13259       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13260                                  player->index_bit, border_side);
13261       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13262                                           CE_PLAYER_TOUCHES_X,
13263                                           player->index_bit, border_side);
13264     }
13265     else if (IS_PLAYER(xx, yy))
13266     {
13267       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13268
13269       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13270       {
13271         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13272           continue;             /* center and border element do not touch */
13273       }
13274
13275       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13276                                  player->index_bit, center_side);
13277       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13278                                           CE_PLAYER_TOUCHES_X,
13279                                           player->index_bit, center_side);
13280       break;
13281     }
13282   }
13283 }
13284
13285 #if USE_ELEMENT_TOUCHING_BUGFIX
13286
13287 void TestIfElementTouchesCustomElement(int x, int y)
13288 {
13289   static int xy[4][2] =
13290   {
13291     { 0, -1 },
13292     { -1, 0 },
13293     { +1, 0 },
13294     { 0, +1 }
13295   };
13296   static int trigger_sides[4][2] =
13297   {
13298     /* center side      border side */
13299     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13300     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13301     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13302     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13303   };
13304   static int touch_dir[4] =
13305   {
13306     MV_LEFT | MV_RIGHT,
13307     MV_UP   | MV_DOWN,
13308     MV_UP   | MV_DOWN,
13309     MV_LEFT | MV_RIGHT
13310   };
13311   boolean change_center_element = FALSE;
13312   int center_element = Feld[x][y];      /* should always be non-moving! */
13313   int border_element_old[NUM_DIRECTIONS];
13314   int i;
13315
13316   for (i = 0; i < NUM_DIRECTIONS; i++)
13317   {
13318     int xx = x + xy[i][0];
13319     int yy = y + xy[i][1];
13320     int border_element;
13321
13322     border_element_old[i] = -1;
13323
13324     if (!IN_LEV_FIELD(xx, yy))
13325       continue;
13326
13327     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13328       border_element = Feld[xx][yy];    /* may be moving! */
13329     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13330       border_element = Feld[xx][yy];
13331     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13332       border_element = MovingOrBlocked2Element(xx, yy);
13333     else
13334       continue;                 /* center and border element do not touch */
13335
13336     border_element_old[i] = border_element;
13337   }
13338
13339   for (i = 0; i < NUM_DIRECTIONS; i++)
13340   {
13341     int xx = x + xy[i][0];
13342     int yy = y + xy[i][1];
13343     int center_side = trigger_sides[i][0];
13344     int border_element = border_element_old[i];
13345
13346     if (border_element == -1)
13347       continue;
13348
13349     /* check for change of border element */
13350     CheckElementChangeBySide(xx, yy, border_element, center_element,
13351                              CE_TOUCHING_X, center_side);
13352   }
13353
13354   for (i = 0; i < NUM_DIRECTIONS; i++)
13355   {
13356     int border_side = trigger_sides[i][1];
13357     int border_element = border_element_old[i];
13358
13359     if (border_element == -1)
13360       continue;
13361
13362     /* check for change of center element (but change it only once) */
13363     if (!change_center_element)
13364       change_center_element =
13365         CheckElementChangeBySide(x, y, center_element, border_element,
13366                                  CE_TOUCHING_X, border_side);
13367   }
13368 }
13369
13370 #else
13371
13372 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13373 {
13374   static int xy[4][2] =
13375   {
13376     { 0, -1 },
13377     { -1, 0 },
13378     { +1, 0 },
13379     { 0, +1 }
13380   };
13381   static int trigger_sides[4][2] =
13382   {
13383     /* center side      border side */
13384     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13385     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13386     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13387     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13388   };
13389   static int touch_dir[4] =
13390   {
13391     MV_LEFT | MV_RIGHT,
13392     MV_UP   | MV_DOWN,
13393     MV_UP   | MV_DOWN,
13394     MV_LEFT | MV_RIGHT
13395   };
13396   boolean change_center_element = FALSE;
13397   int center_element = Feld[x][y];      /* should always be non-moving! */
13398   int i;
13399
13400   for (i = 0; i < NUM_DIRECTIONS; i++)
13401   {
13402     int xx = x + xy[i][0];
13403     int yy = y + xy[i][1];
13404     int center_side = trigger_sides[i][0];
13405     int border_side = trigger_sides[i][1];
13406     int border_element;
13407
13408     if (!IN_LEV_FIELD(xx, yy))
13409       continue;
13410
13411     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13412       border_element = Feld[xx][yy];    /* may be moving! */
13413     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13414       border_element = Feld[xx][yy];
13415     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13416       border_element = MovingOrBlocked2Element(xx, yy);
13417     else
13418       continue;                 /* center and border element do not touch */
13419
13420     /* check for change of center element (but change it only once) */
13421     if (!change_center_element)
13422       change_center_element =
13423         CheckElementChangeBySide(x, y, center_element, border_element,
13424                                  CE_TOUCHING_X, border_side);
13425
13426     /* check for change of border element */
13427     CheckElementChangeBySide(xx, yy, border_element, center_element,
13428                              CE_TOUCHING_X, center_side);
13429   }
13430 }
13431
13432 #endif
13433
13434 void TestIfElementHitsCustomElement(int x, int y, int direction)
13435 {
13436   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13437   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13438   int hitx = x + dx, hity = y + dy;
13439   int hitting_element = Feld[x][y];
13440   int touched_element;
13441
13442   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13443     return;
13444
13445   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13446                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13447
13448   if (IN_LEV_FIELD(hitx, hity))
13449   {
13450     int opposite_direction = MV_DIR_OPPOSITE(direction);
13451     int hitting_side = direction;
13452     int touched_side = opposite_direction;
13453     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13454                           MovDir[hitx][hity] != direction ||
13455                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13456
13457     object_hit = TRUE;
13458
13459     if (object_hit)
13460     {
13461       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13462                                CE_HITTING_X, touched_side);
13463
13464       CheckElementChangeBySide(hitx, hity, touched_element,
13465                                hitting_element, CE_HIT_BY_X, hitting_side);
13466
13467       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13468                                CE_HIT_BY_SOMETHING, opposite_direction);
13469     }
13470   }
13471
13472   /* "hitting something" is also true when hitting the playfield border */
13473   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13474                            CE_HITTING_SOMETHING, direction);
13475 }
13476
13477 #if 0
13478 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13479 {
13480   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13481   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13482   int hitx = x + dx, hity = y + dy;
13483   int hitting_element = Feld[x][y];
13484   int touched_element;
13485 #if 0
13486   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13487                         !IS_FREE(hitx, hity) &&
13488                         (!IS_MOVING(hitx, hity) ||
13489                          MovDir[hitx][hity] != direction ||
13490                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13491 #endif
13492
13493   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13494     return;
13495
13496 #if 0
13497   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13498     return;
13499 #endif
13500
13501   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13502                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13503
13504   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13505                            EP_CAN_SMASH_EVERYTHING, direction);
13506
13507   if (IN_LEV_FIELD(hitx, hity))
13508   {
13509     int opposite_direction = MV_DIR_OPPOSITE(direction);
13510     int hitting_side = direction;
13511     int touched_side = opposite_direction;
13512 #if 0
13513     int touched_element = MovingOrBlocked2Element(hitx, hity);
13514 #endif
13515 #if 1
13516     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13517                           MovDir[hitx][hity] != direction ||
13518                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13519
13520     object_hit = TRUE;
13521 #endif
13522
13523     if (object_hit)
13524     {
13525       int i;
13526
13527       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13528                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13529
13530       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13531                                CE_OTHER_IS_SMASHING, touched_side);
13532
13533       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13534                                CE_OTHER_GETS_SMASHED, hitting_side);
13535     }
13536   }
13537 }
13538 #endif
13539
13540 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13541 {
13542   int i, kill_x = -1, kill_y = -1;
13543
13544   int bad_element = -1;
13545   static int test_xy[4][2] =
13546   {
13547     { 0, -1 },
13548     { -1, 0 },
13549     { +1, 0 },
13550     { 0, +1 }
13551   };
13552   static int test_dir[4] =
13553   {
13554     MV_UP,
13555     MV_LEFT,
13556     MV_RIGHT,
13557     MV_DOWN
13558   };
13559
13560   for (i = 0; i < NUM_DIRECTIONS; i++)
13561   {
13562     int test_x, test_y, test_move_dir, test_element;
13563
13564     test_x = good_x + test_xy[i][0];
13565     test_y = good_y + test_xy[i][1];
13566
13567     if (!IN_LEV_FIELD(test_x, test_y))
13568       continue;
13569
13570     test_move_dir =
13571       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13572
13573     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13574
13575     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13576        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13577     */
13578     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13579         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13580     {
13581       kill_x = test_x;
13582       kill_y = test_y;
13583       bad_element = test_element;
13584
13585       break;
13586     }
13587   }
13588
13589   if (kill_x != -1 || kill_y != -1)
13590   {
13591     if (IS_PLAYER(good_x, good_y))
13592     {
13593       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13594
13595       if (player->shield_deadly_time_left > 0 &&
13596           !IS_INDESTRUCTIBLE(bad_element))
13597         Bang(kill_x, kill_y);
13598       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13599         KillPlayer(player);
13600     }
13601     else
13602       Bang(good_x, good_y);
13603   }
13604 }
13605
13606 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13607 {
13608   int i, kill_x = -1, kill_y = -1;
13609   int bad_element = Feld[bad_x][bad_y];
13610   static int test_xy[4][2] =
13611   {
13612     { 0, -1 },
13613     { -1, 0 },
13614     { +1, 0 },
13615     { 0, +1 }
13616   };
13617   static int touch_dir[4] =
13618   {
13619     MV_LEFT | MV_RIGHT,
13620     MV_UP   | MV_DOWN,
13621     MV_UP   | MV_DOWN,
13622     MV_LEFT | MV_RIGHT
13623   };
13624   static int test_dir[4] =
13625   {
13626     MV_UP,
13627     MV_LEFT,
13628     MV_RIGHT,
13629     MV_DOWN
13630   };
13631
13632   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13633     return;
13634
13635   for (i = 0; i < NUM_DIRECTIONS; i++)
13636   {
13637     int test_x, test_y, test_move_dir, test_element;
13638
13639     test_x = bad_x + test_xy[i][0];
13640     test_y = bad_y + test_xy[i][1];
13641     if (!IN_LEV_FIELD(test_x, test_y))
13642       continue;
13643
13644     test_move_dir =
13645       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13646
13647     test_element = Feld[test_x][test_y];
13648
13649     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13650        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13651     */
13652     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13653         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13654     {
13655       /* good thing is player or penguin that does not move away */
13656       if (IS_PLAYER(test_x, test_y))
13657       {
13658         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13659
13660         if (bad_element == EL_ROBOT && player->is_moving)
13661           continue;     /* robot does not kill player if he is moving */
13662
13663         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13664         {
13665           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13666             continue;           /* center and border element do not touch */
13667         }
13668
13669         kill_x = test_x;
13670         kill_y = test_y;
13671         break;
13672       }
13673       else if (test_element == EL_PENGUIN)
13674       {
13675         kill_x = test_x;
13676         kill_y = test_y;
13677         break;
13678       }
13679     }
13680   }
13681
13682   if (kill_x != -1 || kill_y != -1)
13683   {
13684     if (IS_PLAYER(kill_x, kill_y))
13685     {
13686       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13687
13688       if (player->shield_deadly_time_left > 0 &&
13689           !IS_INDESTRUCTIBLE(bad_element))
13690         Bang(bad_x, bad_y);
13691       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13692         KillPlayer(player);
13693     }
13694     else
13695       Bang(kill_x, kill_y);
13696   }
13697 }
13698
13699 void TestIfPlayerTouchesBadThing(int x, int y)
13700 {
13701   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13702 }
13703
13704 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13705 {
13706   TestIfGoodThingHitsBadThing(x, y, move_dir);
13707 }
13708
13709 void TestIfBadThingTouchesPlayer(int x, int y)
13710 {
13711   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13712 }
13713
13714 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13715 {
13716   TestIfBadThingHitsGoodThing(x, y, move_dir);
13717 }
13718
13719 void TestIfFriendTouchesBadThing(int x, int y)
13720 {
13721   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13722 }
13723
13724 void TestIfBadThingTouchesFriend(int x, int y)
13725 {
13726   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13727 }
13728
13729 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13730 {
13731   int i, kill_x = bad_x, kill_y = bad_y;
13732   static int xy[4][2] =
13733   {
13734     { 0, -1 },
13735     { -1, 0 },
13736     { +1, 0 },
13737     { 0, +1 }
13738   };
13739
13740   for (i = 0; i < NUM_DIRECTIONS; i++)
13741   {
13742     int x, y, element;
13743
13744     x = bad_x + xy[i][0];
13745     y = bad_y + xy[i][1];
13746     if (!IN_LEV_FIELD(x, y))
13747       continue;
13748
13749     element = Feld[x][y];
13750     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13751         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13752     {
13753       kill_x = x;
13754       kill_y = y;
13755       break;
13756     }
13757   }
13758
13759   if (kill_x != bad_x || kill_y != bad_y)
13760     Bang(bad_x, bad_y);
13761 }
13762
13763 void KillPlayer(struct PlayerInfo *player)
13764 {
13765   int jx = player->jx, jy = player->jy;
13766
13767   if (!player->active)
13768     return;
13769
13770   /* the following code was introduced to prevent an infinite loop when calling
13771      -> Bang()
13772      -> CheckTriggeredElementChangeExt()
13773      -> ExecuteCustomElementAction()
13774      -> KillPlayer()
13775      -> (infinitely repeating the above sequence of function calls)
13776      which occurs when killing the player while having a CE with the setting
13777      "kill player X when explosion of <player X>"; the solution using a new
13778      field "player->killed" was chosen for backwards compatibility, although
13779      clever use of the fields "player->active" etc. would probably also work */
13780 #if 1
13781   if (player->killed)
13782     return;
13783 #endif
13784
13785   player->killed = TRUE;
13786
13787   /* remove accessible field at the player's position */
13788   Feld[jx][jy] = EL_EMPTY;
13789
13790   /* deactivate shield (else Bang()/Explode() would not work right) */
13791   player->shield_normal_time_left = 0;
13792   player->shield_deadly_time_left = 0;
13793
13794   Bang(jx, jy);
13795   BuryPlayer(player);
13796 }
13797
13798 static void KillPlayerUnlessEnemyProtected(int x, int y)
13799 {
13800   if (!PLAYER_ENEMY_PROTECTED(x, y))
13801     KillPlayer(PLAYERINFO(x, y));
13802 }
13803
13804 static void KillPlayerUnlessExplosionProtected(int x, int y)
13805 {
13806   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13807     KillPlayer(PLAYERINFO(x, y));
13808 }
13809
13810 void BuryPlayer(struct PlayerInfo *player)
13811 {
13812   int jx = player->jx, jy = player->jy;
13813
13814   if (!player->active)
13815     return;
13816
13817   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13818   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13819
13820   player->GameOver = TRUE;
13821   RemovePlayer(player);
13822 }
13823
13824 void RemovePlayer(struct PlayerInfo *player)
13825 {
13826   int jx = player->jx, jy = player->jy;
13827   int i, found = FALSE;
13828
13829   player->present = FALSE;
13830   player->active = FALSE;
13831
13832   if (!ExplodeField[jx][jy])
13833     StorePlayer[jx][jy] = 0;
13834
13835   if (player->is_moving)
13836     DrawLevelField(player->last_jx, player->last_jy);
13837
13838   for (i = 0; i < MAX_PLAYERS; i++)
13839     if (stored_player[i].active)
13840       found = TRUE;
13841
13842   if (!found)
13843     AllPlayersGone = TRUE;
13844
13845   ExitX = ZX = jx;
13846   ExitY = ZY = jy;
13847 }
13848
13849 #if USE_NEW_SNAP_DELAY
13850 static void setFieldForSnapping(int x, int y, int element, int direction)
13851 {
13852   struct ElementInfo *ei = &element_info[element];
13853   int direction_bit = MV_DIR_TO_BIT(direction);
13854   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13855   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13856                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13857
13858   Feld[x][y] = EL_ELEMENT_SNAPPING;
13859   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13860
13861   ResetGfxAnimation(x, y);
13862
13863   GfxElement[x][y] = element;
13864   GfxAction[x][y] = action;
13865   GfxDir[x][y] = direction;
13866   GfxFrame[x][y] = -1;
13867 }
13868 #endif
13869
13870 /*
13871   =============================================================================
13872   checkDiagonalPushing()
13873   -----------------------------------------------------------------------------
13874   check if diagonal input device direction results in pushing of object
13875   (by checking if the alternative direction is walkable, diggable, ...)
13876   =============================================================================
13877 */
13878
13879 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13880                                     int x, int y, int real_dx, int real_dy)
13881 {
13882   int jx, jy, dx, dy, xx, yy;
13883
13884   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13885     return TRUE;
13886
13887   /* diagonal direction: check alternative direction */
13888   jx = player->jx;
13889   jy = player->jy;
13890   dx = x - jx;
13891   dy = y - jy;
13892   xx = jx + (dx == 0 ? real_dx : 0);
13893   yy = jy + (dy == 0 ? real_dy : 0);
13894
13895   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13896 }
13897
13898 /*
13899   =============================================================================
13900   DigField()
13901   -----------------------------------------------------------------------------
13902   x, y:                 field next to player (non-diagonal) to try to dig to
13903   real_dx, real_dy:     direction as read from input device (can be diagonal)
13904   =============================================================================
13905 */
13906
13907 int DigField(struct PlayerInfo *player,
13908              int oldx, int oldy, int x, int y,
13909              int real_dx, int real_dy, int mode)
13910 {
13911   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13912   boolean player_was_pushing = player->is_pushing;
13913   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13914   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13915   int jx = oldx, jy = oldy;
13916   int dx = x - jx, dy = y - jy;
13917   int nextx = x + dx, nexty = y + dy;
13918   int move_direction = (dx == -1 ? MV_LEFT  :
13919                         dx == +1 ? MV_RIGHT :
13920                         dy == -1 ? MV_UP    :
13921                         dy == +1 ? MV_DOWN  : MV_NONE);
13922   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13923   int dig_side = MV_DIR_OPPOSITE(move_direction);
13924   int old_element = Feld[jx][jy];
13925 #if USE_FIXED_DONT_RUN_INTO
13926   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13927 #else
13928   int element;
13929 #endif
13930   int collect_count;
13931
13932   if (is_player)                /* function can also be called by EL_PENGUIN */
13933   {
13934     if (player->MovPos == 0)
13935     {
13936       player->is_digging = FALSE;
13937       player->is_collecting = FALSE;
13938     }
13939
13940     if (player->MovPos == 0)    /* last pushing move finished */
13941       player->is_pushing = FALSE;
13942
13943     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13944     {
13945       player->is_switching = FALSE;
13946       player->push_delay = -1;
13947
13948       return MP_NO_ACTION;
13949     }
13950   }
13951
13952 #if !USE_FIXED_DONT_RUN_INTO
13953   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13954     return MP_NO_ACTION;
13955 #endif
13956
13957   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13958     old_element = Back[jx][jy];
13959
13960   /* in case of element dropped at player position, check background */
13961   else if (Back[jx][jy] != EL_EMPTY &&
13962            game.engine_version >= VERSION_IDENT(2,2,0,0))
13963     old_element = Back[jx][jy];
13964
13965   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13966     return MP_NO_ACTION;        /* field has no opening in this direction */
13967
13968   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13969     return MP_NO_ACTION;        /* field has no opening in this direction */
13970
13971 #if USE_FIXED_DONT_RUN_INTO
13972   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13973   {
13974     SplashAcid(x, y);
13975
13976     Feld[jx][jy] = player->artwork_element;
13977     InitMovingField(jx, jy, MV_DOWN);
13978     Store[jx][jy] = EL_ACID;
13979     ContinueMoving(jx, jy);
13980     BuryPlayer(player);
13981
13982     return MP_DONT_RUN_INTO;
13983   }
13984 #endif
13985
13986 #if USE_FIXED_DONT_RUN_INTO
13987   if (player_can_move && DONT_RUN_INTO(element))
13988   {
13989     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13990
13991     return MP_DONT_RUN_INTO;
13992   }
13993 #endif
13994
13995 #if USE_FIXED_DONT_RUN_INTO
13996   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13997     return MP_NO_ACTION;
13998 #endif
13999
14000 #if !USE_FIXED_DONT_RUN_INTO
14001   element = Feld[x][y];
14002 #endif
14003
14004   collect_count = element_info[element].collect_count_initial;
14005
14006   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14007     return MP_NO_ACTION;
14008
14009   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14010     player_can_move = player_can_move_or_snap;
14011
14012   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14013       game.engine_version >= VERSION_IDENT(2,2,0,0))
14014   {
14015     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14016                                player->index_bit, dig_side);
14017     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14018                                         player->index_bit, dig_side);
14019
14020     if (element == EL_DC_LANDMINE)
14021       Bang(x, y);
14022
14023     if (Feld[x][y] != element)          /* field changed by snapping */
14024       return MP_ACTION;
14025
14026     return MP_NO_ACTION;
14027   }
14028
14029 #if USE_PLAYER_GRAVITY
14030   if (player->gravity && is_player && !player->is_auto_moving &&
14031       canFallDown(player) && move_direction != MV_DOWN &&
14032       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14033     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14034 #else
14035   if (game.gravity && is_player && !player->is_auto_moving &&
14036       canFallDown(player) && move_direction != MV_DOWN &&
14037       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14038     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14039 #endif
14040
14041   if (player_can_move &&
14042       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14043   {
14044     int sound_element = SND_ELEMENT(element);
14045     int sound_action = ACTION_WALKING;
14046
14047     if (IS_RND_GATE(element))
14048     {
14049       if (!player->key[RND_GATE_NR(element)])
14050         return MP_NO_ACTION;
14051     }
14052     else if (IS_RND_GATE_GRAY(element))
14053     {
14054       if (!player->key[RND_GATE_GRAY_NR(element)])
14055         return MP_NO_ACTION;
14056     }
14057     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14058     {
14059       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14060         return MP_NO_ACTION;
14061     }
14062     else if (element == EL_EXIT_OPEN ||
14063              element == EL_EM_EXIT_OPEN ||
14064              element == EL_STEEL_EXIT_OPEN ||
14065              element == EL_EM_STEEL_EXIT_OPEN ||
14066              element == EL_SP_EXIT_OPEN ||
14067              element == EL_SP_EXIT_OPENING)
14068     {
14069       sound_action = ACTION_PASSING;    /* player is passing exit */
14070     }
14071     else if (element == EL_EMPTY)
14072     {
14073       sound_action = ACTION_MOVING;             /* nothing to walk on */
14074     }
14075
14076     /* play sound from background or player, whatever is available */
14077     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14078       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14079     else
14080       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14081   }
14082   else if (player_can_move &&
14083            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14084   {
14085     if (!ACCESS_FROM(element, opposite_direction))
14086       return MP_NO_ACTION;      /* field not accessible from this direction */
14087
14088     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14089       return MP_NO_ACTION;
14090
14091     if (IS_EM_GATE(element))
14092     {
14093       if (!player->key[EM_GATE_NR(element)])
14094         return MP_NO_ACTION;
14095     }
14096     else if (IS_EM_GATE_GRAY(element))
14097     {
14098       if (!player->key[EM_GATE_GRAY_NR(element)])
14099         return MP_NO_ACTION;
14100     }
14101     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14102     {
14103       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14104         return MP_NO_ACTION;
14105     }
14106     else if (IS_EMC_GATE(element))
14107     {
14108       if (!player->key[EMC_GATE_NR(element)])
14109         return MP_NO_ACTION;
14110     }
14111     else if (IS_EMC_GATE_GRAY(element))
14112     {
14113       if (!player->key[EMC_GATE_GRAY_NR(element)])
14114         return MP_NO_ACTION;
14115     }
14116     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14117     {
14118       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14119         return MP_NO_ACTION;
14120     }
14121     else if (element == EL_DC_GATE_WHITE ||
14122              element == EL_DC_GATE_WHITE_GRAY ||
14123              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14124     {
14125       if (player->num_white_keys == 0)
14126         return MP_NO_ACTION;
14127
14128       player->num_white_keys--;
14129     }
14130     else if (IS_SP_PORT(element))
14131     {
14132       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14133           element == EL_SP_GRAVITY_PORT_RIGHT ||
14134           element == EL_SP_GRAVITY_PORT_UP ||
14135           element == EL_SP_GRAVITY_PORT_DOWN)
14136 #if USE_PLAYER_GRAVITY
14137         player->gravity = !player->gravity;
14138 #else
14139         game.gravity = !game.gravity;
14140 #endif
14141       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14142                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14143                element == EL_SP_GRAVITY_ON_PORT_UP ||
14144                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14145 #if USE_PLAYER_GRAVITY
14146         player->gravity = TRUE;
14147 #else
14148         game.gravity = TRUE;
14149 #endif
14150       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14151                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14152                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14153                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14154 #if USE_PLAYER_GRAVITY
14155         player->gravity = FALSE;
14156 #else
14157         game.gravity = FALSE;
14158 #endif
14159     }
14160
14161     /* automatically move to the next field with double speed */
14162     player->programmed_action = move_direction;
14163
14164     if (player->move_delay_reset_counter == 0)
14165     {
14166       player->move_delay_reset_counter = 2;     /* two double speed steps */
14167
14168       DOUBLE_PLAYER_SPEED(player);
14169     }
14170
14171     PlayLevelSoundAction(x, y, ACTION_PASSING);
14172   }
14173   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14174   {
14175     RemoveField(x, y);
14176
14177     if (mode != DF_SNAP)
14178     {
14179       GfxElement[x][y] = GFX_ELEMENT(element);
14180       player->is_digging = TRUE;
14181     }
14182
14183     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14184
14185     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14186                                         player->index_bit, dig_side);
14187
14188     if (mode == DF_SNAP)
14189     {
14190 #if USE_NEW_SNAP_DELAY
14191       if (level.block_snap_field)
14192         setFieldForSnapping(x, y, element, move_direction);
14193       else
14194         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14195 #else
14196       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14197 #endif
14198
14199       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14200                                           player->index_bit, dig_side);
14201     }
14202   }
14203   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14204   {
14205     RemoveField(x, y);
14206
14207     if (is_player && mode != DF_SNAP)
14208     {
14209       GfxElement[x][y] = element;
14210       player->is_collecting = TRUE;
14211     }
14212
14213     if (element == EL_SPEED_PILL)
14214     {
14215       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14216     }
14217     else if (element == EL_EXTRA_TIME && level.time > 0)
14218     {
14219       TimeLeft += level.extra_time;
14220
14221 #if 1
14222       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14223
14224       DisplayGameControlValues();
14225 #else
14226       DrawGameValue_Time(TimeLeft);
14227 #endif
14228     }
14229     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14230     {
14231       player->shield_normal_time_left += level.shield_normal_time;
14232       if (element == EL_SHIELD_DEADLY)
14233         player->shield_deadly_time_left += level.shield_deadly_time;
14234     }
14235     else if (element == EL_DYNAMITE ||
14236              element == EL_EM_DYNAMITE ||
14237              element == EL_SP_DISK_RED)
14238     {
14239       if (player->inventory_size < MAX_INVENTORY_SIZE)
14240         player->inventory_element[player->inventory_size++] = element;
14241
14242       DrawGameDoorValues();
14243     }
14244     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14245     {
14246       player->dynabomb_count++;
14247       player->dynabombs_left++;
14248     }
14249     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14250     {
14251       player->dynabomb_size++;
14252     }
14253     else if (element == EL_DYNABOMB_INCREASE_POWER)
14254     {
14255       player->dynabomb_xl = TRUE;
14256     }
14257     else if (IS_KEY(element))
14258     {
14259       player->key[KEY_NR(element)] = TRUE;
14260
14261       DrawGameDoorValues();
14262     }
14263     else if (element == EL_DC_KEY_WHITE)
14264     {
14265       player->num_white_keys++;
14266
14267       /* display white keys? */
14268       /* DrawGameDoorValues(); */
14269     }
14270     else if (IS_ENVELOPE(element))
14271     {
14272       player->show_envelope = element;
14273     }
14274     else if (element == EL_EMC_LENSES)
14275     {
14276       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14277
14278       RedrawAllInvisibleElementsForLenses();
14279     }
14280     else if (element == EL_EMC_MAGNIFIER)
14281     {
14282       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14283
14284       RedrawAllInvisibleElementsForMagnifier();
14285     }
14286     else if (IS_DROPPABLE(element) ||
14287              IS_THROWABLE(element))     /* can be collected and dropped */
14288     {
14289       int i;
14290
14291       if (collect_count == 0)
14292         player->inventory_infinite_element = element;
14293       else
14294         for (i = 0; i < collect_count; i++)
14295           if (player->inventory_size < MAX_INVENTORY_SIZE)
14296             player->inventory_element[player->inventory_size++] = element;
14297
14298       DrawGameDoorValues();
14299     }
14300     else if (collect_count > 0)
14301     {
14302       local_player->gems_still_needed -= collect_count;
14303       if (local_player->gems_still_needed < 0)
14304         local_player->gems_still_needed = 0;
14305
14306 #if 1
14307       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14308
14309       DisplayGameControlValues();
14310 #else
14311       DrawGameValue_Emeralds(local_player->gems_still_needed);
14312 #endif
14313     }
14314
14315     RaiseScoreElement(element);
14316     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14317
14318     if (is_player)
14319       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14320                                           player->index_bit, dig_side);
14321
14322     if (mode == DF_SNAP)
14323     {
14324 #if USE_NEW_SNAP_DELAY
14325       if (level.block_snap_field)
14326         setFieldForSnapping(x, y, element, move_direction);
14327       else
14328         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14329 #else
14330       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14331 #endif
14332
14333       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14334                                           player->index_bit, dig_side);
14335     }
14336   }
14337   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14338   {
14339     if (mode == DF_SNAP && element != EL_BD_ROCK)
14340       return MP_NO_ACTION;
14341
14342     if (CAN_FALL(element) && dy)
14343       return MP_NO_ACTION;
14344
14345     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14346         !(element == EL_SPRING && level.use_spring_bug))
14347       return MP_NO_ACTION;
14348
14349     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14350         ((move_direction & MV_VERTICAL &&
14351           ((element_info[element].move_pattern & MV_LEFT &&
14352             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14353            (element_info[element].move_pattern & MV_RIGHT &&
14354             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14355          (move_direction & MV_HORIZONTAL &&
14356           ((element_info[element].move_pattern & MV_UP &&
14357             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14358            (element_info[element].move_pattern & MV_DOWN &&
14359             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14360       return MP_NO_ACTION;
14361
14362     /* do not push elements already moving away faster than player */
14363     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14364         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14365       return MP_NO_ACTION;
14366
14367     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14368     {
14369       if (player->push_delay_value == -1 || !player_was_pushing)
14370         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14371     }
14372     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14373     {
14374       if (player->push_delay_value == -1)
14375         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14376     }
14377     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14378     {
14379       if (!player->is_pushing)
14380         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14381     }
14382
14383     player->is_pushing = TRUE;
14384     player->is_active = TRUE;
14385
14386     if (!(IN_LEV_FIELD(nextx, nexty) &&
14387           (IS_FREE(nextx, nexty) ||
14388            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14389             IS_SB_ELEMENT(element)))))
14390       return MP_NO_ACTION;
14391
14392     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14393       return MP_NO_ACTION;
14394
14395     if (player->push_delay == -1)       /* new pushing; restart delay */
14396       player->push_delay = 0;
14397
14398     if (player->push_delay < player->push_delay_value &&
14399         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14400         element != EL_SPRING && element != EL_BALLOON)
14401     {
14402       /* make sure that there is no move delay before next try to push */
14403       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14404         player->move_delay = 0;
14405
14406       return MP_NO_ACTION;
14407     }
14408
14409     if (IS_SB_ELEMENT(element))
14410     {
14411       if (element == EL_SOKOBAN_FIELD_FULL)
14412       {
14413         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14414         local_player->sokobanfields_still_needed++;
14415       }
14416
14417       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14418       {
14419         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14420         local_player->sokobanfields_still_needed--;
14421       }
14422
14423       Feld[x][y] = EL_SOKOBAN_OBJECT;
14424
14425       if (Back[x][y] == Back[nextx][nexty])
14426         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14427       else if (Back[x][y] != 0)
14428         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14429                                     ACTION_EMPTYING);
14430       else
14431         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14432                                     ACTION_FILLING);
14433
14434       if (local_player->sokobanfields_still_needed == 0 &&
14435           game.emulation == EMU_SOKOBAN)
14436       {
14437         PlayerWins(player);
14438
14439         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14440       }
14441     }
14442     else
14443       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14444
14445     InitMovingField(x, y, move_direction);
14446     GfxAction[x][y] = ACTION_PUSHING;
14447
14448     if (mode == DF_SNAP)
14449       ContinueMoving(x, y);
14450     else
14451       MovPos[x][y] = (dx != 0 ? dx : dy);
14452
14453     Pushed[x][y] = TRUE;
14454     Pushed[nextx][nexty] = TRUE;
14455
14456     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14457       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14458     else
14459       player->push_delay_value = -1;    /* get new value later */
14460
14461     /* check for element change _after_ element has been pushed */
14462     if (game.use_change_when_pushing_bug)
14463     {
14464       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14465                                  player->index_bit, dig_side);
14466       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14467                                           player->index_bit, dig_side);
14468     }
14469   }
14470   else if (IS_SWITCHABLE(element))
14471   {
14472     if (PLAYER_SWITCHING(player, x, y))
14473     {
14474       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14475                                           player->index_bit, dig_side);
14476
14477       return MP_ACTION;
14478     }
14479
14480     player->is_switching = TRUE;
14481     player->switch_x = x;
14482     player->switch_y = y;
14483
14484     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14485
14486     if (element == EL_ROBOT_WHEEL)
14487     {
14488       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14489       ZX = x;
14490       ZY = y;
14491
14492       game.robot_wheel_active = TRUE;
14493
14494       DrawLevelField(x, y);
14495     }
14496     else if (element == EL_SP_TERMINAL)
14497     {
14498       int xx, yy;
14499
14500       SCAN_PLAYFIELD(xx, yy)
14501       {
14502         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14503           Bang(xx, yy);
14504         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14505           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14506       }
14507     }
14508     else if (IS_BELT_SWITCH(element))
14509     {
14510       ToggleBeltSwitch(x, y);
14511     }
14512     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14513              element == EL_SWITCHGATE_SWITCH_DOWN ||
14514              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14515              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14516     {
14517       ToggleSwitchgateSwitch(x, y);
14518     }
14519     else if (element == EL_LIGHT_SWITCH ||
14520              element == EL_LIGHT_SWITCH_ACTIVE)
14521     {
14522       ToggleLightSwitch(x, y);
14523     }
14524     else if (element == EL_TIMEGATE_SWITCH ||
14525              element == EL_DC_TIMEGATE_SWITCH)
14526     {
14527       ActivateTimegateSwitch(x, y);
14528     }
14529     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14530              element == EL_BALLOON_SWITCH_RIGHT ||
14531              element == EL_BALLOON_SWITCH_UP    ||
14532              element == EL_BALLOON_SWITCH_DOWN  ||
14533              element == EL_BALLOON_SWITCH_NONE  ||
14534              element == EL_BALLOON_SWITCH_ANY)
14535     {
14536       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14537                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14538                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14539                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14540                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14541                              move_direction);
14542     }
14543     else if (element == EL_LAMP)
14544     {
14545       Feld[x][y] = EL_LAMP_ACTIVE;
14546       local_player->lights_still_needed--;
14547
14548       ResetGfxAnimation(x, y);
14549       DrawLevelField(x, y);
14550     }
14551     else if (element == EL_TIME_ORB_FULL)
14552     {
14553       Feld[x][y] = EL_TIME_ORB_EMPTY;
14554
14555       if (level.time > 0 || level.use_time_orb_bug)
14556       {
14557         TimeLeft += level.time_orb_time;
14558
14559 #if 1
14560         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14561
14562         DisplayGameControlValues();
14563 #else
14564         DrawGameValue_Time(TimeLeft);
14565 #endif
14566       }
14567
14568       ResetGfxAnimation(x, y);
14569       DrawLevelField(x, y);
14570     }
14571     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14572              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14573     {
14574       int xx, yy;
14575
14576       game.ball_state = !game.ball_state;
14577
14578       SCAN_PLAYFIELD(xx, yy)
14579       {
14580         int e = Feld[xx][yy];
14581
14582         if (game.ball_state)
14583         {
14584           if (e == EL_EMC_MAGIC_BALL)
14585             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14586           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14587             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14588         }
14589         else
14590         {
14591           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14592             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14593           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14594             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14595         }
14596       }
14597     }
14598
14599     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14600                                         player->index_bit, dig_side);
14601
14602     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14603                                         player->index_bit, dig_side);
14604
14605     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14606                                         player->index_bit, dig_side);
14607
14608     return MP_ACTION;
14609   }
14610   else
14611   {
14612     if (!PLAYER_SWITCHING(player, x, y))
14613     {
14614       player->is_switching = TRUE;
14615       player->switch_x = x;
14616       player->switch_y = y;
14617
14618       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14619                                  player->index_bit, dig_side);
14620       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14621                                           player->index_bit, dig_side);
14622
14623       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14624                                  player->index_bit, dig_side);
14625       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14626                                           player->index_bit, dig_side);
14627     }
14628
14629     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14630                                player->index_bit, dig_side);
14631     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14632                                         player->index_bit, dig_side);
14633
14634     return MP_NO_ACTION;
14635   }
14636
14637   player->push_delay = -1;
14638
14639   if (is_player)                /* function can also be called by EL_PENGUIN */
14640   {
14641     if (Feld[x][y] != element)          /* really digged/collected something */
14642     {
14643       player->is_collecting = !player->is_digging;
14644       player->is_active = TRUE;
14645     }
14646   }
14647
14648   return MP_MOVING;
14649 }
14650
14651 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14652 {
14653   int jx = player->jx, jy = player->jy;
14654   int x = jx + dx, y = jy + dy;
14655   int snap_direction = (dx == -1 ? MV_LEFT  :
14656                         dx == +1 ? MV_RIGHT :
14657                         dy == -1 ? MV_UP    :
14658                         dy == +1 ? MV_DOWN  : MV_NONE);
14659   boolean can_continue_snapping = (level.continuous_snapping &&
14660                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14661
14662   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14663     return FALSE;
14664
14665   if (!player->active || !IN_LEV_FIELD(x, y))
14666     return FALSE;
14667
14668   if (dx && dy)
14669     return FALSE;
14670
14671   if (!dx && !dy)
14672   {
14673     if (player->MovPos == 0)
14674       player->is_pushing = FALSE;
14675
14676     player->is_snapping = FALSE;
14677
14678     if (player->MovPos == 0)
14679     {
14680       player->is_moving = FALSE;
14681       player->is_digging = FALSE;
14682       player->is_collecting = FALSE;
14683     }
14684
14685     return FALSE;
14686   }
14687
14688 #if USE_NEW_CONTINUOUS_SNAPPING
14689   /* prevent snapping with already pressed snap key when not allowed */
14690   if (player->is_snapping && !can_continue_snapping)
14691     return FALSE;
14692 #else
14693   if (player->is_snapping)
14694     return FALSE;
14695 #endif
14696
14697   player->MovDir = snap_direction;
14698
14699   if (player->MovPos == 0)
14700   {
14701     player->is_moving = FALSE;
14702     player->is_digging = FALSE;
14703     player->is_collecting = FALSE;
14704   }
14705
14706   player->is_dropping = FALSE;
14707   player->is_dropping_pressed = FALSE;
14708   player->drop_pressed_delay = 0;
14709
14710   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14711     return FALSE;
14712
14713   player->is_snapping = TRUE;
14714   player->is_active = TRUE;
14715
14716   if (player->MovPos == 0)
14717   {
14718     player->is_moving = FALSE;
14719     player->is_digging = FALSE;
14720     player->is_collecting = FALSE;
14721   }
14722
14723   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14724     DrawLevelField(player->last_jx, player->last_jy);
14725
14726   DrawLevelField(x, y);
14727
14728   return TRUE;
14729 }
14730
14731 boolean DropElement(struct PlayerInfo *player)
14732 {
14733   int old_element, new_element;
14734   int dropx = player->jx, dropy = player->jy;
14735   int drop_direction = player->MovDir;
14736   int drop_side = drop_direction;
14737 #if 1
14738   int drop_element = get_next_dropped_element(player);
14739 #else
14740   int drop_element = (player->inventory_size > 0 ?
14741                       player->inventory_element[player->inventory_size - 1] :
14742                       player->inventory_infinite_element != EL_UNDEFINED ?
14743                       player->inventory_infinite_element :
14744                       player->dynabombs_left > 0 ?
14745                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14746                       EL_UNDEFINED);
14747 #endif
14748
14749   player->is_dropping_pressed = TRUE;
14750
14751   /* do not drop an element on top of another element; when holding drop key
14752      pressed without moving, dropped element must move away before the next
14753      element can be dropped (this is especially important if the next element
14754      is dynamite, which can be placed on background for historical reasons) */
14755   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14756     return MP_ACTION;
14757
14758   if (IS_THROWABLE(drop_element))
14759   {
14760     dropx += GET_DX_FROM_DIR(drop_direction);
14761     dropy += GET_DY_FROM_DIR(drop_direction);
14762
14763     if (!IN_LEV_FIELD(dropx, dropy))
14764       return FALSE;
14765   }
14766
14767   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14768   new_element = drop_element;           /* default: no change when dropping */
14769
14770   /* check if player is active, not moving and ready to drop */
14771   if (!player->active || player->MovPos || player->drop_delay > 0)
14772     return FALSE;
14773
14774   /* check if player has anything that can be dropped */
14775   if (new_element == EL_UNDEFINED)
14776     return FALSE;
14777
14778   /* check if drop key was pressed long enough for EM style dynamite */
14779   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14780     return FALSE;
14781
14782   /* check if anything can be dropped at the current position */
14783   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14784     return FALSE;
14785
14786   /* collected custom elements can only be dropped on empty fields */
14787   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14788     return FALSE;
14789
14790   if (old_element != EL_EMPTY)
14791     Back[dropx][dropy] = old_element;   /* store old element on this field */
14792
14793   ResetGfxAnimation(dropx, dropy);
14794   ResetRandomAnimationValue(dropx, dropy);
14795
14796   if (player->inventory_size > 0 ||
14797       player->inventory_infinite_element != EL_UNDEFINED)
14798   {
14799     if (player->inventory_size > 0)
14800     {
14801       player->inventory_size--;
14802
14803       DrawGameDoorValues();
14804
14805       if (new_element == EL_DYNAMITE)
14806         new_element = EL_DYNAMITE_ACTIVE;
14807       else if (new_element == EL_EM_DYNAMITE)
14808         new_element = EL_EM_DYNAMITE_ACTIVE;
14809       else if (new_element == EL_SP_DISK_RED)
14810         new_element = EL_SP_DISK_RED_ACTIVE;
14811     }
14812
14813     Feld[dropx][dropy] = new_element;
14814
14815     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14816       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14817                           el2img(Feld[dropx][dropy]), 0);
14818
14819     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14820
14821     /* needed if previous element just changed to "empty" in the last frame */
14822     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14823
14824     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14825                                player->index_bit, drop_side);
14826     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14827                                         CE_PLAYER_DROPS_X,
14828                                         player->index_bit, drop_side);
14829
14830     TestIfElementTouchesCustomElement(dropx, dropy);
14831   }
14832   else          /* player is dropping a dyna bomb */
14833   {
14834     player->dynabombs_left--;
14835
14836     Feld[dropx][dropy] = new_element;
14837
14838     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14839       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14840                           el2img(Feld[dropx][dropy]), 0);
14841
14842     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14843   }
14844
14845   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14846     InitField_WithBug1(dropx, dropy, FALSE);
14847
14848   new_element = Feld[dropx][dropy];     /* element might have changed */
14849
14850   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14851       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14852   {
14853     int move_direction, nextx, nexty;
14854
14855     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14856       MovDir[dropx][dropy] = drop_direction;
14857
14858     move_direction = MovDir[dropx][dropy];
14859     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14860     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14861
14862     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14863
14864 #if USE_FIX_IMPACT_COLLISION
14865     /* do not cause impact style collision by dropping elements that can fall */
14866     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14867 #else
14868     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14869 #endif
14870   }
14871
14872   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14873   player->is_dropping = TRUE;
14874
14875   player->drop_pressed_delay = 0;
14876   player->is_dropping_pressed = FALSE;
14877
14878   player->drop_x = dropx;
14879   player->drop_y = dropy;
14880
14881   return TRUE;
14882 }
14883
14884 /* ------------------------------------------------------------------------- */
14885 /* game sound playing functions                                              */
14886 /* ------------------------------------------------------------------------- */
14887
14888 static int *loop_sound_frame = NULL;
14889 static int *loop_sound_volume = NULL;
14890
14891 void InitPlayLevelSound()
14892 {
14893   int num_sounds = getSoundListSize();
14894
14895   checked_free(loop_sound_frame);
14896   checked_free(loop_sound_volume);
14897
14898   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14899   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14900 }
14901
14902 static void PlayLevelSound(int x, int y, int nr)
14903 {
14904   int sx = SCREENX(x), sy = SCREENY(y);
14905   int volume, stereo_position;
14906   int max_distance = 8;
14907   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14908
14909   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14910       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14911     return;
14912
14913   if (!IN_LEV_FIELD(x, y) ||
14914       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14915       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14916     return;
14917
14918   volume = SOUND_MAX_VOLUME;
14919
14920   if (!IN_SCR_FIELD(sx, sy))
14921   {
14922     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14923     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14924
14925     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14926   }
14927
14928   stereo_position = (SOUND_MAX_LEFT +
14929                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14930                      (SCR_FIELDX + 2 * max_distance));
14931
14932   if (IS_LOOP_SOUND(nr))
14933   {
14934     /* This assures that quieter loop sounds do not overwrite louder ones,
14935        while restarting sound volume comparison with each new game frame. */
14936
14937     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14938       return;
14939
14940     loop_sound_volume[nr] = volume;
14941     loop_sound_frame[nr] = FrameCounter;
14942   }
14943
14944   PlaySoundExt(nr, volume, stereo_position, type);
14945 }
14946
14947 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14948 {
14949   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14950                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14951                  y < LEVELY(BY1) ? LEVELY(BY1) :
14952                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14953                  sound_action);
14954 }
14955
14956 static void PlayLevelSoundAction(int x, int y, int action)
14957 {
14958   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14959 }
14960
14961 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14962 {
14963   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14964
14965   if (sound_effect != SND_UNDEFINED)
14966     PlayLevelSound(x, y, sound_effect);
14967 }
14968
14969 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14970                                               int action)
14971 {
14972   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14973
14974   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14975     PlayLevelSound(x, y, sound_effect);
14976 }
14977
14978 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14979 {
14980   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14981
14982   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14983     PlayLevelSound(x, y, sound_effect);
14984 }
14985
14986 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14987 {
14988   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14989
14990   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14991     StopSound(sound_effect);
14992 }
14993
14994 static void PlayLevelMusic()
14995 {
14996   if (levelset.music[level_nr] != MUS_UNDEFINED)
14997     PlayMusic(levelset.music[level_nr]);        /* from config file */
14998   else
14999     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15000 }
15001
15002 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15003 {
15004   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15005   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15006   int x = xx - 1 - offset;
15007   int y = yy - 1 - offset;
15008
15009   switch (sample)
15010   {
15011     case SAMPLE_blank:
15012       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15013       break;
15014
15015     case SAMPLE_roll:
15016       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15017       break;
15018
15019     case SAMPLE_stone:
15020       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15021       break;
15022
15023     case SAMPLE_nut:
15024       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15025       break;
15026
15027     case SAMPLE_crack:
15028       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15029       break;
15030
15031     case SAMPLE_bug:
15032       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15033       break;
15034
15035     case SAMPLE_tank:
15036       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15037       break;
15038
15039     case SAMPLE_android_clone:
15040       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15041       break;
15042
15043     case SAMPLE_android_move:
15044       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15045       break;
15046
15047     case SAMPLE_spring:
15048       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15049       break;
15050
15051     case SAMPLE_slurp:
15052       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15053       break;
15054
15055     case SAMPLE_eater:
15056       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15057       break;
15058
15059     case SAMPLE_eater_eat:
15060       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15061       break;
15062
15063     case SAMPLE_alien:
15064       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15065       break;
15066
15067     case SAMPLE_collect:
15068       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15069       break;
15070
15071     case SAMPLE_diamond:
15072       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15073       break;
15074
15075     case SAMPLE_squash:
15076       /* !!! CHECK THIS !!! */
15077 #if 1
15078       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15079 #else
15080       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15081 #endif
15082       break;
15083
15084     case SAMPLE_wonderfall:
15085       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15086       break;
15087
15088     case SAMPLE_drip:
15089       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15090       break;
15091
15092     case SAMPLE_push:
15093       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15094       break;
15095
15096     case SAMPLE_dirt:
15097       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15098       break;
15099
15100     case SAMPLE_acid:
15101       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15102       break;
15103
15104     case SAMPLE_ball:
15105       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15106       break;
15107
15108     case SAMPLE_grow:
15109       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15110       break;
15111
15112     case SAMPLE_wonder:
15113       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15114       break;
15115
15116     case SAMPLE_door:
15117       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15118       break;
15119
15120     case SAMPLE_exit_open:
15121       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15122       break;
15123
15124     case SAMPLE_exit_leave:
15125       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15126       break;
15127
15128     case SAMPLE_dynamite:
15129       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15130       break;
15131
15132     case SAMPLE_tick:
15133       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15134       break;
15135
15136     case SAMPLE_press:
15137       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15138       break;
15139
15140     case SAMPLE_wheel:
15141       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15142       break;
15143
15144     case SAMPLE_boom:
15145       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15146       break;
15147
15148     case SAMPLE_die:
15149       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15150       break;
15151
15152     case SAMPLE_time:
15153       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15154       break;
15155
15156     default:
15157       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15158       break;
15159   }
15160 }
15161
15162 #if 0
15163 void ChangeTime(int value)
15164 {
15165   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15166
15167   *time += value;
15168
15169   /* EMC game engine uses value from time counter of RND game engine */
15170   level.native_em_level->lev->time = *time;
15171
15172   DrawGameValue_Time(*time);
15173 }
15174
15175 void RaiseScore(int value)
15176 {
15177   /* EMC game engine and RND game engine have separate score counters */
15178   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15179                 &level.native_em_level->lev->score : &local_player->score);
15180
15181   *score += value;
15182
15183   DrawGameValue_Score(*score);
15184 }
15185 #endif
15186
15187 void RaiseScore(int value)
15188 {
15189   local_player->score += value;
15190
15191 #if 1
15192   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15193
15194   DisplayGameControlValues();
15195 #else
15196   DrawGameValue_Score(local_player->score);
15197 #endif
15198 }
15199
15200 void RaiseScoreElement(int element)
15201 {
15202   switch (element)
15203   {
15204     case EL_EMERALD:
15205     case EL_BD_DIAMOND:
15206     case EL_EMERALD_YELLOW:
15207     case EL_EMERALD_RED:
15208     case EL_EMERALD_PURPLE:
15209     case EL_SP_INFOTRON:
15210       RaiseScore(level.score[SC_EMERALD]);
15211       break;
15212     case EL_DIAMOND:
15213       RaiseScore(level.score[SC_DIAMOND]);
15214       break;
15215     case EL_CRYSTAL:
15216       RaiseScore(level.score[SC_CRYSTAL]);
15217       break;
15218     case EL_PEARL:
15219       RaiseScore(level.score[SC_PEARL]);
15220       break;
15221     case EL_BUG:
15222     case EL_BD_BUTTERFLY:
15223     case EL_SP_ELECTRON:
15224       RaiseScore(level.score[SC_BUG]);
15225       break;
15226     case EL_SPACESHIP:
15227     case EL_BD_FIREFLY:
15228     case EL_SP_SNIKSNAK:
15229       RaiseScore(level.score[SC_SPACESHIP]);
15230       break;
15231     case EL_YAMYAM:
15232     case EL_DARK_YAMYAM:
15233       RaiseScore(level.score[SC_YAMYAM]);
15234       break;
15235     case EL_ROBOT:
15236       RaiseScore(level.score[SC_ROBOT]);
15237       break;
15238     case EL_PACMAN:
15239       RaiseScore(level.score[SC_PACMAN]);
15240       break;
15241     case EL_NUT:
15242       RaiseScore(level.score[SC_NUT]);
15243       break;
15244     case EL_DYNAMITE:
15245     case EL_EM_DYNAMITE:
15246     case EL_SP_DISK_RED:
15247     case EL_DYNABOMB_INCREASE_NUMBER:
15248     case EL_DYNABOMB_INCREASE_SIZE:
15249     case EL_DYNABOMB_INCREASE_POWER:
15250       RaiseScore(level.score[SC_DYNAMITE]);
15251       break;
15252     case EL_SHIELD_NORMAL:
15253     case EL_SHIELD_DEADLY:
15254       RaiseScore(level.score[SC_SHIELD]);
15255       break;
15256     case EL_EXTRA_TIME:
15257       RaiseScore(level.extra_time_score);
15258       break;
15259     case EL_KEY_1:
15260     case EL_KEY_2:
15261     case EL_KEY_3:
15262     case EL_KEY_4:
15263     case EL_EM_KEY_1:
15264     case EL_EM_KEY_2:
15265     case EL_EM_KEY_3:
15266     case EL_EM_KEY_4:
15267     case EL_EMC_KEY_5:
15268     case EL_EMC_KEY_6:
15269     case EL_EMC_KEY_7:
15270     case EL_EMC_KEY_8:
15271     case EL_DC_KEY_WHITE:
15272       RaiseScore(level.score[SC_KEY]);
15273       break;
15274     default:
15275       RaiseScore(element_info[element].collect_score);
15276       break;
15277   }
15278 }
15279
15280 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15281 {
15282   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15283   {
15284 #if defined(NETWORK_AVALIABLE)
15285     if (options.network)
15286       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15287     else
15288 #endif
15289     {
15290       if (quick_quit)
15291       {
15292 #if 1
15293
15294 #if 1
15295         FadeSkipNextFadeIn();
15296 #else
15297         fading = fading_none;
15298 #endif
15299
15300 #else
15301         OpenDoor(DOOR_CLOSE_1);
15302 #endif
15303
15304         game_status = GAME_MODE_MAIN;
15305
15306 #if 1
15307         DrawAndFadeInMainMenu(REDRAW_FIELD);
15308 #else
15309         DrawMainMenu();
15310 #endif
15311       }
15312       else
15313       {
15314 #if 0
15315         FadeOut(REDRAW_FIELD);
15316 #endif
15317
15318         game_status = GAME_MODE_MAIN;
15319
15320         DrawAndFadeInMainMenu(REDRAW_FIELD);
15321       }
15322     }
15323   }
15324   else          /* continue playing the game */
15325   {
15326     if (tape.playing && tape.deactivate_display)
15327       TapeDeactivateDisplayOff(TRUE);
15328
15329     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15330
15331     if (tape.playing && tape.deactivate_display)
15332       TapeDeactivateDisplayOn();
15333   }
15334 }
15335
15336 void RequestQuitGame(boolean ask_if_really_quit)
15337 {
15338   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15339   boolean skip_request = AllPlayersGone || quick_quit;
15340
15341   RequestQuitGameExt(skip_request, quick_quit,
15342                      "Do you really want to quit the game ?");
15343 }
15344
15345
15346 /* ------------------------------------------------------------------------- */
15347 /* random generator functions                                                */
15348 /* ------------------------------------------------------------------------- */
15349
15350 unsigned int InitEngineRandom_RND(long seed)
15351 {
15352   game.num_random_calls = 0;
15353
15354 #if 0
15355   unsigned int rnd_seed = InitEngineRandom(seed);
15356
15357   printf("::: START RND: %d\n", rnd_seed);
15358
15359   return rnd_seed;
15360 #else
15361
15362   return InitEngineRandom(seed);
15363
15364 #endif
15365
15366 }
15367
15368 unsigned int RND(int max)
15369 {
15370   if (max > 0)
15371   {
15372     game.num_random_calls++;
15373
15374     return GetEngineRandom(max);
15375   }
15376
15377   return 0;
15378 }
15379
15380
15381 /* ------------------------------------------------------------------------- */
15382 /* game engine snapshot handling functions                                   */
15383 /* ------------------------------------------------------------------------- */
15384
15385 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15386
15387 struct EngineSnapshotInfo
15388 {
15389   /* runtime values for custom element collect score */
15390   int collect_score[NUM_CUSTOM_ELEMENTS];
15391
15392   /* runtime values for group element choice position */
15393   int choice_pos[NUM_GROUP_ELEMENTS];
15394
15395   /* runtime values for belt position animations */
15396   int belt_graphic[4 * NUM_BELT_PARTS];
15397   int belt_anim_mode[4 * NUM_BELT_PARTS];
15398 };
15399
15400 struct EngineSnapshotNodeInfo
15401 {
15402   void *buffer_orig;
15403   void *buffer_copy;
15404   int size;
15405 };
15406
15407 static struct EngineSnapshotInfo engine_snapshot_rnd;
15408 static ListNode *engine_snapshot_list = NULL;
15409 static char *snapshot_level_identifier = NULL;
15410 static int snapshot_level_nr = -1;
15411
15412 void FreeEngineSnapshot()
15413 {
15414   while (engine_snapshot_list != NULL)
15415     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15416                        checked_free);
15417
15418   setString(&snapshot_level_identifier, NULL);
15419   snapshot_level_nr = -1;
15420 }
15421
15422 static void SaveEngineSnapshotValues_RND()
15423 {
15424   static int belt_base_active_element[4] =
15425   {
15426     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15427     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15428     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15429     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15430   };
15431   int i, j;
15432
15433   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15434   {
15435     int element = EL_CUSTOM_START + i;
15436
15437     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15438   }
15439
15440   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15441   {
15442     int element = EL_GROUP_START + i;
15443
15444     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15445   }
15446
15447   for (i = 0; i < 4; i++)
15448   {
15449     for (j = 0; j < NUM_BELT_PARTS; j++)
15450     {
15451       int element = belt_base_active_element[i] + j;
15452       int graphic = el2img(element);
15453       int anim_mode = graphic_info[graphic].anim_mode;
15454
15455       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15456       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15457     }
15458   }
15459 }
15460
15461 static void LoadEngineSnapshotValues_RND()
15462 {
15463   unsigned long num_random_calls = game.num_random_calls;
15464   int i, j;
15465
15466   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15467   {
15468     int element = EL_CUSTOM_START + i;
15469
15470     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15471   }
15472
15473   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15474   {
15475     int element = EL_GROUP_START + i;
15476
15477     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15478   }
15479
15480   for (i = 0; i < 4; i++)
15481   {
15482     for (j = 0; j < NUM_BELT_PARTS; j++)
15483     {
15484       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15485       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15486
15487       graphic_info[graphic].anim_mode = anim_mode;
15488     }
15489   }
15490
15491   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15492   {
15493     InitRND(tape.random_seed);
15494     for (i = 0; i < num_random_calls; i++)
15495       RND(1);
15496   }
15497
15498   if (game.num_random_calls != num_random_calls)
15499   {
15500     Error(ERR_INFO, "number of random calls out of sync");
15501     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15502     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15503     Error(ERR_EXIT, "this should not happen -- please debug");
15504   }
15505 }
15506
15507 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15508 {
15509   struct EngineSnapshotNodeInfo *bi =
15510     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15511
15512   bi->buffer_orig = buffer;
15513   bi->buffer_copy = checked_malloc(size);
15514   bi->size = size;
15515
15516   memcpy(bi->buffer_copy, buffer, size);
15517
15518   addNodeToList(&engine_snapshot_list, NULL, bi);
15519 }
15520
15521 void SaveEngineSnapshot()
15522 {
15523   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15524
15525   if (level_editor_test_game)   /* do not save snapshots from editor */
15526     return;
15527
15528   /* copy some special values to a structure better suited for the snapshot */
15529
15530   SaveEngineSnapshotValues_RND();
15531   SaveEngineSnapshotValues_EM();
15532
15533   /* save values stored in special snapshot structure */
15534
15535   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15536   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15537
15538   /* save further RND engine values */
15539
15540   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15541   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15542   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15543
15544   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15545   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15546   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15547   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15548
15549   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15550   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15551   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15553   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15554
15555   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15556   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15558
15559   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15560
15561   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15562
15563   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15565
15566   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15567   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15570   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15572   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15576   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15577   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15579   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15584
15585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15587
15588   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15591
15592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15594
15595   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15599   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15600
15601   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15603
15604   /* save level identification information */
15605
15606   setString(&snapshot_level_identifier, leveldir_current->identifier);
15607   snapshot_level_nr = level_nr;
15608
15609 #if 0
15610   ListNode *node = engine_snapshot_list;
15611   int num_bytes = 0;
15612
15613   while (node != NULL)
15614   {
15615     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15616
15617     node = node->next;
15618   }
15619
15620   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15621 #endif
15622 }
15623
15624 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15625 {
15626   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15627 }
15628
15629 void LoadEngineSnapshot()
15630 {
15631   ListNode *node = engine_snapshot_list;
15632
15633   if (engine_snapshot_list == NULL)
15634     return;
15635
15636   while (node != NULL)
15637   {
15638     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15639
15640     node = node->next;
15641   }
15642
15643   /* restore special values from snapshot structure */
15644
15645   LoadEngineSnapshotValues_RND();
15646   LoadEngineSnapshotValues_EM();
15647 }
15648
15649 boolean CheckEngineSnapshot()
15650 {
15651   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15652           snapshot_level_nr == level_nr);
15653 }
15654
15655
15656 /* ---------- new game button stuff ---------------------------------------- */
15657
15658 /* graphic position values for game buttons */
15659 #define GAME_BUTTON_XSIZE       30
15660 #define GAME_BUTTON_YSIZE       30
15661 #define GAME_BUTTON_XPOS        5
15662 #define GAME_BUTTON_YPOS        215
15663 #define SOUND_BUTTON_XPOS       5
15664 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15665
15666 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15667 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15668 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15669 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15670 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15671 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15672
15673 static struct
15674 {
15675   int *x, *y;
15676   int gd_x, gd_y;
15677   int gadget_id;
15678   char *infotext;
15679 } gamebutton_info[NUM_GAME_BUTTONS] =
15680 {
15681 #if 1
15682   {
15683     &game.button.stop.x,        &game.button.stop.y,
15684     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15685     GAME_CTRL_ID_STOP,
15686     "stop game"
15687   },
15688   {
15689     &game.button.pause.x,       &game.button.pause.y,
15690     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15691     GAME_CTRL_ID_PAUSE,
15692     "pause game"
15693   },
15694   {
15695     &game.button.play.x,        &game.button.play.y,
15696     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15697     GAME_CTRL_ID_PLAY,
15698     "play game"
15699   },
15700   {
15701     &game.button.sound_music.x, &game.button.sound_music.y,
15702     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15703     SOUND_CTRL_ID_MUSIC,
15704     "background music on/off"
15705   },
15706   {
15707     &game.button.sound_loops.x, &game.button.sound_loops.y,
15708     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15709     SOUND_CTRL_ID_LOOPS,
15710     "sound loops on/off"
15711   },
15712   {
15713     &game.button.sound_simple.x,&game.button.sound_simple.y,
15714     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15715     SOUND_CTRL_ID_SIMPLE,
15716     "normal sounds on/off"
15717   }
15718 #else
15719   {
15720     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15721     GAME_CTRL_ID_STOP,
15722     "stop game"
15723   },
15724   {
15725     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15726     GAME_CTRL_ID_PAUSE,
15727     "pause game"
15728   },
15729   {
15730     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15731     GAME_CTRL_ID_PLAY,
15732     "play game"
15733   },
15734   {
15735     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15736     SOUND_CTRL_ID_MUSIC,
15737     "background music on/off"
15738   },
15739   {
15740     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15741     SOUND_CTRL_ID_LOOPS,
15742     "sound loops on/off"
15743   },
15744   {
15745     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15746     SOUND_CTRL_ID_SIMPLE,
15747     "normal sounds on/off"
15748   }
15749 #endif
15750 };
15751
15752 void CreateGameButtons()
15753 {
15754   int i;
15755
15756   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15757   {
15758     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15759     struct GadgetInfo *gi;
15760     int button_type;
15761     boolean checked;
15762     unsigned long event_mask;
15763     int x, y;
15764     int gd_xoffset, gd_yoffset;
15765     int gd_x1, gd_x2, gd_y1, gd_y2;
15766     int id = i;
15767
15768     x = DX + *gamebutton_info[i].x;
15769     y = DY + *gamebutton_info[i].y;
15770     gd_xoffset = gamebutton_info[i].gd_x;
15771     gd_yoffset = gamebutton_info[i].gd_y;
15772     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15773     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15774
15775     if (id == GAME_CTRL_ID_STOP ||
15776         id == GAME_CTRL_ID_PAUSE ||
15777         id == GAME_CTRL_ID_PLAY)
15778     {
15779       button_type = GD_TYPE_NORMAL_BUTTON;
15780       checked = FALSE;
15781       event_mask = GD_EVENT_RELEASED;
15782       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15783       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15784     }
15785     else
15786     {
15787       button_type = GD_TYPE_CHECK_BUTTON;
15788       checked =
15789         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15790          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15791          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15792       event_mask = GD_EVENT_PRESSED;
15793       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15794       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15795     }
15796
15797     gi = CreateGadget(GDI_CUSTOM_ID, id,
15798                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15799 #if 1
15800                       GDI_X, x,
15801                       GDI_Y, y,
15802 #else
15803                       GDI_X, DX + gd_xoffset,
15804                       GDI_Y, DY + gd_yoffset,
15805 #endif
15806                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15807                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15808                       GDI_TYPE, button_type,
15809                       GDI_STATE, GD_BUTTON_UNPRESSED,
15810                       GDI_CHECKED, checked,
15811                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15812                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15813                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15814                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15815                       GDI_DIRECT_DRAW, FALSE,
15816                       GDI_EVENT_MASK, event_mask,
15817                       GDI_CALLBACK_ACTION, HandleGameButtons,
15818                       GDI_END);
15819
15820     if (gi == NULL)
15821       Error(ERR_EXIT, "cannot create gadget");
15822
15823     game_gadget[id] = gi;
15824   }
15825 }
15826
15827 void FreeGameButtons()
15828 {
15829   int i;
15830
15831   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15832     FreeGadget(game_gadget[i]);
15833 }
15834
15835 static void MapGameButtons()
15836 {
15837   int i;
15838
15839   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15840     MapGadget(game_gadget[i]);
15841 }
15842
15843 void UnmapGameButtons()
15844 {
15845   int i;
15846
15847   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15848     UnmapGadget(game_gadget[i]);
15849 }
15850
15851 void RedrawGameButtons()
15852 {
15853   int i;
15854
15855   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15856     RedrawGadget(game_gadget[i]);
15857 }
15858
15859 static void HandleGameButtons(struct GadgetInfo *gi)
15860 {
15861   int id = gi->custom_id;
15862
15863   if (game_status != GAME_MODE_PLAYING)
15864     return;
15865
15866   switch (id)
15867   {
15868     case GAME_CTRL_ID_STOP:
15869       if (tape.playing)
15870         TapeStop();
15871       else
15872         RequestQuitGame(TRUE);
15873       break;
15874
15875     case GAME_CTRL_ID_PAUSE:
15876       if (options.network)
15877       {
15878 #if defined(NETWORK_AVALIABLE)
15879         if (tape.pausing)
15880           SendToServer_ContinuePlaying();
15881         else
15882           SendToServer_PausePlaying();
15883 #endif
15884       }
15885       else
15886         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15887       break;
15888
15889     case GAME_CTRL_ID_PLAY:
15890       if (tape.pausing)
15891       {
15892 #if defined(NETWORK_AVALIABLE)
15893         if (options.network)
15894           SendToServer_ContinuePlaying();
15895         else
15896 #endif
15897         {
15898           tape.pausing = FALSE;
15899           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15900         }
15901       }
15902       break;
15903
15904     case SOUND_CTRL_ID_MUSIC:
15905       if (setup.sound_music)
15906       { 
15907         setup.sound_music = FALSE;
15908         FadeMusic();
15909       }
15910       else if (audio.music_available)
15911       { 
15912         setup.sound = setup.sound_music = TRUE;
15913
15914         SetAudioMode(setup.sound);
15915
15916         PlayLevelMusic();
15917       }
15918       break;
15919
15920     case SOUND_CTRL_ID_LOOPS:
15921       if (setup.sound_loops)
15922         setup.sound_loops = FALSE;
15923       else if (audio.loops_available)
15924       {
15925         setup.sound = setup.sound_loops = TRUE;
15926         SetAudioMode(setup.sound);
15927       }
15928       break;
15929
15930     case SOUND_CTRL_ID_SIMPLE:
15931       if (setup.sound_simple)
15932         setup.sound_simple = FALSE;
15933       else if (audio.sound_available)
15934       {
15935         setup.sound = setup.sound_simple = TRUE;
15936         SetAudioMode(setup.sound);
15937       }
15938       break;
15939
15940     default:
15941       break;
15942   }
15943 }