31aca3936131a87b1fea9e796b16fb096683d25a
[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   boolean use_element_count = FALSE;
2115   int i, j, x, y;
2116
2117   /* first check if it is needed at all to calculate playfield element count */
2118   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2119     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2120       use_element_count = TRUE;
2121
2122   if (!use_element_count)
2123     return;
2124
2125   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2126     element_info[i].element_count = 0;
2127
2128   SCAN_PLAYFIELD(x, y)
2129   {
2130     element_info[Feld[x][y]].element_count++;
2131   }
2132
2133   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2134     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2135       if (IS_IN_GROUP(j, i))
2136         element_info[EL_GROUP_START + i].element_count +=
2137           element_info[j].element_count;
2138 }
2139
2140 void UpdateGameControlValues()
2141 {
2142   int i, k;
2143   int time = (local_player->LevelSolved ?
2144               local_player->LevelSolved_CountingTime :
2145               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2146               level.native_em_level->lev->time :
2147               level.time == 0 ? TimePlayed : TimeLeft);
2148   int score = (local_player->LevelSolved ?
2149                local_player->LevelSolved_CountingScore :
2150                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2151                level.native_em_level->lev->score :
2152                local_player->score);
2153   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2154               level.native_em_level->lev->required :
2155               local_player->gems_still_needed);
2156   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2157                      level.native_em_level->lev->required > 0 :
2158                      local_player->gems_still_needed > 0 ||
2159                      local_player->sokobanfields_still_needed > 0 ||
2160                      local_player->lights_still_needed > 0);
2161
2162   UpdatePlayfieldElementCount();
2163
2164   /* update game panel control values */
2165
2166   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2167   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2168
2169   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2170   for (i = 0; i < MAX_NUM_KEYS; i++)
2171     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2172   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2173   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2174
2175   if (game.centered_player_nr == -1)
2176   {
2177     for (i = 0; i < MAX_PLAYERS; i++)
2178     {
2179       for (k = 0; k < MAX_NUM_KEYS; k++)
2180       {
2181         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2182         {
2183           if (level.native_em_level->ply[i]->keys & (1 << k))
2184             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2185               get_key_element_from_nr(k);
2186         }
2187         else if (stored_player[i].key[k])
2188           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2189             get_key_element_from_nr(k);
2190       }
2191
2192       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2193         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2194           level.native_em_level->ply[i]->dynamite;
2195       else
2196         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2197           stored_player[i].inventory_size;
2198
2199       if (stored_player[i].num_white_keys > 0)
2200         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2201           EL_DC_KEY_WHITE;
2202
2203       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2204         stored_player[i].num_white_keys;
2205     }
2206   }
2207   else
2208   {
2209     int player_nr = game.centered_player_nr;
2210
2211     for (k = 0; k < MAX_NUM_KEYS; k++)
2212     {
2213       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2214       {
2215         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2216           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217             get_key_element_from_nr(k);
2218       }
2219       else if (stored_player[player_nr].key[k])
2220         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221           get_key_element_from_nr(k);
2222     }
2223
2224     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2226         level.native_em_level->ply[player_nr]->dynamite;
2227     else
2228       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229         stored_player[player_nr].inventory_size;
2230
2231     if (stored_player[player_nr].num_white_keys > 0)
2232       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2233
2234     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2235       stored_player[player_nr].num_white_keys;
2236   }
2237
2238   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2239   {
2240     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2241       get_inventory_element_from_pos(local_player, i);
2242     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2243       get_inventory_element_from_pos(local_player, -i - 1);
2244   }
2245
2246   game_panel_controls[GAME_PANEL_SCORE].value = score;
2247
2248   game_panel_controls[GAME_PANEL_TIME].value = time;
2249
2250   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2251   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2252   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2253
2254   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2255     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2256      EL_EMPTY);
2257   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2258     local_player->shield_normal_time_left;
2259   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2260     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2261      EL_EMPTY);
2262   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2263     local_player->shield_deadly_time_left;
2264
2265   game_panel_controls[GAME_PANEL_EXIT].value =
2266     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2267
2268   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2269     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2270   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2271     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2272      EL_EMC_MAGIC_BALL_SWITCH);
2273
2274   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2275     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2276   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2277     game.light_time_left;
2278
2279   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2280     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2281   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2282     game.timegate_time_left;
2283
2284   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2285     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2286
2287   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2288     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2289   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2290     game.lenses_time_left;
2291
2292   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2293     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2294   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2295     game.magnify_time_left;
2296
2297   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2298     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2299      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2300      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2301      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2302      EL_BALLOON_SWITCH_NONE);
2303
2304   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2305     local_player->dynabomb_count;
2306   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2307     local_player->dynabomb_size;
2308   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2309     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2310
2311   game_panel_controls[GAME_PANEL_PENGUINS].value =
2312     local_player->friends_still_needed;
2313
2314   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2315     local_player->sokobanfields_still_needed;
2316   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2317     local_player->sokobanfields_still_needed;
2318
2319   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2320     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2321
2322   for (i = 0; i < NUM_BELTS; i++)
2323   {
2324     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2325       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2326        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2327     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2328       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2329   }
2330
2331   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2332     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2333   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2334     game.magic_wall_time_left;
2335
2336 #if USE_PLAYER_GRAVITY
2337   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2338     local_player->gravity;
2339 #else
2340   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2341 #endif
2342
2343   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2344     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2345
2346   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2347     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2348       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2349        game.panel.element[i].id : EL_UNDEFINED);
2350
2351   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2352     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2353       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2354        element_info[game.panel.element_count[i].id].element_count : 0);
2355
2356   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2357     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2358       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2359        element_info[game.panel.ce_score[i].id].collect_score : 0);
2360
2361   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2362     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2363       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2364        element_info[game.panel.ce_score_element[i].id].collect_score :
2365        EL_UNDEFINED);
2366
2367   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2368   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2369   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2370
2371   /* update game panel control frames */
2372
2373   for (i = 0; game_panel_controls[i].nr != -1; i++)
2374   {
2375     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2376
2377     if (gpc->type == TYPE_ELEMENT)
2378     {
2379       int last_anim_random_frame = gfx.anim_random_frame;
2380       int element = gpc->value;
2381       int graphic = el2panelimg(element);
2382
2383       if (gpc->value != gpc->last_value)
2384       {
2385         gpc->gfx_frame = 0;
2386         gpc->gfx_random = INIT_GFX_RANDOM();
2387       }
2388       else
2389       {
2390         gpc->gfx_frame++;
2391
2392         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2393             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2394           gpc->gfx_random = INIT_GFX_RANDOM();
2395       }
2396
2397       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2398         gfx.anim_random_frame = gpc->gfx_random;
2399
2400       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2401         gpc->gfx_frame = element_info[element].collect_score;
2402
2403       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2404                                             gpc->gfx_frame);
2405
2406       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2407         gfx.anim_random_frame = last_anim_random_frame;
2408     }
2409   }
2410 }
2411
2412 void DisplayGameControlValues()
2413 {
2414   boolean redraw_panel = FALSE;
2415   int i;
2416
2417   for (i = 0; game_panel_controls[i].nr != -1; i++)
2418   {
2419     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2420
2421     if (PANEL_DEACTIVATED(gpc->pos))
2422       continue;
2423
2424     if (gpc->value == gpc->last_value &&
2425         gpc->frame == gpc->last_frame)
2426       continue;
2427
2428     redraw_panel = TRUE;
2429   }
2430
2431   if (!redraw_panel)
2432     return;
2433
2434   /* copy default game door content to main double buffer */
2435   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2436              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2437
2438   /* redraw game control buttons */
2439 #if 1
2440   RedrawGameButtons();
2441 #else
2442   UnmapGameButtons();
2443   MapGameButtons();
2444 #endif
2445
2446   game_status = GAME_MODE_PSEUDO_PANEL;
2447
2448 #if 1
2449   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2450 #else
2451   for (i = 0; game_panel_controls[i].nr != -1; i++)
2452 #endif
2453   {
2454 #if 1
2455     int nr = game_panel_order[i].nr;
2456     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2457 #else
2458     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2459     int nr = gpc->nr;
2460 #endif
2461     struct TextPosInfo *pos = gpc->pos;
2462     int type = gpc->type;
2463     int value = gpc->value;
2464     int frame = gpc->frame;
2465 #if 0
2466     int last_value = gpc->last_value;
2467     int last_frame = gpc->last_frame;
2468 #endif
2469     int size = pos->size;
2470     int font = pos->font;
2471     boolean draw_masked = pos->draw_masked;
2472     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2473
2474     if (PANEL_DEACTIVATED(pos))
2475       continue;
2476
2477 #if 0
2478     if (value == last_value && frame == last_frame)
2479       continue;
2480 #endif
2481
2482     gpc->last_value = value;
2483     gpc->last_frame = frame;
2484
2485 #if 0
2486     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2487 #endif
2488
2489     if (type == TYPE_INTEGER)
2490     {
2491       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2492           nr == GAME_PANEL_TIME)
2493       {
2494         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2495
2496         if (use_dynamic_size)           /* use dynamic number of digits */
2497         {
2498           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2499           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2500           int size2 = size1 + 1;
2501           int font1 = pos->font;
2502           int font2 = pos->font_alt;
2503
2504           size = (value < value_change ? size1 : size2);
2505           font = (value < value_change ? font1 : font2);
2506
2507 #if 0
2508           /* clear background if value just changed its size (dynamic digits) */
2509           if ((last_value < value_change) != (value < value_change))
2510           {
2511             int width1 = size1 * getFontWidth(font1);
2512             int width2 = size2 * getFontWidth(font2);
2513             int max_width = MAX(width1, width2);
2514             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2515
2516             pos->width = max_width;
2517
2518             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2519                                        max_width, max_height);
2520           }
2521 #endif
2522         }
2523       }
2524
2525 #if 1
2526       /* correct text size if "digits" is zero or less */
2527       if (size <= 0)
2528         size = strlen(int2str(value, size));
2529
2530       /* dynamically correct text alignment */
2531       pos->width = size * getFontWidth(font);
2532 #endif
2533
2534       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2535                   int2str(value, size), font, mask_mode);
2536     }
2537     else if (type == TYPE_ELEMENT)
2538     {
2539       int element, graphic;
2540       Bitmap *src_bitmap;
2541       int src_x, src_y;
2542       int width, height;
2543       int dst_x = PANEL_XPOS(pos);
2544       int dst_y = PANEL_YPOS(pos);
2545
2546 #if 1
2547       if (value != EL_UNDEFINED && value != EL_EMPTY)
2548       {
2549         element = value;
2550         graphic = el2panelimg(value);
2551
2552         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2553
2554 #if 1
2555         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2556           size = TILESIZE;
2557 #endif
2558
2559         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2560                               &src_x, &src_y);
2561
2562         width  = graphic_info[graphic].width  * size / TILESIZE;
2563         height = graphic_info[graphic].height * size / TILESIZE;
2564
2565         if (draw_masked)
2566         {
2567           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2568                         dst_x - src_x, dst_y - src_y);
2569           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2570                            dst_x, dst_y);
2571         }
2572         else
2573         {
2574           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2575                      dst_x, dst_y);
2576         }
2577       }
2578 #else
2579       if (value == EL_UNDEFINED || value == EL_EMPTY)
2580       {
2581         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2582         graphic = el2panelimg(element);
2583
2584         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2585         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2586         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2587       }
2588       else
2589       {
2590         element = value;
2591         graphic = el2panelimg(value);
2592
2593         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2594       }
2595
2596       width  = graphic_info[graphic].width  * size / TILESIZE;
2597       height = graphic_info[graphic].height * size / TILESIZE;
2598
2599       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2600 #endif
2601     }
2602     else if (type == TYPE_STRING)
2603     {
2604       boolean active = (value != 0);
2605       char *state_normal = "off";
2606       char *state_active = "on";
2607       char *state = (active ? state_active : state_normal);
2608       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2609                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2610                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2611                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2612
2613       if (nr == GAME_PANEL_GRAVITY_STATE)
2614       {
2615         int font1 = pos->font;          /* (used for normal state) */
2616         int font2 = pos->font_alt;      /* (used for active state) */
2617 #if 0
2618         int size1 = strlen(state_normal);
2619         int size2 = strlen(state_active);
2620         int width1 = size1 * getFontWidth(font1);
2621         int width2 = size2 * getFontWidth(font2);
2622         int max_width = MAX(width1, width2);
2623         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2624
2625         pos->width = max_width;
2626
2627         /* clear background for values that may have changed its size */
2628         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2629                                    max_width, max_height);
2630 #endif
2631
2632         font = (active ? font2 : font1);
2633       }
2634
2635       if (s != NULL)
2636       {
2637         char *s_cut;
2638
2639 #if 1
2640         if (size <= 0)
2641         {
2642           /* don't truncate output if "chars" is zero or less */
2643           size = strlen(s);
2644
2645           /* dynamically correct text alignment */
2646           pos->width = size * getFontWidth(font);
2647         }
2648 #endif
2649
2650         s_cut = getStringCopyN(s, size);
2651
2652         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2653                     s_cut, font, mask_mode);
2654
2655         free(s_cut);
2656       }
2657     }
2658
2659     redraw_mask |= REDRAW_DOOR_1;
2660   }
2661
2662   game_status = GAME_MODE_PLAYING;
2663 }
2664
2665 void DrawGameValue_Emeralds(int value)
2666 {
2667   struct TextPosInfo *pos = &game.panel.gems;
2668 #if 1
2669   int font_nr = pos->font;
2670 #else
2671   int font_nr = FONT_TEXT_2;
2672 #endif
2673   int font_width = getFontWidth(font_nr);
2674   int chars = pos->size;
2675
2676 #if 1
2677   return;       /* !!! USE NEW STUFF !!! */
2678 #endif
2679
2680   if (PANEL_DEACTIVATED(pos))
2681     return;
2682
2683   pos->width = chars * font_width;
2684
2685   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2686 }
2687
2688 void DrawGameValue_Dynamite(int value)
2689 {
2690   struct TextPosInfo *pos = &game.panel.inventory_count;
2691 #if 1
2692   int font_nr = pos->font;
2693 #else
2694   int font_nr = FONT_TEXT_2;
2695 #endif
2696   int font_width = getFontWidth(font_nr);
2697   int chars = pos->size;
2698
2699 #if 1
2700   return;       /* !!! USE NEW STUFF !!! */
2701 #endif
2702
2703   if (PANEL_DEACTIVATED(pos))
2704     return;
2705
2706   pos->width = chars * font_width;
2707
2708   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2709 }
2710
2711 void DrawGameValue_Score(int value)
2712 {
2713   struct TextPosInfo *pos = &game.panel.score;
2714 #if 1
2715   int font_nr = pos->font;
2716 #else
2717   int font_nr = FONT_TEXT_2;
2718 #endif
2719   int font_width = getFontWidth(font_nr);
2720   int chars = pos->size;
2721
2722 #if 1
2723   return;       /* !!! USE NEW STUFF !!! */
2724 #endif
2725
2726   if (PANEL_DEACTIVATED(pos))
2727     return;
2728
2729   pos->width = chars * font_width;
2730
2731   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2732 }
2733
2734 void DrawGameValue_Time(int value)
2735 {
2736   struct TextPosInfo *pos = &game.panel.time;
2737   static int last_value = -1;
2738   int chars1 = 3;
2739   int chars2 = 4;
2740   int chars = pos->size;
2741 #if 1
2742   int font1_nr = pos->font;
2743   int font2_nr = pos->font_alt;
2744 #else
2745   int font1_nr = FONT_TEXT_2;
2746   int font2_nr = FONT_TEXT_1;
2747 #endif
2748   int font_nr = font1_nr;
2749   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2750
2751 #if 1
2752   return;       /* !!! USE NEW STUFF !!! */
2753 #endif
2754
2755   if (PANEL_DEACTIVATED(pos))
2756     return;
2757
2758   if (use_dynamic_chars)                /* use dynamic number of chars */
2759   {
2760     chars   = (value < 1000 ? chars1   : chars2);
2761     font_nr = (value < 1000 ? font1_nr : font2_nr);
2762   }
2763
2764   /* clear background if value just changed its size (dynamic chars only) */
2765   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2766   {
2767     int width1 = chars1 * getFontWidth(font1_nr);
2768     int width2 = chars2 * getFontWidth(font2_nr);
2769     int max_width = MAX(width1, width2);
2770     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2771
2772     pos->width = max_width;
2773
2774     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2775                                max_width, max_height);
2776   }
2777
2778   pos->width = chars * getFontWidth(font_nr);
2779
2780   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2781
2782   last_value = value;
2783 }
2784
2785 void DrawGameValue_Level(int value)
2786 {
2787   struct TextPosInfo *pos = &game.panel.level_number;
2788   int chars1 = 2;
2789   int chars2 = 3;
2790   int chars = pos->size;
2791 #if 1
2792   int font1_nr = pos->font;
2793   int font2_nr = pos->font_alt;
2794 #else
2795   int font1_nr = FONT_TEXT_2;
2796   int font2_nr = FONT_TEXT_1;
2797 #endif
2798   int font_nr = font1_nr;
2799   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2800
2801 #if 1
2802   return;       /* !!! USE NEW STUFF !!! */
2803 #endif
2804
2805   if (PANEL_DEACTIVATED(pos))
2806     return;
2807
2808   if (use_dynamic_chars)                /* use dynamic number of chars */
2809   {
2810     chars   = (level_nr < 100 ? chars1   : chars2);
2811     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2812   }
2813
2814   pos->width = chars * getFontWidth(font_nr);
2815
2816   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2817 }
2818
2819 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2820 {
2821 #if 0
2822   struct TextPosInfo *pos = &game.panel.keys;
2823 #endif
2824 #if 0
2825   int base_key_graphic = EL_KEY_1;
2826 #endif
2827   int i;
2828
2829 #if 1
2830   return;       /* !!! USE NEW STUFF !!! */
2831 #endif
2832
2833 #if 0
2834   if (PANEL_DEACTIVATED(pos))
2835     return;
2836 #endif
2837
2838 #if 0
2839   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2840     base_key_graphic = EL_EM_KEY_1;
2841 #endif
2842
2843 #if 0
2844   pos->width = 4 * MINI_TILEX;
2845 #endif
2846
2847 #if 1
2848   for (i = 0; i < MAX_NUM_KEYS; i++)
2849 #else
2850   /* currently only 4 of 8 possible keys are displayed */
2851   for (i = 0; i < STD_NUM_KEYS; i++)
2852 #endif
2853   {
2854 #if 1
2855     struct TextPosInfo *pos = &game.panel.key[i];
2856 #endif
2857     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2858     int src_y = DOOR_GFX_PAGEY1 + 123;
2859 #if 1
2860     int dst_x = PANEL_XPOS(pos);
2861     int dst_y = PANEL_YPOS(pos);
2862 #else
2863     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2864     int dst_y = PANEL_YPOS(pos);
2865 #endif
2866
2867 #if 1
2868     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2869                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2870                    EL_KEY_1) + i;
2871     int graphic = el2edimg(element);
2872 #endif
2873
2874 #if 1
2875     if (PANEL_DEACTIVATED(pos))
2876       continue;
2877 #endif
2878
2879 #if 0
2880     /* masked blit with tiles from half-size scaled bitmap does not work yet
2881        (no mask bitmap created for these sizes after loading and scaling) --
2882        solution: load without creating mask, scale, then create final mask */
2883
2884     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2885                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2886
2887     if (key[i])
2888     {
2889 #if 0
2890       int graphic = el2edimg(base_key_graphic + i);
2891 #endif
2892       Bitmap *src_bitmap;
2893       int src_x, src_y;
2894
2895       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2896
2897       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2898                     dst_x - src_x, dst_y - src_y);
2899       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2900                        dst_x, dst_y);
2901     }
2902 #else
2903 #if 1
2904     if (key[i])
2905       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2906     else
2907       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2908                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2909 #else
2910     if (key[i])
2911       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2912     else
2913       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2914                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2915 #endif
2916 #endif
2917   }
2918 }
2919
2920 #else
2921
2922 void DrawGameValue_Emeralds(int value)
2923 {
2924   int font_nr = FONT_TEXT_2;
2925   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2926
2927   if (PANEL_DEACTIVATED(game.panel.gems))
2928     return;
2929
2930   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2931 }
2932
2933 void DrawGameValue_Dynamite(int value)
2934 {
2935   int font_nr = FONT_TEXT_2;
2936   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2937
2938   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2939     return;
2940
2941   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2942 }
2943
2944 void DrawGameValue_Score(int value)
2945 {
2946   int font_nr = FONT_TEXT_2;
2947   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2948
2949   if (PANEL_DEACTIVATED(game.panel.score))
2950     return;
2951
2952   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2953 }
2954
2955 void DrawGameValue_Time(int value)
2956 {
2957   int font1_nr = FONT_TEXT_2;
2958 #if 1
2959   int font2_nr = FONT_TEXT_1;
2960 #else
2961   int font2_nr = FONT_LEVEL_NUMBER;
2962 #endif
2963   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2964   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2965
2966   if (PANEL_DEACTIVATED(game.panel.time))
2967     return;
2968
2969   /* clear background if value just changed its size */
2970   if (value == 999 || value == 1000)
2971     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2972
2973   if (value < 1000)
2974     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2975   else
2976     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2977 }
2978
2979 void DrawGameValue_Level(int value)
2980 {
2981   int font1_nr = FONT_TEXT_2;
2982 #if 1
2983   int font2_nr = FONT_TEXT_1;
2984 #else
2985   int font2_nr = FONT_LEVEL_NUMBER;
2986 #endif
2987
2988   if (PANEL_DEACTIVATED(game.panel.level))
2989     return;
2990
2991   if (level_nr < 100)
2992     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2993   else
2994     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2995 }
2996
2997 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2998 {
2999   int base_key_graphic = EL_KEY_1;
3000   int i;
3001
3002   if (PANEL_DEACTIVATED(game.panel.keys))
3003     return;
3004
3005   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3006     base_key_graphic = EL_EM_KEY_1;
3007
3008   /* currently only 4 of 8 possible keys are displayed */
3009   for (i = 0; i < STD_NUM_KEYS; i++)
3010   {
3011     int x = XX_KEYS + i * MINI_TILEX;
3012     int y = YY_KEYS;
3013
3014     if (key[i])
3015       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3016     else
3017       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3018                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3019   }
3020 }
3021
3022 #endif
3023
3024 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3025                        int key_bits)
3026 {
3027   int key[MAX_NUM_KEYS];
3028   int i;
3029
3030   /* prevent EM engine from updating time/score values parallel to GameWon() */
3031   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3032       local_player->LevelSolved)
3033     return;
3034
3035   for (i = 0; i < MAX_NUM_KEYS; i++)
3036     key[i] = key_bits & (1 << i);
3037
3038   DrawGameValue_Level(level_nr);
3039
3040   DrawGameValue_Emeralds(emeralds);
3041   DrawGameValue_Dynamite(dynamite);
3042   DrawGameValue_Score(score);
3043   DrawGameValue_Time(time);
3044
3045   DrawGameValue_Keys(key);
3046 }
3047
3048 void UpdateGameDoorValues()
3049 {
3050   UpdateGameControlValues();
3051 }
3052
3053 void DrawGameDoorValues()
3054 {
3055   DisplayGameControlValues();
3056 }
3057
3058 void DrawGameDoorValues_OLD()
3059 {
3060   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3061   int dynamite_value = 0;
3062   int score_value = (local_player->LevelSolved ? local_player->score_final :
3063                      local_player->score);
3064   int gems_value = local_player->gems_still_needed;
3065   int key_bits = 0;
3066   int i, j;
3067
3068   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3069   {
3070     DrawGameDoorValues_EM();
3071
3072     return;
3073   }
3074
3075   if (game.centered_player_nr == -1)
3076   {
3077     for (i = 0; i < MAX_PLAYERS; i++)
3078     {
3079       for (j = 0; j < MAX_NUM_KEYS; j++)
3080         if (stored_player[i].key[j])
3081           key_bits |= (1 << j);
3082
3083       dynamite_value += stored_player[i].inventory_size;
3084     }
3085   }
3086   else
3087   {
3088     int player_nr = game.centered_player_nr;
3089
3090     for (i = 0; i < MAX_NUM_KEYS; i++)
3091       if (stored_player[player_nr].key[i])
3092         key_bits |= (1 << i);
3093
3094     dynamite_value = stored_player[player_nr].inventory_size;
3095   }
3096
3097   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3098                     key_bits);
3099 }
3100
3101
3102 /*
3103   =============================================================================
3104   InitGameEngine()
3105   -----------------------------------------------------------------------------
3106   initialize game engine due to level / tape version number
3107   =============================================================================
3108 */
3109
3110 static void InitGameEngine()
3111 {
3112   int i, j, k, l, x, y;
3113
3114   /* set game engine from tape file when re-playing, else from level file */
3115   game.engine_version = (tape.playing ? tape.engine_version :
3116                          level.game_version);
3117
3118   /* ---------------------------------------------------------------------- */
3119   /* set flags for bugs and changes according to active game engine version */
3120   /* ---------------------------------------------------------------------- */
3121
3122   /*
3123     Summary of bugfix/change:
3124     Fixed handling for custom elements that change when pushed by the player.
3125
3126     Fixed/changed in version:
3127     3.1.0
3128
3129     Description:
3130     Before 3.1.0, custom elements that "change when pushing" changed directly
3131     after the player started pushing them (until then handled in "DigField()").
3132     Since 3.1.0, these custom elements are not changed until the "pushing"
3133     move of the element is finished (now handled in "ContinueMoving()").
3134
3135     Affected levels/tapes:
3136     The first condition is generally needed for all levels/tapes before version
3137     3.1.0, which might use the old behaviour before it was changed; known tapes
3138     that are affected are some tapes from the level set "Walpurgis Gardens" by
3139     Jamie Cullen.
3140     The second condition is an exception from the above case and is needed for
3141     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3142     above (including some development versions of 3.1.0), but before it was
3143     known that this change would break tapes like the above and was fixed in
3144     3.1.1, so that the changed behaviour was active although the engine version
3145     while recording maybe was before 3.1.0. There is at least one tape that is
3146     affected by this exception, which is the tape for the one-level set "Bug
3147     Machine" by Juergen Bonhagen.
3148   */
3149
3150   game.use_change_when_pushing_bug =
3151     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3152      !(tape.playing &&
3153        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3154        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3155
3156   /*
3157     Summary of bugfix/change:
3158     Fixed handling for blocking the field the player leaves when moving.
3159
3160     Fixed/changed in version:
3161     3.1.1
3162
3163     Description:
3164     Before 3.1.1, when "block last field when moving" was enabled, the field
3165     the player is leaving when moving was blocked for the time of the move,
3166     and was directly unblocked afterwards. This resulted in the last field
3167     being blocked for exactly one less than the number of frames of one player
3168     move. Additionally, even when blocking was disabled, the last field was
3169     blocked for exactly one frame.
3170     Since 3.1.1, due to changes in player movement handling, the last field
3171     is not blocked at all when blocking is disabled. When blocking is enabled,
3172     the last field is blocked for exactly the number of frames of one player
3173     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3174     last field is blocked for exactly one more than the number of frames of
3175     one player move.
3176
3177     Affected levels/tapes:
3178     (!!! yet to be determined -- probably many !!!)
3179   */
3180
3181   game.use_block_last_field_bug =
3182     (game.engine_version < VERSION_IDENT(3,1,1,0));
3183
3184   /*
3185     Summary of bugfix/change:
3186     Changed behaviour of CE changes with multiple changes per single frame.
3187
3188     Fixed/changed in version:
3189     3.2.0-6
3190
3191     Description:
3192     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3193     This resulted in race conditions where CEs seem to behave strange in some
3194     situations (where triggered CE changes were just skipped because there was
3195     already a CE change on that tile in the playfield in that engine frame).
3196     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3197     (The number of changes per frame must be limited in any case, because else
3198     it is easily possible to define CE changes that would result in an infinite
3199     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3200     should be set large enough so that it would only be reached in cases where
3201     the corresponding CE change conditions run into a loop. Therefore, it seems
3202     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3203     maximal number of change pages for custom elements.)
3204
3205     Affected levels/tapes:
3206     Probably many.
3207   */
3208
3209 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3210   game.max_num_changes_per_frame = 1;
3211 #else
3212   game.max_num_changes_per_frame =
3213     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3214 #endif
3215
3216   /* ---------------------------------------------------------------------- */
3217
3218   /* default scan direction: scan playfield from top/left to bottom/right */
3219   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3220
3221   /* dynamically adjust element properties according to game engine version */
3222   InitElementPropertiesEngine(game.engine_version);
3223
3224 #if 0
3225   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3226   printf("          tape version == %06d [%s] [file: %06d]\n",
3227          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3228          tape.file_version);
3229   printf("       => game.engine_version == %06d\n", game.engine_version);
3230 #endif
3231
3232   /* ---------- initialize player's initial move delay --------------------- */
3233
3234   /* dynamically adjust player properties according to level information */
3235   for (i = 0; i < MAX_PLAYERS; i++)
3236     game.initial_move_delay_value[i] =
3237       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3238
3239   /* dynamically adjust player properties according to game engine version */
3240   for (i = 0; i < MAX_PLAYERS; i++)
3241     game.initial_move_delay[i] =
3242       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3243        game.initial_move_delay_value[i] : 0);
3244
3245   /* ---------- initialize player's initial push delay --------------------- */
3246
3247   /* dynamically adjust player properties according to game engine version */
3248   game.initial_push_delay_value =
3249     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3250
3251   /* ---------- initialize changing elements ------------------------------- */
3252
3253   /* initialize changing elements information */
3254   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3255   {
3256     struct ElementInfo *ei = &element_info[i];
3257
3258     /* this pointer might have been changed in the level editor */
3259     ei->change = &ei->change_page[0];
3260
3261     if (!IS_CUSTOM_ELEMENT(i))
3262     {
3263       ei->change->target_element = EL_EMPTY_SPACE;
3264       ei->change->delay_fixed = 0;
3265       ei->change->delay_random = 0;
3266       ei->change->delay_frames = 1;
3267     }
3268
3269     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3270     {
3271       ei->has_change_event[j] = FALSE;
3272
3273       ei->event_page_nr[j] = 0;
3274       ei->event_page[j] = &ei->change_page[0];
3275     }
3276   }
3277
3278   /* add changing elements from pre-defined list */
3279   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3280   {
3281     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3282     struct ElementInfo *ei = &element_info[ch_delay->element];
3283
3284     ei->change->target_element       = ch_delay->target_element;
3285     ei->change->delay_fixed          = ch_delay->change_delay;
3286
3287     ei->change->pre_change_function  = ch_delay->pre_change_function;
3288     ei->change->change_function      = ch_delay->change_function;
3289     ei->change->post_change_function = ch_delay->post_change_function;
3290
3291     ei->change->can_change = TRUE;
3292     ei->change->can_change_or_has_action = TRUE;
3293
3294     ei->has_change_event[CE_DELAY] = TRUE;
3295
3296     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3297     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3298   }
3299
3300   /* ---------- initialize internal run-time variables ------------- */
3301
3302   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3303   {
3304     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3305
3306     for (j = 0; j < ei->num_change_pages; j++)
3307     {
3308       ei->change_page[j].can_change_or_has_action =
3309         (ei->change_page[j].can_change |
3310          ei->change_page[j].has_action);
3311     }
3312   }
3313
3314   /* add change events from custom element configuration */
3315   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3316   {
3317     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3318
3319     for (j = 0; j < ei->num_change_pages; j++)
3320     {
3321       if (!ei->change_page[j].can_change_or_has_action)
3322         continue;
3323
3324       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3325       {
3326         /* only add event page for the first page found with this event */
3327         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3328         {
3329           ei->has_change_event[k] = TRUE;
3330
3331           ei->event_page_nr[k] = j;
3332           ei->event_page[k] = &ei->change_page[j];
3333         }
3334       }
3335     }
3336   }
3337
3338   /* ---------- initialize run-time trigger player and element ------------- */
3339
3340   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3341   {
3342     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3343
3344     for (j = 0; j < ei->num_change_pages; j++)
3345     {
3346       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3347       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3348       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3349       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3350       ei->change_page[j].actual_trigger_ce_value = 0;
3351       ei->change_page[j].actual_trigger_ce_score = 0;
3352     }
3353   }
3354
3355   /* ---------- initialize trigger events ---------------------------------- */
3356
3357   /* initialize trigger events information */
3358   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3359     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3360       trigger_events[i][j] = FALSE;
3361
3362   /* add trigger events from element change event properties */
3363   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3364   {
3365     struct ElementInfo *ei = &element_info[i];
3366
3367     for (j = 0; j < ei->num_change_pages; j++)
3368     {
3369       if (!ei->change_page[j].can_change_or_has_action)
3370         continue;
3371
3372       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3373       {
3374         int trigger_element = ei->change_page[j].trigger_element;
3375
3376         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3377         {
3378           if (ei->change_page[j].has_event[k])
3379           {
3380             if (IS_GROUP_ELEMENT(trigger_element))
3381             {
3382               struct ElementGroupInfo *group =
3383                 element_info[trigger_element].group;
3384
3385               for (l = 0; l < group->num_elements_resolved; l++)
3386                 trigger_events[group->element_resolved[l]][k] = TRUE;
3387             }
3388             else if (trigger_element == EL_ANY_ELEMENT)
3389               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3390                 trigger_events[l][k] = TRUE;
3391             else
3392               trigger_events[trigger_element][k] = TRUE;
3393           }
3394         }
3395       }
3396     }
3397   }
3398
3399   /* ---------- initialize push delay -------------------------------------- */
3400
3401   /* initialize push delay values to default */
3402   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3403   {
3404     if (!IS_CUSTOM_ELEMENT(i))
3405     {
3406       /* set default push delay values (corrected since version 3.0.7-1) */
3407       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3408       {
3409         element_info[i].push_delay_fixed = 2;
3410         element_info[i].push_delay_random = 8;
3411       }
3412       else
3413       {
3414         element_info[i].push_delay_fixed = 8;
3415         element_info[i].push_delay_random = 8;
3416       }
3417     }
3418   }
3419
3420   /* set push delay value for certain elements from pre-defined list */
3421   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3422   {
3423     int e = push_delay_list[i].element;
3424
3425     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3426     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3427   }
3428
3429   /* set push delay value for Supaplex elements for newer engine versions */
3430   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3431   {
3432     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3433     {
3434       if (IS_SP_ELEMENT(i))
3435       {
3436         /* set SP push delay to just enough to push under a falling zonk */
3437         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3438
3439         element_info[i].push_delay_fixed  = delay;
3440         element_info[i].push_delay_random = 0;
3441       }
3442     }
3443   }
3444
3445   /* ---------- initialize move stepsize ----------------------------------- */
3446
3447   /* initialize move stepsize values to default */
3448   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3449     if (!IS_CUSTOM_ELEMENT(i))
3450       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3451
3452   /* set move stepsize value for certain elements from pre-defined list */
3453   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3454   {
3455     int e = move_stepsize_list[i].element;
3456
3457     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3458   }
3459
3460   /* ---------- initialize collect score ----------------------------------- */
3461
3462   /* initialize collect score values for custom elements from initial value */
3463   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3464     if (IS_CUSTOM_ELEMENT(i))
3465       element_info[i].collect_score = element_info[i].collect_score_initial;
3466
3467   /* ---------- initialize collect count ----------------------------------- */
3468
3469   /* initialize collect count values for non-custom elements */
3470   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3471     if (!IS_CUSTOM_ELEMENT(i))
3472       element_info[i].collect_count_initial = 0;
3473
3474   /* add collect count values for all elements from pre-defined list */
3475   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3476     element_info[collect_count_list[i].element].collect_count_initial =
3477       collect_count_list[i].count;
3478
3479   /* ---------- initialize access direction -------------------------------- */
3480
3481   /* initialize access direction values to default (access from every side) */
3482   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3483     if (!IS_CUSTOM_ELEMENT(i))
3484       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3485
3486   /* set access direction value for certain elements from pre-defined list */
3487   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3488     element_info[access_direction_list[i].element].access_direction =
3489       access_direction_list[i].direction;
3490
3491   /* ---------- initialize explosion content ------------------------------- */
3492   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3493   {
3494     if (IS_CUSTOM_ELEMENT(i))
3495       continue;
3496
3497     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3498     {
3499       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3500
3501       element_info[i].content.e[x][y] =
3502         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3503          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3504          i == EL_PLAYER_3 ? EL_EMERALD :
3505          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3506          i == EL_MOLE ? EL_EMERALD_RED :
3507          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3508          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3509          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3510          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3511          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3512          i == EL_WALL_EMERALD ? EL_EMERALD :
3513          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3514          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3515          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3516          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3517          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3518          i == EL_WALL_PEARL ? EL_PEARL :
3519          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3520          EL_EMPTY);
3521     }
3522   }
3523
3524   /* ---------- initialize recursion detection ------------------------------ */
3525   recursion_loop_depth = 0;
3526   recursion_loop_detected = FALSE;
3527   recursion_loop_element = EL_UNDEFINED;
3528
3529   /* ---------- initialize graphics engine ---------------------------------- */
3530   game.scroll_delay_value =
3531     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3532      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3533   game.scroll_delay_value =
3534     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3535 }
3536
3537 int get_num_special_action(int element, int action_first, int action_last)
3538 {
3539   int num_special_action = 0;
3540   int i, j;
3541
3542   for (i = action_first; i <= action_last; i++)
3543   {
3544     boolean found = FALSE;
3545
3546     for (j = 0; j < NUM_DIRECTIONS; j++)
3547       if (el_act_dir2img(element, i, j) !=
3548           el_act_dir2img(element, ACTION_DEFAULT, j))
3549         found = TRUE;
3550
3551     if (found)
3552       num_special_action++;
3553     else
3554       break;
3555   }
3556
3557   return num_special_action;
3558 }
3559
3560
3561 /*
3562   =============================================================================
3563   InitGame()
3564   -----------------------------------------------------------------------------
3565   initialize and start new game
3566   =============================================================================
3567 */
3568
3569 void InitGame()
3570 {
3571   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3572   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3573   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3574 #if 0
3575   boolean do_fading = (game_status == GAME_MODE_MAIN);
3576 #endif
3577   int i, j, x, y;
3578
3579   game_status = GAME_MODE_PLAYING;
3580
3581   InitGameEngine();
3582   InitGameControlValues();
3583
3584   /* don't play tapes over network */
3585   network_playing = (options.network && !tape.playing);
3586
3587   for (i = 0; i < MAX_PLAYERS; i++)
3588   {
3589     struct PlayerInfo *player = &stored_player[i];
3590
3591     player->index_nr = i;
3592     player->index_bit = (1 << i);
3593     player->element_nr = EL_PLAYER_1 + i;
3594
3595     player->present = FALSE;
3596     player->active = FALSE;
3597     player->killed = FALSE;
3598
3599     player->action = 0;
3600     player->effective_action = 0;
3601     player->programmed_action = 0;
3602
3603     player->score = 0;
3604     player->score_final = 0;
3605
3606     player->gems_still_needed = level.gems_needed;
3607     player->sokobanfields_still_needed = 0;
3608     player->lights_still_needed = 0;
3609     player->friends_still_needed = 0;
3610
3611     for (j = 0; j < MAX_NUM_KEYS; j++)
3612       player->key[j] = FALSE;
3613
3614     player->num_white_keys = 0;
3615
3616     player->dynabomb_count = 0;
3617     player->dynabomb_size = 1;
3618     player->dynabombs_left = 0;
3619     player->dynabomb_xl = FALSE;
3620
3621     player->MovDir = MV_NONE;
3622     player->MovPos = 0;
3623     player->GfxPos = 0;
3624     player->GfxDir = MV_NONE;
3625     player->GfxAction = ACTION_DEFAULT;
3626     player->Frame = 0;
3627     player->StepFrame = 0;
3628
3629     player->use_murphy = FALSE;
3630     player->artwork_element =
3631       (level.use_artwork_element[i] ? level.artwork_element[i] :
3632        player->element_nr);
3633
3634     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3635     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3636
3637     player->gravity = level.initial_player_gravity[i];
3638
3639     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3640
3641     player->actual_frame_counter = 0;
3642
3643     player->step_counter = 0;
3644
3645     player->last_move_dir = MV_NONE;
3646
3647     player->is_active = FALSE;
3648
3649     player->is_waiting = FALSE;
3650     player->is_moving = FALSE;
3651     player->is_auto_moving = FALSE;
3652     player->is_digging = FALSE;
3653     player->is_snapping = FALSE;
3654     player->is_collecting = FALSE;
3655     player->is_pushing = FALSE;
3656     player->is_switching = FALSE;
3657     player->is_dropping = FALSE;
3658     player->is_dropping_pressed = FALSE;
3659
3660     player->is_bored = FALSE;
3661     player->is_sleeping = FALSE;
3662
3663     player->frame_counter_bored = -1;
3664     player->frame_counter_sleeping = -1;
3665
3666     player->anim_delay_counter = 0;
3667     player->post_delay_counter = 0;
3668
3669     player->dir_waiting = MV_NONE;
3670     player->action_waiting = ACTION_DEFAULT;
3671     player->last_action_waiting = ACTION_DEFAULT;
3672     player->special_action_bored = ACTION_DEFAULT;
3673     player->special_action_sleeping = ACTION_DEFAULT;
3674
3675     player->switch_x = -1;
3676     player->switch_y = -1;
3677
3678     player->drop_x = -1;
3679     player->drop_y = -1;
3680
3681     player->show_envelope = 0;
3682
3683     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3684
3685     player->push_delay       = -1;      /* initialized when pushing starts */
3686     player->push_delay_value = game.initial_push_delay_value;
3687
3688     player->drop_delay = 0;
3689     player->drop_pressed_delay = 0;
3690
3691     player->last_jx = -1;
3692     player->last_jy = -1;
3693     player->jx = -1;
3694     player->jy = -1;
3695
3696     player->shield_normal_time_left = 0;
3697     player->shield_deadly_time_left = 0;
3698
3699     player->inventory_infinite_element = EL_UNDEFINED;
3700     player->inventory_size = 0;
3701
3702     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3703     SnapField(player, 0, 0);
3704
3705     player->LevelSolved = FALSE;
3706     player->GameOver = FALSE;
3707
3708     player->LevelSolved_GameWon = FALSE;
3709     player->LevelSolved_GameEnd = FALSE;
3710     player->LevelSolved_PanelOff = FALSE;
3711     player->LevelSolved_SaveTape = FALSE;
3712     player->LevelSolved_SaveScore = FALSE;
3713     player->LevelSolved_CountingTime = 0;
3714     player->LevelSolved_CountingScore = 0;
3715   }
3716
3717   network_player_action_received = FALSE;
3718
3719 #if defined(NETWORK_AVALIABLE)
3720   /* initial null action */
3721   if (network_playing)
3722     SendToServer_MovePlayer(MV_NONE);
3723 #endif
3724
3725   ZX = ZY = -1;
3726   ExitX = ExitY = -1;
3727
3728   FrameCounter = 0;
3729   TimeFrames = 0;
3730   TimePlayed = 0;
3731   TimeLeft = level.time;
3732   TapeTime = 0;
3733
3734   ScreenMovDir = MV_NONE;
3735   ScreenMovPos = 0;
3736   ScreenGfxPos = 0;
3737
3738   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3739
3740   AllPlayersGone = FALSE;
3741
3742   game.yamyam_content_nr = 0;
3743   game.robot_wheel_active = FALSE;
3744   game.magic_wall_active = FALSE;
3745   game.magic_wall_time_left = 0;
3746   game.light_time_left = 0;
3747   game.timegate_time_left = 0;
3748   game.switchgate_pos = 0;
3749   game.wind_direction = level.wind_direction_initial;
3750
3751 #if !USE_PLAYER_GRAVITY
3752   game.gravity = FALSE;
3753   game.explosions_delayed = TRUE;
3754 #endif
3755
3756   game.lenses_time_left = 0;
3757   game.magnify_time_left = 0;
3758
3759   game.ball_state = level.ball_state_initial;
3760   game.ball_content_nr = 0;
3761
3762   game.envelope_active = FALSE;
3763
3764   /* set focus to local player for network games, else to all players */
3765   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3766   game.centered_player_nr_next = game.centered_player_nr;
3767   game.set_centered_player = FALSE;
3768
3769   if (network_playing && tape.recording)
3770   {
3771     /* store client dependent player focus when recording network games */
3772     tape.centered_player_nr_next = game.centered_player_nr_next;
3773     tape.set_centered_player = TRUE;
3774   }
3775
3776   for (i = 0; i < NUM_BELTS; i++)
3777   {
3778     game.belt_dir[i] = MV_NONE;
3779     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3780   }
3781
3782   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3783     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3784
3785   SCAN_PLAYFIELD(x, y)
3786   {
3787     Feld[x][y] = level.field[x][y];
3788     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3789     ChangeDelay[x][y] = 0;
3790     ChangePage[x][y] = -1;
3791 #if USE_NEW_CUSTOM_VALUE
3792     CustomValue[x][y] = 0;              /* initialized in InitField() */
3793 #endif
3794     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3795     AmoebaNr[x][y] = 0;
3796     WasJustMoving[x][y] = 0;
3797     WasJustFalling[x][y] = 0;
3798     CheckCollision[x][y] = 0;
3799     CheckImpact[x][y] = 0;
3800     Stop[x][y] = FALSE;
3801     Pushed[x][y] = FALSE;
3802
3803     ChangeCount[x][y] = 0;
3804     ChangeEvent[x][y] = -1;
3805
3806     ExplodePhase[x][y] = 0;
3807     ExplodeDelay[x][y] = 0;
3808     ExplodeField[x][y] = EX_TYPE_NONE;
3809
3810     RunnerVisit[x][y] = 0;
3811     PlayerVisit[x][y] = 0;
3812
3813     GfxFrame[x][y] = 0;
3814     GfxRandom[x][y] = INIT_GFX_RANDOM();
3815     GfxElement[x][y] = EL_UNDEFINED;
3816     GfxAction[x][y] = ACTION_DEFAULT;
3817     GfxDir[x][y] = MV_NONE;
3818   }
3819
3820   SCAN_PLAYFIELD(x, y)
3821   {
3822     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3823       emulate_bd = FALSE;
3824     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3825       emulate_sb = FALSE;
3826     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3827       emulate_sp = FALSE;
3828
3829     InitField(x, y, TRUE);
3830
3831     ResetGfxAnimation(x, y);
3832   }
3833
3834   InitBeltMovement();
3835
3836   for (i = 0; i < MAX_PLAYERS; i++)
3837   {
3838     struct PlayerInfo *player = &stored_player[i];
3839
3840     /* set number of special actions for bored and sleeping animation */
3841     player->num_special_action_bored =
3842       get_num_special_action(player->artwork_element,
3843                              ACTION_BORING_1, ACTION_BORING_LAST);
3844     player->num_special_action_sleeping =
3845       get_num_special_action(player->artwork_element,
3846                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3847   }
3848
3849   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3850                     emulate_sb ? EMU_SOKOBAN :
3851                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3852
3853 #if USE_NEW_ALL_SLIPPERY
3854   /* initialize type of slippery elements */
3855   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3856   {
3857     if (!IS_CUSTOM_ELEMENT(i))
3858     {
3859       /* default: elements slip down either to the left or right randomly */
3860       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3861
3862       /* SP style elements prefer to slip down on the left side */
3863       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3864         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3865
3866       /* BD style elements prefer to slip down on the left side */
3867       if (game.emulation == EMU_BOULDERDASH)
3868         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3869     }
3870   }
3871 #endif
3872
3873   /* initialize explosion and ignition delay */
3874   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3875   {
3876     if (!IS_CUSTOM_ELEMENT(i))
3877     {
3878       int num_phase = 8;
3879       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3880                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3881                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3882       int last_phase = (num_phase + 1) * delay;
3883       int half_phase = (num_phase / 2) * delay;
3884
3885       element_info[i].explosion_delay = last_phase - 1;
3886       element_info[i].ignition_delay = half_phase;
3887
3888       if (i == EL_BLACK_ORB)
3889         element_info[i].ignition_delay = 1;
3890     }
3891
3892 #if 0
3893     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3894       element_info[i].explosion_delay = 1;
3895
3896     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3897       element_info[i].ignition_delay = 1;
3898 #endif
3899   }
3900
3901   /* correct non-moving belts to start moving left */
3902   for (i = 0; i < NUM_BELTS; i++)
3903     if (game.belt_dir[i] == MV_NONE)
3904       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3905
3906   /* check if any connected player was not found in playfield */
3907   for (i = 0; i < MAX_PLAYERS; i++)
3908   {
3909     struct PlayerInfo *player = &stored_player[i];
3910
3911     if (player->connected && !player->present)
3912     {
3913       for (j = 0; j < MAX_PLAYERS; j++)
3914       {
3915         struct PlayerInfo *some_player = &stored_player[j];
3916         int jx = some_player->jx, jy = some_player->jy;
3917
3918         /* assign first free player found that is present in the playfield */
3919         if (some_player->present && !some_player->connected)
3920         {
3921           player->present = TRUE;
3922           player->active = TRUE;
3923
3924           some_player->present = FALSE;
3925           some_player->active = FALSE;
3926
3927           player->artwork_element = some_player->artwork_element;
3928
3929           player->block_last_field       = some_player->block_last_field;
3930           player->block_delay_adjustment = some_player->block_delay_adjustment;
3931
3932           StorePlayer[jx][jy] = player->element_nr;
3933           player->jx = player->last_jx = jx;
3934           player->jy = player->last_jy = jy;
3935
3936           break;
3937         }
3938       }
3939     }
3940   }
3941
3942   if (tape.playing)
3943   {
3944     /* when playing a tape, eliminate all players who do not participate */
3945
3946     for (i = 0; i < MAX_PLAYERS; i++)
3947     {
3948       if (stored_player[i].active && !tape.player_participates[i])
3949       {
3950         struct PlayerInfo *player = &stored_player[i];
3951         int jx = player->jx, jy = player->jy;
3952
3953         player->active = FALSE;
3954         StorePlayer[jx][jy] = 0;
3955         Feld[jx][jy] = EL_EMPTY;
3956       }
3957     }
3958   }
3959   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3960   {
3961     /* when in single player mode, eliminate all but the first active player */
3962
3963     for (i = 0; i < MAX_PLAYERS; i++)
3964     {
3965       if (stored_player[i].active)
3966       {
3967         for (j = i + 1; j < MAX_PLAYERS; j++)
3968         {
3969           if (stored_player[j].active)
3970           {
3971             struct PlayerInfo *player = &stored_player[j];
3972             int jx = player->jx, jy = player->jy;
3973
3974             player->active = FALSE;
3975             player->present = FALSE;
3976
3977             StorePlayer[jx][jy] = 0;
3978             Feld[jx][jy] = EL_EMPTY;
3979           }
3980         }
3981       }
3982     }
3983   }
3984
3985   /* when recording the game, store which players take part in the game */
3986   if (tape.recording)
3987   {
3988     for (i = 0; i < MAX_PLAYERS; i++)
3989       if (stored_player[i].active)
3990         tape.player_participates[i] = TRUE;
3991   }
3992
3993   if (options.debug)
3994   {
3995     for (i = 0; i < MAX_PLAYERS; i++)
3996     {
3997       struct PlayerInfo *player = &stored_player[i];
3998
3999       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4000              i+1,
4001              player->present,
4002              player->connected,
4003              player->active);
4004       if (local_player == player)
4005         printf("Player  %d is local player.\n", i+1);
4006     }
4007   }
4008
4009   if (BorderElement == EL_EMPTY)
4010   {
4011     SBX_Left = 0;
4012     SBX_Right = lev_fieldx - SCR_FIELDX;
4013     SBY_Upper = 0;
4014     SBY_Lower = lev_fieldy - SCR_FIELDY;
4015   }
4016   else
4017   {
4018     SBX_Left = -1;
4019     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4020     SBY_Upper = -1;
4021     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4022   }
4023
4024   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4025     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4026
4027   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4028     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4029
4030   /* if local player not found, look for custom element that might create
4031      the player (make some assumptions about the right custom element) */
4032   if (!local_player->present)
4033   {
4034     int start_x = 0, start_y = 0;
4035     int found_rating = 0;
4036     int found_element = EL_UNDEFINED;
4037     int player_nr = local_player->index_nr;
4038
4039     SCAN_PLAYFIELD(x, y)
4040     {
4041       int element = Feld[x][y];
4042       int content;
4043       int xx, yy;
4044       boolean is_player;
4045
4046       if (level.use_start_element[player_nr] &&
4047           level.start_element[player_nr] == element &&
4048           found_rating < 4)
4049       {
4050         start_x = x;
4051         start_y = y;
4052
4053         found_rating = 4;
4054         found_element = element;
4055       }
4056
4057       if (!IS_CUSTOM_ELEMENT(element))
4058         continue;
4059
4060       if (CAN_CHANGE(element))
4061       {
4062         for (i = 0; i < element_info[element].num_change_pages; i++)
4063         {
4064           /* check for player created from custom element as single target */
4065           content = element_info[element].change_page[i].target_element;
4066           is_player = ELEM_IS_PLAYER(content);
4067
4068           if (is_player && (found_rating < 3 ||
4069                             (found_rating == 3 && element < found_element)))
4070           {
4071             start_x = x;
4072             start_y = y;
4073
4074             found_rating = 3;
4075             found_element = element;
4076           }
4077         }
4078       }
4079
4080       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4081       {
4082         /* check for player created from custom element as explosion content */
4083         content = element_info[element].content.e[xx][yy];
4084         is_player = ELEM_IS_PLAYER(content);
4085
4086         if (is_player && (found_rating < 2 ||
4087                           (found_rating == 2 && element < found_element)))
4088         {
4089           start_x = x + xx - 1;
4090           start_y = y + yy - 1;
4091
4092           found_rating = 2;
4093           found_element = element;
4094         }
4095
4096         if (!CAN_CHANGE(element))
4097           continue;
4098
4099         for (i = 0; i < element_info[element].num_change_pages; i++)
4100         {
4101           /* check for player created from custom element as extended target */
4102           content =
4103             element_info[element].change_page[i].target_content.e[xx][yy];
4104
4105           is_player = ELEM_IS_PLAYER(content);
4106
4107           if (is_player && (found_rating < 1 ||
4108                             (found_rating == 1 && element < found_element)))
4109           {
4110             start_x = x + xx - 1;
4111             start_y = y + yy - 1;
4112
4113             found_rating = 1;
4114             found_element = element;
4115           }
4116         }
4117       }
4118     }
4119
4120     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4121                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4122                 start_x - MIDPOSX);
4123
4124     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4125                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4126                 start_y - MIDPOSY);
4127   }
4128   else
4129   {
4130     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4131                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4132                 local_player->jx - MIDPOSX);
4133
4134     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4135                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4136                 local_player->jy - MIDPOSY);
4137   }
4138
4139 #if 0
4140   /* do not use PLAYING mask for fading out from main screen */
4141   game_status = GAME_MODE_MAIN;
4142 #endif
4143
4144   StopAnimation();
4145
4146   if (!game.restart_level)
4147     CloseDoor(DOOR_CLOSE_1);
4148
4149 #if 1
4150   if (level_editor_test_game)
4151     FadeSkipNextFadeIn();
4152   else
4153     FadeSetEnterScreen();
4154 #else
4155   if (level_editor_test_game)
4156     fading = fading_none;
4157   else
4158     fading = menu.destination;
4159 #endif
4160
4161 #if 1
4162   FadeOut(REDRAW_FIELD);
4163 #else
4164   if (do_fading)
4165     FadeOut(REDRAW_FIELD);
4166 #endif
4167
4168 #if 0
4169   game_status = GAME_MODE_PLAYING;
4170 #endif
4171
4172   /* !!! FIX THIS (START) !!! */
4173   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4174   {
4175     InitGameEngine_EM();
4176
4177     /* blit playfield from scroll buffer to normal back buffer for fading in */
4178     BlitScreenToBitmap_EM(backbuffer);
4179   }
4180   else
4181   {
4182     DrawLevel();
4183     DrawAllPlayers();
4184
4185     /* after drawing the level, correct some elements */
4186     if (game.timegate_time_left == 0)
4187       CloseAllOpenTimegates();
4188
4189     /* blit playfield from scroll buffer to normal back buffer for fading in */
4190     if (setup.soft_scrolling)
4191       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4192
4193     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4194   }
4195   /* !!! FIX THIS (END) !!! */
4196
4197 #if 1
4198   FadeIn(REDRAW_FIELD);
4199 #else
4200   if (do_fading)
4201     FadeIn(REDRAW_FIELD);
4202
4203   BackToFront();
4204 #endif
4205
4206   if (!game.restart_level)
4207   {
4208     /* copy default game door content to main double buffer */
4209     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4210                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4211   }
4212
4213   SetPanelBackground();
4214   SetDrawBackgroundMask(REDRAW_DOOR_1);
4215
4216   UpdateGameDoorValues();
4217   DrawGameDoorValues();
4218
4219   if (!game.restart_level)
4220   {
4221     UnmapGameButtons();
4222     UnmapTapeButtons();
4223     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4224     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4225     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4226     MapGameButtons();
4227     MapTapeButtons();
4228
4229     /* copy actual game door content to door double buffer for OpenDoor() */
4230     BlitBitmap(drawto, bitmap_db_door,
4231                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4232
4233     OpenDoor(DOOR_OPEN_ALL);
4234
4235     PlaySound(SND_GAME_STARTING);
4236
4237     if (setup.sound_music)
4238       PlayLevelMusic();
4239
4240     KeyboardAutoRepeatOffUnlessAutoplay();
4241
4242     if (options.debug)
4243     {
4244       for (i = 0; i < MAX_PLAYERS; i++)
4245         printf("Player %d %sactive.\n",
4246                i + 1, (stored_player[i].active ? "" : "not "));
4247     }
4248   }
4249
4250 #if 1
4251   UnmapAllGadgets();
4252
4253   MapGameButtons();
4254   MapTapeButtons();
4255 #endif
4256
4257   game.restart_level = FALSE;
4258 }
4259
4260 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4261 {
4262   /* this is used for non-R'n'D game engines to update certain engine values */
4263
4264   /* needed to determine if sounds are played within the visible screen area */
4265   scroll_x = actual_scroll_x;
4266   scroll_y = actual_scroll_y;
4267 }
4268
4269 void InitMovDir(int x, int y)
4270 {
4271   int i, element = Feld[x][y];
4272   static int xy[4][2] =
4273   {
4274     {  0, +1 },
4275     { +1,  0 },
4276     {  0, -1 },
4277     { -1,  0 }
4278   };
4279   static int direction[3][4] =
4280   {
4281     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4282     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4283     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4284   };
4285
4286   switch (element)
4287   {
4288     case EL_BUG_RIGHT:
4289     case EL_BUG_UP:
4290     case EL_BUG_LEFT:
4291     case EL_BUG_DOWN:
4292       Feld[x][y] = EL_BUG;
4293       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4294       break;
4295
4296     case EL_SPACESHIP_RIGHT:
4297     case EL_SPACESHIP_UP:
4298     case EL_SPACESHIP_LEFT:
4299     case EL_SPACESHIP_DOWN:
4300       Feld[x][y] = EL_SPACESHIP;
4301       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4302       break;
4303
4304     case EL_BD_BUTTERFLY_RIGHT:
4305     case EL_BD_BUTTERFLY_UP:
4306     case EL_BD_BUTTERFLY_LEFT:
4307     case EL_BD_BUTTERFLY_DOWN:
4308       Feld[x][y] = EL_BD_BUTTERFLY;
4309       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4310       break;
4311
4312     case EL_BD_FIREFLY_RIGHT:
4313     case EL_BD_FIREFLY_UP:
4314     case EL_BD_FIREFLY_LEFT:
4315     case EL_BD_FIREFLY_DOWN:
4316       Feld[x][y] = EL_BD_FIREFLY;
4317       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4318       break;
4319
4320     case EL_PACMAN_RIGHT:
4321     case EL_PACMAN_UP:
4322     case EL_PACMAN_LEFT:
4323     case EL_PACMAN_DOWN:
4324       Feld[x][y] = EL_PACMAN;
4325       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4326       break;
4327
4328     case EL_YAMYAM_LEFT:
4329     case EL_YAMYAM_RIGHT:
4330     case EL_YAMYAM_UP:
4331     case EL_YAMYAM_DOWN:
4332       Feld[x][y] = EL_YAMYAM;
4333       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4334       break;
4335
4336     case EL_SP_SNIKSNAK:
4337       MovDir[x][y] = MV_UP;
4338       break;
4339
4340     case EL_SP_ELECTRON:
4341       MovDir[x][y] = MV_LEFT;
4342       break;
4343
4344     case EL_MOLE_LEFT:
4345     case EL_MOLE_RIGHT:
4346     case EL_MOLE_UP:
4347     case EL_MOLE_DOWN:
4348       Feld[x][y] = EL_MOLE;
4349       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4350       break;
4351
4352     default:
4353       if (IS_CUSTOM_ELEMENT(element))
4354       {
4355         struct ElementInfo *ei = &element_info[element];
4356         int move_direction_initial = ei->move_direction_initial;
4357         int move_pattern = ei->move_pattern;
4358
4359         if (move_direction_initial == MV_START_PREVIOUS)
4360         {
4361           if (MovDir[x][y] != MV_NONE)
4362             return;
4363
4364           move_direction_initial = MV_START_AUTOMATIC;
4365         }
4366
4367         if (move_direction_initial == MV_START_RANDOM)
4368           MovDir[x][y] = 1 << RND(4);
4369         else if (move_direction_initial & MV_ANY_DIRECTION)
4370           MovDir[x][y] = move_direction_initial;
4371         else if (move_pattern == MV_ALL_DIRECTIONS ||
4372                  move_pattern == MV_TURNING_LEFT ||
4373                  move_pattern == MV_TURNING_RIGHT ||
4374                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4375                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4376                  move_pattern == MV_TURNING_RANDOM)
4377           MovDir[x][y] = 1 << RND(4);
4378         else if (move_pattern == MV_HORIZONTAL)
4379           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4380         else if (move_pattern == MV_VERTICAL)
4381           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4382         else if (move_pattern & MV_ANY_DIRECTION)
4383           MovDir[x][y] = element_info[element].move_pattern;
4384         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4385                  move_pattern == MV_ALONG_RIGHT_SIDE)
4386         {
4387           /* use random direction as default start direction */
4388           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4389             MovDir[x][y] = 1 << RND(4);
4390
4391           for (i = 0; i < NUM_DIRECTIONS; i++)
4392           {
4393             int x1 = x + xy[i][0];
4394             int y1 = y + xy[i][1];
4395
4396             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4397             {
4398               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4399                 MovDir[x][y] = direction[0][i];
4400               else
4401                 MovDir[x][y] = direction[1][i];
4402
4403               break;
4404             }
4405           }
4406         }                
4407       }
4408       else
4409       {
4410         MovDir[x][y] = 1 << RND(4);
4411
4412         if (element != EL_BUG &&
4413             element != EL_SPACESHIP &&
4414             element != EL_BD_BUTTERFLY &&
4415             element != EL_BD_FIREFLY)
4416           break;
4417
4418         for (i = 0; i < NUM_DIRECTIONS; i++)
4419         {
4420           int x1 = x + xy[i][0];
4421           int y1 = y + xy[i][1];
4422
4423           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4424           {
4425             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4426             {
4427               MovDir[x][y] = direction[0][i];
4428               break;
4429             }
4430             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4431                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4432             {
4433               MovDir[x][y] = direction[1][i];
4434               break;
4435             }
4436           }
4437         }
4438       }
4439       break;
4440   }
4441
4442   GfxDir[x][y] = MovDir[x][y];
4443 }
4444
4445 void InitAmoebaNr(int x, int y)
4446 {
4447   int i;
4448   int group_nr = AmoebeNachbarNr(x, y);
4449
4450   if (group_nr == 0)
4451   {
4452     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4453     {
4454       if (AmoebaCnt[i] == 0)
4455       {
4456         group_nr = i;
4457         break;
4458       }
4459     }
4460   }
4461
4462   AmoebaNr[x][y] = group_nr;
4463   AmoebaCnt[group_nr]++;
4464   AmoebaCnt2[group_nr]++;
4465 }
4466
4467 static void PlayerWins(struct PlayerInfo *player)
4468 {
4469   player->LevelSolved = TRUE;
4470   player->GameOver = TRUE;
4471
4472   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4473                          level.native_em_level->lev->score : player->score);
4474
4475   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4476   player->LevelSolved_CountingScore = player->score_final;
4477 }
4478
4479 void GameWon()
4480 {
4481   static int time, time_final;
4482   static int score, score_final;
4483   static int game_over_delay_1 = 0;
4484   static int game_over_delay_2 = 0;
4485   int game_over_delay_value_1 = 50;
4486   int game_over_delay_value_2 = 50;
4487
4488   if (!local_player->LevelSolved_GameWon)
4489   {
4490     int i;
4491
4492     /* do not start end game actions before the player stops moving (to exit) */
4493     if (local_player->MovPos)
4494       return;
4495
4496     local_player->LevelSolved_GameWon = TRUE;
4497     local_player->LevelSolved_SaveTape = tape.recording;
4498     local_player->LevelSolved_SaveScore = !tape.playing;
4499
4500     if (tape.auto_play)         /* tape might already be stopped here */
4501       tape.auto_play_level_solved = TRUE;
4502
4503 #if 1
4504     TapeStop();
4505 #endif
4506
4507     game_over_delay_1 = game_over_delay_value_1;
4508     game_over_delay_2 = game_over_delay_value_2;
4509
4510     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4511     score = score_final = local_player->score_final;
4512
4513     if (TimeLeft > 0)
4514     {
4515       time_final = 0;
4516       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4517     }
4518     else if (level.time == 0 && TimePlayed < 999)
4519     {
4520       time_final = 999;
4521       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4522     }
4523
4524     local_player->score_final = score_final;
4525
4526     if (level_editor_test_game)
4527     {
4528       time = time_final;
4529       score = score_final;
4530
4531 #if 1
4532       local_player->LevelSolved_CountingTime = time;
4533       local_player->LevelSolved_CountingScore = score;
4534
4535       game_panel_controls[GAME_PANEL_TIME].value = time;
4536       game_panel_controls[GAME_PANEL_SCORE].value = score;
4537
4538       DisplayGameControlValues();
4539 #else
4540       DrawGameValue_Time(time);
4541       DrawGameValue_Score(score);
4542 #endif
4543     }
4544
4545     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4546     {
4547       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4548       {
4549         /* close exit door after last player */
4550         if ((AllPlayersGone &&
4551              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4552               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4553               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4554             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4555             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4556         {
4557           int element = Feld[ExitX][ExitY];
4558
4559 #if 0
4560           if (element == EL_EM_EXIT_OPEN ||
4561               element == EL_EM_STEEL_EXIT_OPEN)
4562           {
4563             Bang(ExitX, ExitY);
4564           }
4565           else
4566 #endif
4567           {
4568             Feld[ExitX][ExitY] =
4569               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4570                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4571                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4572                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4573                EL_EM_STEEL_EXIT_CLOSING);
4574
4575             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4576           }
4577         }
4578
4579         /* player disappears */
4580         DrawLevelField(ExitX, ExitY);
4581       }
4582
4583       for (i = 0; i < MAX_PLAYERS; i++)
4584       {
4585         struct PlayerInfo *player = &stored_player[i];
4586
4587         if (player->present)
4588         {
4589           RemovePlayer(player);
4590
4591           /* player disappears */
4592           DrawLevelField(player->jx, player->jy);
4593         }
4594       }
4595     }
4596
4597     PlaySound(SND_GAME_WINNING);
4598   }
4599
4600   if (game_over_delay_1 > 0)
4601   {
4602     game_over_delay_1--;
4603
4604     return;
4605   }
4606
4607   if (time != time_final)
4608   {
4609     int time_to_go = ABS(time_final - time);
4610     int time_count_dir = (time < time_final ? +1 : -1);
4611     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4612
4613     time  += time_count_steps * time_count_dir;
4614     score += time_count_steps * level.score[SC_TIME_BONUS];
4615
4616 #if 1
4617     local_player->LevelSolved_CountingTime = time;
4618     local_player->LevelSolved_CountingScore = score;
4619
4620     game_panel_controls[GAME_PANEL_TIME].value = time;
4621     game_panel_controls[GAME_PANEL_SCORE].value = score;
4622
4623     DisplayGameControlValues();
4624 #else
4625     DrawGameValue_Time(time);
4626     DrawGameValue_Score(score);
4627 #endif
4628
4629     if (time == time_final)
4630       StopSound(SND_GAME_LEVELTIME_BONUS);
4631     else if (setup.sound_loops)
4632       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4633     else
4634       PlaySound(SND_GAME_LEVELTIME_BONUS);
4635
4636     return;
4637   }
4638
4639   local_player->LevelSolved_PanelOff = TRUE;
4640
4641   if (game_over_delay_2 > 0)
4642   {
4643     game_over_delay_2--;
4644
4645     return;
4646   }
4647
4648 #if 1
4649   GameEnd();
4650 #endif
4651 }
4652
4653 void GameEnd()
4654 {
4655   int hi_pos;
4656   boolean raise_level = FALSE;
4657
4658   local_player->LevelSolved_GameEnd = TRUE;
4659
4660   CloseDoor(DOOR_CLOSE_1);
4661
4662   if (local_player->LevelSolved_SaveTape)
4663   {
4664 #if 0
4665     TapeStop();
4666 #endif
4667
4668 #if 1
4669     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4670 #else
4671     SaveTape(tape.level_nr);            /* ask to save tape */
4672 #endif
4673   }
4674
4675   if (level_editor_test_game)
4676   {
4677     game_status = GAME_MODE_MAIN;
4678
4679 #if 1
4680     DrawAndFadeInMainMenu(REDRAW_FIELD);
4681 #else
4682     DrawMainMenu();
4683 #endif
4684
4685     return;
4686   }
4687
4688   if (!local_player->LevelSolved_SaveScore)
4689   {
4690 #if 1
4691     FadeOut(REDRAW_FIELD);
4692 #endif
4693
4694     game_status = GAME_MODE_MAIN;
4695
4696     DrawAndFadeInMainMenu(REDRAW_FIELD);
4697
4698     return;
4699   }
4700
4701   if (level_nr == leveldir_current->handicap_level)
4702   {
4703     leveldir_current->handicap_level++;
4704     SaveLevelSetup_SeriesInfo();
4705   }
4706
4707   if (level_nr < leveldir_current->last_level)
4708     raise_level = TRUE;                 /* advance to next level */
4709
4710   if ((hi_pos = NewHiScore()) >= 0) 
4711   {
4712     game_status = GAME_MODE_SCORES;
4713
4714     DrawHallOfFame(hi_pos);
4715
4716     if (raise_level)
4717     {
4718       level_nr++;
4719       TapeErase();
4720     }
4721   }
4722   else
4723   {
4724 #if 1
4725     FadeOut(REDRAW_FIELD);
4726 #endif
4727
4728     game_status = GAME_MODE_MAIN;
4729
4730     if (raise_level)
4731     {
4732       level_nr++;
4733       TapeErase();
4734     }
4735
4736     DrawAndFadeInMainMenu(REDRAW_FIELD);
4737   }
4738 }
4739
4740 int NewHiScore()
4741 {
4742   int k, l;
4743   int position = -1;
4744
4745   LoadScore(level_nr);
4746
4747   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4748       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4749     return -1;
4750
4751   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4752   {
4753     if (local_player->score_final > highscore[k].Score)
4754     {
4755       /* player has made it to the hall of fame */
4756
4757       if (k < MAX_SCORE_ENTRIES - 1)
4758       {
4759         int m = MAX_SCORE_ENTRIES - 1;
4760
4761 #ifdef ONE_PER_NAME
4762         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4763           if (strEqual(setup.player_name, highscore[l].Name))
4764             m = l;
4765         if (m == k)     /* player's new highscore overwrites his old one */
4766           goto put_into_list;
4767 #endif
4768
4769         for (l = m; l > k; l--)
4770         {
4771           strcpy(highscore[l].Name, highscore[l - 1].Name);
4772           highscore[l].Score = highscore[l - 1].Score;
4773         }
4774       }
4775
4776 #ifdef ONE_PER_NAME
4777       put_into_list:
4778 #endif
4779       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4780       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4781       highscore[k].Score = local_player->score_final; 
4782       position = k;
4783       break;
4784     }
4785
4786 #ifdef ONE_PER_NAME
4787     else if (!strncmp(setup.player_name, highscore[k].Name,
4788                       MAX_PLAYER_NAME_LEN))
4789       break;    /* player already there with a higher score */
4790 #endif
4791
4792   }
4793
4794   if (position >= 0) 
4795     SaveScore(level_nr);
4796
4797   return position;
4798 }
4799
4800 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4801 {
4802   int element = Feld[x][y];
4803   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4804   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4805   int horiz_move = (dx != 0);
4806   int sign = (horiz_move ? dx : dy);
4807   int step = sign * element_info[element].move_stepsize;
4808
4809   /* special values for move stepsize for spring and things on conveyor belt */
4810   if (horiz_move)
4811   {
4812     if (CAN_FALL(element) &&
4813         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4814       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4815     else if (element == EL_SPRING)
4816       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4817   }
4818
4819   return step;
4820 }
4821
4822 inline static int getElementMoveStepsize(int x, int y)
4823 {
4824   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4825 }
4826
4827 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4828 {
4829   if (player->GfxAction != action || player->GfxDir != dir)
4830   {
4831 #if 0
4832     printf("Player frame reset! (%d => %d, %d => %d)\n",
4833            player->GfxAction, action, player->GfxDir, dir);
4834 #endif
4835
4836     player->GfxAction = action;
4837     player->GfxDir = dir;
4838     player->Frame = 0;
4839     player->StepFrame = 0;
4840   }
4841 }
4842
4843 #if USE_GFX_RESET_GFX_ANIMATION
4844 static void ResetGfxFrame(int x, int y, boolean redraw)
4845 {
4846   int element = Feld[x][y];
4847   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4848   int last_gfx_frame = GfxFrame[x][y];
4849
4850   if (graphic_info[graphic].anim_global_sync)
4851     GfxFrame[x][y] = FrameCounter;
4852   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4853     GfxFrame[x][y] = CustomValue[x][y];
4854   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4855     GfxFrame[x][y] = element_info[element].collect_score;
4856   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4857     GfxFrame[x][y] = ChangeDelay[x][y];
4858
4859   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4860     DrawLevelGraphicAnimation(x, y, graphic);
4861 }
4862 #endif
4863
4864 static void ResetGfxAnimation(int x, int y)
4865 {
4866   GfxAction[x][y] = ACTION_DEFAULT;
4867   GfxDir[x][y] = MovDir[x][y];
4868   GfxFrame[x][y] = 0;
4869
4870 #if USE_GFX_RESET_GFX_ANIMATION
4871   ResetGfxFrame(x, y, FALSE);
4872 #endif
4873 }
4874
4875 static void ResetRandomAnimationValue(int x, int y)
4876 {
4877   GfxRandom[x][y] = INIT_GFX_RANDOM();
4878 }
4879
4880 void InitMovingField(int x, int y, int direction)
4881 {
4882   int element = Feld[x][y];
4883   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4884   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4885   int newx = x + dx;
4886   int newy = y + dy;
4887   boolean is_moving_before, is_moving_after;
4888 #if 0
4889   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4890 #endif
4891
4892   /* check if element was/is moving or being moved before/after mode change */
4893 #if 1
4894 #if 1
4895   is_moving_before = (WasJustMoving[x][y] != 0);
4896 #else
4897   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4898   is_moving_before = WasJustMoving[x][y];
4899 #endif
4900 #else
4901   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4902 #endif
4903   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4904
4905   /* reset animation only for moving elements which change direction of moving
4906      or which just started or stopped moving
4907      (else CEs with property "can move" / "not moving" are reset each frame) */
4908 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4909 #if 1
4910   if (is_moving_before != is_moving_after ||
4911       direction != MovDir[x][y])
4912     ResetGfxAnimation(x, y);
4913 #else
4914   if ((is_moving_before || is_moving_after) && !continues_moving)
4915     ResetGfxAnimation(x, y);
4916 #endif
4917 #else
4918   if (!continues_moving)
4919     ResetGfxAnimation(x, y);
4920 #endif
4921
4922   MovDir[x][y] = direction;
4923   GfxDir[x][y] = direction;
4924
4925 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4926   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4927                      direction == MV_DOWN && CAN_FALL(element) ?
4928                      ACTION_FALLING : ACTION_MOVING);
4929 #else
4930   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4931                      ACTION_FALLING : ACTION_MOVING);
4932 #endif
4933
4934   /* this is needed for CEs with property "can move" / "not moving" */
4935
4936   if (is_moving_after)
4937   {
4938     if (Feld[newx][newy] == EL_EMPTY)
4939       Feld[newx][newy] = EL_BLOCKED;
4940
4941     MovDir[newx][newy] = MovDir[x][y];
4942
4943 #if USE_NEW_CUSTOM_VALUE
4944     CustomValue[newx][newy] = CustomValue[x][y];
4945 #endif
4946
4947     GfxFrame[newx][newy] = GfxFrame[x][y];
4948     GfxRandom[newx][newy] = GfxRandom[x][y];
4949     GfxAction[newx][newy] = GfxAction[x][y];
4950     GfxDir[newx][newy] = GfxDir[x][y];
4951   }
4952 }
4953
4954 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4955 {
4956   int direction = MovDir[x][y];
4957   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4958   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4959
4960   *goes_to_x = newx;
4961   *goes_to_y = newy;
4962 }
4963
4964 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4965 {
4966   int oldx = x, oldy = y;
4967   int direction = MovDir[x][y];
4968
4969   if (direction == MV_LEFT)
4970     oldx++;
4971   else if (direction == MV_RIGHT)
4972     oldx--;
4973   else if (direction == MV_UP)
4974     oldy++;
4975   else if (direction == MV_DOWN)
4976     oldy--;
4977
4978   *comes_from_x = oldx;
4979   *comes_from_y = oldy;
4980 }
4981
4982 int MovingOrBlocked2Element(int x, int y)
4983 {
4984   int element = Feld[x][y];
4985
4986   if (element == EL_BLOCKED)
4987   {
4988     int oldx, oldy;
4989
4990     Blocked2Moving(x, y, &oldx, &oldy);
4991     return Feld[oldx][oldy];
4992   }
4993   else
4994     return element;
4995 }
4996
4997 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4998 {
4999   /* like MovingOrBlocked2Element(), but if element is moving
5000      and (x,y) is the field the moving element is just leaving,
5001      return EL_BLOCKED instead of the element value */
5002   int element = Feld[x][y];
5003
5004   if (IS_MOVING(x, y))
5005   {
5006     if (element == EL_BLOCKED)
5007     {
5008       int oldx, oldy;
5009
5010       Blocked2Moving(x, y, &oldx, &oldy);
5011       return Feld[oldx][oldy];
5012     }
5013     else
5014       return EL_BLOCKED;
5015   }
5016   else
5017     return element;
5018 }
5019
5020 static void RemoveField(int x, int y)
5021 {
5022   Feld[x][y] = EL_EMPTY;
5023
5024   MovPos[x][y] = 0;
5025   MovDir[x][y] = 0;
5026   MovDelay[x][y] = 0;
5027
5028 #if USE_NEW_CUSTOM_VALUE
5029   CustomValue[x][y] = 0;
5030 #endif
5031
5032   AmoebaNr[x][y] = 0;
5033   ChangeDelay[x][y] = 0;
5034   ChangePage[x][y] = -1;
5035   Pushed[x][y] = FALSE;
5036
5037 #if 0
5038   ExplodeField[x][y] = EX_TYPE_NONE;
5039 #endif
5040
5041   GfxElement[x][y] = EL_UNDEFINED;
5042   GfxAction[x][y] = ACTION_DEFAULT;
5043   GfxDir[x][y] = MV_NONE;
5044 }
5045
5046 void RemoveMovingField(int x, int y)
5047 {
5048   int oldx = x, oldy = y, newx = x, newy = y;
5049   int element = Feld[x][y];
5050   int next_element = EL_UNDEFINED;
5051
5052   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5053     return;
5054
5055   if (IS_MOVING(x, y))
5056   {
5057     Moving2Blocked(x, y, &newx, &newy);
5058
5059     if (Feld[newx][newy] != EL_BLOCKED)
5060     {
5061       /* element is moving, but target field is not free (blocked), but
5062          already occupied by something different (example: acid pool);
5063          in this case, only remove the moving field, but not the target */
5064
5065       RemoveField(oldx, oldy);
5066
5067       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5068
5069       DrawLevelField(oldx, oldy);
5070
5071       return;
5072     }
5073   }
5074   else if (element == EL_BLOCKED)
5075   {
5076     Blocked2Moving(x, y, &oldx, &oldy);
5077     if (!IS_MOVING(oldx, oldy))
5078       return;
5079   }
5080
5081   if (element == EL_BLOCKED &&
5082       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5083        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5084        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5085        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5086        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5087        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5088     next_element = get_next_element(Feld[oldx][oldy]);
5089
5090   RemoveField(oldx, oldy);
5091   RemoveField(newx, newy);
5092
5093   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5094
5095   if (next_element != EL_UNDEFINED)
5096     Feld[oldx][oldy] = next_element;
5097
5098   DrawLevelField(oldx, oldy);
5099   DrawLevelField(newx, newy);
5100 }
5101
5102 void DrawDynamite(int x, int y)
5103 {
5104   int sx = SCREENX(x), sy = SCREENY(y);
5105   int graphic = el2img(Feld[x][y]);
5106   int frame;
5107
5108   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5109     return;
5110
5111   if (IS_WALKABLE_INSIDE(Back[x][y]))
5112     return;
5113
5114   if (Back[x][y])
5115     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5116   else if (Store[x][y])
5117     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5118
5119   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5120
5121   if (Back[x][y] || Store[x][y])
5122     DrawGraphicThruMask(sx, sy, graphic, frame);
5123   else
5124     DrawGraphic(sx, sy, graphic, frame);
5125 }
5126
5127 void CheckDynamite(int x, int y)
5128 {
5129   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5130   {
5131     MovDelay[x][y]--;
5132
5133     if (MovDelay[x][y] != 0)
5134     {
5135       DrawDynamite(x, y);
5136       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5137
5138       return;
5139     }
5140   }
5141
5142   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5143
5144   Bang(x, y);
5145 }
5146
5147 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5148 {
5149   boolean num_checked_players = 0;
5150   int i;
5151
5152   for (i = 0; i < MAX_PLAYERS; i++)
5153   {
5154     if (stored_player[i].active)
5155     {
5156       int sx = stored_player[i].jx;
5157       int sy = stored_player[i].jy;
5158
5159       if (num_checked_players == 0)
5160       {
5161         *sx1 = *sx2 = sx;
5162         *sy1 = *sy2 = sy;
5163       }
5164       else
5165       {
5166         *sx1 = MIN(*sx1, sx);
5167         *sy1 = MIN(*sy1, sy);
5168         *sx2 = MAX(*sx2, sx);
5169         *sy2 = MAX(*sy2, sy);
5170       }
5171
5172       num_checked_players++;
5173     }
5174   }
5175 }
5176
5177 static boolean checkIfAllPlayersFitToScreen_RND()
5178 {
5179   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5180
5181   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5182
5183   return (sx2 - sx1 < SCR_FIELDX &&
5184           sy2 - sy1 < SCR_FIELDY);
5185 }
5186
5187 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5188 {
5189   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5190
5191   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5192
5193   *sx = (sx1 + sx2) / 2;
5194   *sy = (sy1 + sy2) / 2;
5195 }
5196
5197 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5198                         boolean center_screen, boolean quick_relocation)
5199 {
5200   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5201   boolean no_delay = (tape.warp_forward);
5202   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5203   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5204
5205   if (quick_relocation)
5206   {
5207     int offset = game.scroll_delay_value;
5208
5209     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5210     {
5211       if (!level.shifted_relocation || center_screen)
5212       {
5213         /* quick relocation (without scrolling), with centering of screen */
5214
5215         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5216                     x > SBX_Right + MIDPOSX ? SBX_Right :
5217                     x - MIDPOSX);
5218
5219         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5220                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5221                     y - MIDPOSY);
5222       }
5223       else
5224       {
5225         /* quick relocation (without scrolling), but do not center screen */
5226
5227         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5228                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5229                                old_x - MIDPOSX);
5230
5231         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5232                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5233                                old_y - MIDPOSY);
5234
5235         int offset_x = x + (scroll_x - center_scroll_x);
5236         int offset_y = y + (scroll_y - center_scroll_y);
5237
5238         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5239                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5240                     offset_x - MIDPOSX);
5241
5242         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5243                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5244                     offset_y - MIDPOSY);
5245       }
5246     }
5247     else
5248     {
5249       /* quick relocation (without scrolling), inside visible screen area */
5250
5251       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5252           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5253         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5254
5255       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5256           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5257         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5258
5259       /* don't scroll over playfield boundaries */
5260       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5261         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5262
5263       /* don't scroll over playfield boundaries */
5264       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5265         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5266     }
5267
5268     RedrawPlayfield(TRUE, 0,0,0,0);
5269   }
5270   else
5271   {
5272 #if 1
5273     int scroll_xx, scroll_yy;
5274
5275     if (!level.shifted_relocation || center_screen)
5276     {
5277       /* visible relocation (with scrolling), with centering of screen */
5278
5279       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5280                    x > SBX_Right + MIDPOSX ? SBX_Right :
5281                    x - MIDPOSX);
5282
5283       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5284                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5285                    y - MIDPOSY);
5286     }
5287     else
5288     {
5289       /* visible relocation (with scrolling), but do not center screen */
5290
5291       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5292                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5293                              old_x - MIDPOSX);
5294
5295       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5296                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5297                              old_y - MIDPOSY);
5298
5299       int offset_x = x + (scroll_x - center_scroll_x);
5300       int offset_y = y + (scroll_y - center_scroll_y);
5301
5302       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5303                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5304                    offset_x - MIDPOSX);
5305
5306       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5307                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5308                    offset_y - MIDPOSY);
5309     }
5310
5311 #else
5312
5313     /* visible relocation (with scrolling), with centering of screen */
5314
5315     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5316                      x > SBX_Right + MIDPOSX ? SBX_Right :
5317                      x - MIDPOSX);
5318
5319     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5320                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5321                      y - MIDPOSY);
5322 #endif
5323
5324     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5325
5326     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5327     {
5328       int dx = 0, dy = 0;
5329       int fx = FX, fy = FY;
5330
5331       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5332       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5333
5334       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5335         break;
5336
5337       scroll_x -= dx;
5338       scroll_y -= dy;
5339
5340       fx += dx * TILEX / 2;
5341       fy += dy * TILEY / 2;
5342
5343       ScrollLevel(dx, dy);
5344       DrawAllPlayers();
5345
5346       /* scroll in two steps of half tile size to make things smoother */
5347       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5348       FlushDisplay();
5349       Delay(wait_delay_value);
5350
5351       /* scroll second step to align at full tile size */
5352       BackToFront();
5353       Delay(wait_delay_value);
5354     }
5355
5356     DrawAllPlayers();
5357     BackToFront();
5358     Delay(wait_delay_value);
5359   }
5360 }
5361
5362 void RelocatePlayer(int jx, int jy, int el_player_raw)
5363 {
5364   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5365   int player_nr = GET_PLAYER_NR(el_player);
5366   struct PlayerInfo *player = &stored_player[player_nr];
5367   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5368   boolean no_delay = (tape.warp_forward);
5369   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5370   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5371   int old_jx = player->jx;
5372   int old_jy = player->jy;
5373   int old_element = Feld[old_jx][old_jy];
5374   int element = Feld[jx][jy];
5375   boolean player_relocated = (old_jx != jx || old_jy != jy);
5376
5377   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5378   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5379   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5380   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5381   int leave_side_horiz = move_dir_horiz;
5382   int leave_side_vert  = move_dir_vert;
5383   int enter_side = enter_side_horiz | enter_side_vert;
5384   int leave_side = leave_side_horiz | leave_side_vert;
5385
5386   if (player->GameOver)         /* do not reanimate dead player */
5387     return;
5388
5389   if (!player_relocated)        /* no need to relocate the player */
5390     return;
5391
5392   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5393   {
5394     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5395     DrawLevelField(jx, jy);
5396   }
5397
5398   if (player->present)
5399   {
5400     while (player->MovPos)
5401     {
5402       ScrollPlayer(player, SCROLL_GO_ON);
5403       ScrollScreen(NULL, SCROLL_GO_ON);
5404
5405       AdvanceFrameAndPlayerCounters(player->index_nr);
5406
5407       DrawPlayer(player);
5408
5409       BackToFront();
5410       Delay(wait_delay_value);
5411     }
5412
5413     DrawPlayer(player);         /* needed here only to cleanup last field */
5414     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5415
5416     player->is_moving = FALSE;
5417   }
5418
5419   if (IS_CUSTOM_ELEMENT(old_element))
5420     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5421                                CE_LEFT_BY_PLAYER,
5422                                player->index_bit, leave_side);
5423
5424   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5425                                       CE_PLAYER_LEAVES_X,
5426                                       player->index_bit, leave_side);
5427
5428   Feld[jx][jy] = el_player;
5429   InitPlayerField(jx, jy, el_player, TRUE);
5430
5431   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5432   {
5433     Feld[jx][jy] = element;
5434     InitField(jx, jy, FALSE);
5435   }
5436
5437   /* only visually relocate centered player */
5438   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5439                      FALSE, level.instant_relocation);
5440
5441   TestIfPlayerTouchesBadThing(jx, jy);
5442   TestIfPlayerTouchesCustomElement(jx, jy);
5443
5444   if (IS_CUSTOM_ELEMENT(element))
5445     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5446                                player->index_bit, enter_side);
5447
5448   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5449                                       player->index_bit, enter_side);
5450 }
5451
5452 void Explode(int ex, int ey, int phase, int mode)
5453 {
5454   int x, y;
5455   int last_phase;
5456   int border_element;
5457
5458   /* !!! eliminate this variable !!! */
5459   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5460
5461   if (game.explosions_delayed)
5462   {
5463     ExplodeField[ex][ey] = mode;
5464     return;
5465   }
5466
5467   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5468   {
5469     int center_element = Feld[ex][ey];
5470     int artwork_element, explosion_element;     /* set these values later */
5471
5472 #if 0
5473     /* --- This is only really needed (and now handled) in "Impact()". --- */
5474     /* do not explode moving elements that left the explode field in time */
5475     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5476         center_element == EL_EMPTY &&
5477         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5478       return;
5479 #endif
5480
5481 #if 0
5482     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5483     if (mode == EX_TYPE_NORMAL ||
5484         mode == EX_TYPE_CENTER ||
5485         mode == EX_TYPE_CROSS)
5486       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5487 #endif
5488
5489     /* remove things displayed in background while burning dynamite */
5490     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5491       Back[ex][ey] = 0;
5492
5493     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5494     {
5495       /* put moving element to center field (and let it explode there) */
5496       center_element = MovingOrBlocked2Element(ex, ey);
5497       RemoveMovingField(ex, ey);
5498       Feld[ex][ey] = center_element;
5499     }
5500
5501     /* now "center_element" is finally determined -- set related values now */
5502     artwork_element = center_element;           /* for custom player artwork */
5503     explosion_element = center_element;         /* for custom player artwork */
5504
5505     if (IS_PLAYER(ex, ey))
5506     {
5507       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5508
5509       artwork_element = stored_player[player_nr].artwork_element;
5510
5511       if (level.use_explosion_element[player_nr])
5512       {
5513         explosion_element = level.explosion_element[player_nr];
5514         artwork_element = explosion_element;
5515       }
5516     }
5517
5518 #if 1
5519     if (mode == EX_TYPE_NORMAL ||
5520         mode == EX_TYPE_CENTER ||
5521         mode == EX_TYPE_CROSS)
5522       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5523 #endif
5524
5525     last_phase = element_info[explosion_element].explosion_delay + 1;
5526
5527     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5528     {
5529       int xx = x - ex + 1;
5530       int yy = y - ey + 1;
5531       int element;
5532
5533       if (!IN_LEV_FIELD(x, y) ||
5534           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5535           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5536         continue;
5537
5538       element = Feld[x][y];
5539
5540       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5541       {
5542         element = MovingOrBlocked2Element(x, y);
5543
5544         if (!IS_EXPLOSION_PROOF(element))
5545           RemoveMovingField(x, y);
5546       }
5547
5548       /* indestructible elements can only explode in center (but not flames) */
5549       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5550                                            mode == EX_TYPE_BORDER)) ||
5551           element == EL_FLAMES)
5552         continue;
5553
5554       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5555          behaviour, for example when touching a yamyam that explodes to rocks
5556          with active deadly shield, a rock is created under the player !!! */
5557       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5558 #if 0
5559       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5560           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5561            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5562 #else
5563       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5564 #endif
5565       {
5566         if (IS_ACTIVE_BOMB(element))
5567         {
5568           /* re-activate things under the bomb like gate or penguin */
5569           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5570           Back[x][y] = 0;
5571         }
5572
5573         continue;
5574       }
5575
5576       /* save walkable background elements while explosion on same tile */
5577       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5578           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5579         Back[x][y] = element;
5580
5581       /* ignite explodable elements reached by other explosion */
5582       if (element == EL_EXPLOSION)
5583         element = Store2[x][y];
5584
5585       if (AmoebaNr[x][y] &&
5586           (element == EL_AMOEBA_FULL ||
5587            element == EL_BD_AMOEBA ||
5588            element == EL_AMOEBA_GROWING))
5589       {
5590         AmoebaCnt[AmoebaNr[x][y]]--;
5591         AmoebaCnt2[AmoebaNr[x][y]]--;
5592       }
5593
5594       RemoveField(x, y);
5595
5596       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5597       {
5598         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5599
5600         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5601
5602         if (PLAYERINFO(ex, ey)->use_murphy)
5603           Store[x][y] = EL_EMPTY;
5604       }
5605
5606       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5607          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5608       else if (ELEM_IS_PLAYER(center_element))
5609         Store[x][y] = EL_EMPTY;
5610       else if (center_element == EL_YAMYAM)
5611         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5612       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5613         Store[x][y] = element_info[center_element].content.e[xx][yy];
5614 #if 1
5615       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5616          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5617          otherwise) -- FIX THIS !!! */
5618       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5619         Store[x][y] = element_info[element].content.e[1][1];
5620 #else
5621       else if (!CAN_EXPLODE(element))
5622         Store[x][y] = element_info[element].content.e[1][1];
5623 #endif
5624       else
5625         Store[x][y] = EL_EMPTY;
5626
5627       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5628           center_element == EL_AMOEBA_TO_DIAMOND)
5629         Store2[x][y] = element;
5630
5631       Feld[x][y] = EL_EXPLOSION;
5632       GfxElement[x][y] = artwork_element;
5633
5634       ExplodePhase[x][y] = 1;
5635       ExplodeDelay[x][y] = last_phase;
5636
5637       Stop[x][y] = TRUE;
5638     }
5639
5640     if (center_element == EL_YAMYAM)
5641       game.yamyam_content_nr =
5642         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5643
5644     return;
5645   }
5646
5647   if (Stop[ex][ey])
5648     return;
5649
5650   x = ex;
5651   y = ey;
5652
5653   if (phase == 1)
5654     GfxFrame[x][y] = 0;         /* restart explosion animation */
5655
5656   last_phase = ExplodeDelay[x][y];
5657
5658   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5659
5660 #ifdef DEBUG
5661
5662   /* activate this even in non-DEBUG version until cause for crash in
5663      getGraphicAnimationFrame() (see below) is found and eliminated */
5664
5665 #endif
5666 #if 1
5667
5668 #if 1
5669   /* this can happen if the player leaves an explosion just in time */
5670   if (GfxElement[x][y] == EL_UNDEFINED)
5671     GfxElement[x][y] = EL_EMPTY;
5672 #else
5673   if (GfxElement[x][y] == EL_UNDEFINED)
5674   {
5675     printf("\n\n");
5676     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5677     printf("Explode(): This should never happen!\n");
5678     printf("\n\n");
5679
5680     GfxElement[x][y] = EL_EMPTY;
5681   }
5682 #endif
5683
5684 #endif
5685
5686   border_element = Store2[x][y];
5687   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5688     border_element = StorePlayer[x][y];
5689
5690   if (phase == element_info[border_element].ignition_delay ||
5691       phase == last_phase)
5692   {
5693     boolean border_explosion = FALSE;
5694
5695     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5696         !PLAYER_EXPLOSION_PROTECTED(x, y))
5697     {
5698       KillPlayerUnlessExplosionProtected(x, y);
5699       border_explosion = TRUE;
5700     }
5701     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5702     {
5703       Feld[x][y] = Store2[x][y];
5704       Store2[x][y] = 0;
5705       Bang(x, y);
5706       border_explosion = TRUE;
5707     }
5708     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5709     {
5710       AmoebeUmwandeln(x, y);
5711       Store2[x][y] = 0;
5712       border_explosion = TRUE;
5713     }
5714
5715     /* if an element just explodes due to another explosion (chain-reaction),
5716        do not immediately end the new explosion when it was the last frame of
5717        the explosion (as it would be done in the following "if"-statement!) */
5718     if (border_explosion && phase == last_phase)
5719       return;
5720   }
5721
5722   if (phase == last_phase)
5723   {
5724     int element;
5725
5726     element = Feld[x][y] = Store[x][y];
5727     Store[x][y] = Store2[x][y] = 0;
5728     GfxElement[x][y] = EL_UNDEFINED;
5729
5730     /* player can escape from explosions and might therefore be still alive */
5731     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5732         element <= EL_PLAYER_IS_EXPLODING_4)
5733     {
5734       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5735       int explosion_element = EL_PLAYER_1 + player_nr;
5736       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5737       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5738
5739       if (level.use_explosion_element[player_nr])
5740         explosion_element = level.explosion_element[player_nr];
5741
5742       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5743                     element_info[explosion_element].content.e[xx][yy]);
5744     }
5745
5746     /* restore probably existing indestructible background element */
5747     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5748       element = Feld[x][y] = Back[x][y];
5749     Back[x][y] = 0;
5750
5751     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5752     GfxDir[x][y] = MV_NONE;
5753     ChangeDelay[x][y] = 0;
5754     ChangePage[x][y] = -1;
5755
5756 #if USE_NEW_CUSTOM_VALUE
5757     CustomValue[x][y] = 0;
5758 #endif
5759
5760     InitField_WithBug2(x, y, FALSE);
5761
5762     DrawLevelField(x, y);
5763
5764     TestIfElementTouchesCustomElement(x, y);
5765
5766     if (GFX_CRUMBLED(element))
5767       DrawLevelFieldCrumbledSandNeighbours(x, y);
5768
5769     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5770       StorePlayer[x][y] = 0;
5771
5772     if (ELEM_IS_PLAYER(element))
5773       RelocatePlayer(x, y, element);
5774   }
5775   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5776   {
5777     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5778     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5779
5780     if (phase == delay)
5781       DrawLevelFieldCrumbledSand(x, y);
5782
5783     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5784     {
5785       DrawLevelElement(x, y, Back[x][y]);
5786       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5787     }
5788     else if (IS_WALKABLE_UNDER(Back[x][y]))
5789     {
5790       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5791       DrawLevelElementThruMask(x, y, Back[x][y]);
5792     }
5793     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5794       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5795   }
5796 }
5797
5798 void DynaExplode(int ex, int ey)
5799 {
5800   int i, j;
5801   int dynabomb_element = Feld[ex][ey];
5802   int dynabomb_size = 1;
5803   boolean dynabomb_xl = FALSE;
5804   struct PlayerInfo *player;
5805   static int xy[4][2] =
5806   {
5807     { 0, -1 },
5808     { -1, 0 },
5809     { +1, 0 },
5810     { 0, +1 }
5811   };
5812
5813   if (IS_ACTIVE_BOMB(dynabomb_element))
5814   {
5815     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5816     dynabomb_size = player->dynabomb_size;
5817     dynabomb_xl = player->dynabomb_xl;
5818     player->dynabombs_left++;
5819   }
5820
5821   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5822
5823   for (i = 0; i < NUM_DIRECTIONS; i++)
5824   {
5825     for (j = 1; j <= dynabomb_size; j++)
5826     {
5827       int x = ex + j * xy[i][0];
5828       int y = ey + j * xy[i][1];
5829       int element;
5830
5831       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5832         break;
5833
5834       element = Feld[x][y];
5835
5836       /* do not restart explosions of fields with active bombs */
5837       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5838         continue;
5839
5840       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5841
5842       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5843           !IS_DIGGABLE(element) && !dynabomb_xl)
5844         break;
5845     }
5846   }
5847 }
5848
5849 void Bang(int x, int y)
5850 {
5851   int element = MovingOrBlocked2Element(x, y);
5852   int explosion_type = EX_TYPE_NORMAL;
5853
5854   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5855   {
5856     struct PlayerInfo *player = PLAYERINFO(x, y);
5857
5858     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5859                             player->element_nr);
5860
5861     if (level.use_explosion_element[player->index_nr])
5862     {
5863       int explosion_element = level.explosion_element[player->index_nr];
5864
5865       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5866         explosion_type = EX_TYPE_CROSS;
5867       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5868         explosion_type = EX_TYPE_CENTER;
5869     }
5870   }
5871
5872   switch (element)
5873   {
5874     case EL_BUG:
5875     case EL_SPACESHIP:
5876     case EL_BD_BUTTERFLY:
5877     case EL_BD_FIREFLY:
5878     case EL_YAMYAM:
5879     case EL_DARK_YAMYAM:
5880     case EL_ROBOT:
5881     case EL_PACMAN:
5882     case EL_MOLE:
5883       RaiseScoreElement(element);
5884       break;
5885
5886     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5887     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5888     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5889     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5890     case EL_DYNABOMB_INCREASE_NUMBER:
5891     case EL_DYNABOMB_INCREASE_SIZE:
5892     case EL_DYNABOMB_INCREASE_POWER:
5893       explosion_type = EX_TYPE_DYNA;
5894       break;
5895
5896     case EL_DC_LANDMINE:
5897 #if 0
5898     case EL_EM_EXIT_OPEN:
5899     case EL_EM_STEEL_EXIT_OPEN:
5900 #endif
5901       explosion_type = EX_TYPE_CENTER;
5902       break;
5903
5904     case EL_PENGUIN:
5905     case EL_LAMP:
5906     case EL_LAMP_ACTIVE:
5907     case EL_AMOEBA_TO_DIAMOND:
5908       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5909         explosion_type = EX_TYPE_CENTER;
5910       break;
5911
5912     default:
5913       if (element_info[element].explosion_type == EXPLODES_CROSS)
5914         explosion_type = EX_TYPE_CROSS;
5915       else if (element_info[element].explosion_type == EXPLODES_1X1)
5916         explosion_type = EX_TYPE_CENTER;
5917       break;
5918   }
5919
5920   if (explosion_type == EX_TYPE_DYNA)
5921     DynaExplode(x, y);
5922   else
5923     Explode(x, y, EX_PHASE_START, explosion_type);
5924
5925   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5926 }
5927
5928 void SplashAcid(int x, int y)
5929 {
5930   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5931       (!IN_LEV_FIELD(x - 1, y - 2) ||
5932        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5933     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5934
5935   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5936       (!IN_LEV_FIELD(x + 1, y - 2) ||
5937        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5938     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5939
5940   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5941 }
5942
5943 static void InitBeltMovement()
5944 {
5945   static int belt_base_element[4] =
5946   {
5947     EL_CONVEYOR_BELT_1_LEFT,
5948     EL_CONVEYOR_BELT_2_LEFT,
5949     EL_CONVEYOR_BELT_3_LEFT,
5950     EL_CONVEYOR_BELT_4_LEFT
5951   };
5952   static int belt_base_active_element[4] =
5953   {
5954     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5955     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5956     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5957     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5958   };
5959
5960   int x, y, i, j;
5961
5962   /* set frame order for belt animation graphic according to belt direction */
5963   for (i = 0; i < NUM_BELTS; i++)
5964   {
5965     int belt_nr = i;
5966
5967     for (j = 0; j < NUM_BELT_PARTS; j++)
5968     {
5969       int element = belt_base_active_element[belt_nr] + j;
5970       int graphic_1 = el2img(element);
5971       int graphic_2 = el2panelimg(element);
5972
5973       if (game.belt_dir[i] == MV_LEFT)
5974       {
5975         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5976         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5977       }
5978       else
5979       {
5980         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5981         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5982       }
5983     }
5984   }
5985
5986   SCAN_PLAYFIELD(x, y)
5987   {
5988     int element = Feld[x][y];
5989
5990     for (i = 0; i < NUM_BELTS; i++)
5991     {
5992       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5993       {
5994         int e_belt_nr = getBeltNrFromBeltElement(element);
5995         int belt_nr = i;
5996
5997         if (e_belt_nr == belt_nr)
5998         {
5999           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6000
6001           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6002         }
6003       }
6004     }
6005   }
6006 }
6007
6008 static void ToggleBeltSwitch(int x, int y)
6009 {
6010   static int belt_base_element[4] =
6011   {
6012     EL_CONVEYOR_BELT_1_LEFT,
6013     EL_CONVEYOR_BELT_2_LEFT,
6014     EL_CONVEYOR_BELT_3_LEFT,
6015     EL_CONVEYOR_BELT_4_LEFT
6016   };
6017   static int belt_base_active_element[4] =
6018   {
6019     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6020     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6021     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6022     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6023   };
6024   static int belt_base_switch_element[4] =
6025   {
6026     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6027     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6028     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6029     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6030   };
6031   static int belt_move_dir[4] =
6032   {
6033     MV_LEFT,
6034     MV_NONE,
6035     MV_RIGHT,
6036     MV_NONE,
6037   };
6038
6039   int element = Feld[x][y];
6040   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6041   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6042   int belt_dir = belt_move_dir[belt_dir_nr];
6043   int xx, yy, i;
6044
6045   if (!IS_BELT_SWITCH(element))
6046     return;
6047
6048   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6049   game.belt_dir[belt_nr] = belt_dir;
6050
6051   if (belt_dir_nr == 3)
6052     belt_dir_nr = 1;
6053
6054   /* set frame order for belt animation graphic according to belt direction */
6055   for (i = 0; i < NUM_BELT_PARTS; i++)
6056   {
6057     int element = belt_base_active_element[belt_nr] + i;
6058     int graphic_1 = el2img(element);
6059     int graphic_2 = el2panelimg(element);
6060
6061     if (belt_dir == MV_LEFT)
6062     {
6063       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6064       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6065     }
6066     else
6067     {
6068       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6069       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6070     }
6071   }
6072
6073   SCAN_PLAYFIELD(xx, yy)
6074   {
6075     int element = Feld[xx][yy];
6076
6077     if (IS_BELT_SWITCH(element))
6078     {
6079       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6080
6081       if (e_belt_nr == belt_nr)
6082       {
6083         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6084         DrawLevelField(xx, yy);
6085       }
6086     }
6087     else if (IS_BELT(element) && belt_dir != MV_NONE)
6088     {
6089       int e_belt_nr = getBeltNrFromBeltElement(element);
6090
6091       if (e_belt_nr == belt_nr)
6092       {
6093         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6094
6095         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6096         DrawLevelField(xx, yy);
6097       }
6098     }
6099     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6100     {
6101       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6102
6103       if (e_belt_nr == belt_nr)
6104       {
6105         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6106
6107         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6108         DrawLevelField(xx, yy);
6109       }
6110     }
6111   }
6112 }
6113
6114 static void ToggleSwitchgateSwitch(int x, int y)
6115 {
6116   int xx, yy;
6117
6118   game.switchgate_pos = !game.switchgate_pos;
6119
6120   SCAN_PLAYFIELD(xx, yy)
6121   {
6122     int element = Feld[xx][yy];
6123
6124 #if !USE_BOTH_SWITCHGATE_SWITCHES
6125     if (element == EL_SWITCHGATE_SWITCH_UP ||
6126         element == EL_SWITCHGATE_SWITCH_DOWN)
6127     {
6128       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6129       DrawLevelField(xx, yy);
6130     }
6131     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6132              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6133     {
6134       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6135       DrawLevelField(xx, yy);
6136     }
6137 #else
6138     if (element == EL_SWITCHGATE_SWITCH_UP)
6139     {
6140       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6141       DrawLevelField(xx, yy);
6142     }
6143     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6144     {
6145       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6146       DrawLevelField(xx, yy);
6147     }
6148     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6149     {
6150       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6151       DrawLevelField(xx, yy);
6152     }
6153     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6154     {
6155       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6156       DrawLevelField(xx, yy);
6157     }
6158 #endif
6159     else if (element == EL_SWITCHGATE_OPEN ||
6160              element == EL_SWITCHGATE_OPENING)
6161     {
6162       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6163
6164       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6165     }
6166     else if (element == EL_SWITCHGATE_CLOSED ||
6167              element == EL_SWITCHGATE_CLOSING)
6168     {
6169       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6170
6171       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6172     }
6173   }
6174 }
6175
6176 static int getInvisibleActiveFromInvisibleElement(int element)
6177 {
6178   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6179           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6180           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6181           element);
6182 }
6183
6184 static int getInvisibleFromInvisibleActiveElement(int element)
6185 {
6186   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6187           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6188           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6189           element);
6190 }
6191
6192 static void RedrawAllLightSwitchesAndInvisibleElements()
6193 {
6194   int x, y;
6195
6196   SCAN_PLAYFIELD(x, y)
6197   {
6198     int element = Feld[x][y];
6199
6200     if (element == EL_LIGHT_SWITCH &&
6201         game.light_time_left > 0)
6202     {
6203       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6204       DrawLevelField(x, y);
6205     }
6206     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6207              game.light_time_left == 0)
6208     {
6209       Feld[x][y] = EL_LIGHT_SWITCH;
6210       DrawLevelField(x, y);
6211     }
6212     else if (element == EL_EMC_DRIPPER &&
6213              game.light_time_left > 0)
6214     {
6215       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6216       DrawLevelField(x, y);
6217     }
6218     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6219              game.light_time_left == 0)
6220     {
6221       Feld[x][y] = EL_EMC_DRIPPER;
6222       DrawLevelField(x, y);
6223     }
6224     else if (element == EL_INVISIBLE_STEELWALL ||
6225              element == EL_INVISIBLE_WALL ||
6226              element == EL_INVISIBLE_SAND)
6227     {
6228       if (game.light_time_left > 0)
6229         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6230
6231       DrawLevelField(x, y);
6232
6233       /* uncrumble neighbour fields, if needed */
6234       if (element == EL_INVISIBLE_SAND)
6235         DrawLevelFieldCrumbledSandNeighbours(x, y);
6236     }
6237     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6238              element == EL_INVISIBLE_WALL_ACTIVE ||
6239              element == EL_INVISIBLE_SAND_ACTIVE)
6240     {
6241       if (game.light_time_left == 0)
6242         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6243
6244       DrawLevelField(x, y);
6245
6246       /* re-crumble neighbour fields, if needed */
6247       if (element == EL_INVISIBLE_SAND)
6248         DrawLevelFieldCrumbledSandNeighbours(x, y);
6249     }
6250   }
6251 }
6252
6253 static void RedrawAllInvisibleElementsForLenses()
6254 {
6255   int x, y;
6256
6257   SCAN_PLAYFIELD(x, y)
6258   {
6259     int element = Feld[x][y];
6260
6261     if (element == EL_EMC_DRIPPER &&
6262         game.lenses_time_left > 0)
6263     {
6264       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6265       DrawLevelField(x, y);
6266     }
6267     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6268              game.lenses_time_left == 0)
6269     {
6270       Feld[x][y] = EL_EMC_DRIPPER;
6271       DrawLevelField(x, y);
6272     }
6273     else if (element == EL_INVISIBLE_STEELWALL ||
6274              element == EL_INVISIBLE_WALL ||
6275              element == EL_INVISIBLE_SAND)
6276     {
6277       if (game.lenses_time_left > 0)
6278         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6279
6280       DrawLevelField(x, y);
6281
6282       /* uncrumble neighbour fields, if needed */
6283       if (element == EL_INVISIBLE_SAND)
6284         DrawLevelFieldCrumbledSandNeighbours(x, y);
6285     }
6286     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6287              element == EL_INVISIBLE_WALL_ACTIVE ||
6288              element == EL_INVISIBLE_SAND_ACTIVE)
6289     {
6290       if (game.lenses_time_left == 0)
6291         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6292
6293       DrawLevelField(x, y);
6294
6295       /* re-crumble neighbour fields, if needed */
6296       if (element == EL_INVISIBLE_SAND)
6297         DrawLevelFieldCrumbledSandNeighbours(x, y);
6298     }
6299   }
6300 }
6301
6302 static void RedrawAllInvisibleElementsForMagnifier()
6303 {
6304   int x, y;
6305
6306   SCAN_PLAYFIELD(x, y)
6307   {
6308     int element = Feld[x][y];
6309
6310     if (element == EL_EMC_FAKE_GRASS &&
6311         game.magnify_time_left > 0)
6312     {
6313       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6314       DrawLevelField(x, y);
6315     }
6316     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6317              game.magnify_time_left == 0)
6318     {
6319       Feld[x][y] = EL_EMC_FAKE_GRASS;
6320       DrawLevelField(x, y);
6321     }
6322     else if (IS_GATE_GRAY(element) &&
6323              game.magnify_time_left > 0)
6324     {
6325       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6326                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6327                     IS_EM_GATE_GRAY(element) ?
6328                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6329                     IS_EMC_GATE_GRAY(element) ?
6330                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6331                     element);
6332       DrawLevelField(x, y);
6333     }
6334     else if (IS_GATE_GRAY_ACTIVE(element) &&
6335              game.magnify_time_left == 0)
6336     {
6337       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6338                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6339                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6340                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6341                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6342                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6343                     element);
6344       DrawLevelField(x, y);
6345     }
6346   }
6347 }
6348
6349 static void ToggleLightSwitch(int x, int y)
6350 {
6351   int element = Feld[x][y];
6352
6353   game.light_time_left =
6354     (element == EL_LIGHT_SWITCH ?
6355      level.time_light * FRAMES_PER_SECOND : 0);
6356
6357   RedrawAllLightSwitchesAndInvisibleElements();
6358 }
6359
6360 static void ActivateTimegateSwitch(int x, int y)
6361 {
6362   int xx, yy;
6363
6364   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6365
6366   SCAN_PLAYFIELD(xx, yy)
6367   {
6368     int element = Feld[xx][yy];
6369
6370     if (element == EL_TIMEGATE_CLOSED ||
6371         element == EL_TIMEGATE_CLOSING)
6372     {
6373       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6374       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6375     }
6376
6377     /*
6378     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6379     {
6380       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6381       DrawLevelField(xx, yy);
6382     }
6383     */
6384
6385   }
6386
6387 #if 1
6388   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6389                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6390 #else
6391   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6392 #endif
6393 }
6394
6395 void Impact(int x, int y)
6396 {
6397   boolean last_line = (y == lev_fieldy - 1);
6398   boolean object_hit = FALSE;
6399   boolean impact = (last_line || object_hit);
6400   int element = Feld[x][y];
6401   int smashed = EL_STEELWALL;
6402
6403   if (!last_line)       /* check if element below was hit */
6404   {
6405     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6406       return;
6407
6408     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6409                                          MovDir[x][y + 1] != MV_DOWN ||
6410                                          MovPos[x][y + 1] <= TILEY / 2));
6411
6412     /* do not smash moving elements that left the smashed field in time */
6413     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6414         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6415       object_hit = FALSE;
6416
6417 #if USE_QUICKSAND_IMPACT_BUGFIX
6418     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6419     {
6420       RemoveMovingField(x, y + 1);
6421       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6422       Feld[x][y + 2] = EL_ROCK;
6423       DrawLevelField(x, y + 2);
6424
6425       object_hit = TRUE;
6426     }
6427
6428     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6429     {
6430       RemoveMovingField(x, y + 1);
6431       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6432       Feld[x][y + 2] = EL_ROCK;
6433       DrawLevelField(x, y + 2);
6434
6435       object_hit = TRUE;
6436     }
6437 #endif
6438
6439     if (object_hit)
6440       smashed = MovingOrBlocked2Element(x, y + 1);
6441
6442     impact = (last_line || object_hit);
6443   }
6444
6445   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6446   {
6447     SplashAcid(x, y + 1);
6448     return;
6449   }
6450
6451   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6452   /* only reset graphic animation if graphic really changes after impact */
6453   if (impact &&
6454       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6455   {
6456     ResetGfxAnimation(x, y);
6457     DrawLevelField(x, y);
6458   }
6459
6460   if (impact && CAN_EXPLODE_IMPACT(element))
6461   {
6462     Bang(x, y);
6463     return;
6464   }
6465   else if (impact && element == EL_PEARL &&
6466            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6467   {
6468     ResetGfxAnimation(x, y);
6469
6470     Feld[x][y] = EL_PEARL_BREAKING;
6471     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6472     return;
6473   }
6474   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6475   {
6476     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6477
6478     return;
6479   }
6480
6481   if (impact && element == EL_AMOEBA_DROP)
6482   {
6483     if (object_hit && IS_PLAYER(x, y + 1))
6484       KillPlayerUnlessEnemyProtected(x, y + 1);
6485     else if (object_hit && smashed == EL_PENGUIN)
6486       Bang(x, y + 1);
6487     else
6488     {
6489       Feld[x][y] = EL_AMOEBA_GROWING;
6490       Store[x][y] = EL_AMOEBA_WET;
6491
6492       ResetRandomAnimationValue(x, y);
6493     }
6494     return;
6495   }
6496
6497   if (object_hit)               /* check which object was hit */
6498   {
6499     if ((CAN_PASS_MAGIC_WALL(element) && 
6500          (smashed == EL_MAGIC_WALL ||
6501           smashed == EL_BD_MAGIC_WALL)) ||
6502         (CAN_PASS_DC_MAGIC_WALL(element) &&
6503          smashed == EL_DC_MAGIC_WALL))
6504     {
6505       int xx, yy;
6506       int activated_magic_wall =
6507         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6508          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6509          EL_DC_MAGIC_WALL_ACTIVE);
6510
6511       /* activate magic wall / mill */
6512       SCAN_PLAYFIELD(xx, yy)
6513       {
6514         if (Feld[xx][yy] == smashed)
6515           Feld[xx][yy] = activated_magic_wall;
6516       }
6517
6518       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6519       game.magic_wall_active = TRUE;
6520
6521       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6522                             SND_MAGIC_WALL_ACTIVATING :
6523                             smashed == EL_BD_MAGIC_WALL ?
6524                             SND_BD_MAGIC_WALL_ACTIVATING :
6525                             SND_DC_MAGIC_WALL_ACTIVATING));
6526     }
6527
6528     if (IS_PLAYER(x, y + 1))
6529     {
6530       if (CAN_SMASH_PLAYER(element))
6531       {
6532         KillPlayerUnlessEnemyProtected(x, y + 1);
6533         return;
6534       }
6535     }
6536     else if (smashed == EL_PENGUIN)
6537     {
6538       if (CAN_SMASH_PLAYER(element))
6539       {
6540         Bang(x, y + 1);
6541         return;
6542       }
6543     }
6544     else if (element == EL_BD_DIAMOND)
6545     {
6546       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6547       {
6548         Bang(x, y + 1);
6549         return;
6550       }
6551     }
6552     else if (((element == EL_SP_INFOTRON ||
6553                element == EL_SP_ZONK) &&
6554               (smashed == EL_SP_SNIKSNAK ||
6555                smashed == EL_SP_ELECTRON ||
6556                smashed == EL_SP_DISK_ORANGE)) ||
6557              (element == EL_SP_INFOTRON &&
6558               smashed == EL_SP_DISK_YELLOW))
6559     {
6560       Bang(x, y + 1);
6561       return;
6562     }
6563     else if (CAN_SMASH_EVERYTHING(element))
6564     {
6565       if (IS_CLASSIC_ENEMY(smashed) ||
6566           CAN_EXPLODE_SMASHED(smashed))
6567       {
6568         Bang(x, y + 1);
6569         return;
6570       }
6571       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6572       {
6573         if (smashed == EL_LAMP ||
6574             smashed == EL_LAMP_ACTIVE)
6575         {
6576           Bang(x, y + 1);
6577           return;
6578         }
6579         else if (smashed == EL_NUT)
6580         {
6581           Feld[x][y + 1] = EL_NUT_BREAKING;
6582           PlayLevelSound(x, y, SND_NUT_BREAKING);
6583           RaiseScoreElement(EL_NUT);
6584           return;
6585         }
6586         else if (smashed == EL_PEARL)
6587         {
6588           ResetGfxAnimation(x, y);
6589
6590           Feld[x][y + 1] = EL_PEARL_BREAKING;
6591           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6592           return;
6593         }
6594         else if (smashed == EL_DIAMOND)
6595         {
6596           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6597           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6598           return;
6599         }
6600         else if (IS_BELT_SWITCH(smashed))
6601         {
6602           ToggleBeltSwitch(x, y + 1);
6603         }
6604         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6605                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6606                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6607                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6608         {
6609           ToggleSwitchgateSwitch(x, y + 1);
6610         }
6611         else if (smashed == EL_LIGHT_SWITCH ||
6612                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6613         {
6614           ToggleLightSwitch(x, y + 1);
6615         }
6616         else
6617         {
6618 #if 0
6619           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6620 #endif
6621
6622           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6623
6624           CheckElementChangeBySide(x, y + 1, smashed, element,
6625                                    CE_SWITCHED, CH_SIDE_TOP);
6626           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6627                                             CH_SIDE_TOP);
6628         }
6629       }
6630       else
6631       {
6632         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6633       }
6634     }
6635   }
6636
6637   /* play sound of magic wall / mill */
6638   if (!last_line &&
6639       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6640        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6641        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6642   {
6643     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6644       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6645     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6646       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6647     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6648       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6649
6650     return;
6651   }
6652
6653   /* play sound of object that hits the ground */
6654   if (last_line || object_hit)
6655     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6656 }
6657
6658 inline static void TurnRoundExt(int x, int y)
6659 {
6660   static struct
6661   {
6662     int dx, dy;
6663   } move_xy[] =
6664   {
6665     {  0,  0 },
6666     { -1,  0 },
6667     { +1,  0 },
6668     {  0,  0 },
6669     {  0, -1 },
6670     {  0,  0 }, { 0, 0 }, { 0, 0 },
6671     {  0, +1 }
6672   };
6673   static struct
6674   {
6675     int left, right, back;
6676   } turn[] =
6677   {
6678     { 0,        0,              0        },
6679     { MV_DOWN,  MV_UP,          MV_RIGHT },
6680     { MV_UP,    MV_DOWN,        MV_LEFT  },
6681     { 0,        0,              0        },
6682     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6683     { 0,        0,              0        },
6684     { 0,        0,              0        },
6685     { 0,        0,              0        },
6686     { MV_RIGHT, MV_LEFT,        MV_UP    }
6687   };
6688
6689   int element = Feld[x][y];
6690   int move_pattern = element_info[element].move_pattern;
6691
6692   int old_move_dir = MovDir[x][y];
6693   int left_dir  = turn[old_move_dir].left;
6694   int right_dir = turn[old_move_dir].right;
6695   int back_dir  = turn[old_move_dir].back;
6696
6697   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6698   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6699   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6700   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6701
6702   int left_x  = x + left_dx,  left_y  = y + left_dy;
6703   int right_x = x + right_dx, right_y = y + right_dy;
6704   int move_x  = x + move_dx,  move_y  = y + move_dy;
6705
6706   int xx, yy;
6707
6708   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6709   {
6710     TestIfBadThingTouchesOtherBadThing(x, y);
6711
6712     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6713       MovDir[x][y] = right_dir;
6714     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6715       MovDir[x][y] = left_dir;
6716
6717     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6718       MovDelay[x][y] = 9;
6719     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6720       MovDelay[x][y] = 1;
6721   }
6722   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6723   {
6724     TestIfBadThingTouchesOtherBadThing(x, y);
6725
6726     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6727       MovDir[x][y] = left_dir;
6728     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6729       MovDir[x][y] = right_dir;
6730
6731     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6732       MovDelay[x][y] = 9;
6733     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6734       MovDelay[x][y] = 1;
6735   }
6736   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6737   {
6738     TestIfBadThingTouchesOtherBadThing(x, y);
6739
6740     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6741       MovDir[x][y] = left_dir;
6742     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6743       MovDir[x][y] = right_dir;
6744
6745     if (MovDir[x][y] != old_move_dir)
6746       MovDelay[x][y] = 9;
6747   }
6748   else if (element == EL_YAMYAM)
6749   {
6750     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6751     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6752
6753     if (can_turn_left && can_turn_right)
6754       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6755     else if (can_turn_left)
6756       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6757     else if (can_turn_right)
6758       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6759     else
6760       MovDir[x][y] = back_dir;
6761
6762     MovDelay[x][y] = 16 + 16 * RND(3);
6763   }
6764   else if (element == EL_DARK_YAMYAM)
6765   {
6766     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6767                                                          left_x, left_y);
6768     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6769                                                          right_x, right_y);
6770
6771     if (can_turn_left && can_turn_right)
6772       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6773     else if (can_turn_left)
6774       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6775     else if (can_turn_right)
6776       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6777     else
6778       MovDir[x][y] = back_dir;
6779
6780     MovDelay[x][y] = 16 + 16 * RND(3);
6781   }
6782   else if (element == EL_PACMAN)
6783   {
6784     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6785     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6786
6787     if (can_turn_left && can_turn_right)
6788       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6789     else if (can_turn_left)
6790       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6791     else if (can_turn_right)
6792       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6793     else
6794       MovDir[x][y] = back_dir;
6795
6796     MovDelay[x][y] = 6 + RND(40);
6797   }
6798   else if (element == EL_PIG)
6799   {
6800     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6801     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6802     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6803     boolean should_turn_left, should_turn_right, should_move_on;
6804     int rnd_value = 24;
6805     int rnd = RND(rnd_value);
6806
6807     should_turn_left = (can_turn_left &&
6808                         (!can_move_on ||
6809                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6810                                                    y + back_dy + left_dy)));
6811     should_turn_right = (can_turn_right &&
6812                          (!can_move_on ||
6813                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6814                                                     y + back_dy + right_dy)));
6815     should_move_on = (can_move_on &&
6816                       (!can_turn_left ||
6817                        !can_turn_right ||
6818                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6819                                                  y + move_dy + left_dy) ||
6820                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6821                                                  y + move_dy + right_dy)));
6822
6823     if (should_turn_left || should_turn_right || should_move_on)
6824     {
6825       if (should_turn_left && should_turn_right && should_move_on)
6826         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6827                         rnd < 2 * rnd_value / 3 ? right_dir :
6828                         old_move_dir);
6829       else if (should_turn_left && should_turn_right)
6830         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6831       else if (should_turn_left && should_move_on)
6832         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6833       else if (should_turn_right && should_move_on)
6834         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6835       else if (should_turn_left)
6836         MovDir[x][y] = left_dir;
6837       else if (should_turn_right)
6838         MovDir[x][y] = right_dir;
6839       else if (should_move_on)
6840         MovDir[x][y] = old_move_dir;
6841     }
6842     else if (can_move_on && rnd > rnd_value / 8)
6843       MovDir[x][y] = old_move_dir;
6844     else if (can_turn_left && can_turn_right)
6845       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6846     else if (can_turn_left && rnd > rnd_value / 8)
6847       MovDir[x][y] = left_dir;
6848     else if (can_turn_right && rnd > rnd_value/8)
6849       MovDir[x][y] = right_dir;
6850     else
6851       MovDir[x][y] = back_dir;
6852
6853     xx = x + move_xy[MovDir[x][y]].dx;
6854     yy = y + move_xy[MovDir[x][y]].dy;
6855
6856     if (!IN_LEV_FIELD(xx, yy) ||
6857         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6858       MovDir[x][y] = old_move_dir;
6859
6860     MovDelay[x][y] = 0;
6861   }
6862   else if (element == EL_DRAGON)
6863   {
6864     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6865     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6866     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6867     int rnd_value = 24;
6868     int rnd = RND(rnd_value);
6869
6870     if (can_move_on && rnd > rnd_value / 8)
6871       MovDir[x][y] = old_move_dir;
6872     else if (can_turn_left && can_turn_right)
6873       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6874     else if (can_turn_left && rnd > rnd_value / 8)
6875       MovDir[x][y] = left_dir;
6876     else if (can_turn_right && rnd > rnd_value / 8)
6877       MovDir[x][y] = right_dir;
6878     else
6879       MovDir[x][y] = back_dir;
6880
6881     xx = x + move_xy[MovDir[x][y]].dx;
6882     yy = y + move_xy[MovDir[x][y]].dy;
6883
6884     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6885       MovDir[x][y] = old_move_dir;
6886
6887     MovDelay[x][y] = 0;
6888   }
6889   else if (element == EL_MOLE)
6890   {
6891     boolean can_move_on =
6892       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6893                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6894                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6895     if (!can_move_on)
6896     {
6897       boolean can_turn_left =
6898         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6899                               IS_AMOEBOID(Feld[left_x][left_y])));
6900
6901       boolean can_turn_right =
6902         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6903                               IS_AMOEBOID(Feld[right_x][right_y])));
6904
6905       if (can_turn_left && can_turn_right)
6906         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6907       else if (can_turn_left)
6908         MovDir[x][y] = left_dir;
6909       else
6910         MovDir[x][y] = right_dir;
6911     }
6912
6913     if (MovDir[x][y] != old_move_dir)
6914       MovDelay[x][y] = 9;
6915   }
6916   else if (element == EL_BALLOON)
6917   {
6918     MovDir[x][y] = game.wind_direction;
6919     MovDelay[x][y] = 0;
6920   }
6921   else if (element == EL_SPRING)
6922   {
6923 #if USE_NEW_SPRING_BUMPER
6924     if (MovDir[x][y] & MV_HORIZONTAL)
6925     {
6926       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6927           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6928       {
6929         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6930         ResetGfxAnimation(move_x, move_y);
6931         DrawLevelField(move_x, move_y);
6932
6933         MovDir[x][y] = back_dir;
6934       }
6935       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6936                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6937         MovDir[x][y] = MV_NONE;
6938     }
6939 #else
6940     if (MovDir[x][y] & MV_HORIZONTAL &&
6941         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6942          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6943       MovDir[x][y] = MV_NONE;
6944 #endif
6945
6946     MovDelay[x][y] = 0;
6947   }
6948   else if (element == EL_ROBOT ||
6949            element == EL_SATELLITE ||
6950            element == EL_PENGUIN ||
6951            element == EL_EMC_ANDROID)
6952   {
6953     int attr_x = -1, attr_y = -1;
6954
6955     if (AllPlayersGone)
6956     {
6957       attr_x = ExitX;
6958       attr_y = ExitY;
6959     }
6960     else
6961     {
6962       int i;
6963
6964       for (i = 0; i < MAX_PLAYERS; i++)
6965       {
6966         struct PlayerInfo *player = &stored_player[i];
6967         int jx = player->jx, jy = player->jy;
6968
6969         if (!player->active)
6970           continue;
6971
6972         if (attr_x == -1 ||
6973             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6974         {
6975           attr_x = jx;
6976           attr_y = jy;
6977         }
6978       }
6979     }
6980
6981     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6982         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6983          game.engine_version < VERSION_IDENT(3,1,0,0)))
6984     {
6985       attr_x = ZX;
6986       attr_y = ZY;
6987     }
6988
6989     if (element == EL_PENGUIN)
6990     {
6991       int i;
6992       static int xy[4][2] =
6993       {
6994         { 0, -1 },
6995         { -1, 0 },
6996         { +1, 0 },
6997         { 0, +1 }
6998       };
6999
7000       for (i = 0; i < NUM_DIRECTIONS; i++)
7001       {
7002         int ex = x + xy[i][0];
7003         int ey = y + xy[i][1];
7004
7005         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7006                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7007                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7008                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7009         {
7010           attr_x = ex;
7011           attr_y = ey;
7012           break;
7013         }
7014       }
7015     }
7016
7017     MovDir[x][y] = MV_NONE;
7018     if (attr_x < x)
7019       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7020     else if (attr_x > x)
7021       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7022     if (attr_y < y)
7023       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7024     else if (attr_y > y)
7025       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7026
7027     if (element == EL_ROBOT)
7028     {
7029       int newx, newy;
7030
7031       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7032         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7033       Moving2Blocked(x, y, &newx, &newy);
7034
7035       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7036         MovDelay[x][y] = 8 + 8 * !RND(3);
7037       else
7038         MovDelay[x][y] = 16;
7039     }
7040     else if (element == EL_PENGUIN)
7041     {
7042       int newx, newy;
7043
7044       MovDelay[x][y] = 1;
7045
7046       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7047       {
7048         boolean first_horiz = RND(2);
7049         int new_move_dir = MovDir[x][y];
7050
7051         MovDir[x][y] =
7052           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7053         Moving2Blocked(x, y, &newx, &newy);
7054
7055         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7056           return;
7057
7058         MovDir[x][y] =
7059           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7060         Moving2Blocked(x, y, &newx, &newy);
7061
7062         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7063           return;
7064
7065         MovDir[x][y] = old_move_dir;
7066         return;
7067       }
7068     }
7069     else if (element == EL_SATELLITE)
7070     {
7071       int newx, newy;
7072
7073       MovDelay[x][y] = 1;
7074
7075       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7076       {
7077         boolean first_horiz = RND(2);
7078         int new_move_dir = MovDir[x][y];
7079
7080         MovDir[x][y] =
7081           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7082         Moving2Blocked(x, y, &newx, &newy);
7083
7084         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7085           return;
7086
7087         MovDir[x][y] =
7088           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7089         Moving2Blocked(x, y, &newx, &newy);
7090
7091         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7092           return;
7093
7094         MovDir[x][y] = old_move_dir;
7095         return;
7096       }
7097     }
7098     else if (element == EL_EMC_ANDROID)
7099     {
7100       static int check_pos[16] =
7101       {
7102         -1,             /*  0 => (invalid)          */
7103         7,              /*  1 => MV_LEFT            */
7104         3,              /*  2 => MV_RIGHT           */
7105         -1,             /*  3 => (invalid)          */
7106         1,              /*  4 =>            MV_UP   */
7107         0,              /*  5 => MV_LEFT  | MV_UP   */
7108         2,              /*  6 => MV_RIGHT | MV_UP   */
7109         -1,             /*  7 => (invalid)          */
7110         5,              /*  8 =>            MV_DOWN */
7111         6,              /*  9 => MV_LEFT  | MV_DOWN */
7112         4,              /* 10 => MV_RIGHT | MV_DOWN */
7113         -1,             /* 11 => (invalid)          */
7114         -1,             /* 12 => (invalid)          */
7115         -1,             /* 13 => (invalid)          */
7116         -1,             /* 14 => (invalid)          */
7117         -1,             /* 15 => (invalid)          */
7118       };
7119       static struct
7120       {
7121         int dx, dy;
7122         int dir;
7123       } check_xy[8] =
7124       {
7125         { -1, -1,       MV_LEFT  | MV_UP   },
7126         {  0, -1,                  MV_UP   },
7127         { +1, -1,       MV_RIGHT | MV_UP   },
7128         { +1,  0,       MV_RIGHT           },
7129         { +1, +1,       MV_RIGHT | MV_DOWN },
7130         {  0, +1,                  MV_DOWN },
7131         { -1, +1,       MV_LEFT  | MV_DOWN },
7132         { -1,  0,       MV_LEFT            },
7133       };
7134       int start_pos, check_order;
7135       boolean can_clone = FALSE;
7136       int i;
7137
7138       /* check if there is any free field around current position */
7139       for (i = 0; i < 8; i++)
7140       {
7141         int newx = x + check_xy[i].dx;
7142         int newy = y + check_xy[i].dy;
7143
7144         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7145         {
7146           can_clone = TRUE;
7147
7148           break;
7149         }
7150       }
7151
7152       if (can_clone)            /* randomly find an element to clone */
7153       {
7154         can_clone = FALSE;
7155
7156         start_pos = check_pos[RND(8)];
7157         check_order = (RND(2) ? -1 : +1);
7158
7159         for (i = 0; i < 8; i++)
7160         {
7161           int pos_raw = start_pos + i * check_order;
7162           int pos = (pos_raw + 8) % 8;
7163           int newx = x + check_xy[pos].dx;
7164           int newy = y + check_xy[pos].dy;
7165
7166           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7167           {
7168             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7169             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7170
7171             Store[x][y] = Feld[newx][newy];
7172
7173             can_clone = TRUE;
7174
7175             break;
7176           }
7177         }
7178       }
7179
7180       if (can_clone)            /* randomly find a direction to move */
7181       {
7182         can_clone = FALSE;
7183
7184         start_pos = check_pos[RND(8)];
7185         check_order = (RND(2) ? -1 : +1);
7186
7187         for (i = 0; i < 8; i++)
7188         {
7189           int pos_raw = start_pos + i * check_order;
7190           int pos = (pos_raw + 8) % 8;
7191           int newx = x + check_xy[pos].dx;
7192           int newy = y + check_xy[pos].dy;
7193           int new_move_dir = check_xy[pos].dir;
7194
7195           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7196           {
7197             MovDir[x][y] = new_move_dir;
7198             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7199
7200             can_clone = TRUE;
7201
7202             break;
7203           }
7204         }
7205       }
7206
7207       if (can_clone)            /* cloning and moving successful */
7208         return;
7209
7210       /* cannot clone -- try to move towards player */
7211
7212       start_pos = check_pos[MovDir[x][y] & 0x0f];
7213       check_order = (RND(2) ? -1 : +1);
7214
7215       for (i = 0; i < 3; i++)
7216       {
7217         /* first check start_pos, then previous/next or (next/previous) pos */
7218         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7219         int pos = (pos_raw + 8) % 8;
7220         int newx = x + check_xy[pos].dx;
7221         int newy = y + check_xy[pos].dy;
7222         int new_move_dir = check_xy[pos].dir;
7223
7224         if (IS_PLAYER(newx, newy))
7225           break;
7226
7227         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7228         {
7229           MovDir[x][y] = new_move_dir;
7230           MovDelay[x][y] = level.android_move_time * 8 + 1;
7231
7232           break;
7233         }
7234       }
7235     }
7236   }
7237   else if (move_pattern == MV_TURNING_LEFT ||
7238            move_pattern == MV_TURNING_RIGHT ||
7239            move_pattern == MV_TURNING_LEFT_RIGHT ||
7240            move_pattern == MV_TURNING_RIGHT_LEFT ||
7241            move_pattern == MV_TURNING_RANDOM ||
7242            move_pattern == MV_ALL_DIRECTIONS)
7243   {
7244     boolean can_turn_left =
7245       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7246     boolean can_turn_right =
7247       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7248
7249     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7250       return;
7251
7252     if (move_pattern == MV_TURNING_LEFT)
7253       MovDir[x][y] = left_dir;
7254     else if (move_pattern == MV_TURNING_RIGHT)
7255       MovDir[x][y] = right_dir;
7256     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7257       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7258     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7259       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7260     else if (move_pattern == MV_TURNING_RANDOM)
7261       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7262                       can_turn_right && !can_turn_left ? right_dir :
7263                       RND(2) ? left_dir : right_dir);
7264     else if (can_turn_left && can_turn_right)
7265       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7266     else if (can_turn_left)
7267       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7268     else if (can_turn_right)
7269       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7270     else
7271       MovDir[x][y] = back_dir;
7272
7273     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7274   }
7275   else if (move_pattern == MV_HORIZONTAL ||
7276            move_pattern == MV_VERTICAL)
7277   {
7278     if (move_pattern & old_move_dir)
7279       MovDir[x][y] = back_dir;
7280     else if (move_pattern == MV_HORIZONTAL)
7281       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7282     else if (move_pattern == MV_VERTICAL)
7283       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7284
7285     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7286   }
7287   else if (move_pattern & MV_ANY_DIRECTION)
7288   {
7289     MovDir[x][y] = move_pattern;
7290     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7291   }
7292   else if (move_pattern & MV_WIND_DIRECTION)
7293   {
7294     MovDir[x][y] = game.wind_direction;
7295     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7296   }
7297   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7298   {
7299     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7300       MovDir[x][y] = left_dir;
7301     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7302       MovDir[x][y] = right_dir;
7303
7304     if (MovDir[x][y] != old_move_dir)
7305       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7306   }
7307   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7308   {
7309     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7310       MovDir[x][y] = right_dir;
7311     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7312       MovDir[x][y] = left_dir;
7313
7314     if (MovDir[x][y] != old_move_dir)
7315       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7316   }
7317   else if (move_pattern == MV_TOWARDS_PLAYER ||
7318            move_pattern == MV_AWAY_FROM_PLAYER)
7319   {
7320     int attr_x = -1, attr_y = -1;
7321     int newx, newy;
7322     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7323
7324     if (AllPlayersGone)
7325     {
7326       attr_x = ExitX;
7327       attr_y = ExitY;
7328     }
7329     else
7330     {
7331       int i;
7332
7333       for (i = 0; i < MAX_PLAYERS; i++)
7334       {
7335         struct PlayerInfo *player = &stored_player[i];
7336         int jx = player->jx, jy = player->jy;
7337
7338         if (!player->active)
7339           continue;
7340
7341         if (attr_x == -1 ||
7342             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7343         {
7344           attr_x = jx;
7345           attr_y = jy;
7346         }
7347       }
7348     }
7349
7350     MovDir[x][y] = MV_NONE;
7351     if (attr_x < x)
7352       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7353     else if (attr_x > x)
7354       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7355     if (attr_y < y)
7356       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7357     else if (attr_y > y)
7358       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7359
7360     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7361
7362     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7363     {
7364       boolean first_horiz = RND(2);
7365       int new_move_dir = MovDir[x][y];
7366
7367       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7368       {
7369         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7370         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7371
7372         return;
7373       }
7374
7375       MovDir[x][y] =
7376         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7377       Moving2Blocked(x, y, &newx, &newy);
7378
7379       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7380         return;
7381
7382       MovDir[x][y] =
7383         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7384       Moving2Blocked(x, y, &newx, &newy);
7385
7386       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7387         return;
7388
7389       MovDir[x][y] = old_move_dir;
7390     }
7391   }
7392   else if (move_pattern == MV_WHEN_PUSHED ||
7393            move_pattern == MV_WHEN_DROPPED)
7394   {
7395     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7396       MovDir[x][y] = MV_NONE;
7397
7398     MovDelay[x][y] = 0;
7399   }
7400   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7401   {
7402     static int test_xy[7][2] =
7403     {
7404       { 0, -1 },
7405       { -1, 0 },
7406       { +1, 0 },
7407       { 0, +1 },
7408       { 0, -1 },
7409       { -1, 0 },
7410       { +1, 0 },
7411     };
7412     static int test_dir[7] =
7413     {
7414       MV_UP,
7415       MV_LEFT,
7416       MV_RIGHT,
7417       MV_DOWN,
7418       MV_UP,
7419       MV_LEFT,
7420       MV_RIGHT,
7421     };
7422     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7423     int move_preference = -1000000;     /* start with very low preference */
7424     int new_move_dir = MV_NONE;
7425     int start_test = RND(4);
7426     int i;
7427
7428     for (i = 0; i < NUM_DIRECTIONS; i++)
7429     {
7430       int move_dir = test_dir[start_test + i];
7431       int move_dir_preference;
7432
7433       xx = x + test_xy[start_test + i][0];
7434       yy = y + test_xy[start_test + i][1];
7435
7436       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7437           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7438       {
7439         new_move_dir = move_dir;
7440
7441         break;
7442       }
7443
7444       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7445         continue;
7446
7447       move_dir_preference = -1 * RunnerVisit[xx][yy];
7448       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7449         move_dir_preference = PlayerVisit[xx][yy];
7450
7451       if (move_dir_preference > move_preference)
7452       {
7453         /* prefer field that has not been visited for the longest time */
7454         move_preference = move_dir_preference;
7455         new_move_dir = move_dir;
7456       }
7457       else if (move_dir_preference == move_preference &&
7458                move_dir == old_move_dir)
7459       {
7460         /* prefer last direction when all directions are preferred equally */
7461         move_preference = move_dir_preference;
7462         new_move_dir = move_dir;
7463       }
7464     }
7465
7466     MovDir[x][y] = new_move_dir;
7467     if (old_move_dir != new_move_dir)
7468       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7469   }
7470 }
7471
7472 static void TurnRound(int x, int y)
7473 {
7474   int direction = MovDir[x][y];
7475
7476   TurnRoundExt(x, y);
7477
7478   GfxDir[x][y] = MovDir[x][y];
7479
7480   if (direction != MovDir[x][y])
7481     GfxFrame[x][y] = 0;
7482
7483   if (MovDelay[x][y])
7484     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7485
7486   ResetGfxFrame(x, y, FALSE);
7487 }
7488
7489 static boolean JustBeingPushed(int x, int y)
7490 {
7491   int i;
7492
7493   for (i = 0; i < MAX_PLAYERS; i++)
7494   {
7495     struct PlayerInfo *player = &stored_player[i];
7496
7497     if (player->active && player->is_pushing && player->MovPos)
7498     {
7499       int next_jx = player->jx + (player->jx - player->last_jx);
7500       int next_jy = player->jy + (player->jy - player->last_jy);
7501
7502       if (x == next_jx && y == next_jy)
7503         return TRUE;
7504     }
7505   }
7506
7507   return FALSE;
7508 }
7509
7510 void StartMoving(int x, int y)
7511 {
7512   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7513   int element = Feld[x][y];
7514
7515   if (Stop[x][y])
7516     return;
7517
7518   if (MovDelay[x][y] == 0)
7519     GfxAction[x][y] = ACTION_DEFAULT;
7520
7521   if (CAN_FALL(element) && y < lev_fieldy - 1)
7522   {
7523     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7524         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7525       if (JustBeingPushed(x, y))
7526         return;
7527
7528     if (element == EL_QUICKSAND_FULL)
7529     {
7530       if (IS_FREE(x, y + 1))
7531       {
7532         InitMovingField(x, y, MV_DOWN);
7533         started_moving = TRUE;
7534
7535         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7536 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7537         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7538           Store[x][y] = EL_ROCK;
7539 #else
7540         Store[x][y] = EL_ROCK;
7541 #endif
7542
7543         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7544       }
7545       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7546       {
7547         if (!MovDelay[x][y])
7548           MovDelay[x][y] = TILEY + 1;
7549
7550         if (MovDelay[x][y])
7551         {
7552           MovDelay[x][y]--;
7553           if (MovDelay[x][y])
7554             return;
7555         }
7556
7557         Feld[x][y] = EL_QUICKSAND_EMPTY;
7558         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7559         Store[x][y + 1] = Store[x][y];
7560         Store[x][y] = 0;
7561
7562         PlayLevelSoundAction(x, y, ACTION_FILLING);
7563       }
7564     }
7565     else if (element == EL_QUICKSAND_FAST_FULL)
7566     {
7567       if (IS_FREE(x, y + 1))
7568       {
7569         InitMovingField(x, y, MV_DOWN);
7570         started_moving = TRUE;
7571
7572         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7573 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7574         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7575           Store[x][y] = EL_ROCK;
7576 #else
7577         Store[x][y] = EL_ROCK;
7578 #endif
7579
7580         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7581       }
7582       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7583       {
7584         if (!MovDelay[x][y])
7585           MovDelay[x][y] = TILEY + 1;
7586
7587         if (MovDelay[x][y])
7588         {
7589           MovDelay[x][y]--;
7590           if (MovDelay[x][y])
7591             return;
7592         }
7593
7594         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7595         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7596         Store[x][y + 1] = Store[x][y];
7597         Store[x][y] = 0;
7598
7599         PlayLevelSoundAction(x, y, ACTION_FILLING);
7600       }
7601     }
7602     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7603              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7604     {
7605       InitMovingField(x, y, MV_DOWN);
7606       started_moving = TRUE;
7607
7608       Feld[x][y] = EL_QUICKSAND_FILLING;
7609       Store[x][y] = element;
7610
7611       PlayLevelSoundAction(x, y, ACTION_FILLING);
7612     }
7613     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7614              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7615     {
7616       InitMovingField(x, y, MV_DOWN);
7617       started_moving = TRUE;
7618
7619       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7620       Store[x][y] = element;
7621
7622       PlayLevelSoundAction(x, y, ACTION_FILLING);
7623     }
7624     else if (element == EL_MAGIC_WALL_FULL)
7625     {
7626       if (IS_FREE(x, y + 1))
7627       {
7628         InitMovingField(x, y, MV_DOWN);
7629         started_moving = TRUE;
7630
7631         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7632         Store[x][y] = EL_CHANGED(Store[x][y]);
7633       }
7634       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7635       {
7636         if (!MovDelay[x][y])
7637           MovDelay[x][y] = TILEY/4 + 1;
7638
7639         if (MovDelay[x][y])
7640         {
7641           MovDelay[x][y]--;
7642           if (MovDelay[x][y])
7643             return;
7644         }
7645
7646         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7647         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7648         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7649         Store[x][y] = 0;
7650       }
7651     }
7652     else if (element == EL_BD_MAGIC_WALL_FULL)
7653     {
7654       if (IS_FREE(x, y + 1))
7655       {
7656         InitMovingField(x, y, MV_DOWN);
7657         started_moving = TRUE;
7658
7659         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7660         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7661       }
7662       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7663       {
7664         if (!MovDelay[x][y])
7665           MovDelay[x][y] = TILEY/4 + 1;
7666
7667         if (MovDelay[x][y])
7668         {
7669           MovDelay[x][y]--;
7670           if (MovDelay[x][y])
7671             return;
7672         }
7673
7674         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7675         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7676         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7677         Store[x][y] = 0;
7678       }
7679     }
7680     else if (element == EL_DC_MAGIC_WALL_FULL)
7681     {
7682       if (IS_FREE(x, y + 1))
7683       {
7684         InitMovingField(x, y, MV_DOWN);
7685         started_moving = TRUE;
7686
7687         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7688         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7689       }
7690       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7691       {
7692         if (!MovDelay[x][y])
7693           MovDelay[x][y] = TILEY/4 + 1;
7694
7695         if (MovDelay[x][y])
7696         {
7697           MovDelay[x][y]--;
7698           if (MovDelay[x][y])
7699             return;
7700         }
7701
7702         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7703         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7704         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7705         Store[x][y] = 0;
7706       }
7707     }
7708     else if ((CAN_PASS_MAGIC_WALL(element) &&
7709               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7710                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7711              (CAN_PASS_DC_MAGIC_WALL(element) &&
7712               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7713
7714     {
7715       InitMovingField(x, y, MV_DOWN);
7716       started_moving = TRUE;
7717
7718       Feld[x][y] =
7719         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7720          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7721          EL_DC_MAGIC_WALL_FILLING);
7722       Store[x][y] = element;
7723     }
7724     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7725     {
7726       SplashAcid(x, y + 1);
7727
7728       InitMovingField(x, y, MV_DOWN);
7729       started_moving = TRUE;
7730
7731       Store[x][y] = EL_ACID;
7732     }
7733     else if (
7734 #if USE_FIX_IMPACT_COLLISION
7735              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7736               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7737 #else
7738              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7739               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7740 #endif
7741              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7742               CAN_FALL(element) && WasJustFalling[x][y] &&
7743               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7744
7745              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7746               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7747               (Feld[x][y + 1] == EL_BLOCKED)))
7748     {
7749       /* this is needed for a special case not covered by calling "Impact()"
7750          from "ContinueMoving()": if an element moves to a tile directly below
7751          another element which was just falling on that tile (which was empty
7752          in the previous frame), the falling element above would just stop
7753          instead of smashing the element below (in previous version, the above
7754          element was just checked for "moving" instead of "falling", resulting
7755          in incorrect smashes caused by horizontal movement of the above
7756          element; also, the case of the player being the element to smash was
7757          simply not covered here... :-/ ) */
7758
7759       CheckCollision[x][y] = 0;
7760       CheckImpact[x][y] = 0;
7761
7762       Impact(x, y);
7763     }
7764     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7765     {
7766       if (MovDir[x][y] == MV_NONE)
7767       {
7768         InitMovingField(x, y, MV_DOWN);
7769         started_moving = TRUE;
7770       }
7771     }
7772     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7773     {
7774       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7775         MovDir[x][y] = MV_DOWN;
7776
7777       InitMovingField(x, y, MV_DOWN);
7778       started_moving = TRUE;
7779     }
7780     else if (element == EL_AMOEBA_DROP)
7781     {
7782       Feld[x][y] = EL_AMOEBA_GROWING;
7783       Store[x][y] = EL_AMOEBA_WET;
7784     }
7785     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7786               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7787              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7788              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7789     {
7790       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7791                                 (IS_FREE(x - 1, y + 1) ||
7792                                  Feld[x - 1][y + 1] == EL_ACID));
7793       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7794                                 (IS_FREE(x + 1, y + 1) ||
7795                                  Feld[x + 1][y + 1] == EL_ACID));
7796       boolean can_fall_any  = (can_fall_left || can_fall_right);
7797       boolean can_fall_both = (can_fall_left && can_fall_right);
7798       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7799
7800 #if USE_NEW_ALL_SLIPPERY
7801       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7802       {
7803         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7804           can_fall_right = FALSE;
7805         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7806           can_fall_left = FALSE;
7807         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7808           can_fall_right = FALSE;
7809         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7810           can_fall_left = FALSE;
7811
7812         can_fall_any  = (can_fall_left || can_fall_right);
7813         can_fall_both = FALSE;
7814       }
7815 #else
7816       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7817       {
7818         if (slippery_type == SLIPPERY_ONLY_LEFT)
7819           can_fall_right = FALSE;
7820         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7821           can_fall_left = FALSE;
7822         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7823           can_fall_right = FALSE;
7824         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7825           can_fall_left = FALSE;
7826
7827         can_fall_any  = (can_fall_left || can_fall_right);
7828         can_fall_both = (can_fall_left && can_fall_right);
7829       }
7830 #endif
7831
7832 #if USE_NEW_ALL_SLIPPERY
7833 #else
7834 #if USE_NEW_SP_SLIPPERY
7835       /* !!! better use the same properties as for custom elements here !!! */
7836       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7837                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7838       {
7839         can_fall_right = FALSE;         /* slip down on left side */
7840         can_fall_both = FALSE;
7841       }
7842 #endif
7843 #endif
7844
7845 #if USE_NEW_ALL_SLIPPERY
7846       if (can_fall_both)
7847       {
7848         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7849           can_fall_right = FALSE;       /* slip down on left side */
7850         else
7851           can_fall_left = !(can_fall_right = RND(2));
7852
7853         can_fall_both = FALSE;
7854       }
7855 #else
7856       if (can_fall_both)
7857       {
7858         if (game.emulation == EMU_BOULDERDASH ||
7859             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7860           can_fall_right = FALSE;       /* slip down on left side */
7861         else
7862           can_fall_left = !(can_fall_right = RND(2));
7863
7864         can_fall_both = FALSE;
7865       }
7866 #endif
7867
7868       if (can_fall_any)
7869       {
7870         /* if not determined otherwise, prefer left side for slipping down */
7871         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7872         started_moving = TRUE;
7873       }
7874     }
7875 #if 0
7876     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7877 #else
7878     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7879 #endif
7880     {
7881       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7882       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7883       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7884       int belt_dir = game.belt_dir[belt_nr];
7885
7886       if ((belt_dir == MV_LEFT  && left_is_free) ||
7887           (belt_dir == MV_RIGHT && right_is_free))
7888       {
7889         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7890
7891         InitMovingField(x, y, belt_dir);
7892         started_moving = TRUE;
7893
7894         Pushed[x][y] = TRUE;
7895         Pushed[nextx][y] = TRUE;
7896
7897         GfxAction[x][y] = ACTION_DEFAULT;
7898       }
7899       else
7900       {
7901         MovDir[x][y] = 0;       /* if element was moving, stop it */
7902       }
7903     }
7904   }
7905
7906   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7907 #if 0
7908   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7909 #else
7910   if (CAN_MOVE(element) && !started_moving)
7911 #endif
7912   {
7913     int move_pattern = element_info[element].move_pattern;
7914     int newx, newy;
7915
7916 #if 0
7917 #if DEBUG
7918     if (MovDir[x][y] == MV_NONE)
7919     {
7920       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7921              x, y, element, element_info[element].token_name);
7922       printf("StartMoving(): This should never happen!\n");
7923     }
7924 #endif
7925 #endif
7926
7927     Moving2Blocked(x, y, &newx, &newy);
7928
7929     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7930       return;
7931
7932     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7933         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7934     {
7935       WasJustMoving[x][y] = 0;
7936       CheckCollision[x][y] = 0;
7937
7938       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7939
7940       if (Feld[x][y] != element)        /* element has changed */
7941         return;
7942     }
7943
7944     if (!MovDelay[x][y])        /* start new movement phase */
7945     {
7946       /* all objects that can change their move direction after each step
7947          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7948
7949       if (element != EL_YAMYAM &&
7950           element != EL_DARK_YAMYAM &&
7951           element != EL_PACMAN &&
7952           !(move_pattern & MV_ANY_DIRECTION) &&
7953           move_pattern != MV_TURNING_LEFT &&
7954           move_pattern != MV_TURNING_RIGHT &&
7955           move_pattern != MV_TURNING_LEFT_RIGHT &&
7956           move_pattern != MV_TURNING_RIGHT_LEFT &&
7957           move_pattern != MV_TURNING_RANDOM)
7958       {
7959         TurnRound(x, y);
7960
7961         if (MovDelay[x][y] && (element == EL_BUG ||
7962                                element == EL_SPACESHIP ||
7963                                element == EL_SP_SNIKSNAK ||
7964                                element == EL_SP_ELECTRON ||
7965                                element == EL_MOLE))
7966           DrawLevelField(x, y);
7967       }
7968     }
7969
7970     if (MovDelay[x][y])         /* wait some time before next movement */
7971     {
7972       MovDelay[x][y]--;
7973
7974       if (element == EL_ROBOT ||
7975           element == EL_YAMYAM ||
7976           element == EL_DARK_YAMYAM)
7977       {
7978         DrawLevelElementAnimationIfNeeded(x, y, element);
7979         PlayLevelSoundAction(x, y, ACTION_WAITING);
7980       }
7981       else if (element == EL_SP_ELECTRON)
7982         DrawLevelElementAnimationIfNeeded(x, y, element);
7983       else if (element == EL_DRAGON)
7984       {
7985         int i;
7986         int dir = MovDir[x][y];
7987         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7988         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7989         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7990                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7991                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7992                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7993         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7994
7995         GfxAction[x][y] = ACTION_ATTACKING;
7996
7997         if (IS_PLAYER(x, y))
7998           DrawPlayerField(x, y);
7999         else
8000           DrawLevelField(x, y);
8001
8002         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8003
8004         for (i = 1; i <= 3; i++)
8005         {
8006           int xx = x + i * dx;
8007           int yy = y + i * dy;
8008           int sx = SCREENX(xx);
8009           int sy = SCREENY(yy);
8010           int flame_graphic = graphic + (i - 1);
8011
8012           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8013             break;
8014
8015           if (MovDelay[x][y])
8016           {
8017             int flamed = MovingOrBlocked2Element(xx, yy);
8018
8019             /* !!! */
8020 #if 0
8021             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8022               Bang(xx, yy);
8023             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8024               RemoveMovingField(xx, yy);
8025             else
8026               RemoveField(xx, yy);
8027 #else
8028             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8029               Bang(xx, yy);
8030             else
8031               RemoveMovingField(xx, yy);
8032 #endif
8033
8034             ChangeDelay[xx][yy] = 0;
8035
8036             Feld[xx][yy] = EL_FLAMES;
8037
8038             if (IN_SCR_FIELD(sx, sy))
8039             {
8040               DrawLevelFieldCrumbledSand(xx, yy);
8041               DrawGraphic(sx, sy, flame_graphic, frame);
8042             }
8043           }
8044           else
8045           {
8046             if (Feld[xx][yy] == EL_FLAMES)
8047               Feld[xx][yy] = EL_EMPTY;
8048             DrawLevelField(xx, yy);
8049           }
8050         }
8051       }
8052
8053       if (MovDelay[x][y])       /* element still has to wait some time */
8054       {
8055         PlayLevelSoundAction(x, y, ACTION_WAITING);
8056
8057         return;
8058       }
8059     }
8060
8061     /* now make next step */
8062
8063     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8064
8065     if (DONT_COLLIDE_WITH(element) &&
8066         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8067         !PLAYER_ENEMY_PROTECTED(newx, newy))
8068     {
8069       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8070
8071       return;
8072     }
8073
8074     else if (CAN_MOVE_INTO_ACID(element) &&
8075              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8076              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8077              (MovDir[x][y] == MV_DOWN ||
8078               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8079     {
8080       SplashAcid(newx, newy);
8081       Store[x][y] = EL_ACID;
8082     }
8083     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8084     {
8085       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8086           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8087           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8088           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8089       {
8090         RemoveField(x, y);
8091         DrawLevelField(x, y);
8092
8093         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8094         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8095           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8096
8097         local_player->friends_still_needed--;
8098         if (!local_player->friends_still_needed &&
8099             !local_player->GameOver && AllPlayersGone)
8100           PlayerWins(local_player);
8101
8102         return;
8103       }
8104       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8105       {
8106         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8107           DrawLevelField(newx, newy);
8108         else
8109           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8110       }
8111       else if (!IS_FREE(newx, newy))
8112       {
8113         GfxAction[x][y] = ACTION_WAITING;
8114
8115         if (IS_PLAYER(x, y))
8116           DrawPlayerField(x, y);
8117         else
8118           DrawLevelField(x, y);
8119
8120         return;
8121       }
8122     }
8123     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8124     {
8125       if (IS_FOOD_PIG(Feld[newx][newy]))
8126       {
8127         if (IS_MOVING(newx, newy))
8128           RemoveMovingField(newx, newy);
8129         else
8130         {
8131           Feld[newx][newy] = EL_EMPTY;
8132           DrawLevelField(newx, newy);
8133         }
8134
8135         PlayLevelSound(x, y, SND_PIG_DIGGING);
8136       }
8137       else if (!IS_FREE(newx, newy))
8138       {
8139         if (IS_PLAYER(x, y))
8140           DrawPlayerField(x, y);
8141         else
8142           DrawLevelField(x, y);
8143
8144         return;
8145       }
8146     }
8147     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8148     {
8149       if (Store[x][y] != EL_EMPTY)
8150       {
8151         boolean can_clone = FALSE;
8152         int xx, yy;
8153
8154         /* check if element to clone is still there */
8155         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8156         {
8157           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8158           {
8159             can_clone = TRUE;
8160
8161             break;
8162           }
8163         }
8164
8165         /* cannot clone or target field not free anymore -- do not clone */
8166         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8167           Store[x][y] = EL_EMPTY;
8168       }
8169
8170       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8171       {
8172         if (IS_MV_DIAGONAL(MovDir[x][y]))
8173         {
8174           int diagonal_move_dir = MovDir[x][y];
8175           int stored = Store[x][y];
8176           int change_delay = 8;
8177           int graphic;
8178
8179           /* android is moving diagonally */
8180
8181           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8182
8183           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8184           GfxElement[x][y] = EL_EMC_ANDROID;
8185           GfxAction[x][y] = ACTION_SHRINKING;
8186           GfxDir[x][y] = diagonal_move_dir;
8187           ChangeDelay[x][y] = change_delay;
8188
8189           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8190                                    GfxDir[x][y]);
8191
8192           DrawLevelGraphicAnimation(x, y, graphic);
8193           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8194
8195           if (Feld[newx][newy] == EL_ACID)
8196           {
8197             SplashAcid(newx, newy);
8198
8199             return;
8200           }
8201
8202           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8203
8204           Store[newx][newy] = EL_EMC_ANDROID;
8205           GfxElement[newx][newy] = EL_EMC_ANDROID;
8206           GfxAction[newx][newy] = ACTION_GROWING;
8207           GfxDir[newx][newy] = diagonal_move_dir;
8208           ChangeDelay[newx][newy] = change_delay;
8209
8210           graphic = el_act_dir2img(GfxElement[newx][newy],
8211                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8212
8213           DrawLevelGraphicAnimation(newx, newy, graphic);
8214           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8215
8216           return;
8217         }
8218         else
8219         {
8220           Feld[newx][newy] = EL_EMPTY;
8221           DrawLevelField(newx, newy);
8222
8223           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8224         }
8225       }
8226       else if (!IS_FREE(newx, newy))
8227       {
8228 #if 0
8229         if (IS_PLAYER(x, y))
8230           DrawPlayerField(x, y);
8231         else
8232           DrawLevelField(x, y);
8233 #endif
8234
8235         return;
8236       }
8237     }
8238     else if (IS_CUSTOM_ELEMENT(element) &&
8239              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8240     {
8241       int new_element = Feld[newx][newy];
8242
8243       if (!IS_FREE(newx, newy))
8244       {
8245         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8246                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8247                       ACTION_BREAKING);
8248
8249         /* no element can dig solid indestructible elements */
8250         if (IS_INDESTRUCTIBLE(new_element) &&
8251             !IS_DIGGABLE(new_element) &&
8252             !IS_COLLECTIBLE(new_element))
8253           return;
8254
8255         if (AmoebaNr[newx][newy] &&
8256             (new_element == EL_AMOEBA_FULL ||
8257              new_element == EL_BD_AMOEBA ||
8258              new_element == EL_AMOEBA_GROWING))
8259         {
8260           AmoebaCnt[AmoebaNr[newx][newy]]--;
8261           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8262         }
8263
8264         if (IS_MOVING(newx, newy))
8265           RemoveMovingField(newx, newy);
8266         else
8267         {
8268           RemoveField(newx, newy);
8269           DrawLevelField(newx, newy);
8270         }
8271
8272         /* if digged element was about to explode, prevent the explosion */
8273         ExplodeField[newx][newy] = EX_TYPE_NONE;
8274
8275         PlayLevelSoundAction(x, y, action);
8276       }
8277
8278       Store[newx][newy] = EL_EMPTY;
8279 #if 1
8280       /* this makes it possible to leave the removed element again */
8281       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8282         Store[newx][newy] = new_element;
8283 #else
8284       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8285       {
8286         int move_leave_element = element_info[element].move_leave_element;
8287
8288         /* this makes it possible to leave the removed element again */
8289         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8290                              new_element : move_leave_element);
8291       }
8292 #endif
8293
8294       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8295       {
8296         RunnerVisit[x][y] = FrameCounter;
8297         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8298       }
8299     }
8300     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8301     {
8302       if (!IS_FREE(newx, newy))
8303       {
8304         if (IS_PLAYER(x, y))
8305           DrawPlayerField(x, y);
8306         else
8307           DrawLevelField(x, y);
8308
8309         return;
8310       }
8311       else
8312       {
8313         boolean wanna_flame = !RND(10);
8314         int dx = newx - x, dy = newy - y;
8315         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8316         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8317         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8318                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8319         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8320                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8321
8322         if ((wanna_flame ||
8323              IS_CLASSIC_ENEMY(element1) ||
8324              IS_CLASSIC_ENEMY(element2)) &&
8325             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8326             element1 != EL_FLAMES && element2 != EL_FLAMES)
8327         {
8328           ResetGfxAnimation(x, y);
8329           GfxAction[x][y] = ACTION_ATTACKING;
8330
8331           if (IS_PLAYER(x, y))
8332             DrawPlayerField(x, y);
8333           else
8334             DrawLevelField(x, y);
8335
8336           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8337
8338           MovDelay[x][y] = 50;
8339
8340           /* !!! */
8341 #if 0
8342           RemoveField(newx, newy);
8343 #endif
8344           Feld[newx][newy] = EL_FLAMES;
8345           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8346           {
8347 #if 0
8348             RemoveField(newx1, newy1);
8349 #endif
8350             Feld[newx1][newy1] = EL_FLAMES;
8351           }
8352           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8353           {
8354 #if 0
8355             RemoveField(newx2, newy2);
8356 #endif
8357             Feld[newx2][newy2] = EL_FLAMES;
8358           }
8359
8360           return;
8361         }
8362       }
8363     }
8364     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8365              Feld[newx][newy] == EL_DIAMOND)
8366     {
8367       if (IS_MOVING(newx, newy))
8368         RemoveMovingField(newx, newy);
8369       else
8370       {
8371         Feld[newx][newy] = EL_EMPTY;
8372         DrawLevelField(newx, newy);
8373       }
8374
8375       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8376     }
8377     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8378              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8379     {
8380       if (AmoebaNr[newx][newy])
8381       {
8382         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8383         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8384             Feld[newx][newy] == EL_BD_AMOEBA)
8385           AmoebaCnt[AmoebaNr[newx][newy]]--;
8386       }
8387
8388 #if 0
8389       /* !!! test !!! */
8390       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8391       {
8392         RemoveMovingField(newx, newy);
8393       }
8394 #else
8395       if (IS_MOVING(newx, newy))
8396       {
8397         RemoveMovingField(newx, newy);
8398       }
8399 #endif
8400       else
8401       {
8402         Feld[newx][newy] = EL_EMPTY;
8403         DrawLevelField(newx, newy);
8404       }
8405
8406       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8407     }
8408     else if ((element == EL_PACMAN || element == EL_MOLE)
8409              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8410     {
8411       if (AmoebaNr[newx][newy])
8412       {
8413         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8414         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8415             Feld[newx][newy] == EL_BD_AMOEBA)
8416           AmoebaCnt[AmoebaNr[newx][newy]]--;
8417       }
8418
8419       if (element == EL_MOLE)
8420       {
8421         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8422         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8423
8424         ResetGfxAnimation(x, y);
8425         GfxAction[x][y] = ACTION_DIGGING;
8426         DrawLevelField(x, y);
8427
8428         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8429
8430         return;                         /* wait for shrinking amoeba */
8431       }
8432       else      /* element == EL_PACMAN */
8433       {
8434         Feld[newx][newy] = EL_EMPTY;
8435         DrawLevelField(newx, newy);
8436         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8437       }
8438     }
8439     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8440              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8441               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8442     {
8443       /* wait for shrinking amoeba to completely disappear */
8444       return;
8445     }
8446     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8447     {
8448       /* object was running against a wall */
8449
8450       TurnRound(x, y);
8451
8452 #if 0
8453       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8454       if (move_pattern & MV_ANY_DIRECTION &&
8455           move_pattern == MovDir[x][y])
8456       {
8457         int blocking_element =
8458           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8459
8460         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8461                                  MovDir[x][y]);
8462
8463         element = Feld[x][y];   /* element might have changed */
8464       }
8465 #endif
8466
8467       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8468         DrawLevelElementAnimation(x, y, element);
8469
8470       if (DONT_TOUCH(element))
8471         TestIfBadThingTouchesPlayer(x, y);
8472
8473       return;
8474     }
8475
8476     InitMovingField(x, y, MovDir[x][y]);
8477
8478     PlayLevelSoundAction(x, y, ACTION_MOVING);
8479   }
8480
8481   if (MovDir[x][y])
8482     ContinueMoving(x, y);
8483 }
8484
8485 void ContinueMoving(int x, int y)
8486 {
8487   int element = Feld[x][y];
8488   struct ElementInfo *ei = &element_info[element];
8489   int direction = MovDir[x][y];
8490   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8491   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8492   int newx = x + dx, newy = y + dy;
8493   int stored = Store[x][y];
8494   int stored_new = Store[newx][newy];
8495   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8496   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8497   boolean last_line = (newy == lev_fieldy - 1);
8498
8499   MovPos[x][y] += getElementMoveStepsize(x, y);
8500
8501   if (pushed_by_player) /* special case: moving object pushed by player */
8502     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8503
8504   if (ABS(MovPos[x][y]) < TILEX)
8505   {
8506 #if 0
8507     int ee = Feld[x][y];
8508     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8509     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8510
8511     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8512            x, y, ABS(MovPos[x][y]),
8513            ee, gg, ff,
8514            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8515 #endif
8516
8517     DrawLevelField(x, y);
8518
8519     return;     /* element is still moving */
8520   }
8521
8522   /* element reached destination field */
8523
8524   Feld[x][y] = EL_EMPTY;
8525   Feld[newx][newy] = element;
8526   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8527
8528   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8529   {
8530     element = Feld[newx][newy] = EL_ACID;
8531   }
8532   else if (element == EL_MOLE)
8533   {
8534     Feld[x][y] = EL_SAND;
8535
8536     DrawLevelFieldCrumbledSandNeighbours(x, y);
8537   }
8538   else if (element == EL_QUICKSAND_FILLING)
8539   {
8540     element = Feld[newx][newy] = get_next_element(element);
8541     Store[newx][newy] = Store[x][y];
8542   }
8543   else if (element == EL_QUICKSAND_EMPTYING)
8544   {
8545     Feld[x][y] = get_next_element(element);
8546     element = Feld[newx][newy] = Store[x][y];
8547   }
8548   else if (element == EL_QUICKSAND_FAST_FILLING)
8549   {
8550     element = Feld[newx][newy] = get_next_element(element);
8551     Store[newx][newy] = Store[x][y];
8552   }
8553   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8554   {
8555     Feld[x][y] = get_next_element(element);
8556     element = Feld[newx][newy] = Store[x][y];
8557   }
8558   else if (element == EL_MAGIC_WALL_FILLING)
8559   {
8560     element = Feld[newx][newy] = get_next_element(element);
8561     if (!game.magic_wall_active)
8562       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8563     Store[newx][newy] = Store[x][y];
8564   }
8565   else if (element == EL_MAGIC_WALL_EMPTYING)
8566   {
8567     Feld[x][y] = get_next_element(element);
8568     if (!game.magic_wall_active)
8569       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8570     element = Feld[newx][newy] = Store[x][y];
8571
8572 #if USE_NEW_CUSTOM_VALUE
8573     InitField(newx, newy, FALSE);
8574 #endif
8575   }
8576   else if (element == EL_BD_MAGIC_WALL_FILLING)
8577   {
8578     element = Feld[newx][newy] = get_next_element(element);
8579     if (!game.magic_wall_active)
8580       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8581     Store[newx][newy] = Store[x][y];
8582   }
8583   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8584   {
8585     Feld[x][y] = get_next_element(element);
8586     if (!game.magic_wall_active)
8587       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8588     element = Feld[newx][newy] = Store[x][y];
8589
8590 #if USE_NEW_CUSTOM_VALUE
8591     InitField(newx, newy, FALSE);
8592 #endif
8593   }
8594   else if (element == EL_DC_MAGIC_WALL_FILLING)
8595   {
8596     element = Feld[newx][newy] = get_next_element(element);
8597     if (!game.magic_wall_active)
8598       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8599     Store[newx][newy] = Store[x][y];
8600   }
8601   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8602   {
8603     Feld[x][y] = get_next_element(element);
8604     if (!game.magic_wall_active)
8605       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8606     element = Feld[newx][newy] = Store[x][y];
8607
8608 #if USE_NEW_CUSTOM_VALUE
8609     InitField(newx, newy, FALSE);
8610 #endif
8611   }
8612   else if (element == EL_AMOEBA_DROPPING)
8613   {
8614     Feld[x][y] = get_next_element(element);
8615     element = Feld[newx][newy] = Store[x][y];
8616   }
8617   else if (element == EL_SOKOBAN_OBJECT)
8618   {
8619     if (Back[x][y])
8620       Feld[x][y] = Back[x][y];
8621
8622     if (Back[newx][newy])
8623       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8624
8625     Back[x][y] = Back[newx][newy] = 0;
8626   }
8627
8628   Store[x][y] = EL_EMPTY;
8629   MovPos[x][y] = 0;
8630   MovDir[x][y] = 0;
8631   MovDelay[x][y] = 0;
8632
8633   MovDelay[newx][newy] = 0;
8634
8635   if (CAN_CHANGE_OR_HAS_ACTION(element))
8636   {
8637     /* copy element change control values to new field */
8638     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8639     ChangePage[newx][newy]  = ChangePage[x][y];
8640     ChangeCount[newx][newy] = ChangeCount[x][y];
8641     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8642   }
8643
8644 #if USE_NEW_CUSTOM_VALUE
8645     CustomValue[newx][newy] = CustomValue[x][y];
8646 #endif
8647
8648   ChangeDelay[x][y] = 0;
8649   ChangePage[x][y] = -1;
8650   ChangeCount[x][y] = 0;
8651   ChangeEvent[x][y] = -1;
8652
8653 #if USE_NEW_CUSTOM_VALUE
8654   CustomValue[x][y] = 0;
8655 #endif
8656
8657   /* copy animation control values to new field */
8658   GfxFrame[newx][newy]  = GfxFrame[x][y];
8659   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8660   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8661   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8662
8663   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8664
8665   /* some elements can leave other elements behind after moving */
8666 #if 1
8667   if (ei->move_leave_element != EL_EMPTY &&
8668       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8669       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8670 #else
8671   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8672       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8673       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8674 #endif
8675   {
8676     int move_leave_element = ei->move_leave_element;
8677
8678 #if 1
8679 #if 1
8680     /* this makes it possible to leave the removed element again */
8681     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8682       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8683 #else
8684     /* this makes it possible to leave the removed element again */
8685     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8686       move_leave_element = stored;
8687 #endif
8688 #else
8689     /* this makes it possible to leave the removed element again */
8690     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8691         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8692       move_leave_element = stored;
8693 #endif
8694
8695     Feld[x][y] = move_leave_element;
8696
8697     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8698       MovDir[x][y] = direction;
8699
8700     InitField(x, y, FALSE);
8701
8702     if (GFX_CRUMBLED(Feld[x][y]))
8703       DrawLevelFieldCrumbledSandNeighbours(x, y);
8704
8705     if (ELEM_IS_PLAYER(move_leave_element))
8706       RelocatePlayer(x, y, move_leave_element);
8707   }
8708
8709   /* do this after checking for left-behind element */
8710   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8711
8712   if (!CAN_MOVE(element) ||
8713       (CAN_FALL(element) && direction == MV_DOWN &&
8714        (element == EL_SPRING ||
8715         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8716         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8717     GfxDir[x][y] = MovDir[newx][newy] = 0;
8718
8719   DrawLevelField(x, y);
8720   DrawLevelField(newx, newy);
8721
8722   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8723
8724   /* prevent pushed element from moving on in pushed direction */
8725   if (pushed_by_player && CAN_MOVE(element) &&
8726       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8727       !(element_info[element].move_pattern & direction))
8728     TurnRound(newx, newy);
8729
8730   /* prevent elements on conveyor belt from moving on in last direction */
8731   if (pushed_by_conveyor && CAN_FALL(element) &&
8732       direction & MV_HORIZONTAL)
8733     MovDir[newx][newy] = 0;
8734
8735   if (!pushed_by_player)
8736   {
8737     int nextx = newx + dx, nexty = newy + dy;
8738     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8739
8740     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8741
8742     if (CAN_FALL(element) && direction == MV_DOWN)
8743       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8744
8745     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8746       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8747
8748 #if USE_FIX_IMPACT_COLLISION
8749     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8750       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8751 #endif
8752   }
8753
8754   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8755   {
8756     TestIfBadThingTouchesPlayer(newx, newy);
8757     TestIfBadThingTouchesFriend(newx, newy);
8758
8759     if (!IS_CUSTOM_ELEMENT(element))
8760       TestIfBadThingTouchesOtherBadThing(newx, newy);
8761   }
8762   else if (element == EL_PENGUIN)
8763     TestIfFriendTouchesBadThing(newx, newy);
8764
8765   /* give the player one last chance (one more frame) to move away */
8766   if (CAN_FALL(element) && direction == MV_DOWN &&
8767       (last_line || (!IS_FREE(x, newy + 1) &&
8768                      (!IS_PLAYER(x, newy + 1) ||
8769                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8770     Impact(x, newy);
8771
8772   if (pushed_by_player && !game.use_change_when_pushing_bug)
8773   {
8774     int push_side = MV_DIR_OPPOSITE(direction);
8775     struct PlayerInfo *player = PLAYERINFO(x, y);
8776
8777     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8778                                player->index_bit, push_side);
8779     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8780                                         player->index_bit, push_side);
8781   }
8782
8783   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8784     MovDelay[newx][newy] = 1;
8785
8786   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8787
8788   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8789
8790 #if 0
8791   if (ChangePage[newx][newy] != -1)             /* delayed change */
8792   {
8793     int page = ChangePage[newx][newy];
8794     struct ElementChangeInfo *change = &ei->change_page[page];
8795
8796     ChangePage[newx][newy] = -1;
8797
8798     if (change->can_change)
8799     {
8800       if (ChangeElement(newx, newy, element, page))
8801       {
8802         if (change->post_change_function)
8803           change->post_change_function(newx, newy);
8804       }
8805     }
8806
8807     if (change->has_action)
8808       ExecuteCustomElementAction(newx, newy, element, page);
8809   }
8810 #endif
8811
8812   TestIfElementHitsCustomElement(newx, newy, direction);
8813   TestIfPlayerTouchesCustomElement(newx, newy);
8814   TestIfElementTouchesCustomElement(newx, newy);
8815
8816   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8817       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8818     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8819                              MV_DIR_OPPOSITE(direction));
8820 }
8821
8822 int AmoebeNachbarNr(int ax, int ay)
8823 {
8824   int i;
8825   int element = Feld[ax][ay];
8826   int group_nr = 0;
8827   static int xy[4][2] =
8828   {
8829     { 0, -1 },
8830     { -1, 0 },
8831     { +1, 0 },
8832     { 0, +1 }
8833   };
8834
8835   for (i = 0; i < NUM_DIRECTIONS; i++)
8836   {
8837     int x = ax + xy[i][0];
8838     int y = ay + xy[i][1];
8839
8840     if (!IN_LEV_FIELD(x, y))
8841       continue;
8842
8843     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8844       group_nr = AmoebaNr[x][y];
8845   }
8846
8847   return group_nr;
8848 }
8849
8850 void AmoebenVereinigen(int ax, int ay)
8851 {
8852   int i, x, y, xx, yy;
8853   int new_group_nr = AmoebaNr[ax][ay];
8854   static int xy[4][2] =
8855   {
8856     { 0, -1 },
8857     { -1, 0 },
8858     { +1, 0 },
8859     { 0, +1 }
8860   };
8861
8862   if (new_group_nr == 0)
8863     return;
8864
8865   for (i = 0; i < NUM_DIRECTIONS; i++)
8866   {
8867     x = ax + xy[i][0];
8868     y = ay + xy[i][1];
8869
8870     if (!IN_LEV_FIELD(x, y))
8871       continue;
8872
8873     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8874          Feld[x][y] == EL_BD_AMOEBA ||
8875          Feld[x][y] == EL_AMOEBA_DEAD) &&
8876         AmoebaNr[x][y] != new_group_nr)
8877     {
8878       int old_group_nr = AmoebaNr[x][y];
8879
8880       if (old_group_nr == 0)
8881         return;
8882
8883       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8884       AmoebaCnt[old_group_nr] = 0;
8885       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8886       AmoebaCnt2[old_group_nr] = 0;
8887
8888       SCAN_PLAYFIELD(xx, yy)
8889       {
8890         if (AmoebaNr[xx][yy] == old_group_nr)
8891           AmoebaNr[xx][yy] = new_group_nr;
8892       }
8893     }
8894   }
8895 }
8896
8897 void AmoebeUmwandeln(int ax, int ay)
8898 {
8899   int i, x, y;
8900
8901   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8902   {
8903     int group_nr = AmoebaNr[ax][ay];
8904
8905 #ifdef DEBUG
8906     if (group_nr == 0)
8907     {
8908       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8909       printf("AmoebeUmwandeln(): This should never happen!\n");
8910       return;
8911     }
8912 #endif
8913
8914     SCAN_PLAYFIELD(x, y)
8915     {
8916       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8917       {
8918         AmoebaNr[x][y] = 0;
8919         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8920       }
8921     }
8922
8923     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8924                             SND_AMOEBA_TURNING_TO_GEM :
8925                             SND_AMOEBA_TURNING_TO_ROCK));
8926     Bang(ax, ay);
8927   }
8928   else
8929   {
8930     static int xy[4][2] =
8931     {
8932       { 0, -1 },
8933       { -1, 0 },
8934       { +1, 0 },
8935       { 0, +1 }
8936     };
8937
8938     for (i = 0; i < NUM_DIRECTIONS; i++)
8939     {
8940       x = ax + xy[i][0];
8941       y = ay + xy[i][1];
8942
8943       if (!IN_LEV_FIELD(x, y))
8944         continue;
8945
8946       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8947       {
8948         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8949                               SND_AMOEBA_TURNING_TO_GEM :
8950                               SND_AMOEBA_TURNING_TO_ROCK));
8951         Bang(x, y);
8952       }
8953     }
8954   }
8955 }
8956
8957 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8958 {
8959   int x, y;
8960   int group_nr = AmoebaNr[ax][ay];
8961   boolean done = FALSE;
8962
8963 #ifdef DEBUG
8964   if (group_nr == 0)
8965   {
8966     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8967     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8968     return;
8969   }
8970 #endif
8971
8972   SCAN_PLAYFIELD(x, y)
8973   {
8974     if (AmoebaNr[x][y] == group_nr &&
8975         (Feld[x][y] == EL_AMOEBA_DEAD ||
8976          Feld[x][y] == EL_BD_AMOEBA ||
8977          Feld[x][y] == EL_AMOEBA_GROWING))
8978     {
8979       AmoebaNr[x][y] = 0;
8980       Feld[x][y] = new_element;
8981       InitField(x, y, FALSE);
8982       DrawLevelField(x, y);
8983       done = TRUE;
8984     }
8985   }
8986
8987   if (done)
8988     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8989                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8990                             SND_BD_AMOEBA_TURNING_TO_GEM));
8991 }
8992
8993 void AmoebeWaechst(int x, int y)
8994 {
8995   static unsigned long sound_delay = 0;
8996   static unsigned long sound_delay_value = 0;
8997
8998   if (!MovDelay[x][y])          /* start new growing cycle */
8999   {
9000     MovDelay[x][y] = 7;
9001
9002     if (DelayReached(&sound_delay, sound_delay_value))
9003     {
9004       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9005       sound_delay_value = 30;
9006     }
9007   }
9008
9009   if (MovDelay[x][y])           /* wait some time before growing bigger */
9010   {
9011     MovDelay[x][y]--;
9012     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9013     {
9014       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9015                                            6 - MovDelay[x][y]);
9016
9017       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9018     }
9019
9020     if (!MovDelay[x][y])
9021     {
9022       Feld[x][y] = Store[x][y];
9023       Store[x][y] = 0;
9024       DrawLevelField(x, y);
9025     }
9026   }
9027 }
9028
9029 void AmoebaDisappearing(int x, int y)
9030 {
9031   static unsigned long sound_delay = 0;
9032   static unsigned long sound_delay_value = 0;
9033
9034   if (!MovDelay[x][y])          /* start new shrinking cycle */
9035   {
9036     MovDelay[x][y] = 7;
9037
9038     if (DelayReached(&sound_delay, sound_delay_value))
9039       sound_delay_value = 30;
9040   }
9041
9042   if (MovDelay[x][y])           /* wait some time before shrinking */
9043   {
9044     MovDelay[x][y]--;
9045     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9046     {
9047       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9048                                            6 - MovDelay[x][y]);
9049
9050       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9051     }
9052
9053     if (!MovDelay[x][y])
9054     {
9055       Feld[x][y] = EL_EMPTY;
9056       DrawLevelField(x, y);
9057
9058       /* don't let mole enter this field in this cycle;
9059          (give priority to objects falling to this field from above) */
9060       Stop[x][y] = TRUE;
9061     }
9062   }
9063 }
9064
9065 void AmoebeAbleger(int ax, int ay)
9066 {
9067   int i;
9068   int element = Feld[ax][ay];
9069   int graphic = el2img(element);
9070   int newax = ax, neway = ay;
9071   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9072   static int xy[4][2] =
9073   {
9074     { 0, -1 },
9075     { -1, 0 },
9076     { +1, 0 },
9077     { 0, +1 }
9078   };
9079
9080   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9081   {
9082     Feld[ax][ay] = EL_AMOEBA_DEAD;
9083     DrawLevelField(ax, ay);
9084     return;
9085   }
9086
9087   if (IS_ANIMATED(graphic))
9088     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9089
9090   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9091     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9092
9093   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9094   {
9095     MovDelay[ax][ay]--;
9096     if (MovDelay[ax][ay])
9097       return;
9098   }
9099
9100   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9101   {
9102     int start = RND(4);
9103     int x = ax + xy[start][0];
9104     int y = ay + xy[start][1];
9105
9106     if (!IN_LEV_FIELD(x, y))
9107       return;
9108
9109     if (IS_FREE(x, y) ||
9110         CAN_GROW_INTO(Feld[x][y]) ||
9111         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9112         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9113     {
9114       newax = x;
9115       neway = y;
9116     }
9117
9118     if (newax == ax && neway == ay)
9119       return;
9120   }
9121   else                          /* normal or "filled" (BD style) amoeba */
9122   {
9123     int start = RND(4);
9124     boolean waiting_for_player = FALSE;
9125
9126     for (i = 0; i < NUM_DIRECTIONS; i++)
9127     {
9128       int j = (start + i) % 4;
9129       int x = ax + xy[j][0];
9130       int y = ay + xy[j][1];
9131
9132       if (!IN_LEV_FIELD(x, y))
9133         continue;
9134
9135       if (IS_FREE(x, y) ||
9136           CAN_GROW_INTO(Feld[x][y]) ||
9137           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9138           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9139       {
9140         newax = x;
9141         neway = y;
9142         break;
9143       }
9144       else if (IS_PLAYER(x, y))
9145         waiting_for_player = TRUE;
9146     }
9147
9148     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9149     {
9150       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9151       {
9152         Feld[ax][ay] = EL_AMOEBA_DEAD;
9153         DrawLevelField(ax, ay);
9154         AmoebaCnt[AmoebaNr[ax][ay]]--;
9155
9156         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9157         {
9158           if (element == EL_AMOEBA_FULL)
9159             AmoebeUmwandeln(ax, ay);
9160           else if (element == EL_BD_AMOEBA)
9161             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9162         }
9163       }
9164       return;
9165     }
9166     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9167     {
9168       /* amoeba gets larger by growing in some direction */
9169
9170       int new_group_nr = AmoebaNr[ax][ay];
9171
9172 #ifdef DEBUG
9173   if (new_group_nr == 0)
9174   {
9175     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9176     printf("AmoebeAbleger(): This should never happen!\n");
9177     return;
9178   }
9179 #endif
9180
9181       AmoebaNr[newax][neway] = new_group_nr;
9182       AmoebaCnt[new_group_nr]++;
9183       AmoebaCnt2[new_group_nr]++;
9184
9185       /* if amoeba touches other amoeba(s) after growing, unify them */
9186       AmoebenVereinigen(newax, neway);
9187
9188       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9189       {
9190         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9191         return;
9192       }
9193     }
9194   }
9195
9196   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9197       (neway == lev_fieldy - 1 && newax != ax))
9198   {
9199     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9200     Store[newax][neway] = element;
9201   }
9202   else if (neway == ay || element == EL_EMC_DRIPPER)
9203   {
9204     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9205
9206     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9207   }
9208   else
9209   {
9210     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9211     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9212     Store[ax][ay] = EL_AMOEBA_DROP;
9213     ContinueMoving(ax, ay);
9214     return;
9215   }
9216
9217   DrawLevelField(newax, neway);
9218 }
9219
9220 void Life(int ax, int ay)
9221 {
9222   int x1, y1, x2, y2;
9223   int life_time = 40;
9224   int element = Feld[ax][ay];
9225   int graphic = el2img(element);
9226   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9227                          level.biomaze);
9228   boolean changed = FALSE;
9229
9230   if (IS_ANIMATED(graphic))
9231     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9232
9233   if (Stop[ax][ay])
9234     return;
9235
9236   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9237     MovDelay[ax][ay] = life_time;
9238
9239   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9240   {
9241     MovDelay[ax][ay]--;
9242     if (MovDelay[ax][ay])
9243       return;
9244   }
9245
9246   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9247   {
9248     int xx = ax+x1, yy = ay+y1;
9249     int nachbarn = 0;
9250
9251     if (!IN_LEV_FIELD(xx, yy))
9252       continue;
9253
9254     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9255     {
9256       int x = xx+x2, y = yy+y2;
9257
9258       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9259         continue;
9260
9261       if (((Feld[x][y] == element ||
9262             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9263            !Stop[x][y]) ||
9264           (IS_FREE(x, y) && Stop[x][y]))
9265         nachbarn++;
9266     }
9267
9268     if (xx == ax && yy == ay)           /* field in the middle */
9269     {
9270       if (nachbarn < life_parameter[0] ||
9271           nachbarn > life_parameter[1])
9272       {
9273         Feld[xx][yy] = EL_EMPTY;
9274         if (!Stop[xx][yy])
9275           DrawLevelField(xx, yy);
9276         Stop[xx][yy] = TRUE;
9277         changed = TRUE;
9278       }
9279     }
9280     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9281     {                                   /* free border field */
9282       if (nachbarn >= life_parameter[2] &&
9283           nachbarn <= life_parameter[3])
9284       {
9285         Feld[xx][yy] = element;
9286         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9287         if (!Stop[xx][yy])
9288           DrawLevelField(xx, yy);
9289         Stop[xx][yy] = TRUE;
9290         changed = TRUE;
9291       }
9292     }
9293   }
9294
9295   if (changed)
9296     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9297                    SND_GAME_OF_LIFE_GROWING);
9298 }
9299
9300 static void InitRobotWheel(int x, int y)
9301 {
9302   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9303 }
9304
9305 static void RunRobotWheel(int x, int y)
9306 {
9307   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9308 }
9309
9310 static void StopRobotWheel(int x, int y)
9311 {
9312   if (ZX == x && ZY == y)
9313   {
9314     ZX = ZY = -1;
9315
9316     game.robot_wheel_active = FALSE;
9317   }
9318 }
9319
9320 static void InitTimegateWheel(int x, int y)
9321 {
9322   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9323 }
9324
9325 static void RunTimegateWheel(int x, int y)
9326 {
9327   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9328 }
9329
9330 static void InitMagicBallDelay(int x, int y)
9331 {
9332 #if 1
9333   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9334 #else
9335   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9336 #endif
9337 }
9338
9339 static void ActivateMagicBall(int bx, int by)
9340 {
9341   int x, y;
9342
9343   if (level.ball_random)
9344   {
9345     int pos_border = RND(8);    /* select one of the eight border elements */
9346     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9347     int xx = pos_content % 3;
9348     int yy = pos_content / 3;
9349
9350     x = bx - 1 + xx;
9351     y = by - 1 + yy;
9352
9353     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9354       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9355   }
9356   else
9357   {
9358     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9359     {
9360       int xx = x - bx + 1;
9361       int yy = y - by + 1;
9362
9363       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9364         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9365     }
9366   }
9367
9368   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9369 }
9370
9371 void CheckExit(int x, int y)
9372 {
9373   if (local_player->gems_still_needed > 0 ||
9374       local_player->sokobanfields_still_needed > 0 ||
9375       local_player->lights_still_needed > 0)
9376   {
9377     int element = Feld[x][y];
9378     int graphic = el2img(element);
9379
9380     if (IS_ANIMATED(graphic))
9381       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9382
9383     return;
9384   }
9385
9386   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9387     return;
9388
9389   Feld[x][y] = EL_EXIT_OPENING;
9390
9391   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9392 }
9393
9394 void CheckExitEM(int x, int y)
9395 {
9396   if (local_player->gems_still_needed > 0 ||
9397       local_player->sokobanfields_still_needed > 0 ||
9398       local_player->lights_still_needed > 0)
9399   {
9400     int element = Feld[x][y];
9401     int graphic = el2img(element);
9402
9403     if (IS_ANIMATED(graphic))
9404       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9405
9406     return;
9407   }
9408
9409   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9410     return;
9411
9412   Feld[x][y] = EL_EM_EXIT_OPENING;
9413
9414   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9415 }
9416
9417 void CheckExitSteel(int x, int y)
9418 {
9419   if (local_player->gems_still_needed > 0 ||
9420       local_player->sokobanfields_still_needed > 0 ||
9421       local_player->lights_still_needed > 0)
9422   {
9423     int element = Feld[x][y];
9424     int graphic = el2img(element);
9425
9426     if (IS_ANIMATED(graphic))
9427       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9428
9429     return;
9430   }
9431
9432   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9433     return;
9434
9435   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9436
9437   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9438 }
9439
9440 void CheckExitSteelEM(int x, int y)
9441 {
9442   if (local_player->gems_still_needed > 0 ||
9443       local_player->sokobanfields_still_needed > 0 ||
9444       local_player->lights_still_needed > 0)
9445   {
9446     int element = Feld[x][y];
9447     int graphic = el2img(element);
9448
9449     if (IS_ANIMATED(graphic))
9450       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9451
9452     return;
9453   }
9454
9455   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9456     return;
9457
9458   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9459
9460   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9461 }
9462
9463 void CheckExitSP(int x, int y)
9464 {
9465   if (local_player->gems_still_needed > 0)
9466   {
9467     int element = Feld[x][y];
9468     int graphic = el2img(element);
9469
9470     if (IS_ANIMATED(graphic))
9471       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9472
9473     return;
9474   }
9475
9476   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9477     return;
9478
9479   Feld[x][y] = EL_SP_EXIT_OPENING;
9480
9481   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9482 }
9483
9484 static void CloseAllOpenTimegates()
9485 {
9486   int x, y;
9487
9488   SCAN_PLAYFIELD(x, y)
9489   {
9490     int element = Feld[x][y];
9491
9492     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9493     {
9494       Feld[x][y] = EL_TIMEGATE_CLOSING;
9495
9496       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9497     }
9498   }
9499 }
9500
9501 void DrawTwinkleOnField(int x, int y)
9502 {
9503   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9504     return;
9505
9506   if (Feld[x][y] == EL_BD_DIAMOND)
9507     return;
9508
9509   if (MovDelay[x][y] == 0)      /* next animation frame */
9510     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9511
9512   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9513   {
9514     MovDelay[x][y]--;
9515
9516     DrawLevelElementAnimation(x, y, Feld[x][y]);
9517
9518     if (MovDelay[x][y] != 0)
9519     {
9520       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9521                                            10 - MovDelay[x][y]);
9522
9523       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9524     }
9525   }
9526 }
9527
9528 void MauerWaechst(int x, int y)
9529 {
9530   int delay = 6;
9531
9532   if (!MovDelay[x][y])          /* next animation frame */
9533     MovDelay[x][y] = 3 * delay;
9534
9535   if (MovDelay[x][y])           /* wait some time before next frame */
9536   {
9537     MovDelay[x][y]--;
9538
9539     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9540     {
9541       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9542       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9543
9544       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9545     }
9546
9547     if (!MovDelay[x][y])
9548     {
9549       if (MovDir[x][y] == MV_LEFT)
9550       {
9551         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9552           DrawLevelField(x - 1, y);
9553       }
9554       else if (MovDir[x][y] == MV_RIGHT)
9555       {
9556         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9557           DrawLevelField(x + 1, y);
9558       }
9559       else if (MovDir[x][y] == MV_UP)
9560       {
9561         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9562           DrawLevelField(x, y - 1);
9563       }
9564       else
9565       {
9566         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9567           DrawLevelField(x, y + 1);
9568       }
9569
9570       Feld[x][y] = Store[x][y];
9571       Store[x][y] = 0;
9572       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9573       DrawLevelField(x, y);
9574     }
9575   }
9576 }
9577
9578 void MauerAbleger(int ax, int ay)
9579 {
9580   int element = Feld[ax][ay];
9581   int graphic = el2img(element);
9582   boolean oben_frei = FALSE, unten_frei = FALSE;
9583   boolean links_frei = FALSE, rechts_frei = FALSE;
9584   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9585   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9586   boolean new_wall = FALSE;
9587
9588   if (IS_ANIMATED(graphic))
9589     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9590
9591   if (!MovDelay[ax][ay])        /* start building new wall */
9592     MovDelay[ax][ay] = 6;
9593
9594   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9595   {
9596     MovDelay[ax][ay]--;
9597     if (MovDelay[ax][ay])
9598       return;
9599   }
9600
9601   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9602     oben_frei = TRUE;
9603   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9604     unten_frei = TRUE;
9605   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9606     links_frei = TRUE;
9607   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9608     rechts_frei = TRUE;
9609
9610   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9611       element == EL_EXPANDABLE_WALL_ANY)
9612   {
9613     if (oben_frei)
9614     {
9615       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9616       Store[ax][ay-1] = element;
9617       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9618       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9619         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9620                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9621       new_wall = TRUE;
9622     }
9623     if (unten_frei)
9624     {
9625       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9626       Store[ax][ay+1] = element;
9627       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9628       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9629         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9630                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9631       new_wall = TRUE;
9632     }
9633   }
9634
9635   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9636       element == EL_EXPANDABLE_WALL_ANY ||
9637       element == EL_EXPANDABLE_WALL ||
9638       element == EL_BD_EXPANDABLE_WALL)
9639   {
9640     if (links_frei)
9641     {
9642       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9643       Store[ax-1][ay] = element;
9644       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9645       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9646         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9647                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9648       new_wall = TRUE;
9649     }
9650
9651     if (rechts_frei)
9652     {
9653       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9654       Store[ax+1][ay] = element;
9655       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9656       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9657         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9658                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9659       new_wall = TRUE;
9660     }
9661   }
9662
9663   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9664     DrawLevelField(ax, ay);
9665
9666   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9667     oben_massiv = TRUE;
9668   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9669     unten_massiv = TRUE;
9670   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9671     links_massiv = TRUE;
9672   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9673     rechts_massiv = TRUE;
9674
9675   if (((oben_massiv && unten_massiv) ||
9676        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9677        element == EL_EXPANDABLE_WALL) &&
9678       ((links_massiv && rechts_massiv) ||
9679        element == EL_EXPANDABLE_WALL_VERTICAL))
9680     Feld[ax][ay] = EL_WALL;
9681
9682   if (new_wall)
9683     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9684 }
9685
9686 void MauerAblegerStahl(int ax, int ay)
9687 {
9688   int element = Feld[ax][ay];
9689   int graphic = el2img(element);
9690   boolean oben_frei = FALSE, unten_frei = FALSE;
9691   boolean links_frei = FALSE, rechts_frei = FALSE;
9692   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9693   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9694   boolean new_wall = FALSE;
9695
9696   if (IS_ANIMATED(graphic))
9697     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9698
9699   if (!MovDelay[ax][ay])        /* start building new wall */
9700     MovDelay[ax][ay] = 6;
9701
9702   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9703   {
9704     MovDelay[ax][ay]--;
9705     if (MovDelay[ax][ay])
9706       return;
9707   }
9708
9709   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9710     oben_frei = TRUE;
9711   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9712     unten_frei = TRUE;
9713   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9714     links_frei = TRUE;
9715   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9716     rechts_frei = TRUE;
9717
9718   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9719       element == EL_EXPANDABLE_STEELWALL_ANY)
9720   {
9721     if (oben_frei)
9722     {
9723       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9724       Store[ax][ay-1] = element;
9725       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9726       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9727         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9728                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9729       new_wall = TRUE;
9730     }
9731     if (unten_frei)
9732     {
9733       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9734       Store[ax][ay+1] = element;
9735       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9736       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9737         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9738                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9739       new_wall = TRUE;
9740     }
9741   }
9742
9743   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9744       element == EL_EXPANDABLE_STEELWALL_ANY)
9745   {
9746     if (links_frei)
9747     {
9748       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9749       Store[ax-1][ay] = element;
9750       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9751       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9752         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9753                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9754       new_wall = TRUE;
9755     }
9756
9757     if (rechts_frei)
9758     {
9759       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9760       Store[ax+1][ay] = element;
9761       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9762       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9763         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9764                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9765       new_wall = TRUE;
9766     }
9767   }
9768
9769   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9770     oben_massiv = TRUE;
9771   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9772     unten_massiv = TRUE;
9773   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9774     links_massiv = TRUE;
9775   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9776     rechts_massiv = TRUE;
9777
9778   if (((oben_massiv && unten_massiv) ||
9779        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9780       ((links_massiv && rechts_massiv) ||
9781        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9782     Feld[ax][ay] = EL_WALL;
9783
9784   if (new_wall)
9785     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9786 }
9787
9788 void CheckForDragon(int x, int y)
9789 {
9790   int i, j;
9791   boolean dragon_found = FALSE;
9792   static int xy[4][2] =
9793   {
9794     { 0, -1 },
9795     { -1, 0 },
9796     { +1, 0 },
9797     { 0, +1 }
9798   };
9799
9800   for (i = 0; i < NUM_DIRECTIONS; i++)
9801   {
9802     for (j = 0; j < 4; j++)
9803     {
9804       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9805
9806       if (IN_LEV_FIELD(xx, yy) &&
9807           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9808       {
9809         if (Feld[xx][yy] == EL_DRAGON)
9810           dragon_found = TRUE;
9811       }
9812       else
9813         break;
9814     }
9815   }
9816
9817   if (!dragon_found)
9818   {
9819     for (i = 0; i < NUM_DIRECTIONS; i++)
9820     {
9821       for (j = 0; j < 3; j++)
9822       {
9823         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9824   
9825         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9826         {
9827           Feld[xx][yy] = EL_EMPTY;
9828           DrawLevelField(xx, yy);
9829         }
9830         else
9831           break;
9832       }
9833     }
9834   }
9835 }
9836
9837 static void InitBuggyBase(int x, int y)
9838 {
9839   int element = Feld[x][y];
9840   int activating_delay = FRAMES_PER_SECOND / 4;
9841
9842   ChangeDelay[x][y] =
9843     (element == EL_SP_BUGGY_BASE ?
9844      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9845      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9846      activating_delay :
9847      element == EL_SP_BUGGY_BASE_ACTIVE ?
9848      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9849 }
9850
9851 static void WarnBuggyBase(int x, int y)
9852 {
9853   int i;
9854   static int xy[4][2] =
9855   {
9856     { 0, -1 },
9857     { -1, 0 },
9858     { +1, 0 },
9859     { 0, +1 }
9860   };
9861
9862   for (i = 0; i < NUM_DIRECTIONS; i++)
9863   {
9864     int xx = x + xy[i][0];
9865     int yy = y + xy[i][1];
9866
9867     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9868     {
9869       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9870
9871       break;
9872     }
9873   }
9874 }
9875
9876 static void InitTrap(int x, int y)
9877 {
9878   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9879 }
9880
9881 static void ActivateTrap(int x, int y)
9882 {
9883   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9884 }
9885
9886 static void ChangeActiveTrap(int x, int y)
9887 {
9888   int graphic = IMG_TRAP_ACTIVE;
9889
9890   /* if new animation frame was drawn, correct crumbled sand border */
9891   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9892     DrawLevelFieldCrumbledSand(x, y);
9893 }
9894
9895 static int getSpecialActionElement(int element, int number, int base_element)
9896 {
9897   return (element != EL_EMPTY ? element :
9898           number != -1 ? base_element + number - 1 :
9899           EL_EMPTY);
9900 }
9901
9902 static int getModifiedActionNumber(int value_old, int operator, int operand,
9903                                    int value_min, int value_max)
9904 {
9905   int value_new = (operator == CA_MODE_SET      ? operand :
9906                    operator == CA_MODE_ADD      ? value_old + operand :
9907                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9908                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9909                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9910                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9911                    value_old);
9912
9913   return (value_new < value_min ? value_min :
9914           value_new > value_max ? value_max :
9915           value_new);
9916 }
9917
9918 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9919 {
9920   struct ElementInfo *ei = &element_info[element];
9921   struct ElementChangeInfo *change = &ei->change_page[page];
9922   int target_element = change->target_element;
9923   int action_type = change->action_type;
9924   int action_mode = change->action_mode;
9925   int action_arg = change->action_arg;
9926   int i;
9927
9928   if (!change->has_action)
9929     return;
9930
9931   /* ---------- determine action paramater values -------------------------- */
9932
9933   int level_time_value =
9934     (level.time > 0 ? TimeLeft :
9935      TimePlayed);
9936
9937   int action_arg_element =
9938     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9939      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9940      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9941      EL_EMPTY);
9942
9943   int action_arg_direction =
9944     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9945      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9946      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9947      change->actual_trigger_side :
9948      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9949      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9950      MV_NONE);
9951
9952   int action_arg_number_min =
9953     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9954      CA_ARG_MIN);
9955
9956   int action_arg_number_max =
9957     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9958      action_type == CA_SET_LEVEL_GEMS ? 999 :
9959      action_type == CA_SET_LEVEL_TIME ? 9999 :
9960      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9961      action_type == CA_SET_CE_VALUE ? 9999 :
9962      action_type == CA_SET_CE_SCORE ? 9999 :
9963      CA_ARG_MAX);
9964
9965   int action_arg_number_reset =
9966     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9967      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9968      action_type == CA_SET_LEVEL_TIME ? level.time :
9969      action_type == CA_SET_LEVEL_SCORE ? 0 :
9970 #if USE_NEW_CUSTOM_VALUE
9971      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9972 #else
9973      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9974 #endif
9975      action_type == CA_SET_CE_SCORE ? 0 :
9976      0);
9977
9978   int action_arg_number =
9979     (action_arg <= CA_ARG_MAX ? action_arg :
9980      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9981      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9982      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9983      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9984      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9985      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9986 #if USE_NEW_CUSTOM_VALUE
9987      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9988 #else
9989      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9990 #endif
9991      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9992      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9993      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9994      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9995      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9996      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9997      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9998      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9999      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10000      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10001      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10002      -1);
10003
10004   int action_arg_number_old =
10005     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10006      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10007      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10008      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10009      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10010      0);
10011
10012   int action_arg_number_new =
10013     getModifiedActionNumber(action_arg_number_old,
10014                             action_mode, action_arg_number,
10015                             action_arg_number_min, action_arg_number_max);
10016
10017 #if 1
10018   int trigger_player_bits = change->actual_trigger_player_bits;
10019 #else
10020   int trigger_player_bits =
10021     (change->actual_trigger_player >= EL_PLAYER_1 &&
10022      change->actual_trigger_player <= EL_PLAYER_4 ?
10023      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10024      PLAYER_BITS_ANY);
10025 #endif
10026
10027   int action_arg_player_bits =
10028     (action_arg >= CA_ARG_PLAYER_1 &&
10029      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10030      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10031      PLAYER_BITS_ANY);
10032
10033   /* ---------- execute action  -------------------------------------------- */
10034
10035   switch (action_type)
10036   {
10037     case CA_NO_ACTION:
10038     {
10039       return;
10040     }
10041
10042     /* ---------- level actions  ------------------------------------------- */
10043
10044     case CA_RESTART_LEVEL:
10045     {
10046       game.restart_level = TRUE;
10047
10048       break;
10049     }
10050
10051     case CA_SHOW_ENVELOPE:
10052     {
10053       int element = getSpecialActionElement(action_arg_element,
10054                                             action_arg_number, EL_ENVELOPE_1);
10055
10056       if (IS_ENVELOPE(element))
10057         local_player->show_envelope = element;
10058
10059       break;
10060     }
10061
10062     case CA_SET_LEVEL_TIME:
10063     {
10064       if (level.time > 0)       /* only modify limited time value */
10065       {
10066         TimeLeft = action_arg_number_new;
10067
10068 #if 1
10069         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10070
10071         DisplayGameControlValues();
10072 #else
10073         DrawGameValue_Time(TimeLeft);
10074 #endif
10075
10076         if (!TimeLeft && setup.time_limit)
10077           for (i = 0; i < MAX_PLAYERS; i++)
10078             KillPlayer(&stored_player[i]);
10079       }
10080
10081       break;
10082     }
10083
10084     case CA_SET_LEVEL_SCORE:
10085     {
10086       local_player->score = action_arg_number_new;
10087
10088 #if 1
10089       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10090
10091       DisplayGameControlValues();
10092 #else
10093       DrawGameValue_Score(local_player->score);
10094 #endif
10095
10096       break;
10097     }
10098
10099     case CA_SET_LEVEL_GEMS:
10100     {
10101       local_player->gems_still_needed = action_arg_number_new;
10102
10103 #if 1
10104       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10105
10106       DisplayGameControlValues();
10107 #else
10108       DrawGameValue_Emeralds(local_player->gems_still_needed);
10109 #endif
10110
10111       break;
10112     }
10113
10114 #if !USE_PLAYER_GRAVITY
10115     case CA_SET_LEVEL_GRAVITY:
10116     {
10117       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10118                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10119                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10120                       game.gravity);
10121       break;
10122     }
10123 #endif
10124
10125     case CA_SET_LEVEL_WIND:
10126     {
10127       game.wind_direction = action_arg_direction;
10128
10129       break;
10130     }
10131
10132     /* ---------- player actions  ------------------------------------------ */
10133
10134     case CA_MOVE_PLAYER:
10135     {
10136       /* automatically move to the next field in specified direction */
10137       for (i = 0; i < MAX_PLAYERS; i++)
10138         if (trigger_player_bits & (1 << i))
10139           stored_player[i].programmed_action = action_arg_direction;
10140
10141       break;
10142     }
10143
10144     case CA_EXIT_PLAYER:
10145     {
10146       for (i = 0; i < MAX_PLAYERS; i++)
10147         if (action_arg_player_bits & (1 << i))
10148           PlayerWins(&stored_player[i]);
10149
10150       break;
10151     }
10152
10153     case CA_KILL_PLAYER:
10154     {
10155       for (i = 0; i < MAX_PLAYERS; i++)
10156         if (action_arg_player_bits & (1 << i))
10157           KillPlayer(&stored_player[i]);
10158
10159       break;
10160     }
10161
10162     case CA_SET_PLAYER_KEYS:
10163     {
10164       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10165       int element = getSpecialActionElement(action_arg_element,
10166                                             action_arg_number, EL_KEY_1);
10167
10168       if (IS_KEY(element))
10169       {
10170         for (i = 0; i < MAX_PLAYERS; i++)
10171         {
10172           if (trigger_player_bits & (1 << i))
10173           {
10174             stored_player[i].key[KEY_NR(element)] = key_state;
10175
10176             DrawGameDoorValues();
10177           }
10178         }
10179       }
10180
10181       break;
10182     }
10183
10184     case CA_SET_PLAYER_SPEED:
10185     {
10186       for (i = 0; i < MAX_PLAYERS; i++)
10187       {
10188         if (trigger_player_bits & (1 << i))
10189         {
10190           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10191
10192           if (action_arg == CA_ARG_SPEED_FASTER &&
10193               stored_player[i].cannot_move)
10194           {
10195             action_arg_number = STEPSIZE_VERY_SLOW;
10196           }
10197           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10198                    action_arg == CA_ARG_SPEED_FASTER)
10199           {
10200             action_arg_number = 2;
10201             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10202                            CA_MODE_MULTIPLY);
10203           }
10204           else if (action_arg == CA_ARG_NUMBER_RESET)
10205           {
10206             action_arg_number = level.initial_player_stepsize[i];
10207           }
10208
10209           move_stepsize =
10210             getModifiedActionNumber(move_stepsize,
10211                                     action_mode,
10212                                     action_arg_number,
10213                                     action_arg_number_min,
10214                                     action_arg_number_max);
10215
10216           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10217         }
10218       }
10219
10220       break;
10221     }
10222
10223     case CA_SET_PLAYER_SHIELD:
10224     {
10225       for (i = 0; i < MAX_PLAYERS; i++)
10226       {
10227         if (trigger_player_bits & (1 << i))
10228         {
10229           if (action_arg == CA_ARG_SHIELD_OFF)
10230           {
10231             stored_player[i].shield_normal_time_left = 0;
10232             stored_player[i].shield_deadly_time_left = 0;
10233           }
10234           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10235           {
10236             stored_player[i].shield_normal_time_left = 999999;
10237           }
10238           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10239           {
10240             stored_player[i].shield_normal_time_left = 999999;
10241             stored_player[i].shield_deadly_time_left = 999999;
10242           }
10243         }
10244       }
10245
10246       break;
10247     }
10248
10249 #if USE_PLAYER_GRAVITY
10250     case CA_SET_PLAYER_GRAVITY:
10251     {
10252       for (i = 0; i < MAX_PLAYERS; i++)
10253       {
10254         if (trigger_player_bits & (1 << i))
10255         {
10256           stored_player[i].gravity =
10257             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10258              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10259              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10260              stored_player[i].gravity);
10261         }
10262       }
10263
10264       break;
10265     }
10266 #endif
10267
10268     case CA_SET_PLAYER_ARTWORK:
10269     {
10270       for (i = 0; i < MAX_PLAYERS; i++)
10271       {
10272         if (trigger_player_bits & (1 << i))
10273         {
10274           int artwork_element = action_arg_element;
10275
10276           if (action_arg == CA_ARG_ELEMENT_RESET)
10277             artwork_element =
10278               (level.use_artwork_element[i] ? level.artwork_element[i] :
10279                stored_player[i].element_nr);
10280
10281 #if USE_GFX_RESET_PLAYER_ARTWORK
10282           if (stored_player[i].artwork_element != artwork_element)
10283             stored_player[i].Frame = 0;
10284 #endif
10285
10286           stored_player[i].artwork_element = artwork_element;
10287
10288           SetPlayerWaiting(&stored_player[i], FALSE);
10289
10290           /* set number of special actions for bored and sleeping animation */
10291           stored_player[i].num_special_action_bored =
10292             get_num_special_action(artwork_element,
10293                                    ACTION_BORING_1, ACTION_BORING_LAST);
10294           stored_player[i].num_special_action_sleeping =
10295             get_num_special_action(artwork_element,
10296                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10297         }
10298       }
10299
10300       break;
10301     }
10302
10303     /* ---------- CE actions  ---------------------------------------------- */
10304
10305     case CA_SET_CE_VALUE:
10306     {
10307 #if USE_NEW_CUSTOM_VALUE
10308       int last_ce_value = CustomValue[x][y];
10309
10310       CustomValue[x][y] = action_arg_number_new;
10311
10312       if (CustomValue[x][y] != last_ce_value)
10313       {
10314         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10315         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10316
10317         if (CustomValue[x][y] == 0)
10318         {
10319           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10320           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10321         }
10322       }
10323 #endif
10324
10325       break;
10326     }
10327
10328     case CA_SET_CE_SCORE:
10329     {
10330 #if USE_NEW_CUSTOM_VALUE
10331       int last_ce_score = ei->collect_score;
10332
10333       ei->collect_score = action_arg_number_new;
10334
10335       if (ei->collect_score != last_ce_score)
10336       {
10337         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10338         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10339
10340         if (ei->collect_score == 0)
10341         {
10342           int xx, yy;
10343
10344           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10345           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10346
10347           /*
10348             This is a very special case that seems to be a mixture between
10349             CheckElementChange() and CheckTriggeredElementChange(): while
10350             the first one only affects single elements that are triggered
10351             directly, the second one affects multiple elements in the playfield
10352             that are triggered indirectly by another element. This is a third
10353             case: Changing the CE score always affects multiple identical CEs,
10354             so every affected CE must be checked, not only the single CE for
10355             which the CE score was changed in the first place (as every instance
10356             of that CE shares the same CE score, and therefore also can change)!
10357           */
10358           SCAN_PLAYFIELD(xx, yy)
10359           {
10360             if (Feld[xx][yy] == element)
10361               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10362                                  CE_SCORE_GETS_ZERO);
10363           }
10364         }
10365       }
10366 #endif
10367
10368       break;
10369     }
10370
10371     /* ---------- engine actions  ------------------------------------------ */
10372
10373     case CA_SET_ENGINE_SCAN_MODE:
10374     {
10375       InitPlayfieldScanMode(action_arg);
10376
10377       break;
10378     }
10379
10380     default:
10381       break;
10382   }
10383 }
10384
10385 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10386 {
10387   int old_element = Feld[x][y];
10388   int new_element = GetElementFromGroupElement(element);
10389   int previous_move_direction = MovDir[x][y];
10390 #if USE_NEW_CUSTOM_VALUE
10391   int last_ce_value = CustomValue[x][y];
10392 #endif
10393   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10394   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10395   boolean add_player_onto_element = (new_element_is_player &&
10396 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10397                                      /* this breaks SnakeBite when a snake is
10398                                         halfway through a door that closes */
10399                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10400                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10401 #endif
10402                                      IS_WALKABLE(old_element));
10403
10404 #if 0
10405   /* check if element under the player changes from accessible to unaccessible
10406      (needed for special case of dropping element which then changes) */
10407   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10408       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10409   {
10410     Bang(x, y);
10411
10412     return;
10413   }
10414 #endif
10415
10416   if (!add_player_onto_element)
10417   {
10418     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10419       RemoveMovingField(x, y);
10420     else
10421       RemoveField(x, y);
10422
10423     Feld[x][y] = new_element;
10424
10425 #if !USE_GFX_RESET_GFX_ANIMATION
10426     ResetGfxAnimation(x, y);
10427     ResetRandomAnimationValue(x, y);
10428 #endif
10429
10430     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10431       MovDir[x][y] = previous_move_direction;
10432
10433 #if USE_NEW_CUSTOM_VALUE
10434     if (element_info[new_element].use_last_ce_value)
10435       CustomValue[x][y] = last_ce_value;
10436 #endif
10437
10438     InitField_WithBug1(x, y, FALSE);
10439
10440     new_element = Feld[x][y];   /* element may have changed */
10441
10442 #if USE_GFX_RESET_GFX_ANIMATION
10443     ResetGfxAnimation(x, y);
10444     ResetRandomAnimationValue(x, y);
10445 #endif
10446
10447     DrawLevelField(x, y);
10448
10449     if (GFX_CRUMBLED(new_element))
10450       DrawLevelFieldCrumbledSandNeighbours(x, y);
10451   }
10452
10453 #if 1
10454   /* check if element under the player changes from accessible to unaccessible
10455      (needed for special case of dropping element which then changes) */
10456   /* (must be checked after creating new element for walkable group elements) */
10457 #if USE_FIX_KILLED_BY_NON_WALKABLE
10458   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10459       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10460   {
10461     Bang(x, y);
10462
10463     return;
10464   }
10465 #else
10466   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10467       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10468   {
10469     Bang(x, y);
10470
10471     return;
10472   }
10473 #endif
10474 #endif
10475
10476   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10477   if (new_element_is_player)
10478     RelocatePlayer(x, y, new_element);
10479
10480   if (is_change)
10481     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10482
10483   TestIfBadThingTouchesPlayer(x, y);
10484   TestIfPlayerTouchesCustomElement(x, y);
10485   TestIfElementTouchesCustomElement(x, y);
10486 }
10487
10488 static void CreateField(int x, int y, int element)
10489 {
10490   CreateFieldExt(x, y, element, FALSE);
10491 }
10492
10493 static void CreateElementFromChange(int x, int y, int element)
10494 {
10495   element = GET_VALID_RUNTIME_ELEMENT(element);
10496
10497 #if USE_STOP_CHANGED_ELEMENTS
10498   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10499   {
10500     int old_element = Feld[x][y];
10501
10502     /* prevent changed element from moving in same engine frame
10503        unless both old and new element can either fall or move */
10504     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10505         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10506       Stop[x][y] = TRUE;
10507   }
10508 #endif
10509
10510   CreateFieldExt(x, y, element, TRUE);
10511 }
10512
10513 static boolean ChangeElement(int x, int y, int element, int page)
10514 {
10515   struct ElementInfo *ei = &element_info[element];
10516   struct ElementChangeInfo *change = &ei->change_page[page];
10517   int ce_value = CustomValue[x][y];
10518   int ce_score = ei->collect_score;
10519   int target_element;
10520   int old_element = Feld[x][y];
10521
10522   /* always use default change event to prevent running into a loop */
10523   if (ChangeEvent[x][y] == -1)
10524     ChangeEvent[x][y] = CE_DELAY;
10525
10526   if (ChangeEvent[x][y] == CE_DELAY)
10527   {
10528     /* reset actual trigger element, trigger player and action element */
10529     change->actual_trigger_element = EL_EMPTY;
10530     change->actual_trigger_player = EL_PLAYER_1;
10531     change->actual_trigger_player_bits = CH_PLAYER_1;
10532     change->actual_trigger_side = CH_SIDE_NONE;
10533     change->actual_trigger_ce_value = 0;
10534     change->actual_trigger_ce_score = 0;
10535   }
10536
10537   /* do not change elements more than a specified maximum number of changes */
10538   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10539     return FALSE;
10540
10541   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10542
10543   if (change->explode)
10544   {
10545     Bang(x, y);
10546
10547     return TRUE;
10548   }
10549
10550   if (change->use_target_content)
10551   {
10552     boolean complete_replace = TRUE;
10553     boolean can_replace[3][3];
10554     int xx, yy;
10555
10556     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10557     {
10558       boolean is_empty;
10559       boolean is_walkable;
10560       boolean is_diggable;
10561       boolean is_collectible;
10562       boolean is_removable;
10563       boolean is_destructible;
10564       int ex = x + xx - 1;
10565       int ey = y + yy - 1;
10566       int content_element = change->target_content.e[xx][yy];
10567       int e;
10568
10569       can_replace[xx][yy] = TRUE;
10570
10571       if (ex == x && ey == y)   /* do not check changing element itself */
10572         continue;
10573
10574       if (content_element == EL_EMPTY_SPACE)
10575       {
10576         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10577
10578         continue;
10579       }
10580
10581       if (!IN_LEV_FIELD(ex, ey))
10582       {
10583         can_replace[xx][yy] = FALSE;
10584         complete_replace = FALSE;
10585
10586         continue;
10587       }
10588
10589       e = Feld[ex][ey];
10590
10591       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10592         e = MovingOrBlocked2Element(ex, ey);
10593
10594       is_empty = (IS_FREE(ex, ey) ||
10595                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10596
10597       is_walkable     = (is_empty || IS_WALKABLE(e));
10598       is_diggable     = (is_empty || IS_DIGGABLE(e));
10599       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10600       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10601       is_removable    = (is_diggable || is_collectible);
10602
10603       can_replace[xx][yy] =
10604         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10605           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10606           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10607           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10608           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10609           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10610          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10611
10612       if (!can_replace[xx][yy])
10613         complete_replace = FALSE;
10614     }
10615
10616     if (!change->only_if_complete || complete_replace)
10617     {
10618       boolean something_has_changed = FALSE;
10619
10620       if (change->only_if_complete && change->use_random_replace &&
10621           RND(100) < change->random_percentage)
10622         return FALSE;
10623
10624       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10625       {
10626         int ex = x + xx - 1;
10627         int ey = y + yy - 1;
10628         int content_element;
10629
10630         if (can_replace[xx][yy] && (!change->use_random_replace ||
10631                                     RND(100) < change->random_percentage))
10632         {
10633           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10634             RemoveMovingField(ex, ey);
10635
10636           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10637
10638           content_element = change->target_content.e[xx][yy];
10639           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10640                                               ce_value, ce_score);
10641
10642           CreateElementFromChange(ex, ey, target_element);
10643
10644           something_has_changed = TRUE;
10645
10646           /* for symmetry reasons, freeze newly created border elements */
10647           if (ex != x || ey != y)
10648             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10649         }
10650       }
10651
10652       if (something_has_changed)
10653       {
10654         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10655         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10656       }
10657     }
10658   }
10659   else
10660   {
10661     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10662                                         ce_value, ce_score);
10663
10664     if (element == EL_DIAGONAL_GROWING ||
10665         element == EL_DIAGONAL_SHRINKING)
10666     {
10667       target_element = Store[x][y];
10668
10669       Store[x][y] = EL_EMPTY;
10670     }
10671
10672     CreateElementFromChange(x, y, target_element);
10673
10674     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10675     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10676   }
10677
10678   /* this uses direct change before indirect change */
10679   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10680
10681   return TRUE;
10682 }
10683
10684 #if USE_NEW_DELAYED_ACTION
10685
10686 static void HandleElementChange(int x, int y, int page)
10687 {
10688   int element = MovingOrBlocked2Element(x, y);
10689   struct ElementInfo *ei = &element_info[element];
10690   struct ElementChangeInfo *change = &ei->change_page[page];
10691
10692 #ifdef DEBUG
10693   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10694       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10695   {
10696     printf("\n\n");
10697     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10698            x, y, element, element_info[element].token_name);
10699     printf("HandleElementChange(): This should never happen!\n");
10700     printf("\n\n");
10701   }
10702 #endif
10703
10704   /* this can happen with classic bombs on walkable, changing elements */
10705   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10706   {
10707 #if 0
10708     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10709       ChangeDelay[x][y] = 0;
10710 #endif
10711
10712     return;
10713   }
10714
10715   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10716   {
10717     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10718
10719     if (change->can_change)
10720     {
10721 #if 1
10722       /* !!! not clear why graphic animation should be reset at all here !!! */
10723       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10724 #if USE_GFX_RESET_WHEN_NOT_MOVING
10725       /* when a custom element is about to change (for example by change delay),
10726          do not reset graphic animation when the custom element is moving */
10727       if (!IS_MOVING(x, y))
10728 #endif
10729       {
10730         ResetGfxAnimation(x, y);
10731         ResetRandomAnimationValue(x, y);
10732       }
10733 #endif
10734
10735       if (change->pre_change_function)
10736         change->pre_change_function(x, y);
10737     }
10738   }
10739
10740   ChangeDelay[x][y]--;
10741
10742   if (ChangeDelay[x][y] != 0)           /* continue element change */
10743   {
10744     if (change->can_change)
10745     {
10746       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10747
10748       if (IS_ANIMATED(graphic))
10749         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10750
10751       if (change->change_function)
10752         change->change_function(x, y);
10753     }
10754   }
10755   else                                  /* finish element change */
10756   {
10757     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10758     {
10759       page = ChangePage[x][y];
10760       ChangePage[x][y] = -1;
10761
10762       change = &ei->change_page[page];
10763     }
10764
10765     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10766     {
10767       ChangeDelay[x][y] = 1;            /* try change after next move step */
10768       ChangePage[x][y] = page;          /* remember page to use for change */
10769
10770       return;
10771     }
10772
10773     if (change->can_change)
10774     {
10775       if (ChangeElement(x, y, element, page))
10776       {
10777         if (change->post_change_function)
10778           change->post_change_function(x, y);
10779       }
10780     }
10781
10782     if (change->has_action)
10783       ExecuteCustomElementAction(x, y, element, page);
10784   }
10785 }
10786
10787 #else
10788
10789 static void HandleElementChange(int x, int y, int page)
10790 {
10791   int element = MovingOrBlocked2Element(x, y);
10792   struct ElementInfo *ei = &element_info[element];
10793   struct ElementChangeInfo *change = &ei->change_page[page];
10794
10795 #ifdef DEBUG
10796   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10797   {
10798     printf("\n\n");
10799     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10800            x, y, element, element_info[element].token_name);
10801     printf("HandleElementChange(): This should never happen!\n");
10802     printf("\n\n");
10803   }
10804 #endif
10805
10806   /* this can happen with classic bombs on walkable, changing elements */
10807   if (!CAN_CHANGE(element))
10808   {
10809 #if 0
10810     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10811       ChangeDelay[x][y] = 0;
10812 #endif
10813
10814     return;
10815   }
10816
10817   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10818   {
10819     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10820
10821     ResetGfxAnimation(x, y);
10822     ResetRandomAnimationValue(x, y);
10823
10824     if (change->pre_change_function)
10825       change->pre_change_function(x, y);
10826   }
10827
10828   ChangeDelay[x][y]--;
10829
10830   if (ChangeDelay[x][y] != 0)           /* continue element change */
10831   {
10832     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10833
10834     if (IS_ANIMATED(graphic))
10835       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10836
10837     if (change->change_function)
10838       change->change_function(x, y);
10839   }
10840   else                                  /* finish element change */
10841   {
10842     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10843     {
10844       page = ChangePage[x][y];
10845       ChangePage[x][y] = -1;
10846
10847       change = &ei->change_page[page];
10848     }
10849
10850     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10851     {
10852       ChangeDelay[x][y] = 1;            /* try change after next move step */
10853       ChangePage[x][y] = page;          /* remember page to use for change */
10854
10855       return;
10856     }
10857
10858     if (ChangeElement(x, y, element, page))
10859     {
10860       if (change->post_change_function)
10861         change->post_change_function(x, y);
10862     }
10863   }
10864 }
10865
10866 #endif
10867
10868 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10869                                               int trigger_element,
10870                                               int trigger_event,
10871                                               int trigger_player,
10872                                               int trigger_side,
10873                                               int trigger_page)
10874 {
10875   boolean change_done_any = FALSE;
10876   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10877   int i;
10878
10879   if (!(trigger_events[trigger_element][trigger_event]))
10880     return FALSE;
10881
10882 #if 0
10883   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10884          trigger_event, recursion_loop_depth, recursion_loop_detected,
10885          recursion_loop_element, EL_NAME(recursion_loop_element));
10886 #endif
10887
10888   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10889
10890   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10891   {
10892     int element = EL_CUSTOM_START + i;
10893     boolean change_done = FALSE;
10894     int p;
10895
10896     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10897         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10898       continue;
10899
10900     for (p = 0; p < element_info[element].num_change_pages; p++)
10901     {
10902       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10903
10904       if (change->can_change_or_has_action &&
10905           change->has_event[trigger_event] &&
10906           change->trigger_side & trigger_side &&
10907           change->trigger_player & trigger_player &&
10908           change->trigger_page & trigger_page_bits &&
10909           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10910       {
10911         change->actual_trigger_element = trigger_element;
10912         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10913         change->actual_trigger_player_bits = trigger_player;
10914         change->actual_trigger_side = trigger_side;
10915         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10916         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10917
10918         if ((change->can_change && !change_done) || change->has_action)
10919         {
10920           int x, y;
10921
10922           SCAN_PLAYFIELD(x, y)
10923           {
10924             if (Feld[x][y] == element)
10925             {
10926               if (change->can_change && !change_done)
10927               {
10928                 ChangeDelay[x][y] = 1;
10929                 ChangeEvent[x][y] = trigger_event;
10930
10931                 HandleElementChange(x, y, p);
10932               }
10933 #if USE_NEW_DELAYED_ACTION
10934               else if (change->has_action)
10935               {
10936                 ExecuteCustomElementAction(x, y, element, p);
10937                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10938               }
10939 #else
10940               if (change->has_action)
10941               {
10942                 ExecuteCustomElementAction(x, y, element, p);
10943                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10944               }
10945 #endif
10946             }
10947           }
10948
10949           if (change->can_change)
10950           {
10951             change_done = TRUE;
10952             change_done_any = TRUE;
10953           }
10954         }
10955       }
10956     }
10957   }
10958
10959   RECURSION_LOOP_DETECTION_END();
10960
10961   return change_done_any;
10962 }
10963
10964 static boolean CheckElementChangeExt(int x, int y,
10965                                      int element,
10966                                      int trigger_element,
10967                                      int trigger_event,
10968                                      int trigger_player,
10969                                      int trigger_side)
10970 {
10971   boolean change_done = FALSE;
10972   int p;
10973
10974   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10975       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10976     return FALSE;
10977
10978   if (Feld[x][y] == EL_BLOCKED)
10979   {
10980     Blocked2Moving(x, y, &x, &y);
10981     element = Feld[x][y];
10982   }
10983
10984 #if 0
10985   /* check if element has already changed */
10986   if (Feld[x][y] != element)
10987     return FALSE;
10988 #else
10989   /* check if element has already changed or is about to change after moving */
10990   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10991        Feld[x][y] != element) ||
10992
10993       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10994        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10995         ChangePage[x][y] != -1)))
10996     return FALSE;
10997 #endif
10998
10999 #if 0
11000   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11001          trigger_event, recursion_loop_depth, recursion_loop_detected,
11002          recursion_loop_element, EL_NAME(recursion_loop_element));
11003 #endif
11004
11005   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11006
11007   for (p = 0; p < element_info[element].num_change_pages; p++)
11008   {
11009     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11010
11011     /* check trigger element for all events where the element that is checked
11012        for changing interacts with a directly adjacent element -- this is
11013        different to element changes that affect other elements to change on the
11014        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11015     boolean check_trigger_element =
11016       (trigger_event == CE_TOUCHING_X ||
11017        trigger_event == CE_HITTING_X ||
11018        trigger_event == CE_HIT_BY_X ||
11019 #if 1
11020        /* this one was forgotten until 3.2.3 */
11021        trigger_event == CE_DIGGING_X);
11022 #endif
11023
11024     if (change->can_change_or_has_action &&
11025         change->has_event[trigger_event] &&
11026         change->trigger_side & trigger_side &&
11027         change->trigger_player & trigger_player &&
11028         (!check_trigger_element ||
11029          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11030     {
11031       change->actual_trigger_element = trigger_element;
11032       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11033       change->actual_trigger_player_bits = trigger_player;
11034       change->actual_trigger_side = trigger_side;
11035       change->actual_trigger_ce_value = CustomValue[x][y];
11036       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11037
11038       /* special case: trigger element not at (x,y) position for some events */
11039       if (check_trigger_element)
11040       {
11041         static struct
11042         {
11043           int dx, dy;
11044         } move_xy[] =
11045           {
11046             {  0,  0 },
11047             { -1,  0 },
11048             { +1,  0 },
11049             {  0,  0 },
11050             {  0, -1 },
11051             {  0,  0 }, { 0, 0 }, { 0, 0 },
11052             {  0, +1 }
11053           };
11054
11055         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11056         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11057
11058         change->actual_trigger_ce_value = CustomValue[xx][yy];
11059         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11060       }
11061
11062       if (change->can_change && !change_done)
11063       {
11064         ChangeDelay[x][y] = 1;
11065         ChangeEvent[x][y] = trigger_event;
11066
11067         HandleElementChange(x, y, p);
11068
11069         change_done = TRUE;
11070       }
11071 #if USE_NEW_DELAYED_ACTION
11072       else if (change->has_action)
11073       {
11074         ExecuteCustomElementAction(x, y, element, p);
11075         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11076       }
11077 #else
11078       if (change->has_action)
11079       {
11080         ExecuteCustomElementAction(x, y, element, p);
11081         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11082       }
11083 #endif
11084     }
11085   }
11086
11087   RECURSION_LOOP_DETECTION_END();
11088
11089   return change_done;
11090 }
11091
11092 static void PlayPlayerSound(struct PlayerInfo *player)
11093 {
11094   int jx = player->jx, jy = player->jy;
11095   int sound_element = player->artwork_element;
11096   int last_action = player->last_action_waiting;
11097   int action = player->action_waiting;
11098
11099   if (player->is_waiting)
11100   {
11101     if (action != last_action)
11102       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11103     else
11104       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11105   }
11106   else
11107   {
11108     if (action != last_action)
11109       StopSound(element_info[sound_element].sound[last_action]);
11110
11111     if (last_action == ACTION_SLEEPING)
11112       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11113   }
11114 }
11115
11116 static void PlayAllPlayersSound()
11117 {
11118   int i;
11119
11120   for (i = 0; i < MAX_PLAYERS; i++)
11121     if (stored_player[i].active)
11122       PlayPlayerSound(&stored_player[i]);
11123 }
11124
11125 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11126 {
11127   boolean last_waiting = player->is_waiting;
11128   int move_dir = player->MovDir;
11129
11130   player->dir_waiting = move_dir;
11131   player->last_action_waiting = player->action_waiting;
11132
11133   if (is_waiting)
11134   {
11135     if (!last_waiting)          /* not waiting -> waiting */
11136     {
11137       player->is_waiting = TRUE;
11138
11139       player->frame_counter_bored =
11140         FrameCounter +
11141         game.player_boring_delay_fixed +
11142         GetSimpleRandom(game.player_boring_delay_random);
11143       player->frame_counter_sleeping =
11144         FrameCounter +
11145         game.player_sleeping_delay_fixed +
11146         GetSimpleRandom(game.player_sleeping_delay_random);
11147
11148       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11149     }
11150
11151     if (game.player_sleeping_delay_fixed +
11152         game.player_sleeping_delay_random > 0 &&
11153         player->anim_delay_counter == 0 &&
11154         player->post_delay_counter == 0 &&
11155         FrameCounter >= player->frame_counter_sleeping)
11156       player->is_sleeping = TRUE;
11157     else if (game.player_boring_delay_fixed +
11158              game.player_boring_delay_random > 0 &&
11159              FrameCounter >= player->frame_counter_bored)
11160       player->is_bored = TRUE;
11161
11162     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11163                               player->is_bored ? ACTION_BORING :
11164                               ACTION_WAITING);
11165
11166     if (player->is_sleeping && player->use_murphy)
11167     {
11168       /* special case for sleeping Murphy when leaning against non-free tile */
11169
11170       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11171           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11172            !IS_MOVING(player->jx - 1, player->jy)))
11173         move_dir = MV_LEFT;
11174       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11175                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11176                 !IS_MOVING(player->jx + 1, player->jy)))
11177         move_dir = MV_RIGHT;
11178       else
11179         player->is_sleeping = FALSE;
11180
11181       player->dir_waiting = move_dir;
11182     }
11183
11184     if (player->is_sleeping)
11185     {
11186       if (player->num_special_action_sleeping > 0)
11187       {
11188         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11189         {
11190           int last_special_action = player->special_action_sleeping;
11191           int num_special_action = player->num_special_action_sleeping;
11192           int special_action =
11193             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11194              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11195              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11196              last_special_action + 1 : ACTION_SLEEPING);
11197           int special_graphic =
11198             el_act_dir2img(player->artwork_element, special_action, move_dir);
11199
11200           player->anim_delay_counter =
11201             graphic_info[special_graphic].anim_delay_fixed +
11202             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11203           player->post_delay_counter =
11204             graphic_info[special_graphic].post_delay_fixed +
11205             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11206
11207           player->special_action_sleeping = special_action;
11208         }
11209
11210         if (player->anim_delay_counter > 0)
11211         {
11212           player->action_waiting = player->special_action_sleeping;
11213           player->anim_delay_counter--;
11214         }
11215         else if (player->post_delay_counter > 0)
11216         {
11217           player->post_delay_counter--;
11218         }
11219       }
11220     }
11221     else if (player->is_bored)
11222     {
11223       if (player->num_special_action_bored > 0)
11224       {
11225         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11226         {
11227           int special_action =
11228             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11229           int special_graphic =
11230             el_act_dir2img(player->artwork_element, special_action, move_dir);
11231
11232           player->anim_delay_counter =
11233             graphic_info[special_graphic].anim_delay_fixed +
11234             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11235           player->post_delay_counter =
11236             graphic_info[special_graphic].post_delay_fixed +
11237             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11238
11239           player->special_action_bored = special_action;
11240         }
11241
11242         if (player->anim_delay_counter > 0)
11243         {
11244           player->action_waiting = player->special_action_bored;
11245           player->anim_delay_counter--;
11246         }
11247         else if (player->post_delay_counter > 0)
11248         {
11249           player->post_delay_counter--;
11250         }
11251       }
11252     }
11253   }
11254   else if (last_waiting)        /* waiting -> not waiting */
11255   {
11256     player->is_waiting = FALSE;
11257     player->is_bored = FALSE;
11258     player->is_sleeping = FALSE;
11259
11260     player->frame_counter_bored = -1;
11261     player->frame_counter_sleeping = -1;
11262
11263     player->anim_delay_counter = 0;
11264     player->post_delay_counter = 0;
11265
11266     player->dir_waiting = player->MovDir;
11267     player->action_waiting = ACTION_DEFAULT;
11268
11269     player->special_action_bored = ACTION_DEFAULT;
11270     player->special_action_sleeping = ACTION_DEFAULT;
11271   }
11272 }
11273
11274 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11275 {
11276   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11277   int left      = player_action & JOY_LEFT;
11278   int right     = player_action & JOY_RIGHT;
11279   int up        = player_action & JOY_UP;
11280   int down      = player_action & JOY_DOWN;
11281   int button1   = player_action & JOY_BUTTON_1;
11282   int button2   = player_action & JOY_BUTTON_2;
11283   int dx        = (left ? -1 : right ? 1 : 0);
11284   int dy        = (up   ? -1 : down  ? 1 : 0);
11285
11286   if (!player->active || tape.pausing)
11287     return 0;
11288
11289   if (player_action)
11290   {
11291     if (button1)
11292       snapped = SnapField(player, dx, dy);
11293     else
11294     {
11295       if (button2)
11296         dropped = DropElement(player);
11297
11298       moved = MovePlayer(player, dx, dy);
11299     }
11300
11301     if (tape.single_step && tape.recording && !tape.pausing)
11302     {
11303       if (button1 || (dropped && !moved))
11304       {
11305         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11306         SnapField(player, 0, 0);                /* stop snapping */
11307       }
11308     }
11309
11310     SetPlayerWaiting(player, FALSE);
11311
11312     return player_action;
11313   }
11314   else
11315   {
11316     /* no actions for this player (no input at player's configured device) */
11317
11318     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11319     SnapField(player, 0, 0);
11320     CheckGravityMovementWhenNotMoving(player);
11321
11322     if (player->MovPos == 0)
11323       SetPlayerWaiting(player, TRUE);
11324
11325     if (player->MovPos == 0)    /* needed for tape.playing */
11326       player->is_moving = FALSE;
11327
11328     player->is_dropping = FALSE;
11329     player->is_dropping_pressed = FALSE;
11330     player->drop_pressed_delay = 0;
11331
11332     return 0;
11333   }
11334 }
11335
11336 static void CheckLevelTime()
11337 {
11338   int i;
11339
11340   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11341   {
11342     if (level.native_em_level->lev->home == 0)  /* all players at home */
11343     {
11344       PlayerWins(local_player);
11345
11346       AllPlayersGone = TRUE;
11347
11348       level.native_em_level->lev->home = -1;
11349     }
11350
11351     if (level.native_em_level->ply[0]->alive == 0 &&
11352         level.native_em_level->ply[1]->alive == 0 &&
11353         level.native_em_level->ply[2]->alive == 0 &&
11354         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11355       AllPlayersGone = TRUE;
11356   }
11357
11358   if (TimeFrames >= FRAMES_PER_SECOND)
11359   {
11360     TimeFrames = 0;
11361     TapeTime++;
11362
11363     for (i = 0; i < MAX_PLAYERS; i++)
11364     {
11365       struct PlayerInfo *player = &stored_player[i];
11366
11367       if (SHIELD_ON(player))
11368       {
11369         player->shield_normal_time_left--;
11370
11371         if (player->shield_deadly_time_left > 0)
11372           player->shield_deadly_time_left--;
11373       }
11374     }
11375
11376     if (!local_player->LevelSolved && !level.use_step_counter)
11377     {
11378       TimePlayed++;
11379
11380       if (TimeLeft > 0)
11381       {
11382         TimeLeft--;
11383
11384         if (TimeLeft <= 10 && setup.time_limit)
11385           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11386
11387 #if 1
11388         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11389
11390         DisplayGameControlValues();
11391 #else
11392         DrawGameValue_Time(TimeLeft);
11393 #endif
11394
11395         if (!TimeLeft && setup.time_limit)
11396         {
11397           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11398             level.native_em_level->lev->killed_out_of_time = TRUE;
11399           else
11400             for (i = 0; i < MAX_PLAYERS; i++)
11401               KillPlayer(&stored_player[i]);
11402         }
11403       }
11404 #if 1
11405       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11406       {
11407         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11408
11409         DisplayGameControlValues();
11410       }
11411 #else
11412       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11413         DrawGameValue_Time(TimePlayed);
11414 #endif
11415
11416       level.native_em_level->lev->time =
11417         (level.time == 0 ? TimePlayed : TimeLeft);
11418     }
11419
11420     if (tape.recording || tape.playing)
11421       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11422   }
11423
11424   UpdateGameDoorValues();
11425   DrawGameDoorValues();
11426 }
11427
11428 void AdvanceFrameAndPlayerCounters(int player_nr)
11429 {
11430   int i;
11431
11432   /* advance frame counters (global frame counter and time frame counter) */
11433   FrameCounter++;
11434   TimeFrames++;
11435
11436   /* advance player counters (counters for move delay, move animation etc.) */
11437   for (i = 0; i < MAX_PLAYERS; i++)
11438   {
11439     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11440     int move_delay_value = stored_player[i].move_delay_value;
11441     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11442
11443     if (!advance_player_counters)       /* not all players may be affected */
11444       continue;
11445
11446 #if USE_NEW_PLAYER_ANIM
11447     if (move_frames == 0)       /* less than one move per game frame */
11448     {
11449       int stepsize = TILEX / move_delay_value;
11450       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11451       int count = (stored_player[i].is_moving ?
11452                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11453
11454       if (count % delay == 0)
11455         move_frames = 1;
11456     }
11457 #endif
11458
11459     stored_player[i].Frame += move_frames;
11460
11461     if (stored_player[i].MovPos != 0)
11462       stored_player[i].StepFrame += move_frames;
11463
11464     if (stored_player[i].move_delay > 0)
11465       stored_player[i].move_delay--;
11466
11467     /* due to bugs in previous versions, counter must count up, not down */
11468     if (stored_player[i].push_delay != -1)
11469       stored_player[i].push_delay++;
11470
11471     if (stored_player[i].drop_delay > 0)
11472       stored_player[i].drop_delay--;
11473
11474     if (stored_player[i].is_dropping_pressed)
11475       stored_player[i].drop_pressed_delay++;
11476   }
11477 }
11478
11479 void StartGameActions(boolean init_network_game, boolean record_tape,
11480                       long random_seed)
11481 {
11482   unsigned long new_random_seed = InitRND(random_seed);
11483
11484   if (record_tape)
11485     TapeStartRecording(new_random_seed);
11486
11487 #if defined(NETWORK_AVALIABLE)
11488   if (init_network_game)
11489   {
11490     SendToServer_StartPlaying();
11491
11492     return;
11493   }
11494 #endif
11495
11496   InitGame();
11497 }
11498
11499 void GameActions()
11500 {
11501   static unsigned long game_frame_delay = 0;
11502   unsigned long game_frame_delay_value;
11503   byte *recorded_player_action;
11504   byte summarized_player_action = 0;
11505   byte tape_action[MAX_PLAYERS];
11506   int i;
11507
11508   /* detect endless loops, caused by custom element programming */
11509   if (recursion_loop_detected && recursion_loop_depth == 0)
11510   {
11511     char *message = getStringCat3("Internal Error ! Element ",
11512                                   EL_NAME(recursion_loop_element),
11513                                   " caused endless loop ! Quit the game ?");
11514
11515     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11516           EL_NAME(recursion_loop_element));
11517
11518     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11519
11520     recursion_loop_detected = FALSE;    /* if game should be continued */
11521
11522     free(message);
11523
11524     return;
11525   }
11526
11527   if (game.restart_level)
11528     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11529
11530   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11531   {
11532     if (level.native_em_level->lev->home == 0)  /* all players at home */
11533     {
11534       PlayerWins(local_player);
11535
11536       AllPlayersGone = TRUE;
11537
11538       level.native_em_level->lev->home = -1;
11539     }
11540
11541     if (level.native_em_level->ply[0]->alive == 0 &&
11542         level.native_em_level->ply[1]->alive == 0 &&
11543         level.native_em_level->ply[2]->alive == 0 &&
11544         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11545       AllPlayersGone = TRUE;
11546   }
11547
11548   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11549     GameWon();
11550
11551   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11552     TapeStop();
11553
11554   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11555     return;
11556
11557   game_frame_delay_value =
11558     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11559
11560   if (tape.playing && tape.warp_forward && !tape.pausing)
11561     game_frame_delay_value = 0;
11562
11563   /* ---------- main game synchronization point ---------- */
11564
11565   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11566
11567   if (network_playing && !network_player_action_received)
11568   {
11569     /* try to get network player actions in time */
11570
11571 #if defined(NETWORK_AVALIABLE)
11572     /* last chance to get network player actions without main loop delay */
11573     HandleNetworking();
11574 #endif
11575
11576     /* game was quit by network peer */
11577     if (game_status != GAME_MODE_PLAYING)
11578       return;
11579
11580     if (!network_player_action_received)
11581       return;           /* failed to get network player actions in time */
11582
11583     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11584   }
11585
11586   if (tape.pausing)
11587     return;
11588
11589   /* at this point we know that we really continue executing the game */
11590
11591   network_player_action_received = FALSE;
11592
11593   /* when playing tape, read previously recorded player input from tape data */
11594   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11595
11596 #if 1
11597   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11598   if (tape.pausing)
11599     return;
11600 #endif
11601
11602   if (tape.set_centered_player)
11603   {
11604     game.centered_player_nr_next = tape.centered_player_nr_next;
11605     game.set_centered_player = TRUE;
11606   }
11607
11608   for (i = 0; i < MAX_PLAYERS; i++)
11609   {
11610     summarized_player_action |= stored_player[i].action;
11611
11612     if (!network_playing)
11613       stored_player[i].effective_action = stored_player[i].action;
11614   }
11615
11616 #if defined(NETWORK_AVALIABLE)
11617   if (network_playing)
11618     SendToServer_MovePlayer(summarized_player_action);
11619 #endif
11620
11621   if (!options.network && !setup.team_mode)
11622     local_player->effective_action = summarized_player_action;
11623
11624   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11625   {
11626     for (i = 0; i < MAX_PLAYERS; i++)
11627       stored_player[i].effective_action =
11628         (i == game.centered_player_nr ? summarized_player_action : 0);
11629   }
11630
11631   if (recorded_player_action != NULL)
11632     for (i = 0; i < MAX_PLAYERS; i++)
11633       stored_player[i].effective_action = recorded_player_action[i];
11634
11635   for (i = 0; i < MAX_PLAYERS; i++)
11636   {
11637     tape_action[i] = stored_player[i].effective_action;
11638
11639     /* (this can only happen in the R'n'D game engine) */
11640     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11641       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11642   }
11643
11644   /* only record actions from input devices, but not programmed actions */
11645   if (tape.recording)
11646     TapeRecordAction(tape_action);
11647
11648   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11649   {
11650     GameActions_EM_Main();
11651   }
11652   else
11653   {
11654     GameActions_RND();
11655   }
11656 }
11657
11658 void GameActions_EM_Main()
11659 {
11660   byte effective_action[MAX_PLAYERS];
11661   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11662   int i;
11663
11664   for (i = 0; i < MAX_PLAYERS; i++)
11665     effective_action[i] = stored_player[i].effective_action;
11666
11667   GameActions_EM(effective_action, warp_mode);
11668
11669   CheckLevelTime();
11670
11671   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11672 }
11673
11674 void GameActions_RND()
11675 {
11676   int magic_wall_x = 0, magic_wall_y = 0;
11677   int i, x, y, element, graphic;
11678
11679   InitPlayfieldScanModeVars();
11680
11681 #if USE_ONE_MORE_CHANGE_PER_FRAME
11682   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11683   {
11684     SCAN_PLAYFIELD(x, y)
11685     {
11686       ChangeCount[x][y] = 0;
11687       ChangeEvent[x][y] = -1;
11688     }
11689   }
11690 #endif
11691
11692   if (game.set_centered_player)
11693   {
11694     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11695
11696     /* switching to "all players" only possible if all players fit to screen */
11697     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11698     {
11699       game.centered_player_nr_next = game.centered_player_nr;
11700       game.set_centered_player = FALSE;
11701     }
11702
11703     /* do not switch focus to non-existing (or non-active) player */
11704     if (game.centered_player_nr_next >= 0 &&
11705         !stored_player[game.centered_player_nr_next].active)
11706     {
11707       game.centered_player_nr_next = game.centered_player_nr;
11708       game.set_centered_player = FALSE;
11709     }
11710   }
11711
11712   if (game.set_centered_player &&
11713       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11714   {
11715     int sx, sy;
11716
11717     if (game.centered_player_nr_next == -1)
11718     {
11719       setScreenCenteredToAllPlayers(&sx, &sy);
11720     }
11721     else
11722     {
11723       sx = stored_player[game.centered_player_nr_next].jx;
11724       sy = stored_player[game.centered_player_nr_next].jy;
11725     }
11726
11727     game.centered_player_nr = game.centered_player_nr_next;
11728     game.set_centered_player = FALSE;
11729
11730     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11731     DrawGameDoorValues();
11732   }
11733
11734   for (i = 0; i < MAX_PLAYERS; i++)
11735   {
11736     int actual_player_action = stored_player[i].effective_action;
11737
11738 #if 1
11739     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11740        - rnd_equinox_tetrachloride 048
11741        - rnd_equinox_tetrachloride_ii 096
11742        - rnd_emanuel_schmieg 002
11743        - doctor_sloan_ww 001, 020
11744     */
11745     if (stored_player[i].MovPos == 0)
11746       CheckGravityMovement(&stored_player[i]);
11747 #endif
11748
11749     /* overwrite programmed action with tape action */
11750     if (stored_player[i].programmed_action)
11751       actual_player_action = stored_player[i].programmed_action;
11752
11753     PlayerActions(&stored_player[i], actual_player_action);
11754
11755     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11756   }
11757
11758   ScrollScreen(NULL, SCROLL_GO_ON);
11759
11760   /* for backwards compatibility, the following code emulates a fixed bug that
11761      occured when pushing elements (causing elements that just made their last
11762      pushing step to already (if possible) make their first falling step in the
11763      same game frame, which is bad); this code is also needed to use the famous
11764      "spring push bug" which is used in older levels and might be wanted to be
11765      used also in newer levels, but in this case the buggy pushing code is only
11766      affecting the "spring" element and no other elements */
11767
11768   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11769   {
11770     for (i = 0; i < MAX_PLAYERS; i++)
11771     {
11772       struct PlayerInfo *player = &stored_player[i];
11773       int x = player->jx;
11774       int y = player->jy;
11775
11776       if (player->active && player->is_pushing && player->is_moving &&
11777           IS_MOVING(x, y) &&
11778           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11779            Feld[x][y] == EL_SPRING))
11780       {
11781         ContinueMoving(x, y);
11782
11783         /* continue moving after pushing (this is actually a bug) */
11784         if (!IS_MOVING(x, y))
11785           Stop[x][y] = FALSE;
11786       }
11787     }
11788   }
11789
11790 #if 0
11791   debug_print_timestamp(0, "start main loop profiling");
11792 #endif
11793
11794   SCAN_PLAYFIELD(x, y)
11795   {
11796     ChangeCount[x][y] = 0;
11797     ChangeEvent[x][y] = -1;
11798
11799     /* this must be handled before main playfield loop */
11800     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11801     {
11802       MovDelay[x][y]--;
11803       if (MovDelay[x][y] <= 0)
11804         RemoveField(x, y);
11805     }
11806
11807 #if USE_NEW_SNAP_DELAY
11808     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11809     {
11810       MovDelay[x][y]--;
11811       if (MovDelay[x][y] <= 0)
11812       {
11813         RemoveField(x, y);
11814         DrawLevelField(x, y);
11815
11816         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11817       }
11818     }
11819 #endif
11820
11821 #if DEBUG
11822     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11823     {
11824       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11825       printf("GameActions(): This should never happen!\n");
11826
11827       ChangePage[x][y] = -1;
11828     }
11829 #endif
11830
11831     Stop[x][y] = FALSE;
11832     if (WasJustMoving[x][y] > 0)
11833       WasJustMoving[x][y]--;
11834     if (WasJustFalling[x][y] > 0)
11835       WasJustFalling[x][y]--;
11836     if (CheckCollision[x][y] > 0)
11837       CheckCollision[x][y]--;
11838     if (CheckImpact[x][y] > 0)
11839       CheckImpact[x][y]--;
11840
11841     GfxFrame[x][y]++;
11842
11843     /* reset finished pushing action (not done in ContinueMoving() to allow
11844        continuous pushing animation for elements with zero push delay) */
11845     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11846     {
11847       ResetGfxAnimation(x, y);
11848       DrawLevelField(x, y);
11849     }
11850
11851 #if DEBUG
11852     if (IS_BLOCKED(x, y))
11853     {
11854       int oldx, oldy;
11855
11856       Blocked2Moving(x, y, &oldx, &oldy);
11857       if (!IS_MOVING(oldx, oldy))
11858       {
11859         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11860         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11861         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11862         printf("GameActions(): This should never happen!\n");
11863       }
11864     }
11865 #endif
11866   }
11867
11868 #if 0
11869   debug_print_timestamp(0, "- time for pre-main loop:");
11870 #endif
11871
11872 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11873   SCAN_PLAYFIELD(x, y)
11874   {
11875     element = Feld[x][y];
11876     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11877
11878 #if 1
11879     {
11880 #if 1
11881       int element2 = element;
11882       int graphic2 = graphic;
11883 #else
11884       int element2 = Feld[x][y];
11885       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11886 #endif
11887       int last_gfx_frame = GfxFrame[x][y];
11888
11889       if (graphic_info[graphic2].anim_global_sync)
11890         GfxFrame[x][y] = FrameCounter;
11891       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11892         GfxFrame[x][y] = CustomValue[x][y];
11893       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11894         GfxFrame[x][y] = element_info[element2].collect_score;
11895       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11896         GfxFrame[x][y] = ChangeDelay[x][y];
11897
11898       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11899         DrawLevelGraphicAnimation(x, y, graphic2);
11900     }
11901 #else
11902     ResetGfxFrame(x, y, TRUE);
11903 #endif
11904
11905 #if 1
11906     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11907         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11908       ResetRandomAnimationValue(x, y);
11909 #endif
11910
11911 #if 1
11912     SetRandomAnimationValue(x, y);
11913 #endif
11914
11915 #if 1
11916     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11917 #endif
11918   }
11919 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11920
11921 #if 0
11922   debug_print_timestamp(0, "- time for TEST loop:     -->");
11923 #endif
11924
11925   SCAN_PLAYFIELD(x, y)
11926   {
11927     element = Feld[x][y];
11928     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11929
11930     ResetGfxFrame(x, y, TRUE);
11931
11932     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11933         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11934       ResetRandomAnimationValue(x, y);
11935
11936     SetRandomAnimationValue(x, y);
11937
11938     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11939
11940     if (IS_INACTIVE(element))
11941     {
11942       if (IS_ANIMATED(graphic))
11943         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11944
11945       continue;
11946     }
11947
11948     /* this may take place after moving, so 'element' may have changed */
11949     if (IS_CHANGING(x, y) &&
11950         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11951     {
11952       int page = element_info[element].event_page_nr[CE_DELAY];
11953
11954 #if 1
11955       HandleElementChange(x, y, page);
11956 #else
11957       if (CAN_CHANGE(element))
11958         HandleElementChange(x, y, page);
11959
11960       if (HAS_ACTION(element))
11961         ExecuteCustomElementAction(x, y, element, page);
11962 #endif
11963
11964       element = Feld[x][y];
11965       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11966     }
11967
11968 #if 0   // ---------------------------------------------------------------------
11969
11970     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11971     {
11972       StartMoving(x, y);
11973
11974       element = Feld[x][y];
11975       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11976
11977       if (IS_ANIMATED(graphic) &&
11978           !IS_MOVING(x, y) &&
11979           !Stop[x][y])
11980         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11981
11982       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11983         DrawTwinkleOnField(x, y);
11984     }
11985     else if (IS_MOVING(x, y))
11986       ContinueMoving(x, y);
11987     else
11988     {
11989       switch (element)
11990       {
11991         case EL_ACID:
11992         case EL_EXIT_OPEN:
11993         case EL_EM_EXIT_OPEN:
11994         case EL_SP_EXIT_OPEN:
11995         case EL_STEEL_EXIT_OPEN:
11996         case EL_EM_STEEL_EXIT_OPEN:
11997         case EL_SP_TERMINAL:
11998         case EL_SP_TERMINAL_ACTIVE:
11999         case EL_EXTRA_TIME:
12000         case EL_SHIELD_NORMAL:
12001         case EL_SHIELD_DEADLY:
12002           if (IS_ANIMATED(graphic))
12003             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12004           break;
12005
12006         case EL_DYNAMITE_ACTIVE:
12007         case EL_EM_DYNAMITE_ACTIVE:
12008         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12009         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12010         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12011         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12012         case EL_SP_DISK_RED_ACTIVE:
12013           CheckDynamite(x, y);
12014           break;
12015
12016         case EL_AMOEBA_GROWING:
12017           AmoebeWaechst(x, y);
12018           break;
12019
12020         case EL_AMOEBA_SHRINKING:
12021           AmoebaDisappearing(x, y);
12022           break;
12023
12024 #if !USE_NEW_AMOEBA_CODE
12025         case EL_AMOEBA_WET:
12026         case EL_AMOEBA_DRY:
12027         case EL_AMOEBA_FULL:
12028         case EL_BD_AMOEBA:
12029         case EL_EMC_DRIPPER:
12030           AmoebeAbleger(x, y);
12031           break;
12032 #endif
12033
12034         case EL_GAME_OF_LIFE:
12035         case EL_BIOMAZE:
12036           Life(x, y);
12037           break;
12038
12039         case EL_EXIT_CLOSED:
12040           CheckExit(x, y);
12041           break;
12042
12043         case EL_EM_EXIT_CLOSED:
12044           CheckExitEM(x, y);
12045           break;
12046
12047         case EL_STEEL_EXIT_CLOSED:
12048           CheckExitSteel(x, y);
12049           break;
12050
12051         case EL_EM_STEEL_EXIT_CLOSED:
12052           CheckExitSteelEM(x, y);
12053           break;
12054
12055         case EL_SP_EXIT_CLOSED:
12056           CheckExitSP(x, y);
12057           break;
12058
12059         case EL_EXPANDABLE_WALL_GROWING:
12060         case EL_EXPANDABLE_STEELWALL_GROWING:
12061           MauerWaechst(x, y);
12062           break;
12063
12064         case EL_EXPANDABLE_WALL:
12065         case EL_EXPANDABLE_WALL_HORIZONTAL:
12066         case EL_EXPANDABLE_WALL_VERTICAL:
12067         case EL_EXPANDABLE_WALL_ANY:
12068         case EL_BD_EXPANDABLE_WALL:
12069           MauerAbleger(x, y);
12070           break;
12071
12072         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12073         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12074         case EL_EXPANDABLE_STEELWALL_ANY:
12075           MauerAblegerStahl(x, y);
12076           break;
12077
12078         case EL_FLAMES:
12079           CheckForDragon(x, y);
12080           break;
12081
12082         case EL_EXPLOSION:
12083           break;
12084
12085         case EL_ELEMENT_SNAPPING:
12086         case EL_DIAGONAL_SHRINKING:
12087         case EL_DIAGONAL_GROWING:
12088         {
12089           graphic =
12090             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12091
12092           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12093           break;
12094         }
12095
12096         default:
12097           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12098             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12099           break;
12100       }
12101     }
12102
12103 #else   // ---------------------------------------------------------------------
12104
12105     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12106     {
12107       StartMoving(x, y);
12108
12109       element = Feld[x][y];
12110       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12111
12112       if (IS_ANIMATED(graphic) &&
12113           !IS_MOVING(x, y) &&
12114           !Stop[x][y])
12115         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12116
12117       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12118         DrawTwinkleOnField(x, y);
12119     }
12120     else if ((element == EL_ACID ||
12121               element == EL_EXIT_OPEN ||
12122               element == EL_EM_EXIT_OPEN ||
12123               element == EL_SP_EXIT_OPEN ||
12124               element == EL_STEEL_EXIT_OPEN ||
12125               element == EL_EM_STEEL_EXIT_OPEN ||
12126               element == EL_SP_TERMINAL ||
12127               element == EL_SP_TERMINAL_ACTIVE ||
12128               element == EL_EXTRA_TIME ||
12129               element == EL_SHIELD_NORMAL ||
12130               element == EL_SHIELD_DEADLY) &&
12131              IS_ANIMATED(graphic))
12132       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12133     else if (IS_MOVING(x, y))
12134       ContinueMoving(x, y);
12135     else if (IS_ACTIVE_BOMB(element))
12136       CheckDynamite(x, y);
12137     else if (element == EL_AMOEBA_GROWING)
12138       AmoebeWaechst(x, y);
12139     else if (element == EL_AMOEBA_SHRINKING)
12140       AmoebaDisappearing(x, y);
12141
12142 #if !USE_NEW_AMOEBA_CODE
12143     else if (IS_AMOEBALIVE(element))
12144       AmoebeAbleger(x, y);
12145 #endif
12146
12147     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12148       Life(x, y);
12149     else if (element == EL_EXIT_CLOSED)
12150       CheckExit(x, y);
12151     else if (element == EL_EM_EXIT_CLOSED)
12152       CheckExitEM(x, y);
12153     else if (element == EL_STEEL_EXIT_CLOSED)
12154       CheckExitSteel(x, y);
12155     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12156       CheckExitSteelEM(x, y);
12157     else if (element == EL_SP_EXIT_CLOSED)
12158       CheckExitSP(x, y);
12159     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12160              element == EL_EXPANDABLE_STEELWALL_GROWING)
12161       MauerWaechst(x, y);
12162     else if (element == EL_EXPANDABLE_WALL ||
12163              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12164              element == EL_EXPANDABLE_WALL_VERTICAL ||
12165              element == EL_EXPANDABLE_WALL_ANY ||
12166              element == EL_BD_EXPANDABLE_WALL)
12167       MauerAbleger(x, y);
12168     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12169              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12170              element == EL_EXPANDABLE_STEELWALL_ANY)
12171       MauerAblegerStahl(x, y);
12172     else if (element == EL_FLAMES)
12173       CheckForDragon(x, y);
12174     else if (element == EL_EXPLOSION)
12175       ; /* drawing of correct explosion animation is handled separately */
12176     else if (element == EL_ELEMENT_SNAPPING ||
12177              element == EL_DIAGONAL_SHRINKING ||
12178              element == EL_DIAGONAL_GROWING)
12179     {
12180       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12181
12182       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12183     }
12184     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12185       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12186
12187 #endif  // ---------------------------------------------------------------------
12188
12189     if (IS_BELT_ACTIVE(element))
12190       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12191
12192     if (game.magic_wall_active)
12193     {
12194       int jx = local_player->jx, jy = local_player->jy;
12195
12196       /* play the element sound at the position nearest to the player */
12197       if ((element == EL_MAGIC_WALL_FULL ||
12198            element == EL_MAGIC_WALL_ACTIVE ||
12199            element == EL_MAGIC_WALL_EMPTYING ||
12200            element == EL_BD_MAGIC_WALL_FULL ||
12201            element == EL_BD_MAGIC_WALL_ACTIVE ||
12202            element == EL_BD_MAGIC_WALL_EMPTYING ||
12203            element == EL_DC_MAGIC_WALL_FULL ||
12204            element == EL_DC_MAGIC_WALL_ACTIVE ||
12205            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12206           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12207       {
12208         magic_wall_x = x;
12209         magic_wall_y = y;
12210       }
12211     }
12212   }
12213
12214 #if 0
12215   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12216 #endif
12217
12218 #if USE_NEW_AMOEBA_CODE
12219   /* new experimental amoeba growth stuff */
12220   if (!(FrameCounter % 8))
12221   {
12222     static unsigned long random = 1684108901;
12223
12224     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12225     {
12226       x = RND(lev_fieldx);
12227       y = RND(lev_fieldy);
12228       element = Feld[x][y];
12229
12230       if (!IS_PLAYER(x,y) &&
12231           (element == EL_EMPTY ||
12232            CAN_GROW_INTO(element) ||
12233            element == EL_QUICKSAND_EMPTY ||
12234            element == EL_QUICKSAND_FAST_EMPTY ||
12235            element == EL_ACID_SPLASH_LEFT ||
12236            element == EL_ACID_SPLASH_RIGHT))
12237       {
12238         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12239             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12240             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12241             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12242           Feld[x][y] = EL_AMOEBA_DROP;
12243       }
12244
12245       random = random * 129 + 1;
12246     }
12247   }
12248 #endif
12249
12250 #if 0
12251   if (game.explosions_delayed)
12252 #endif
12253   {
12254     game.explosions_delayed = FALSE;
12255
12256     SCAN_PLAYFIELD(x, y)
12257     {
12258       element = Feld[x][y];
12259
12260       if (ExplodeField[x][y])
12261         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12262       else if (element == EL_EXPLOSION)
12263         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12264
12265       ExplodeField[x][y] = EX_TYPE_NONE;
12266     }
12267
12268     game.explosions_delayed = TRUE;
12269   }
12270
12271   if (game.magic_wall_active)
12272   {
12273     if (!(game.magic_wall_time_left % 4))
12274     {
12275       int element = Feld[magic_wall_x][magic_wall_y];
12276
12277       if (element == EL_BD_MAGIC_WALL_FULL ||
12278           element == EL_BD_MAGIC_WALL_ACTIVE ||
12279           element == EL_BD_MAGIC_WALL_EMPTYING)
12280         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12281       else if (element == EL_DC_MAGIC_WALL_FULL ||
12282                element == EL_DC_MAGIC_WALL_ACTIVE ||
12283                element == EL_DC_MAGIC_WALL_EMPTYING)
12284         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12285       else
12286         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12287     }
12288
12289     if (game.magic_wall_time_left > 0)
12290     {
12291       game.magic_wall_time_left--;
12292
12293       if (!game.magic_wall_time_left)
12294       {
12295         SCAN_PLAYFIELD(x, y)
12296         {
12297           element = Feld[x][y];
12298
12299           if (element == EL_MAGIC_WALL_ACTIVE ||
12300               element == EL_MAGIC_WALL_FULL)
12301           {
12302             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12303             DrawLevelField(x, y);
12304           }
12305           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12306                    element == EL_BD_MAGIC_WALL_FULL)
12307           {
12308             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12309             DrawLevelField(x, y);
12310           }
12311           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12312                    element == EL_DC_MAGIC_WALL_FULL)
12313           {
12314             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12315             DrawLevelField(x, y);
12316           }
12317         }
12318
12319         game.magic_wall_active = FALSE;
12320       }
12321     }
12322   }
12323
12324   if (game.light_time_left > 0)
12325   {
12326     game.light_time_left--;
12327
12328     if (game.light_time_left == 0)
12329       RedrawAllLightSwitchesAndInvisibleElements();
12330   }
12331
12332   if (game.timegate_time_left > 0)
12333   {
12334     game.timegate_time_left--;
12335
12336     if (game.timegate_time_left == 0)
12337       CloseAllOpenTimegates();
12338   }
12339
12340   if (game.lenses_time_left > 0)
12341   {
12342     game.lenses_time_left--;
12343
12344     if (game.lenses_time_left == 0)
12345       RedrawAllInvisibleElementsForLenses();
12346   }
12347
12348   if (game.magnify_time_left > 0)
12349   {
12350     game.magnify_time_left--;
12351
12352     if (game.magnify_time_left == 0)
12353       RedrawAllInvisibleElementsForMagnifier();
12354   }
12355
12356   for (i = 0; i < MAX_PLAYERS; i++)
12357   {
12358     struct PlayerInfo *player = &stored_player[i];
12359
12360     if (SHIELD_ON(player))
12361     {
12362       if (player->shield_deadly_time_left)
12363         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12364       else if (player->shield_normal_time_left)
12365         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12366     }
12367   }
12368
12369   CheckLevelTime();
12370
12371   DrawAllPlayers();
12372   PlayAllPlayersSound();
12373
12374   if (options.debug)                    /* calculate frames per second */
12375   {
12376     static unsigned long fps_counter = 0;
12377     static int fps_frames = 0;
12378     unsigned long fps_delay_ms = Counter() - fps_counter;
12379
12380     fps_frames++;
12381
12382     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12383     {
12384       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12385
12386       fps_frames = 0;
12387       fps_counter = Counter();
12388     }
12389
12390     redraw_mask |= REDRAW_FPS;
12391   }
12392
12393   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12394
12395   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12396   {
12397     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12398
12399     local_player->show_envelope = 0;
12400   }
12401
12402 #if 0
12403   debug_print_timestamp(0, "stop main loop profiling ");
12404   printf("----------------------------------------------------------\n");
12405 #endif
12406
12407   /* use random number generator in every frame to make it less predictable */
12408   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12409     RND(1);
12410 }
12411
12412 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12413 {
12414   int min_x = x, min_y = y, max_x = x, max_y = y;
12415   int i;
12416
12417   for (i = 0; i < MAX_PLAYERS; i++)
12418   {
12419     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12420
12421     if (!stored_player[i].active || &stored_player[i] == player)
12422       continue;
12423
12424     min_x = MIN(min_x, jx);
12425     min_y = MIN(min_y, jy);
12426     max_x = MAX(max_x, jx);
12427     max_y = MAX(max_y, jy);
12428   }
12429
12430   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12431 }
12432
12433 static boolean AllPlayersInVisibleScreen()
12434 {
12435   int i;
12436
12437   for (i = 0; i < MAX_PLAYERS; i++)
12438   {
12439     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12440
12441     if (!stored_player[i].active)
12442       continue;
12443
12444     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12445       return FALSE;
12446   }
12447
12448   return TRUE;
12449 }
12450
12451 void ScrollLevel(int dx, int dy)
12452 {
12453 #if 1
12454   static Bitmap *bitmap_db_field2 = NULL;
12455   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12456   int x, y;
12457 #else
12458   int i, x, y;
12459 #endif
12460
12461 #if 0
12462   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12463   /* only horizontal XOR vertical scroll direction allowed */
12464   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12465     return;
12466 #endif
12467
12468 #if 1
12469   if (bitmap_db_field2 == NULL)
12470     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12471
12472   /* needed when blitting directly to same bitmap -- should not be needed with
12473      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12474   BlitBitmap(drawto_field, bitmap_db_field2,
12475              FX + TILEX * (dx == -1) - softscroll_offset,
12476              FY + TILEY * (dy == -1) - softscroll_offset,
12477              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12478              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12479              FX + TILEX * (dx == 1) - softscroll_offset,
12480              FY + TILEY * (dy == 1) - softscroll_offset);
12481   BlitBitmap(bitmap_db_field2, drawto_field,
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
12489 #else
12490
12491 #if 0
12492   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12493   int xsize = (BX2 - BX1 + 1);
12494   int ysize = (BY2 - BY1 + 1);
12495   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12496   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12497   int step  = (start < end ? +1 : -1);
12498
12499   for (i = start; i != end; i += step)
12500   {
12501     BlitBitmap(drawto_field, drawto_field,
12502                FX + TILEX * (dx != 0 ? i + step : 0),
12503                FY + TILEY * (dy != 0 ? i + step : 0),
12504                TILEX * (dx != 0 ? 1 : xsize),
12505                TILEY * (dy != 0 ? 1 : ysize),
12506                FX + TILEX * (dx != 0 ? i : 0),
12507                FY + TILEY * (dy != 0 ? i : 0));
12508   }
12509
12510 #else
12511
12512   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12513
12514   BlitBitmap(drawto_field, drawto_field,
12515              FX + TILEX * (dx == -1) - softscroll_offset,
12516              FY + TILEY * (dy == -1) - softscroll_offset,
12517              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12518              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12519              FX + TILEX * (dx == 1) - softscroll_offset,
12520              FY + TILEY * (dy == 1) - softscroll_offset);
12521 #endif
12522 #endif
12523
12524   if (dx != 0)
12525   {
12526     x = (dx == 1 ? BX1 : BX2);
12527     for (y = BY1; y <= BY2; y++)
12528       DrawScreenField(x, y);
12529   }
12530
12531   if (dy != 0)
12532   {
12533     y = (dy == 1 ? BY1 : BY2);
12534     for (x = BX1; x <= BX2; x++)
12535       DrawScreenField(x, y);
12536   }
12537
12538   redraw_mask |= REDRAW_FIELD;
12539 }
12540
12541 static boolean canFallDown(struct PlayerInfo *player)
12542 {
12543   int jx = player->jx, jy = player->jy;
12544
12545   return (IN_LEV_FIELD(jx, jy + 1) &&
12546           (IS_FREE(jx, jy + 1) ||
12547            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12548           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12549           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12550 }
12551
12552 static boolean canPassField(int x, int y, int move_dir)
12553 {
12554   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12555   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12556   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12557   int nextx = x + dx;
12558   int nexty = y + dy;
12559   int element = Feld[x][y];
12560
12561   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12562           !CAN_MOVE(element) &&
12563           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12564           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12565           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12566 }
12567
12568 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12569 {
12570   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12571   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12572   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12573   int newx = x + dx;
12574   int newy = y + dy;
12575
12576   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12577           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12578           (IS_DIGGABLE(Feld[newx][newy]) ||
12579            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12580            canPassField(newx, newy, move_dir)));
12581 }
12582
12583 static void CheckGravityMovement(struct PlayerInfo *player)
12584 {
12585 #if USE_PLAYER_GRAVITY
12586   if (player->gravity && !player->programmed_action)
12587 #else
12588   if (game.gravity && !player->programmed_action)
12589 #endif
12590   {
12591     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12592     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12593     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12594     int jx = player->jx, jy = player->jy;
12595     boolean player_is_moving_to_valid_field =
12596       (!player_is_snapping &&
12597        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12598         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12599     boolean player_can_fall_down = canFallDown(player);
12600
12601     if (player_can_fall_down &&
12602         !player_is_moving_to_valid_field)
12603       player->programmed_action = MV_DOWN;
12604   }
12605 }
12606
12607 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12608 {
12609   return CheckGravityMovement(player);
12610
12611 #if USE_PLAYER_GRAVITY
12612   if (player->gravity && !player->programmed_action)
12613 #else
12614   if (game.gravity && !player->programmed_action)
12615 #endif
12616   {
12617     int jx = player->jx, jy = player->jy;
12618     boolean field_under_player_is_free =
12619       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12620     boolean player_is_standing_on_valid_field =
12621       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12622        (IS_WALKABLE(Feld[jx][jy]) &&
12623         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12624
12625     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12626       player->programmed_action = MV_DOWN;
12627   }
12628 }
12629
12630 /*
12631   MovePlayerOneStep()
12632   -----------------------------------------------------------------------------
12633   dx, dy:               direction (non-diagonal) to try to move the player to
12634   real_dx, real_dy:     direction as read from input device (can be diagonal)
12635 */
12636
12637 boolean MovePlayerOneStep(struct PlayerInfo *player,
12638                           int dx, int dy, int real_dx, int real_dy)
12639 {
12640   int jx = player->jx, jy = player->jy;
12641   int new_jx = jx + dx, new_jy = jy + dy;
12642 #if !USE_FIXED_DONT_RUN_INTO
12643   int element;
12644 #endif
12645   int can_move;
12646   boolean player_can_move = !player->cannot_move;
12647
12648   if (!player->active || (!dx && !dy))
12649     return MP_NO_ACTION;
12650
12651   player->MovDir = (dx < 0 ? MV_LEFT :
12652                     dx > 0 ? MV_RIGHT :
12653                     dy < 0 ? MV_UP :
12654                     dy > 0 ? MV_DOWN :  MV_NONE);
12655
12656   if (!IN_LEV_FIELD(new_jx, new_jy))
12657     return MP_NO_ACTION;
12658
12659   if (!player_can_move)
12660   {
12661     if (player->MovPos == 0)
12662     {
12663       player->is_moving = FALSE;
12664       player->is_digging = FALSE;
12665       player->is_collecting = FALSE;
12666       player->is_snapping = FALSE;
12667       player->is_pushing = FALSE;
12668     }
12669   }
12670
12671 #if 1
12672   if (!options.network && game.centered_player_nr == -1 &&
12673       !AllPlayersInSight(player, new_jx, new_jy))
12674     return MP_NO_ACTION;
12675 #else
12676   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12677     return MP_NO_ACTION;
12678 #endif
12679
12680 #if !USE_FIXED_DONT_RUN_INTO
12681   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12682
12683   /* (moved to DigField()) */
12684   if (player_can_move && DONT_RUN_INTO(element))
12685   {
12686     if (element == EL_ACID && dx == 0 && dy == 1)
12687     {
12688       SplashAcid(new_jx, new_jy);
12689       Feld[jx][jy] = EL_PLAYER_1;
12690       InitMovingField(jx, jy, MV_DOWN);
12691       Store[jx][jy] = EL_ACID;
12692       ContinueMoving(jx, jy);
12693       BuryPlayer(player);
12694     }
12695     else
12696       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12697
12698     return MP_MOVING;
12699   }
12700 #endif
12701
12702   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12703   if (can_move != MP_MOVING)
12704     return can_move;
12705
12706   /* check if DigField() has caused relocation of the player */
12707   if (player->jx != jx || player->jy != jy)
12708     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12709
12710   StorePlayer[jx][jy] = 0;
12711   player->last_jx = jx;
12712   player->last_jy = jy;
12713   player->jx = new_jx;
12714   player->jy = new_jy;
12715   StorePlayer[new_jx][new_jy] = player->element_nr;
12716
12717   if (player->move_delay_value_next != -1)
12718   {
12719     player->move_delay_value = player->move_delay_value_next;
12720     player->move_delay_value_next = -1;
12721   }
12722
12723   player->MovPos =
12724     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12725
12726   player->step_counter++;
12727
12728   PlayerVisit[jx][jy] = FrameCounter;
12729
12730 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12731   player->is_moving = TRUE;
12732 #endif
12733
12734 #if 1
12735   /* should better be called in MovePlayer(), but this breaks some tapes */
12736   ScrollPlayer(player, SCROLL_INIT);
12737 #endif
12738
12739   return MP_MOVING;
12740 }
12741
12742 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12743 {
12744   int jx = player->jx, jy = player->jy;
12745   int old_jx = jx, old_jy = jy;
12746   int moved = MP_NO_ACTION;
12747
12748   if (!player->active)
12749     return FALSE;
12750
12751   if (!dx && !dy)
12752   {
12753     if (player->MovPos == 0)
12754     {
12755       player->is_moving = FALSE;
12756       player->is_digging = FALSE;
12757       player->is_collecting = FALSE;
12758       player->is_snapping = FALSE;
12759       player->is_pushing = FALSE;
12760     }
12761
12762     return FALSE;
12763   }
12764
12765   if (player->move_delay > 0)
12766     return FALSE;
12767
12768   player->move_delay = -1;              /* set to "uninitialized" value */
12769
12770   /* store if player is automatically moved to next field */
12771   player->is_auto_moving = (player->programmed_action != MV_NONE);
12772
12773   /* remove the last programmed player action */
12774   player->programmed_action = 0;
12775
12776   if (player->MovPos)
12777   {
12778     /* should only happen if pre-1.2 tape recordings are played */
12779     /* this is only for backward compatibility */
12780
12781     int original_move_delay_value = player->move_delay_value;
12782
12783 #if DEBUG
12784     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12785            tape.counter);
12786 #endif
12787
12788     /* scroll remaining steps with finest movement resolution */
12789     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12790
12791     while (player->MovPos)
12792     {
12793       ScrollPlayer(player, SCROLL_GO_ON);
12794       ScrollScreen(NULL, SCROLL_GO_ON);
12795
12796       AdvanceFrameAndPlayerCounters(player->index_nr);
12797
12798       DrawAllPlayers();
12799       BackToFront();
12800     }
12801
12802     player->move_delay_value = original_move_delay_value;
12803   }
12804
12805   player->is_active = FALSE;
12806
12807   if (player->last_move_dir & MV_HORIZONTAL)
12808   {
12809     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12810       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12811   }
12812   else
12813   {
12814     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12815       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12816   }
12817
12818 #if USE_FIXED_BORDER_RUNNING_GFX
12819   if (!moved && !player->is_active)
12820   {
12821     player->is_moving = FALSE;
12822     player->is_digging = FALSE;
12823     player->is_collecting = FALSE;
12824     player->is_snapping = FALSE;
12825     player->is_pushing = FALSE;
12826   }
12827 #endif
12828
12829   jx = player->jx;
12830   jy = player->jy;
12831
12832 #if 1
12833   if (moved & MP_MOVING && !ScreenMovPos &&
12834       (player->index_nr == game.centered_player_nr ||
12835        game.centered_player_nr == -1))
12836 #else
12837   if (moved & MP_MOVING && !ScreenMovPos &&
12838       (player == local_player || !options.network))
12839 #endif
12840   {
12841     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12842     int offset = game.scroll_delay_value;
12843
12844     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12845     {
12846       /* actual player has left the screen -- scroll in that direction */
12847       if (jx != old_jx)         /* player has moved horizontally */
12848         scroll_x += (jx - old_jx);
12849       else                      /* player has moved vertically */
12850         scroll_y += (jy - old_jy);
12851     }
12852     else
12853     {
12854       if (jx != old_jx)         /* player has moved horizontally */
12855       {
12856         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12857             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12858           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12859
12860         /* don't scroll over playfield boundaries */
12861         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12862           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12863
12864         /* don't scroll more than one field at a time */
12865         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12866
12867         /* don't scroll against the player's moving direction */
12868         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12869             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12870           scroll_x = old_scroll_x;
12871       }
12872       else                      /* player has moved vertically */
12873       {
12874         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12875             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12876           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12877
12878         /* don't scroll over playfield boundaries */
12879         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12880           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12881
12882         /* don't scroll more than one field at a time */
12883         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12884
12885         /* don't scroll against the player's moving direction */
12886         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12887             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12888           scroll_y = old_scroll_y;
12889       }
12890     }
12891
12892     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12893     {
12894 #if 1
12895       if (!options.network && game.centered_player_nr == -1 &&
12896           !AllPlayersInVisibleScreen())
12897       {
12898         scroll_x = old_scroll_x;
12899         scroll_y = old_scroll_y;
12900       }
12901       else
12902 #else
12903       if (!options.network && !AllPlayersInVisibleScreen())
12904       {
12905         scroll_x = old_scroll_x;
12906         scroll_y = old_scroll_y;
12907       }
12908       else
12909 #endif
12910       {
12911         ScrollScreen(player, SCROLL_INIT);
12912         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12913       }
12914     }
12915   }
12916
12917   player->StepFrame = 0;
12918
12919   if (moved & MP_MOVING)
12920   {
12921     if (old_jx != jx && old_jy == jy)
12922       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12923     else if (old_jx == jx && old_jy != jy)
12924       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12925
12926     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12927
12928     player->last_move_dir = player->MovDir;
12929     player->is_moving = TRUE;
12930     player->is_snapping = FALSE;
12931     player->is_switching = FALSE;
12932     player->is_dropping = FALSE;
12933     player->is_dropping_pressed = FALSE;
12934     player->drop_pressed_delay = 0;
12935
12936 #if 0
12937     /* should better be called here than above, but this breaks some tapes */
12938     ScrollPlayer(player, SCROLL_INIT);
12939 #endif
12940   }
12941   else
12942   {
12943     CheckGravityMovementWhenNotMoving(player);
12944
12945     player->is_moving = FALSE;
12946
12947     /* at this point, the player is allowed to move, but cannot move right now
12948        (e.g. because of something blocking the way) -- ensure that the player
12949        is also allowed to move in the next frame (in old versions before 3.1.1,
12950        the player was forced to wait again for eight frames before next try) */
12951
12952     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12953       player->move_delay = 0;   /* allow direct movement in the next frame */
12954   }
12955
12956   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12957     player->move_delay = player->move_delay_value;
12958
12959   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12960   {
12961     TestIfPlayerTouchesBadThing(jx, jy);
12962     TestIfPlayerTouchesCustomElement(jx, jy);
12963   }
12964
12965   if (!player->active)
12966     RemovePlayer(player);
12967
12968   return moved;
12969 }
12970
12971 void ScrollPlayer(struct PlayerInfo *player, int mode)
12972 {
12973   int jx = player->jx, jy = player->jy;
12974   int last_jx = player->last_jx, last_jy = player->last_jy;
12975   int move_stepsize = TILEX / player->move_delay_value;
12976
12977 #if USE_NEW_PLAYER_SPEED
12978   if (!player->active)
12979     return;
12980
12981   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12982     return;
12983 #else
12984   if (!player->active || player->MovPos == 0)
12985     return;
12986 #endif
12987
12988   if (mode == SCROLL_INIT)
12989   {
12990     player->actual_frame_counter = FrameCounter;
12991     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12992
12993     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12994         Feld[last_jx][last_jy] == EL_EMPTY)
12995     {
12996       int last_field_block_delay = 0;   /* start with no blocking at all */
12997       int block_delay_adjustment = player->block_delay_adjustment;
12998
12999       /* if player blocks last field, add delay for exactly one move */
13000       if (player->block_last_field)
13001       {
13002         last_field_block_delay += player->move_delay_value;
13003
13004         /* when blocking enabled, prevent moving up despite gravity */
13005 #if USE_PLAYER_GRAVITY
13006         if (player->gravity && player->MovDir == MV_UP)
13007           block_delay_adjustment = -1;
13008 #else
13009         if (game.gravity && player->MovDir == MV_UP)
13010           block_delay_adjustment = -1;
13011 #endif
13012       }
13013
13014       /* add block delay adjustment (also possible when not blocking) */
13015       last_field_block_delay += block_delay_adjustment;
13016
13017       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13018       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13019     }
13020
13021 #if USE_NEW_PLAYER_SPEED
13022     if (player->MovPos != 0)    /* player has not yet reached destination */
13023       return;
13024 #else
13025     return;
13026 #endif
13027   }
13028   else if (!FrameReached(&player->actual_frame_counter, 1))
13029     return;
13030
13031 #if USE_NEW_PLAYER_SPEED
13032   if (player->MovPos != 0)
13033   {
13034     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13035     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13036
13037     /* before DrawPlayer() to draw correct player graphic for this case */
13038     if (player->MovPos == 0)
13039       CheckGravityMovement(player);
13040   }
13041 #else
13042   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13043   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13044
13045   /* before DrawPlayer() to draw correct player graphic for this case */
13046   if (player->MovPos == 0)
13047     CheckGravityMovement(player);
13048 #endif
13049
13050   if (player->MovPos == 0)      /* player reached destination field */
13051   {
13052     if (player->move_delay_reset_counter > 0)
13053     {
13054       player->move_delay_reset_counter--;
13055
13056       if (player->move_delay_reset_counter == 0)
13057       {
13058         /* continue with normal speed after quickly moving through gate */
13059         HALVE_PLAYER_SPEED(player);
13060
13061         /* be able to make the next move without delay */
13062         player->move_delay = 0;
13063       }
13064     }
13065
13066     player->last_jx = jx;
13067     player->last_jy = jy;
13068
13069     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13070         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13071         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13072         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13073         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13074         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13075     {
13076       DrawPlayer(player);       /* needed here only to cleanup last field */
13077       RemovePlayer(player);
13078
13079       if (local_player->friends_still_needed == 0 ||
13080           IS_SP_ELEMENT(Feld[jx][jy]))
13081         PlayerWins(player);
13082     }
13083
13084     /* this breaks one level: "machine", level 000 */
13085     {
13086       int move_direction = player->MovDir;
13087       int enter_side = MV_DIR_OPPOSITE(move_direction);
13088       int leave_side = move_direction;
13089       int old_jx = last_jx;
13090       int old_jy = last_jy;
13091       int old_element = Feld[old_jx][old_jy];
13092       int new_element = Feld[jx][jy];
13093
13094       if (IS_CUSTOM_ELEMENT(old_element))
13095         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13096                                    CE_LEFT_BY_PLAYER,
13097                                    player->index_bit, leave_side);
13098
13099       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13100                                           CE_PLAYER_LEAVES_X,
13101                                           player->index_bit, leave_side);
13102
13103       if (IS_CUSTOM_ELEMENT(new_element))
13104         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13105                                    player->index_bit, enter_side);
13106
13107       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13108                                           CE_PLAYER_ENTERS_X,
13109                                           player->index_bit, enter_side);
13110
13111       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13112                                         CE_MOVE_OF_X, move_direction);
13113     }
13114
13115     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13116     {
13117       TestIfPlayerTouchesBadThing(jx, jy);
13118       TestIfPlayerTouchesCustomElement(jx, jy);
13119
13120       /* needed because pushed element has not yet reached its destination,
13121          so it would trigger a change event at its previous field location */
13122       if (!player->is_pushing)
13123         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13124
13125       if (!player->active)
13126         RemovePlayer(player);
13127     }
13128
13129     if (!local_player->LevelSolved && level.use_step_counter)
13130     {
13131       int i;
13132
13133       TimePlayed++;
13134
13135       if (TimeLeft > 0)
13136       {
13137         TimeLeft--;
13138
13139         if (TimeLeft <= 10 && setup.time_limit)
13140           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13141
13142 #if 1
13143         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13144
13145         DisplayGameControlValues();
13146 #else
13147         DrawGameValue_Time(TimeLeft);
13148 #endif
13149
13150         if (!TimeLeft && setup.time_limit)
13151           for (i = 0; i < MAX_PLAYERS; i++)
13152             KillPlayer(&stored_player[i]);
13153       }
13154 #if 1
13155       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13156       {
13157         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13158
13159         DisplayGameControlValues();
13160       }
13161 #else
13162       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13163         DrawGameValue_Time(TimePlayed);
13164 #endif
13165     }
13166
13167     if (tape.single_step && tape.recording && !tape.pausing &&
13168         !player->programmed_action)
13169       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13170   }
13171 }
13172
13173 void ScrollScreen(struct PlayerInfo *player, int mode)
13174 {
13175   static unsigned long screen_frame_counter = 0;
13176
13177   if (mode == SCROLL_INIT)
13178   {
13179     /* set scrolling step size according to actual player's moving speed */
13180     ScrollStepSize = TILEX / player->move_delay_value;
13181
13182     screen_frame_counter = FrameCounter;
13183     ScreenMovDir = player->MovDir;
13184     ScreenMovPos = player->MovPos;
13185     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13186     return;
13187   }
13188   else if (!FrameReached(&screen_frame_counter, 1))
13189     return;
13190
13191   if (ScreenMovPos)
13192   {
13193     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13194     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13195     redraw_mask |= REDRAW_FIELD;
13196   }
13197   else
13198     ScreenMovDir = MV_NONE;
13199 }
13200
13201 void TestIfPlayerTouchesCustomElement(int x, int y)
13202 {
13203   static int xy[4][2] =
13204   {
13205     { 0, -1 },
13206     { -1, 0 },
13207     { +1, 0 },
13208     { 0, +1 }
13209   };
13210   static int trigger_sides[4][2] =
13211   {
13212     /* center side       border side */
13213     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13214     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13215     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13216     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13217   };
13218   static int touch_dir[4] =
13219   {
13220     MV_LEFT | MV_RIGHT,
13221     MV_UP   | MV_DOWN,
13222     MV_UP   | MV_DOWN,
13223     MV_LEFT | MV_RIGHT
13224   };
13225   int center_element = Feld[x][y];      /* should always be non-moving! */
13226   int i;
13227
13228   for (i = 0; i < NUM_DIRECTIONS; i++)
13229   {
13230     int xx = x + xy[i][0];
13231     int yy = y + xy[i][1];
13232     int center_side = trigger_sides[i][0];
13233     int border_side = trigger_sides[i][1];
13234     int border_element;
13235
13236     if (!IN_LEV_FIELD(xx, yy))
13237       continue;
13238
13239     if (IS_PLAYER(x, y))
13240     {
13241       struct PlayerInfo *player = PLAYERINFO(x, y);
13242
13243       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13244         border_element = Feld[xx][yy];          /* may be moving! */
13245       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13246         border_element = Feld[xx][yy];
13247       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13248         border_element = MovingOrBlocked2Element(xx, yy);
13249       else
13250         continue;               /* center and border element do not touch */
13251
13252       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13253                                  player->index_bit, border_side);
13254       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13255                                           CE_PLAYER_TOUCHES_X,
13256                                           player->index_bit, border_side);
13257     }
13258     else if (IS_PLAYER(xx, yy))
13259     {
13260       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13261
13262       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13263       {
13264         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13265           continue;             /* center and border element do not touch */
13266       }
13267
13268       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13269                                  player->index_bit, center_side);
13270       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13271                                           CE_PLAYER_TOUCHES_X,
13272                                           player->index_bit, center_side);
13273       break;
13274     }
13275   }
13276 }
13277
13278 #if USE_ELEMENT_TOUCHING_BUGFIX
13279
13280 void TestIfElementTouchesCustomElement(int x, int y)
13281 {
13282   static int xy[4][2] =
13283   {
13284     { 0, -1 },
13285     { -1, 0 },
13286     { +1, 0 },
13287     { 0, +1 }
13288   };
13289   static int trigger_sides[4][2] =
13290   {
13291     /* center side      border side */
13292     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13293     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13294     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13295     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13296   };
13297   static int touch_dir[4] =
13298   {
13299     MV_LEFT | MV_RIGHT,
13300     MV_UP   | MV_DOWN,
13301     MV_UP   | MV_DOWN,
13302     MV_LEFT | MV_RIGHT
13303   };
13304   boolean change_center_element = FALSE;
13305   int center_element = Feld[x][y];      /* should always be non-moving! */
13306   int border_element_old[NUM_DIRECTIONS];
13307   int i;
13308
13309   for (i = 0; i < NUM_DIRECTIONS; i++)
13310   {
13311     int xx = x + xy[i][0];
13312     int yy = y + xy[i][1];
13313     int border_element;
13314
13315     border_element_old[i] = -1;
13316
13317     if (!IN_LEV_FIELD(xx, yy))
13318       continue;
13319
13320     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13321       border_element = Feld[xx][yy];    /* may be moving! */
13322     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13323       border_element = Feld[xx][yy];
13324     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13325       border_element = MovingOrBlocked2Element(xx, yy);
13326     else
13327       continue;                 /* center and border element do not touch */
13328
13329     border_element_old[i] = border_element;
13330   }
13331
13332   for (i = 0; i < NUM_DIRECTIONS; i++)
13333   {
13334     int xx = x + xy[i][0];
13335     int yy = y + xy[i][1];
13336     int center_side = trigger_sides[i][0];
13337     int border_element = border_element_old[i];
13338
13339     if (border_element == -1)
13340       continue;
13341
13342     /* check for change of border element */
13343     CheckElementChangeBySide(xx, yy, border_element, center_element,
13344                              CE_TOUCHING_X, center_side);
13345   }
13346
13347   for (i = 0; i < NUM_DIRECTIONS; i++)
13348   {
13349     int border_side = trigger_sides[i][1];
13350     int border_element = border_element_old[i];
13351
13352     if (border_element == -1)
13353       continue;
13354
13355     /* check for change of center element (but change it only once) */
13356     if (!change_center_element)
13357       change_center_element =
13358         CheckElementChangeBySide(x, y, center_element, border_element,
13359                                  CE_TOUCHING_X, border_side);
13360   }
13361 }
13362
13363 #else
13364
13365 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13366 {
13367   static int xy[4][2] =
13368   {
13369     { 0, -1 },
13370     { -1, 0 },
13371     { +1, 0 },
13372     { 0, +1 }
13373   };
13374   static int trigger_sides[4][2] =
13375   {
13376     /* center side      border side */
13377     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13378     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13379     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13380     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13381   };
13382   static int touch_dir[4] =
13383   {
13384     MV_LEFT | MV_RIGHT,
13385     MV_UP   | MV_DOWN,
13386     MV_UP   | MV_DOWN,
13387     MV_LEFT | MV_RIGHT
13388   };
13389   boolean change_center_element = FALSE;
13390   int center_element = Feld[x][y];      /* should always be non-moving! */
13391   int i;
13392
13393   for (i = 0; i < NUM_DIRECTIONS; i++)
13394   {
13395     int xx = x + xy[i][0];
13396     int yy = y + xy[i][1];
13397     int center_side = trigger_sides[i][0];
13398     int border_side = trigger_sides[i][1];
13399     int border_element;
13400
13401     if (!IN_LEV_FIELD(xx, yy))
13402       continue;
13403
13404     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13405       border_element = Feld[xx][yy];    /* may be moving! */
13406     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13407       border_element = Feld[xx][yy];
13408     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13409       border_element = MovingOrBlocked2Element(xx, yy);
13410     else
13411       continue;                 /* center and border element do not touch */
13412
13413     /* check for change of center element (but change it only once) */
13414     if (!change_center_element)
13415       change_center_element =
13416         CheckElementChangeBySide(x, y, center_element, border_element,
13417                                  CE_TOUCHING_X, border_side);
13418
13419     /* check for change of border element */
13420     CheckElementChangeBySide(xx, yy, border_element, center_element,
13421                              CE_TOUCHING_X, center_side);
13422   }
13423 }
13424
13425 #endif
13426
13427 void TestIfElementHitsCustomElement(int x, int y, int direction)
13428 {
13429   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13430   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13431   int hitx = x + dx, hity = y + dy;
13432   int hitting_element = Feld[x][y];
13433   int touched_element;
13434
13435   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13436     return;
13437
13438   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13439                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13440
13441   if (IN_LEV_FIELD(hitx, hity))
13442   {
13443     int opposite_direction = MV_DIR_OPPOSITE(direction);
13444     int hitting_side = direction;
13445     int touched_side = opposite_direction;
13446     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13447                           MovDir[hitx][hity] != direction ||
13448                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13449
13450     object_hit = TRUE;
13451
13452     if (object_hit)
13453     {
13454       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13455                                CE_HITTING_X, touched_side);
13456
13457       CheckElementChangeBySide(hitx, hity, touched_element,
13458                                hitting_element, CE_HIT_BY_X, hitting_side);
13459
13460       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13461                                CE_HIT_BY_SOMETHING, opposite_direction);
13462     }
13463   }
13464
13465   /* "hitting something" is also true when hitting the playfield border */
13466   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13467                            CE_HITTING_SOMETHING, direction);
13468 }
13469
13470 #if 0
13471 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13472 {
13473   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13474   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13475   int hitx = x + dx, hity = y + dy;
13476   int hitting_element = Feld[x][y];
13477   int touched_element;
13478 #if 0
13479   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13480                         !IS_FREE(hitx, hity) &&
13481                         (!IS_MOVING(hitx, hity) ||
13482                          MovDir[hitx][hity] != direction ||
13483                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13484 #endif
13485
13486   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13487     return;
13488
13489 #if 0
13490   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13491     return;
13492 #endif
13493
13494   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13495                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13496
13497   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13498                            EP_CAN_SMASH_EVERYTHING, direction);
13499
13500   if (IN_LEV_FIELD(hitx, hity))
13501   {
13502     int opposite_direction = MV_DIR_OPPOSITE(direction);
13503     int hitting_side = direction;
13504     int touched_side = opposite_direction;
13505 #if 0
13506     int touched_element = MovingOrBlocked2Element(hitx, hity);
13507 #endif
13508 #if 1
13509     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13510                           MovDir[hitx][hity] != direction ||
13511                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13512
13513     object_hit = TRUE;
13514 #endif
13515
13516     if (object_hit)
13517     {
13518       int i;
13519
13520       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13521                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13522
13523       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13524                                CE_OTHER_IS_SMASHING, touched_side);
13525
13526       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13527                                CE_OTHER_GETS_SMASHED, hitting_side);
13528     }
13529   }
13530 }
13531 #endif
13532
13533 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13534 {
13535   int i, kill_x = -1, kill_y = -1;
13536
13537   int bad_element = -1;
13538   static int test_xy[4][2] =
13539   {
13540     { 0, -1 },
13541     { -1, 0 },
13542     { +1, 0 },
13543     { 0, +1 }
13544   };
13545   static int test_dir[4] =
13546   {
13547     MV_UP,
13548     MV_LEFT,
13549     MV_RIGHT,
13550     MV_DOWN
13551   };
13552
13553   for (i = 0; i < NUM_DIRECTIONS; i++)
13554   {
13555     int test_x, test_y, test_move_dir, test_element;
13556
13557     test_x = good_x + test_xy[i][0];
13558     test_y = good_y + test_xy[i][1];
13559
13560     if (!IN_LEV_FIELD(test_x, test_y))
13561       continue;
13562
13563     test_move_dir =
13564       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13565
13566     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13567
13568     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13569        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13570     */
13571     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13572         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13573     {
13574       kill_x = test_x;
13575       kill_y = test_y;
13576       bad_element = test_element;
13577
13578       break;
13579     }
13580   }
13581
13582   if (kill_x != -1 || kill_y != -1)
13583   {
13584     if (IS_PLAYER(good_x, good_y))
13585     {
13586       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13587
13588       if (player->shield_deadly_time_left > 0 &&
13589           !IS_INDESTRUCTIBLE(bad_element))
13590         Bang(kill_x, kill_y);
13591       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13592         KillPlayer(player);
13593     }
13594     else
13595       Bang(good_x, good_y);
13596   }
13597 }
13598
13599 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13600 {
13601   int i, kill_x = -1, kill_y = -1;
13602   int bad_element = Feld[bad_x][bad_y];
13603   static int test_xy[4][2] =
13604   {
13605     { 0, -1 },
13606     { -1, 0 },
13607     { +1, 0 },
13608     { 0, +1 }
13609   };
13610   static int touch_dir[4] =
13611   {
13612     MV_LEFT | MV_RIGHT,
13613     MV_UP   | MV_DOWN,
13614     MV_UP   | MV_DOWN,
13615     MV_LEFT | MV_RIGHT
13616   };
13617   static int test_dir[4] =
13618   {
13619     MV_UP,
13620     MV_LEFT,
13621     MV_RIGHT,
13622     MV_DOWN
13623   };
13624
13625   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13626     return;
13627
13628   for (i = 0; i < NUM_DIRECTIONS; i++)
13629   {
13630     int test_x, test_y, test_move_dir, test_element;
13631
13632     test_x = bad_x + test_xy[i][0];
13633     test_y = bad_y + test_xy[i][1];
13634     if (!IN_LEV_FIELD(test_x, test_y))
13635       continue;
13636
13637     test_move_dir =
13638       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13639
13640     test_element = Feld[test_x][test_y];
13641
13642     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13643        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13644     */
13645     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13646         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13647     {
13648       /* good thing is player or penguin that does not move away */
13649       if (IS_PLAYER(test_x, test_y))
13650       {
13651         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13652
13653         if (bad_element == EL_ROBOT && player->is_moving)
13654           continue;     /* robot does not kill player if he is moving */
13655
13656         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13657         {
13658           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13659             continue;           /* center and border element do not touch */
13660         }
13661
13662         kill_x = test_x;
13663         kill_y = test_y;
13664         break;
13665       }
13666       else if (test_element == EL_PENGUIN)
13667       {
13668         kill_x = test_x;
13669         kill_y = test_y;
13670         break;
13671       }
13672     }
13673   }
13674
13675   if (kill_x != -1 || kill_y != -1)
13676   {
13677     if (IS_PLAYER(kill_x, kill_y))
13678     {
13679       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13680
13681       if (player->shield_deadly_time_left > 0 &&
13682           !IS_INDESTRUCTIBLE(bad_element))
13683         Bang(bad_x, bad_y);
13684       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13685         KillPlayer(player);
13686     }
13687     else
13688       Bang(kill_x, kill_y);
13689   }
13690 }
13691
13692 void TestIfPlayerTouchesBadThing(int x, int y)
13693 {
13694   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13695 }
13696
13697 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13698 {
13699   TestIfGoodThingHitsBadThing(x, y, move_dir);
13700 }
13701
13702 void TestIfBadThingTouchesPlayer(int x, int y)
13703 {
13704   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13705 }
13706
13707 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13708 {
13709   TestIfBadThingHitsGoodThing(x, y, move_dir);
13710 }
13711
13712 void TestIfFriendTouchesBadThing(int x, int y)
13713 {
13714   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13715 }
13716
13717 void TestIfBadThingTouchesFriend(int x, int y)
13718 {
13719   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13720 }
13721
13722 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13723 {
13724   int i, kill_x = bad_x, kill_y = bad_y;
13725   static int xy[4][2] =
13726   {
13727     { 0, -1 },
13728     { -1, 0 },
13729     { +1, 0 },
13730     { 0, +1 }
13731   };
13732
13733   for (i = 0; i < NUM_DIRECTIONS; i++)
13734   {
13735     int x, y, element;
13736
13737     x = bad_x + xy[i][0];
13738     y = bad_y + xy[i][1];
13739     if (!IN_LEV_FIELD(x, y))
13740       continue;
13741
13742     element = Feld[x][y];
13743     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13744         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13745     {
13746       kill_x = x;
13747       kill_y = y;
13748       break;
13749     }
13750   }
13751
13752   if (kill_x != bad_x || kill_y != bad_y)
13753     Bang(bad_x, bad_y);
13754 }
13755
13756 void KillPlayer(struct PlayerInfo *player)
13757 {
13758   int jx = player->jx, jy = player->jy;
13759
13760   if (!player->active)
13761     return;
13762
13763   /* the following code was introduced to prevent an infinite loop when calling
13764      -> Bang()
13765      -> CheckTriggeredElementChangeExt()
13766      -> ExecuteCustomElementAction()
13767      -> KillPlayer()
13768      -> (infinitely repeating the above sequence of function calls)
13769      which occurs when killing the player while having a CE with the setting
13770      "kill player X when explosion of <player X>"; the solution using a new
13771      field "player->killed" was chosen for backwards compatibility, although
13772      clever use of the fields "player->active" etc. would probably also work */
13773 #if 1
13774   if (player->killed)
13775     return;
13776 #endif
13777
13778   player->killed = TRUE;
13779
13780   /* remove accessible field at the player's position */
13781   Feld[jx][jy] = EL_EMPTY;
13782
13783   /* deactivate shield (else Bang()/Explode() would not work right) */
13784   player->shield_normal_time_left = 0;
13785   player->shield_deadly_time_left = 0;
13786
13787   Bang(jx, jy);
13788   BuryPlayer(player);
13789 }
13790
13791 static void KillPlayerUnlessEnemyProtected(int x, int y)
13792 {
13793   if (!PLAYER_ENEMY_PROTECTED(x, y))
13794     KillPlayer(PLAYERINFO(x, y));
13795 }
13796
13797 static void KillPlayerUnlessExplosionProtected(int x, int y)
13798 {
13799   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13800     KillPlayer(PLAYERINFO(x, y));
13801 }
13802
13803 void BuryPlayer(struct PlayerInfo *player)
13804 {
13805   int jx = player->jx, jy = player->jy;
13806
13807   if (!player->active)
13808     return;
13809
13810   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13811   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13812
13813   player->GameOver = TRUE;
13814   RemovePlayer(player);
13815 }
13816
13817 void RemovePlayer(struct PlayerInfo *player)
13818 {
13819   int jx = player->jx, jy = player->jy;
13820   int i, found = FALSE;
13821
13822   player->present = FALSE;
13823   player->active = FALSE;
13824
13825   if (!ExplodeField[jx][jy])
13826     StorePlayer[jx][jy] = 0;
13827
13828   if (player->is_moving)
13829     DrawLevelField(player->last_jx, player->last_jy);
13830
13831   for (i = 0; i < MAX_PLAYERS; i++)
13832     if (stored_player[i].active)
13833       found = TRUE;
13834
13835   if (!found)
13836     AllPlayersGone = TRUE;
13837
13838   ExitX = ZX = jx;
13839   ExitY = ZY = jy;
13840 }
13841
13842 #if USE_NEW_SNAP_DELAY
13843 static void setFieldForSnapping(int x, int y, int element, int direction)
13844 {
13845   struct ElementInfo *ei = &element_info[element];
13846   int direction_bit = MV_DIR_TO_BIT(direction);
13847   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13848   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13849                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13850
13851   Feld[x][y] = EL_ELEMENT_SNAPPING;
13852   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13853
13854   ResetGfxAnimation(x, y);
13855
13856   GfxElement[x][y] = element;
13857   GfxAction[x][y] = action;
13858   GfxDir[x][y] = direction;
13859   GfxFrame[x][y] = -1;
13860 }
13861 #endif
13862
13863 /*
13864   =============================================================================
13865   checkDiagonalPushing()
13866   -----------------------------------------------------------------------------
13867   check if diagonal input device direction results in pushing of object
13868   (by checking if the alternative direction is walkable, diggable, ...)
13869   =============================================================================
13870 */
13871
13872 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13873                                     int x, int y, int real_dx, int real_dy)
13874 {
13875   int jx, jy, dx, dy, xx, yy;
13876
13877   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13878     return TRUE;
13879
13880   /* diagonal direction: check alternative direction */
13881   jx = player->jx;
13882   jy = player->jy;
13883   dx = x - jx;
13884   dy = y - jy;
13885   xx = jx + (dx == 0 ? real_dx : 0);
13886   yy = jy + (dy == 0 ? real_dy : 0);
13887
13888   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13889 }
13890
13891 /*
13892   =============================================================================
13893   DigField()
13894   -----------------------------------------------------------------------------
13895   x, y:                 field next to player (non-diagonal) to try to dig to
13896   real_dx, real_dy:     direction as read from input device (can be diagonal)
13897   =============================================================================
13898 */
13899
13900 int DigField(struct PlayerInfo *player,
13901              int oldx, int oldy, int x, int y,
13902              int real_dx, int real_dy, int mode)
13903 {
13904   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13905   boolean player_was_pushing = player->is_pushing;
13906   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13907   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13908   int jx = oldx, jy = oldy;
13909   int dx = x - jx, dy = y - jy;
13910   int nextx = x + dx, nexty = y + dy;
13911   int move_direction = (dx == -1 ? MV_LEFT  :
13912                         dx == +1 ? MV_RIGHT :
13913                         dy == -1 ? MV_UP    :
13914                         dy == +1 ? MV_DOWN  : MV_NONE);
13915   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13916   int dig_side = MV_DIR_OPPOSITE(move_direction);
13917   int old_element = Feld[jx][jy];
13918 #if USE_FIXED_DONT_RUN_INTO
13919   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13920 #else
13921   int element;
13922 #endif
13923   int collect_count;
13924
13925   if (is_player)                /* function can also be called by EL_PENGUIN */
13926   {
13927     if (player->MovPos == 0)
13928     {
13929       player->is_digging = FALSE;
13930       player->is_collecting = FALSE;
13931     }
13932
13933     if (player->MovPos == 0)    /* last pushing move finished */
13934       player->is_pushing = FALSE;
13935
13936     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13937     {
13938       player->is_switching = FALSE;
13939       player->push_delay = -1;
13940
13941       return MP_NO_ACTION;
13942     }
13943   }
13944
13945 #if !USE_FIXED_DONT_RUN_INTO
13946   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13947     return MP_NO_ACTION;
13948 #endif
13949
13950   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13951     old_element = Back[jx][jy];
13952
13953   /* in case of element dropped at player position, check background */
13954   else if (Back[jx][jy] != EL_EMPTY &&
13955            game.engine_version >= VERSION_IDENT(2,2,0,0))
13956     old_element = Back[jx][jy];
13957
13958   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13959     return MP_NO_ACTION;        /* field has no opening in this direction */
13960
13961   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13962     return MP_NO_ACTION;        /* field has no opening in this direction */
13963
13964 #if USE_FIXED_DONT_RUN_INTO
13965   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13966   {
13967     SplashAcid(x, y);
13968
13969     Feld[jx][jy] = player->artwork_element;
13970     InitMovingField(jx, jy, MV_DOWN);
13971     Store[jx][jy] = EL_ACID;
13972     ContinueMoving(jx, jy);
13973     BuryPlayer(player);
13974
13975     return MP_DONT_RUN_INTO;
13976   }
13977 #endif
13978
13979 #if USE_FIXED_DONT_RUN_INTO
13980   if (player_can_move && DONT_RUN_INTO(element))
13981   {
13982     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13983
13984     return MP_DONT_RUN_INTO;
13985   }
13986 #endif
13987
13988 #if USE_FIXED_DONT_RUN_INTO
13989   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13990     return MP_NO_ACTION;
13991 #endif
13992
13993 #if !USE_FIXED_DONT_RUN_INTO
13994   element = Feld[x][y];
13995 #endif
13996
13997   collect_count = element_info[element].collect_count_initial;
13998
13999   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14000     return MP_NO_ACTION;
14001
14002   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14003     player_can_move = player_can_move_or_snap;
14004
14005   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14006       game.engine_version >= VERSION_IDENT(2,2,0,0))
14007   {
14008     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14009                                player->index_bit, dig_side);
14010     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14011                                         player->index_bit, dig_side);
14012
14013     if (element == EL_DC_LANDMINE)
14014       Bang(x, y);
14015
14016     if (Feld[x][y] != element)          /* field changed by snapping */
14017       return MP_ACTION;
14018
14019     return MP_NO_ACTION;
14020   }
14021
14022 #if USE_PLAYER_GRAVITY
14023   if (player->gravity && is_player && !player->is_auto_moving &&
14024       canFallDown(player) && move_direction != MV_DOWN &&
14025       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14026     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14027 #else
14028   if (game.gravity && is_player && !player->is_auto_moving &&
14029       canFallDown(player) && move_direction != MV_DOWN &&
14030       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14031     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14032 #endif
14033
14034   if (player_can_move &&
14035       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14036   {
14037     int sound_element = SND_ELEMENT(element);
14038     int sound_action = ACTION_WALKING;
14039
14040     if (IS_RND_GATE(element))
14041     {
14042       if (!player->key[RND_GATE_NR(element)])
14043         return MP_NO_ACTION;
14044     }
14045     else if (IS_RND_GATE_GRAY(element))
14046     {
14047       if (!player->key[RND_GATE_GRAY_NR(element)])
14048         return MP_NO_ACTION;
14049     }
14050     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14051     {
14052       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14053         return MP_NO_ACTION;
14054     }
14055     else if (element == EL_EXIT_OPEN ||
14056              element == EL_EM_EXIT_OPEN ||
14057              element == EL_STEEL_EXIT_OPEN ||
14058              element == EL_EM_STEEL_EXIT_OPEN ||
14059              element == EL_SP_EXIT_OPEN ||
14060              element == EL_SP_EXIT_OPENING)
14061     {
14062       sound_action = ACTION_PASSING;    /* player is passing exit */
14063     }
14064     else if (element == EL_EMPTY)
14065     {
14066       sound_action = ACTION_MOVING;             /* nothing to walk on */
14067     }
14068
14069     /* play sound from background or player, whatever is available */
14070     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14071       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14072     else
14073       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14074   }
14075   else if (player_can_move &&
14076            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14077   {
14078     if (!ACCESS_FROM(element, opposite_direction))
14079       return MP_NO_ACTION;      /* field not accessible from this direction */
14080
14081     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14082       return MP_NO_ACTION;
14083
14084     if (IS_EM_GATE(element))
14085     {
14086       if (!player->key[EM_GATE_NR(element)])
14087         return MP_NO_ACTION;
14088     }
14089     else if (IS_EM_GATE_GRAY(element))
14090     {
14091       if (!player->key[EM_GATE_GRAY_NR(element)])
14092         return MP_NO_ACTION;
14093     }
14094     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14095     {
14096       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14097         return MP_NO_ACTION;
14098     }
14099     else if (IS_EMC_GATE(element))
14100     {
14101       if (!player->key[EMC_GATE_NR(element)])
14102         return MP_NO_ACTION;
14103     }
14104     else if (IS_EMC_GATE_GRAY(element))
14105     {
14106       if (!player->key[EMC_GATE_GRAY_NR(element)])
14107         return MP_NO_ACTION;
14108     }
14109     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14110     {
14111       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14112         return MP_NO_ACTION;
14113     }
14114     else if (element == EL_DC_GATE_WHITE ||
14115              element == EL_DC_GATE_WHITE_GRAY ||
14116              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14117     {
14118       if (player->num_white_keys == 0)
14119         return MP_NO_ACTION;
14120
14121       player->num_white_keys--;
14122     }
14123     else if (IS_SP_PORT(element))
14124     {
14125       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14126           element == EL_SP_GRAVITY_PORT_RIGHT ||
14127           element == EL_SP_GRAVITY_PORT_UP ||
14128           element == EL_SP_GRAVITY_PORT_DOWN)
14129 #if USE_PLAYER_GRAVITY
14130         player->gravity = !player->gravity;
14131 #else
14132         game.gravity = !game.gravity;
14133 #endif
14134       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14135                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14136                element == EL_SP_GRAVITY_ON_PORT_UP ||
14137                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14138 #if USE_PLAYER_GRAVITY
14139         player->gravity = TRUE;
14140 #else
14141         game.gravity = TRUE;
14142 #endif
14143       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14144                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14145                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14146                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14147 #if USE_PLAYER_GRAVITY
14148         player->gravity = FALSE;
14149 #else
14150         game.gravity = FALSE;
14151 #endif
14152     }
14153
14154     /* automatically move to the next field with double speed */
14155     player->programmed_action = move_direction;
14156
14157     if (player->move_delay_reset_counter == 0)
14158     {
14159       player->move_delay_reset_counter = 2;     /* two double speed steps */
14160
14161       DOUBLE_PLAYER_SPEED(player);
14162     }
14163
14164     PlayLevelSoundAction(x, y, ACTION_PASSING);
14165   }
14166   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14167   {
14168     RemoveField(x, y);
14169
14170     if (mode != DF_SNAP)
14171     {
14172       GfxElement[x][y] = GFX_ELEMENT(element);
14173       player->is_digging = TRUE;
14174     }
14175
14176     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14177
14178     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14179                                         player->index_bit, dig_side);
14180
14181     if (mode == DF_SNAP)
14182     {
14183 #if USE_NEW_SNAP_DELAY
14184       if (level.block_snap_field)
14185         setFieldForSnapping(x, y, element, move_direction);
14186       else
14187         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14188 #else
14189       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14190 #endif
14191
14192       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14193                                           player->index_bit, dig_side);
14194     }
14195   }
14196   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14197   {
14198     RemoveField(x, y);
14199
14200     if (is_player && mode != DF_SNAP)
14201     {
14202       GfxElement[x][y] = element;
14203       player->is_collecting = TRUE;
14204     }
14205
14206     if (element == EL_SPEED_PILL)
14207     {
14208       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14209     }
14210     else if (element == EL_EXTRA_TIME && level.time > 0)
14211     {
14212       TimeLeft += level.extra_time;
14213
14214 #if 1
14215       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14216
14217       DisplayGameControlValues();
14218 #else
14219       DrawGameValue_Time(TimeLeft);
14220 #endif
14221     }
14222     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14223     {
14224       player->shield_normal_time_left += level.shield_normal_time;
14225       if (element == EL_SHIELD_DEADLY)
14226         player->shield_deadly_time_left += level.shield_deadly_time;
14227     }
14228     else if (element == EL_DYNAMITE ||
14229              element == EL_EM_DYNAMITE ||
14230              element == EL_SP_DISK_RED)
14231     {
14232       if (player->inventory_size < MAX_INVENTORY_SIZE)
14233         player->inventory_element[player->inventory_size++] = element;
14234
14235       DrawGameDoorValues();
14236     }
14237     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14238     {
14239       player->dynabomb_count++;
14240       player->dynabombs_left++;
14241     }
14242     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14243     {
14244       player->dynabomb_size++;
14245     }
14246     else if (element == EL_DYNABOMB_INCREASE_POWER)
14247     {
14248       player->dynabomb_xl = TRUE;
14249     }
14250     else if (IS_KEY(element))
14251     {
14252       player->key[KEY_NR(element)] = TRUE;
14253
14254       DrawGameDoorValues();
14255     }
14256     else if (element == EL_DC_KEY_WHITE)
14257     {
14258       player->num_white_keys++;
14259
14260       /* display white keys? */
14261       /* DrawGameDoorValues(); */
14262     }
14263     else if (IS_ENVELOPE(element))
14264     {
14265       player->show_envelope = element;
14266     }
14267     else if (element == EL_EMC_LENSES)
14268     {
14269       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14270
14271       RedrawAllInvisibleElementsForLenses();
14272     }
14273     else if (element == EL_EMC_MAGNIFIER)
14274     {
14275       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14276
14277       RedrawAllInvisibleElementsForMagnifier();
14278     }
14279     else if (IS_DROPPABLE(element) ||
14280              IS_THROWABLE(element))     /* can be collected and dropped */
14281     {
14282       int i;
14283
14284       if (collect_count == 0)
14285         player->inventory_infinite_element = element;
14286       else
14287         for (i = 0; i < collect_count; i++)
14288           if (player->inventory_size < MAX_INVENTORY_SIZE)
14289             player->inventory_element[player->inventory_size++] = element;
14290
14291       DrawGameDoorValues();
14292     }
14293     else if (collect_count > 0)
14294     {
14295       local_player->gems_still_needed -= collect_count;
14296       if (local_player->gems_still_needed < 0)
14297         local_player->gems_still_needed = 0;
14298
14299 #if 1
14300       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14301
14302       DisplayGameControlValues();
14303 #else
14304       DrawGameValue_Emeralds(local_player->gems_still_needed);
14305 #endif
14306     }
14307
14308     RaiseScoreElement(element);
14309     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14310
14311     if (is_player)
14312       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14313                                           player->index_bit, dig_side);
14314
14315     if (mode == DF_SNAP)
14316     {
14317 #if USE_NEW_SNAP_DELAY
14318       if (level.block_snap_field)
14319         setFieldForSnapping(x, y, element, move_direction);
14320       else
14321         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14322 #else
14323       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14324 #endif
14325
14326       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14327                                           player->index_bit, dig_side);
14328     }
14329   }
14330   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14331   {
14332     if (mode == DF_SNAP && element != EL_BD_ROCK)
14333       return MP_NO_ACTION;
14334
14335     if (CAN_FALL(element) && dy)
14336       return MP_NO_ACTION;
14337
14338     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14339         !(element == EL_SPRING && level.use_spring_bug))
14340       return MP_NO_ACTION;
14341
14342     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14343         ((move_direction & MV_VERTICAL &&
14344           ((element_info[element].move_pattern & MV_LEFT &&
14345             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14346            (element_info[element].move_pattern & MV_RIGHT &&
14347             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14348          (move_direction & MV_HORIZONTAL &&
14349           ((element_info[element].move_pattern & MV_UP &&
14350             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14351            (element_info[element].move_pattern & MV_DOWN &&
14352             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14353       return MP_NO_ACTION;
14354
14355     /* do not push elements already moving away faster than player */
14356     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14357         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14358       return MP_NO_ACTION;
14359
14360     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14361     {
14362       if (player->push_delay_value == -1 || !player_was_pushing)
14363         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14364     }
14365     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14366     {
14367       if (player->push_delay_value == -1)
14368         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14369     }
14370     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14371     {
14372       if (!player->is_pushing)
14373         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14374     }
14375
14376     player->is_pushing = TRUE;
14377     player->is_active = TRUE;
14378
14379     if (!(IN_LEV_FIELD(nextx, nexty) &&
14380           (IS_FREE(nextx, nexty) ||
14381            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14382             IS_SB_ELEMENT(element)))))
14383       return MP_NO_ACTION;
14384
14385     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14386       return MP_NO_ACTION;
14387
14388     if (player->push_delay == -1)       /* new pushing; restart delay */
14389       player->push_delay = 0;
14390
14391     if (player->push_delay < player->push_delay_value &&
14392         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14393         element != EL_SPRING && element != EL_BALLOON)
14394     {
14395       /* make sure that there is no move delay before next try to push */
14396       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14397         player->move_delay = 0;
14398
14399       return MP_NO_ACTION;
14400     }
14401
14402     if (IS_SB_ELEMENT(element))
14403     {
14404       if (element == EL_SOKOBAN_FIELD_FULL)
14405       {
14406         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14407         local_player->sokobanfields_still_needed++;
14408       }
14409
14410       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14411       {
14412         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14413         local_player->sokobanfields_still_needed--;
14414       }
14415
14416       Feld[x][y] = EL_SOKOBAN_OBJECT;
14417
14418       if (Back[x][y] == Back[nextx][nexty])
14419         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14420       else if (Back[x][y] != 0)
14421         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14422                                     ACTION_EMPTYING);
14423       else
14424         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14425                                     ACTION_FILLING);
14426
14427       if (local_player->sokobanfields_still_needed == 0 &&
14428           game.emulation == EMU_SOKOBAN)
14429       {
14430         PlayerWins(player);
14431
14432         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14433       }
14434     }
14435     else
14436       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14437
14438     InitMovingField(x, y, move_direction);
14439     GfxAction[x][y] = ACTION_PUSHING;
14440
14441     if (mode == DF_SNAP)
14442       ContinueMoving(x, y);
14443     else
14444       MovPos[x][y] = (dx != 0 ? dx : dy);
14445
14446     Pushed[x][y] = TRUE;
14447     Pushed[nextx][nexty] = TRUE;
14448
14449     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14450       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14451     else
14452       player->push_delay_value = -1;    /* get new value later */
14453
14454     /* check for element change _after_ element has been pushed */
14455     if (game.use_change_when_pushing_bug)
14456     {
14457       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14458                                  player->index_bit, dig_side);
14459       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14460                                           player->index_bit, dig_side);
14461     }
14462   }
14463   else if (IS_SWITCHABLE(element))
14464   {
14465     if (PLAYER_SWITCHING(player, x, y))
14466     {
14467       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14468                                           player->index_bit, dig_side);
14469
14470       return MP_ACTION;
14471     }
14472
14473     player->is_switching = TRUE;
14474     player->switch_x = x;
14475     player->switch_y = y;
14476
14477     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14478
14479     if (element == EL_ROBOT_WHEEL)
14480     {
14481       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14482       ZX = x;
14483       ZY = y;
14484
14485       game.robot_wheel_active = TRUE;
14486
14487       DrawLevelField(x, y);
14488     }
14489     else if (element == EL_SP_TERMINAL)
14490     {
14491       int xx, yy;
14492
14493       SCAN_PLAYFIELD(xx, yy)
14494       {
14495         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14496           Bang(xx, yy);
14497         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14498           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14499       }
14500     }
14501     else if (IS_BELT_SWITCH(element))
14502     {
14503       ToggleBeltSwitch(x, y);
14504     }
14505     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14506              element == EL_SWITCHGATE_SWITCH_DOWN ||
14507              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14508              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14509     {
14510       ToggleSwitchgateSwitch(x, y);
14511     }
14512     else if (element == EL_LIGHT_SWITCH ||
14513              element == EL_LIGHT_SWITCH_ACTIVE)
14514     {
14515       ToggleLightSwitch(x, y);
14516     }
14517     else if (element == EL_TIMEGATE_SWITCH ||
14518              element == EL_DC_TIMEGATE_SWITCH)
14519     {
14520       ActivateTimegateSwitch(x, y);
14521     }
14522     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14523              element == EL_BALLOON_SWITCH_RIGHT ||
14524              element == EL_BALLOON_SWITCH_UP    ||
14525              element == EL_BALLOON_SWITCH_DOWN  ||
14526              element == EL_BALLOON_SWITCH_NONE  ||
14527              element == EL_BALLOON_SWITCH_ANY)
14528     {
14529       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14530                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14531                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14532                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14533                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14534                              move_direction);
14535     }
14536     else if (element == EL_LAMP)
14537     {
14538       Feld[x][y] = EL_LAMP_ACTIVE;
14539       local_player->lights_still_needed--;
14540
14541       ResetGfxAnimation(x, y);
14542       DrawLevelField(x, y);
14543     }
14544     else if (element == EL_TIME_ORB_FULL)
14545     {
14546       Feld[x][y] = EL_TIME_ORB_EMPTY;
14547
14548       if (level.time > 0 || level.use_time_orb_bug)
14549       {
14550         TimeLeft += level.time_orb_time;
14551
14552 #if 1
14553         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14554
14555         DisplayGameControlValues();
14556 #else
14557         DrawGameValue_Time(TimeLeft);
14558 #endif
14559       }
14560
14561       ResetGfxAnimation(x, y);
14562       DrawLevelField(x, y);
14563     }
14564     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14565              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14566     {
14567       int xx, yy;
14568
14569       game.ball_state = !game.ball_state;
14570
14571       SCAN_PLAYFIELD(xx, yy)
14572       {
14573         int e = Feld[xx][yy];
14574
14575         if (game.ball_state)
14576         {
14577           if (e == EL_EMC_MAGIC_BALL)
14578             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14579           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14580             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14581         }
14582         else
14583         {
14584           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14585             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14586           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14587             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14588         }
14589       }
14590     }
14591
14592     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14593                                         player->index_bit, dig_side);
14594
14595     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14596                                         player->index_bit, dig_side);
14597
14598     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14599                                         player->index_bit, dig_side);
14600
14601     return MP_ACTION;
14602   }
14603   else
14604   {
14605     if (!PLAYER_SWITCHING(player, x, y))
14606     {
14607       player->is_switching = TRUE;
14608       player->switch_x = x;
14609       player->switch_y = y;
14610
14611       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14612                                  player->index_bit, dig_side);
14613       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14614                                           player->index_bit, dig_side);
14615
14616       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14617                                  player->index_bit, dig_side);
14618       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14619                                           player->index_bit, dig_side);
14620     }
14621
14622     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14623                                player->index_bit, dig_side);
14624     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14625                                         player->index_bit, dig_side);
14626
14627     return MP_NO_ACTION;
14628   }
14629
14630   player->push_delay = -1;
14631
14632   if (is_player)                /* function can also be called by EL_PENGUIN */
14633   {
14634     if (Feld[x][y] != element)          /* really digged/collected something */
14635     {
14636       player->is_collecting = !player->is_digging;
14637       player->is_active = TRUE;
14638     }
14639   }
14640
14641   return MP_MOVING;
14642 }
14643
14644 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14645 {
14646   int jx = player->jx, jy = player->jy;
14647   int x = jx + dx, y = jy + dy;
14648   int snap_direction = (dx == -1 ? MV_LEFT  :
14649                         dx == +1 ? MV_RIGHT :
14650                         dy == -1 ? MV_UP    :
14651                         dy == +1 ? MV_DOWN  : MV_NONE);
14652   boolean can_continue_snapping = (level.continuous_snapping &&
14653                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14654
14655   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14656     return FALSE;
14657
14658   if (!player->active || !IN_LEV_FIELD(x, y))
14659     return FALSE;
14660
14661   if (dx && dy)
14662     return FALSE;
14663
14664   if (!dx && !dy)
14665   {
14666     if (player->MovPos == 0)
14667       player->is_pushing = FALSE;
14668
14669     player->is_snapping = FALSE;
14670
14671     if (player->MovPos == 0)
14672     {
14673       player->is_moving = FALSE;
14674       player->is_digging = FALSE;
14675       player->is_collecting = FALSE;
14676     }
14677
14678     return FALSE;
14679   }
14680
14681 #if USE_NEW_CONTINUOUS_SNAPPING
14682   /* prevent snapping with already pressed snap key when not allowed */
14683   if (player->is_snapping && !can_continue_snapping)
14684     return FALSE;
14685 #else
14686   if (player->is_snapping)
14687     return FALSE;
14688 #endif
14689
14690   player->MovDir = snap_direction;
14691
14692   if (player->MovPos == 0)
14693   {
14694     player->is_moving = FALSE;
14695     player->is_digging = FALSE;
14696     player->is_collecting = FALSE;
14697   }
14698
14699   player->is_dropping = FALSE;
14700   player->is_dropping_pressed = FALSE;
14701   player->drop_pressed_delay = 0;
14702
14703   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14704     return FALSE;
14705
14706   player->is_snapping = TRUE;
14707   player->is_active = TRUE;
14708
14709   if (player->MovPos == 0)
14710   {
14711     player->is_moving = FALSE;
14712     player->is_digging = FALSE;
14713     player->is_collecting = FALSE;
14714   }
14715
14716   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14717     DrawLevelField(player->last_jx, player->last_jy);
14718
14719   DrawLevelField(x, y);
14720
14721   return TRUE;
14722 }
14723
14724 boolean DropElement(struct PlayerInfo *player)
14725 {
14726   int old_element, new_element;
14727   int dropx = player->jx, dropy = player->jy;
14728   int drop_direction = player->MovDir;
14729   int drop_side = drop_direction;
14730 #if 1
14731   int drop_element = get_next_dropped_element(player);
14732 #else
14733   int drop_element = (player->inventory_size > 0 ?
14734                       player->inventory_element[player->inventory_size - 1] :
14735                       player->inventory_infinite_element != EL_UNDEFINED ?
14736                       player->inventory_infinite_element :
14737                       player->dynabombs_left > 0 ?
14738                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14739                       EL_UNDEFINED);
14740 #endif
14741
14742   player->is_dropping_pressed = TRUE;
14743
14744   /* do not drop an element on top of another element; when holding drop key
14745      pressed without moving, dropped element must move away before the next
14746      element can be dropped (this is especially important if the next element
14747      is dynamite, which can be placed on background for historical reasons) */
14748   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14749     return MP_ACTION;
14750
14751   if (IS_THROWABLE(drop_element))
14752   {
14753     dropx += GET_DX_FROM_DIR(drop_direction);
14754     dropy += GET_DY_FROM_DIR(drop_direction);
14755
14756     if (!IN_LEV_FIELD(dropx, dropy))
14757       return FALSE;
14758   }
14759
14760   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14761   new_element = drop_element;           /* default: no change when dropping */
14762
14763   /* check if player is active, not moving and ready to drop */
14764   if (!player->active || player->MovPos || player->drop_delay > 0)
14765     return FALSE;
14766
14767   /* check if player has anything that can be dropped */
14768   if (new_element == EL_UNDEFINED)
14769     return FALSE;
14770
14771   /* check if drop key was pressed long enough for EM style dynamite */
14772   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14773     return FALSE;
14774
14775   /* check if anything can be dropped at the current position */
14776   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14777     return FALSE;
14778
14779   /* collected custom elements can only be dropped on empty fields */
14780   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14781     return FALSE;
14782
14783   if (old_element != EL_EMPTY)
14784     Back[dropx][dropy] = old_element;   /* store old element on this field */
14785
14786   ResetGfxAnimation(dropx, dropy);
14787   ResetRandomAnimationValue(dropx, dropy);
14788
14789   if (player->inventory_size > 0 ||
14790       player->inventory_infinite_element != EL_UNDEFINED)
14791   {
14792     if (player->inventory_size > 0)
14793     {
14794       player->inventory_size--;
14795
14796       DrawGameDoorValues();
14797
14798       if (new_element == EL_DYNAMITE)
14799         new_element = EL_DYNAMITE_ACTIVE;
14800       else if (new_element == EL_EM_DYNAMITE)
14801         new_element = EL_EM_DYNAMITE_ACTIVE;
14802       else if (new_element == EL_SP_DISK_RED)
14803         new_element = EL_SP_DISK_RED_ACTIVE;
14804     }
14805
14806     Feld[dropx][dropy] = new_element;
14807
14808     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14809       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14810                           el2img(Feld[dropx][dropy]), 0);
14811
14812     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14813
14814     /* needed if previous element just changed to "empty" in the last frame */
14815     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14816
14817     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14818                                player->index_bit, drop_side);
14819     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14820                                         CE_PLAYER_DROPS_X,
14821                                         player->index_bit, drop_side);
14822
14823     TestIfElementTouchesCustomElement(dropx, dropy);
14824   }
14825   else          /* player is dropping a dyna bomb */
14826   {
14827     player->dynabombs_left--;
14828
14829     Feld[dropx][dropy] = new_element;
14830
14831     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14832       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14833                           el2img(Feld[dropx][dropy]), 0);
14834
14835     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14836   }
14837
14838   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14839     InitField_WithBug1(dropx, dropy, FALSE);
14840
14841   new_element = Feld[dropx][dropy];     /* element might have changed */
14842
14843   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14844       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14845   {
14846     int move_direction, nextx, nexty;
14847
14848     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14849       MovDir[dropx][dropy] = drop_direction;
14850
14851     move_direction = MovDir[dropx][dropy];
14852     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14853     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14854
14855     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14856
14857 #if USE_FIX_IMPACT_COLLISION
14858     /* do not cause impact style collision by dropping elements that can fall */
14859     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14860 #else
14861     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14862 #endif
14863   }
14864
14865   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14866   player->is_dropping = TRUE;
14867
14868   player->drop_pressed_delay = 0;
14869   player->is_dropping_pressed = FALSE;
14870
14871   player->drop_x = dropx;
14872   player->drop_y = dropy;
14873
14874   return TRUE;
14875 }
14876
14877 /* ------------------------------------------------------------------------- */
14878 /* game sound playing functions                                              */
14879 /* ------------------------------------------------------------------------- */
14880
14881 static int *loop_sound_frame = NULL;
14882 static int *loop_sound_volume = NULL;
14883
14884 void InitPlayLevelSound()
14885 {
14886   int num_sounds = getSoundListSize();
14887
14888   checked_free(loop_sound_frame);
14889   checked_free(loop_sound_volume);
14890
14891   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14892   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14893 }
14894
14895 static void PlayLevelSound(int x, int y, int nr)
14896 {
14897   int sx = SCREENX(x), sy = SCREENY(y);
14898   int volume, stereo_position;
14899   int max_distance = 8;
14900   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14901
14902   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14903       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14904     return;
14905
14906   if (!IN_LEV_FIELD(x, y) ||
14907       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14908       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14909     return;
14910
14911   volume = SOUND_MAX_VOLUME;
14912
14913   if (!IN_SCR_FIELD(sx, sy))
14914   {
14915     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14916     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14917
14918     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14919   }
14920
14921   stereo_position = (SOUND_MAX_LEFT +
14922                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14923                      (SCR_FIELDX + 2 * max_distance));
14924
14925   if (IS_LOOP_SOUND(nr))
14926   {
14927     /* This assures that quieter loop sounds do not overwrite louder ones,
14928        while restarting sound volume comparison with each new game frame. */
14929
14930     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14931       return;
14932
14933     loop_sound_volume[nr] = volume;
14934     loop_sound_frame[nr] = FrameCounter;
14935   }
14936
14937   PlaySoundExt(nr, volume, stereo_position, type);
14938 }
14939
14940 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14941 {
14942   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14943                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14944                  y < LEVELY(BY1) ? LEVELY(BY1) :
14945                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14946                  sound_action);
14947 }
14948
14949 static void PlayLevelSoundAction(int x, int y, int action)
14950 {
14951   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14952 }
14953
14954 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14955 {
14956   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14957
14958   if (sound_effect != SND_UNDEFINED)
14959     PlayLevelSound(x, y, sound_effect);
14960 }
14961
14962 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14963                                               int action)
14964 {
14965   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14966
14967   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14968     PlayLevelSound(x, y, sound_effect);
14969 }
14970
14971 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14972 {
14973   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14974
14975   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14976     PlayLevelSound(x, y, sound_effect);
14977 }
14978
14979 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14980 {
14981   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14982
14983   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14984     StopSound(sound_effect);
14985 }
14986
14987 static void PlayLevelMusic()
14988 {
14989   if (levelset.music[level_nr] != MUS_UNDEFINED)
14990     PlayMusic(levelset.music[level_nr]);        /* from config file */
14991   else
14992     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14993 }
14994
14995 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14996 {
14997   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14998   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14999   int x = xx - 1 - offset;
15000   int y = yy - 1 - offset;
15001
15002   switch (sample)
15003   {
15004     case SAMPLE_blank:
15005       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15006       break;
15007
15008     case SAMPLE_roll:
15009       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15010       break;
15011
15012     case SAMPLE_stone:
15013       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15014       break;
15015
15016     case SAMPLE_nut:
15017       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15018       break;
15019
15020     case SAMPLE_crack:
15021       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15022       break;
15023
15024     case SAMPLE_bug:
15025       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15026       break;
15027
15028     case SAMPLE_tank:
15029       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15030       break;
15031
15032     case SAMPLE_android_clone:
15033       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15034       break;
15035
15036     case SAMPLE_android_move:
15037       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15038       break;
15039
15040     case SAMPLE_spring:
15041       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15042       break;
15043
15044     case SAMPLE_slurp:
15045       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15046       break;
15047
15048     case SAMPLE_eater:
15049       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15050       break;
15051
15052     case SAMPLE_eater_eat:
15053       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15054       break;
15055
15056     case SAMPLE_alien:
15057       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15058       break;
15059
15060     case SAMPLE_collect:
15061       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15062       break;
15063
15064     case SAMPLE_diamond:
15065       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15066       break;
15067
15068     case SAMPLE_squash:
15069       /* !!! CHECK THIS !!! */
15070 #if 1
15071       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15072 #else
15073       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15074 #endif
15075       break;
15076
15077     case SAMPLE_wonderfall:
15078       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15079       break;
15080
15081     case SAMPLE_drip:
15082       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15083       break;
15084
15085     case SAMPLE_push:
15086       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15087       break;
15088
15089     case SAMPLE_dirt:
15090       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15091       break;
15092
15093     case SAMPLE_acid:
15094       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15095       break;
15096
15097     case SAMPLE_ball:
15098       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15099       break;
15100
15101     case SAMPLE_grow:
15102       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15103       break;
15104
15105     case SAMPLE_wonder:
15106       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15107       break;
15108
15109     case SAMPLE_door:
15110       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15111       break;
15112
15113     case SAMPLE_exit_open:
15114       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15115       break;
15116
15117     case SAMPLE_exit_leave:
15118       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15119       break;
15120
15121     case SAMPLE_dynamite:
15122       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15123       break;
15124
15125     case SAMPLE_tick:
15126       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15127       break;
15128
15129     case SAMPLE_press:
15130       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15131       break;
15132
15133     case SAMPLE_wheel:
15134       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15135       break;
15136
15137     case SAMPLE_boom:
15138       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15139       break;
15140
15141     case SAMPLE_die:
15142       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15143       break;
15144
15145     case SAMPLE_time:
15146       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15147       break;
15148
15149     default:
15150       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15151       break;
15152   }
15153 }
15154
15155 #if 0
15156 void ChangeTime(int value)
15157 {
15158   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15159
15160   *time += value;
15161
15162   /* EMC game engine uses value from time counter of RND game engine */
15163   level.native_em_level->lev->time = *time;
15164
15165   DrawGameValue_Time(*time);
15166 }
15167
15168 void RaiseScore(int value)
15169 {
15170   /* EMC game engine and RND game engine have separate score counters */
15171   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15172                 &level.native_em_level->lev->score : &local_player->score);
15173
15174   *score += value;
15175
15176   DrawGameValue_Score(*score);
15177 }
15178 #endif
15179
15180 void RaiseScore(int value)
15181 {
15182   local_player->score += value;
15183
15184 #if 1
15185   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15186
15187   DisplayGameControlValues();
15188 #else
15189   DrawGameValue_Score(local_player->score);
15190 #endif
15191 }
15192
15193 void RaiseScoreElement(int element)
15194 {
15195   switch (element)
15196   {
15197     case EL_EMERALD:
15198     case EL_BD_DIAMOND:
15199     case EL_EMERALD_YELLOW:
15200     case EL_EMERALD_RED:
15201     case EL_EMERALD_PURPLE:
15202     case EL_SP_INFOTRON:
15203       RaiseScore(level.score[SC_EMERALD]);
15204       break;
15205     case EL_DIAMOND:
15206       RaiseScore(level.score[SC_DIAMOND]);
15207       break;
15208     case EL_CRYSTAL:
15209       RaiseScore(level.score[SC_CRYSTAL]);
15210       break;
15211     case EL_PEARL:
15212       RaiseScore(level.score[SC_PEARL]);
15213       break;
15214     case EL_BUG:
15215     case EL_BD_BUTTERFLY:
15216     case EL_SP_ELECTRON:
15217       RaiseScore(level.score[SC_BUG]);
15218       break;
15219     case EL_SPACESHIP:
15220     case EL_BD_FIREFLY:
15221     case EL_SP_SNIKSNAK:
15222       RaiseScore(level.score[SC_SPACESHIP]);
15223       break;
15224     case EL_YAMYAM:
15225     case EL_DARK_YAMYAM:
15226       RaiseScore(level.score[SC_YAMYAM]);
15227       break;
15228     case EL_ROBOT:
15229       RaiseScore(level.score[SC_ROBOT]);
15230       break;
15231     case EL_PACMAN:
15232       RaiseScore(level.score[SC_PACMAN]);
15233       break;
15234     case EL_NUT:
15235       RaiseScore(level.score[SC_NUT]);
15236       break;
15237     case EL_DYNAMITE:
15238     case EL_EM_DYNAMITE:
15239     case EL_SP_DISK_RED:
15240     case EL_DYNABOMB_INCREASE_NUMBER:
15241     case EL_DYNABOMB_INCREASE_SIZE:
15242     case EL_DYNABOMB_INCREASE_POWER:
15243       RaiseScore(level.score[SC_DYNAMITE]);
15244       break;
15245     case EL_SHIELD_NORMAL:
15246     case EL_SHIELD_DEADLY:
15247       RaiseScore(level.score[SC_SHIELD]);
15248       break;
15249     case EL_EXTRA_TIME:
15250       RaiseScore(level.extra_time_score);
15251       break;
15252     case EL_KEY_1:
15253     case EL_KEY_2:
15254     case EL_KEY_3:
15255     case EL_KEY_4:
15256     case EL_EM_KEY_1:
15257     case EL_EM_KEY_2:
15258     case EL_EM_KEY_3:
15259     case EL_EM_KEY_4:
15260     case EL_EMC_KEY_5:
15261     case EL_EMC_KEY_6:
15262     case EL_EMC_KEY_7:
15263     case EL_EMC_KEY_8:
15264     case EL_DC_KEY_WHITE:
15265       RaiseScore(level.score[SC_KEY]);
15266       break;
15267     default:
15268       RaiseScore(element_info[element].collect_score);
15269       break;
15270   }
15271 }
15272
15273 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15274 {
15275   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15276   {
15277 #if defined(NETWORK_AVALIABLE)
15278     if (options.network)
15279       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15280     else
15281 #endif
15282     {
15283       if (quick_quit)
15284       {
15285 #if 1
15286
15287 #if 1
15288         FadeSkipNextFadeIn();
15289 #else
15290         fading = fading_none;
15291 #endif
15292
15293 #else
15294         OpenDoor(DOOR_CLOSE_1);
15295 #endif
15296
15297         game_status = GAME_MODE_MAIN;
15298
15299 #if 1
15300         DrawAndFadeInMainMenu(REDRAW_FIELD);
15301 #else
15302         DrawMainMenu();
15303 #endif
15304       }
15305       else
15306       {
15307 #if 0
15308         FadeOut(REDRAW_FIELD);
15309 #endif
15310
15311         game_status = GAME_MODE_MAIN;
15312
15313         DrawAndFadeInMainMenu(REDRAW_FIELD);
15314       }
15315     }
15316   }
15317   else          /* continue playing the game */
15318   {
15319     if (tape.playing && tape.deactivate_display)
15320       TapeDeactivateDisplayOff(TRUE);
15321
15322     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15323
15324     if (tape.playing && tape.deactivate_display)
15325       TapeDeactivateDisplayOn();
15326   }
15327 }
15328
15329 void RequestQuitGame(boolean ask_if_really_quit)
15330 {
15331   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15332   boolean skip_request = AllPlayersGone || quick_quit;
15333
15334   RequestQuitGameExt(skip_request, quick_quit,
15335                      "Do you really want to quit the game ?");
15336 }
15337
15338
15339 /* ------------------------------------------------------------------------- */
15340 /* random generator functions                                                */
15341 /* ------------------------------------------------------------------------- */
15342
15343 unsigned int InitEngineRandom_RND(long seed)
15344 {
15345   game.num_random_calls = 0;
15346
15347 #if 0
15348   unsigned int rnd_seed = InitEngineRandom(seed);
15349
15350   printf("::: START RND: %d\n", rnd_seed);
15351
15352   return rnd_seed;
15353 #else
15354
15355   return InitEngineRandom(seed);
15356
15357 #endif
15358
15359 }
15360
15361 unsigned int RND(int max)
15362 {
15363   if (max > 0)
15364   {
15365     game.num_random_calls++;
15366
15367     return GetEngineRandom(max);
15368   }
15369
15370   return 0;
15371 }
15372
15373
15374 /* ------------------------------------------------------------------------- */
15375 /* game engine snapshot handling functions                                   */
15376 /* ------------------------------------------------------------------------- */
15377
15378 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15379
15380 struct EngineSnapshotInfo
15381 {
15382   /* runtime values for custom element collect score */
15383   int collect_score[NUM_CUSTOM_ELEMENTS];
15384
15385   /* runtime values for group element choice position */
15386   int choice_pos[NUM_GROUP_ELEMENTS];
15387
15388   /* runtime values for belt position animations */
15389   int belt_graphic[4 * NUM_BELT_PARTS];
15390   int belt_anim_mode[4 * NUM_BELT_PARTS];
15391 };
15392
15393 struct EngineSnapshotNodeInfo
15394 {
15395   void *buffer_orig;
15396   void *buffer_copy;
15397   int size;
15398 };
15399
15400 static struct EngineSnapshotInfo engine_snapshot_rnd;
15401 static ListNode *engine_snapshot_list = NULL;
15402 static char *snapshot_level_identifier = NULL;
15403 static int snapshot_level_nr = -1;
15404
15405 void FreeEngineSnapshot()
15406 {
15407   while (engine_snapshot_list != NULL)
15408     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15409                        checked_free);
15410
15411   setString(&snapshot_level_identifier, NULL);
15412   snapshot_level_nr = -1;
15413 }
15414
15415 static void SaveEngineSnapshotValues_RND()
15416 {
15417   static int belt_base_active_element[4] =
15418   {
15419     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15420     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15421     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15422     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15423   };
15424   int i, j;
15425
15426   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15427   {
15428     int element = EL_CUSTOM_START + i;
15429
15430     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15431   }
15432
15433   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15434   {
15435     int element = EL_GROUP_START + i;
15436
15437     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15438   }
15439
15440   for (i = 0; i < 4; i++)
15441   {
15442     for (j = 0; j < NUM_BELT_PARTS; j++)
15443     {
15444       int element = belt_base_active_element[i] + j;
15445       int graphic = el2img(element);
15446       int anim_mode = graphic_info[graphic].anim_mode;
15447
15448       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15449       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15450     }
15451   }
15452 }
15453
15454 static void LoadEngineSnapshotValues_RND()
15455 {
15456   unsigned long num_random_calls = game.num_random_calls;
15457   int i, j;
15458
15459   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15460   {
15461     int element = EL_CUSTOM_START + i;
15462
15463     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15464   }
15465
15466   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15467   {
15468     int element = EL_GROUP_START + i;
15469
15470     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15471   }
15472
15473   for (i = 0; i < 4; i++)
15474   {
15475     for (j = 0; j < NUM_BELT_PARTS; j++)
15476     {
15477       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15478       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15479
15480       graphic_info[graphic].anim_mode = anim_mode;
15481     }
15482   }
15483
15484   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15485   {
15486     InitRND(tape.random_seed);
15487     for (i = 0; i < num_random_calls; i++)
15488       RND(1);
15489   }
15490
15491   if (game.num_random_calls != num_random_calls)
15492   {
15493     Error(ERR_INFO, "number of random calls out of sync");
15494     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15495     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15496     Error(ERR_EXIT, "this should not happen -- please debug");
15497   }
15498 }
15499
15500 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15501 {
15502   struct EngineSnapshotNodeInfo *bi =
15503     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15504
15505   bi->buffer_orig = buffer;
15506   bi->buffer_copy = checked_malloc(size);
15507   bi->size = size;
15508
15509   memcpy(bi->buffer_copy, buffer, size);
15510
15511   addNodeToList(&engine_snapshot_list, NULL, bi);
15512 }
15513
15514 void SaveEngineSnapshot()
15515 {
15516   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15517
15518   if (level_editor_test_game)   /* do not save snapshots from editor */
15519     return;
15520
15521   /* copy some special values to a structure better suited for the snapshot */
15522
15523   SaveEngineSnapshotValues_RND();
15524   SaveEngineSnapshotValues_EM();
15525
15526   /* save values stored in special snapshot structure */
15527
15528   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15529   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15530
15531   /* save further RND engine values */
15532
15533   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15534   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15535   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15536
15537   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15538   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15539   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15540   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15541
15542   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15543   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15544   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15545   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15546   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15547
15548   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15549   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15550   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15551
15552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15553
15554   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15555
15556   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15558
15559   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15560   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15561   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15562   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15563   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15565   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15566   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15567   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15570   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15572   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15576   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15577
15578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15579   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15580
15581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15584
15585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15587
15588   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15593
15594   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15595   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15596
15597   /* save level identification information */
15598
15599   setString(&snapshot_level_identifier, leveldir_current->identifier);
15600   snapshot_level_nr = level_nr;
15601
15602 #if 0
15603   ListNode *node = engine_snapshot_list;
15604   int num_bytes = 0;
15605
15606   while (node != NULL)
15607   {
15608     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15609
15610     node = node->next;
15611   }
15612
15613   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15614 #endif
15615 }
15616
15617 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15618 {
15619   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15620 }
15621
15622 void LoadEngineSnapshot()
15623 {
15624   ListNode *node = engine_snapshot_list;
15625
15626   if (engine_snapshot_list == NULL)
15627     return;
15628
15629   while (node != NULL)
15630   {
15631     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15632
15633     node = node->next;
15634   }
15635
15636   /* restore special values from snapshot structure */
15637
15638   LoadEngineSnapshotValues_RND();
15639   LoadEngineSnapshotValues_EM();
15640 }
15641
15642 boolean CheckEngineSnapshot()
15643 {
15644   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15645           snapshot_level_nr == level_nr);
15646 }
15647
15648
15649 /* ---------- new game button stuff ---------------------------------------- */
15650
15651 /* graphic position values for game buttons */
15652 #define GAME_BUTTON_XSIZE       30
15653 #define GAME_BUTTON_YSIZE       30
15654 #define GAME_BUTTON_XPOS        5
15655 #define GAME_BUTTON_YPOS        215
15656 #define SOUND_BUTTON_XPOS       5
15657 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15658
15659 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15660 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15661 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15662 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15663 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15664 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15665
15666 static struct
15667 {
15668   int *x, *y;
15669   int gd_x, gd_y;
15670   int gadget_id;
15671   char *infotext;
15672 } gamebutton_info[NUM_GAME_BUTTONS] =
15673 {
15674 #if 1
15675   {
15676     &game.button.stop.x,        &game.button.stop.y,
15677     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15678     GAME_CTRL_ID_STOP,
15679     "stop game"
15680   },
15681   {
15682     &game.button.pause.x,       &game.button.pause.y,
15683     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15684     GAME_CTRL_ID_PAUSE,
15685     "pause game"
15686   },
15687   {
15688     &game.button.play.x,        &game.button.play.y,
15689     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15690     GAME_CTRL_ID_PLAY,
15691     "play game"
15692   },
15693   {
15694     &game.button.sound_music.x, &game.button.sound_music.y,
15695     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15696     SOUND_CTRL_ID_MUSIC,
15697     "background music on/off"
15698   },
15699   {
15700     &game.button.sound_loops.x, &game.button.sound_loops.y,
15701     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15702     SOUND_CTRL_ID_LOOPS,
15703     "sound loops on/off"
15704   },
15705   {
15706     &game.button.sound_simple.x,&game.button.sound_simple.y,
15707     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15708     SOUND_CTRL_ID_SIMPLE,
15709     "normal sounds on/off"
15710   }
15711 #else
15712   {
15713     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15714     GAME_CTRL_ID_STOP,
15715     "stop game"
15716   },
15717   {
15718     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15719     GAME_CTRL_ID_PAUSE,
15720     "pause game"
15721   },
15722   {
15723     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15724     GAME_CTRL_ID_PLAY,
15725     "play game"
15726   },
15727   {
15728     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15729     SOUND_CTRL_ID_MUSIC,
15730     "background music on/off"
15731   },
15732   {
15733     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15734     SOUND_CTRL_ID_LOOPS,
15735     "sound loops on/off"
15736   },
15737   {
15738     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15739     SOUND_CTRL_ID_SIMPLE,
15740     "normal sounds on/off"
15741   }
15742 #endif
15743 };
15744
15745 void CreateGameButtons()
15746 {
15747   int i;
15748
15749   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15750   {
15751     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15752     struct GadgetInfo *gi;
15753     int button_type;
15754     boolean checked;
15755     unsigned long event_mask;
15756     int x, y;
15757     int gd_xoffset, gd_yoffset;
15758     int gd_x1, gd_x2, gd_y1, gd_y2;
15759     int id = i;
15760
15761     x = DX + *gamebutton_info[i].x;
15762     y = DY + *gamebutton_info[i].y;
15763     gd_xoffset = gamebutton_info[i].gd_x;
15764     gd_yoffset = gamebutton_info[i].gd_y;
15765     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15766     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15767
15768     if (id == GAME_CTRL_ID_STOP ||
15769         id == GAME_CTRL_ID_PAUSE ||
15770         id == GAME_CTRL_ID_PLAY)
15771     {
15772       button_type = GD_TYPE_NORMAL_BUTTON;
15773       checked = FALSE;
15774       event_mask = GD_EVENT_RELEASED;
15775       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15776       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15777     }
15778     else
15779     {
15780       button_type = GD_TYPE_CHECK_BUTTON;
15781       checked =
15782         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15783          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15784          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15785       event_mask = GD_EVENT_PRESSED;
15786       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15787       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15788     }
15789
15790     gi = CreateGadget(GDI_CUSTOM_ID, id,
15791                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15792 #if 1
15793                       GDI_X, x,
15794                       GDI_Y, y,
15795 #else
15796                       GDI_X, DX + gd_xoffset,
15797                       GDI_Y, DY + gd_yoffset,
15798 #endif
15799                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15800                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15801                       GDI_TYPE, button_type,
15802                       GDI_STATE, GD_BUTTON_UNPRESSED,
15803                       GDI_CHECKED, checked,
15804                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15805                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15806                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15807                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15808                       GDI_DIRECT_DRAW, FALSE,
15809                       GDI_EVENT_MASK, event_mask,
15810                       GDI_CALLBACK_ACTION, HandleGameButtons,
15811                       GDI_END);
15812
15813     if (gi == NULL)
15814       Error(ERR_EXIT, "cannot create gadget");
15815
15816     game_gadget[id] = gi;
15817   }
15818 }
15819
15820 void FreeGameButtons()
15821 {
15822   int i;
15823
15824   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15825     FreeGadget(game_gadget[i]);
15826 }
15827
15828 static void MapGameButtons()
15829 {
15830   int i;
15831
15832   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15833     MapGadget(game_gadget[i]);
15834 }
15835
15836 void UnmapGameButtons()
15837 {
15838   int i;
15839
15840   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15841     UnmapGadget(game_gadget[i]);
15842 }
15843
15844 void RedrawGameButtons()
15845 {
15846   int i;
15847
15848   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15849     RedrawGadget(game_gadget[i]);
15850 }
15851
15852 static void HandleGameButtons(struct GadgetInfo *gi)
15853 {
15854   int id = gi->custom_id;
15855
15856   if (game_status != GAME_MODE_PLAYING)
15857     return;
15858
15859   switch (id)
15860   {
15861     case GAME_CTRL_ID_STOP:
15862       if (tape.playing)
15863         TapeStop();
15864       else
15865         RequestQuitGame(TRUE);
15866       break;
15867
15868     case GAME_CTRL_ID_PAUSE:
15869       if (options.network)
15870       {
15871 #if defined(NETWORK_AVALIABLE)
15872         if (tape.pausing)
15873           SendToServer_ContinuePlaying();
15874         else
15875           SendToServer_PausePlaying();
15876 #endif
15877       }
15878       else
15879         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15880       break;
15881
15882     case GAME_CTRL_ID_PLAY:
15883       if (tape.pausing)
15884       {
15885 #if defined(NETWORK_AVALIABLE)
15886         if (options.network)
15887           SendToServer_ContinuePlaying();
15888         else
15889 #endif
15890         {
15891           tape.pausing = FALSE;
15892           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15893         }
15894       }
15895       break;
15896
15897     case SOUND_CTRL_ID_MUSIC:
15898       if (setup.sound_music)
15899       { 
15900         setup.sound_music = FALSE;
15901         FadeMusic();
15902       }
15903       else if (audio.music_available)
15904       { 
15905         setup.sound = setup.sound_music = TRUE;
15906
15907         SetAudioMode(setup.sound);
15908
15909         PlayLevelMusic();
15910       }
15911       break;
15912
15913     case SOUND_CTRL_ID_LOOPS:
15914       if (setup.sound_loops)
15915         setup.sound_loops = FALSE;
15916       else if (audio.loops_available)
15917       {
15918         setup.sound = setup.sound_loops = TRUE;
15919         SetAudioMode(setup.sound);
15920       }
15921       break;
15922
15923     case SOUND_CTRL_ID_SIMPLE:
15924       if (setup.sound_simple)
15925         setup.sound_simple = FALSE;
15926       else if (audio.sound_available)
15927       {
15928         setup.sound = setup.sound_simple = TRUE;
15929         SetAudioMode(setup.sound);
15930       }
15931       break;
15932
15933     default:
15934       break;
15935   }
15936 }