rnd-20070418-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_PANEL_LEVEL_NUMBER                 0
135 #define GAME_PANEL_GEMS                         1
136 #define GAME_PANEL_INVENTORY_COUNT              2
137 #define GAME_PANEL_INVENTORY_FIRST_1            3
138 #define GAME_PANEL_INVENTORY_FIRST_2            4
139 #define GAME_PANEL_INVENTORY_FIRST_3            5
140 #define GAME_PANEL_INVENTORY_FIRST_4            6
141 #define GAME_PANEL_INVENTORY_FIRST_5            7
142 #define GAME_PANEL_INVENTORY_FIRST_6            8
143 #define GAME_PANEL_INVENTORY_FIRST_7            9
144 #define GAME_PANEL_INVENTORY_FIRST_8            10
145 #define GAME_PANEL_INVENTORY_LAST_1             11
146 #define GAME_PANEL_INVENTORY_LAST_2             12
147 #define GAME_PANEL_INVENTORY_LAST_3             13
148 #define GAME_PANEL_INVENTORY_LAST_4             14
149 #define GAME_PANEL_INVENTORY_LAST_5             15
150 #define GAME_PANEL_INVENTORY_LAST_6             16
151 #define GAME_PANEL_INVENTORY_LAST_7             17
152 #define GAME_PANEL_INVENTORY_LAST_8             18
153 #define GAME_PANEL_KEY_1                        19
154 #define GAME_PANEL_KEY_2                        20
155 #define GAME_PANEL_KEY_3                        21
156 #define GAME_PANEL_KEY_4                        22
157 #define GAME_PANEL_KEY_5                        23
158 #define GAME_PANEL_KEY_6                        24
159 #define GAME_PANEL_KEY_7                        25
160 #define GAME_PANEL_KEY_8                        26
161 #define GAME_PANEL_KEY_WHITE                    27
162 #define GAME_PANEL_KEY_WHITE_COUNT              28
163 #define GAME_PANEL_SCORE                        29
164 #define GAME_PANEL_TIME                         30
165 #define GAME_PANEL_TIME_HH                      31
166 #define GAME_PANEL_TIME_MM                      32
167 #define GAME_PANEL_TIME_SS                      33
168 #define GAME_PANEL_SHIELD_NORMAL                34
169 #define GAME_PANEL_SHIELD_NORMAL_TIME           35
170 #define GAME_PANEL_SHIELD_DEADLY                36
171 #define GAME_PANEL_SHIELD_DEADLY_TIME           37
172 #define GAME_PANEL_EXIT                         38
173 #define GAME_PANEL_EMC_MAGIC_BALL               39
174 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        40
175 #define GAME_PANEL_LIGHT_SWITCH                 41
176 #define GAME_PANEL_LIGHT_SWITCH_TIME            42
177 #define GAME_PANEL_TIMEGATE_SWITCH              43
178 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         44
179 #define GAME_PANEL_SWITCHGATE_SWITCH            45
180 #define GAME_PANEL_EMC_LENSES                   46
181 #define GAME_PANEL_EMC_LENSES_TIME              47
182 #define GAME_PANEL_EMC_MAGNIFIER                48
183 #define GAME_PANEL_EMC_MAGNIFIER_TIME           49
184 #define GAME_PANEL_BALLOON_SWITCH               50
185 #define GAME_PANEL_DYNABOMB_NUMBER              51
186 #define GAME_PANEL_DYNABOMB_SIZE                52
187 #define GAME_PANEL_DYNABOMB_POWER               53
188 #define GAME_PANEL_PENGUINS                     54
189 #define GAME_PANEL_SOKOBAN_OBJECTS              55
190 #define GAME_PANEL_SOKOBAN_FIELDS               56
191 #define GAME_PANEL_ROBOT_WHEEL                  57
192 #define GAME_PANEL_CONVEYOR_BELT_1              58
193 #define GAME_PANEL_CONVEYOR_BELT_2              59
194 #define GAME_PANEL_CONVEYOR_BELT_3              60
195 #define GAME_PANEL_CONVEYOR_BELT_4              61
196 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       62
197 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       63
198 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       64
199 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       65
200 #define GAME_PANEL_MAGIC_WALL                   66
201 #define GAME_PANEL_MAGIC_WALL_TIME              67
202 #define GAME_PANEL_GRAVITY_STATE                68
203 #define GAME_PANEL_GRAPHIC_1                    69
204 #define GAME_PANEL_GRAPHIC_2                    70
205 #define GAME_PANEL_GRAPHIC_3                    71
206 #define GAME_PANEL_GRAPHIC_4                    72
207 #define GAME_PANEL_GRAPHIC_5                    73
208 #define GAME_PANEL_GRAPHIC_6                    74
209 #define GAME_PANEL_GRAPHIC_7                    75
210 #define GAME_PANEL_GRAPHIC_8                    76
211 #define GAME_PANEL_ELEMENT_1                    77
212 #define GAME_PANEL_ELEMENT_2                    78
213 #define GAME_PANEL_ELEMENT_3                    79
214 #define GAME_PANEL_ELEMENT_4                    80
215 #define GAME_PANEL_ELEMENT_5                    81
216 #define GAME_PANEL_ELEMENT_6                    82
217 #define GAME_PANEL_ELEMENT_7                    83
218 #define GAME_PANEL_ELEMENT_8                    84
219 #define GAME_PANEL_ELEMENT_COUNT_1              85
220 #define GAME_PANEL_ELEMENT_COUNT_2              86
221 #define GAME_PANEL_ELEMENT_COUNT_3              87
222 #define GAME_PANEL_ELEMENT_COUNT_4              88
223 #define GAME_PANEL_ELEMENT_COUNT_5              89
224 #define GAME_PANEL_ELEMENT_COUNT_6              90
225 #define GAME_PANEL_ELEMENT_COUNT_7              91
226 #define GAME_PANEL_ELEMENT_COUNT_8              92
227 #define GAME_PANEL_CE_SCORE_1                   93
228 #define GAME_PANEL_CE_SCORE_2                   94
229 #define GAME_PANEL_CE_SCORE_3                   95
230 #define GAME_PANEL_CE_SCORE_4                   96
231 #define GAME_PANEL_CE_SCORE_5                   97
232 #define GAME_PANEL_CE_SCORE_6                   98
233 #define GAME_PANEL_CE_SCORE_7                   99
234 #define GAME_PANEL_CE_SCORE_8                   100
235 #define GAME_PANEL_CE_SCORE_1_ELEMENT           101
236 #define GAME_PANEL_CE_SCORE_2_ELEMENT           102
237 #define GAME_PANEL_CE_SCORE_3_ELEMENT           103
238 #define GAME_PANEL_CE_SCORE_4_ELEMENT           104
239 #define GAME_PANEL_CE_SCORE_5_ELEMENT           105
240 #define GAME_PANEL_CE_SCORE_6_ELEMENT           106
241 #define GAME_PANEL_CE_SCORE_7_ELEMENT           107
242 #define GAME_PANEL_CE_SCORE_8_ELEMENT           108
243 #define GAME_PANEL_PLAYER_NAME                  109
244 #define GAME_PANEL_LEVEL_NAME                   110
245 #define GAME_PANEL_LEVEL_AUTHOR                 111
246
247 #define NUM_GAME_PANEL_CONTROLS                 112
248
249 struct GamePanelOrderInfo
250 {
251   int nr;
252   int sort_priority;
253 };
254
255 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
256
257 struct GamePanelControlInfo
258 {
259   int nr;
260
261   struct TextPosInfo *pos;
262   int type;
263
264   int value, last_value;
265   int frame, last_frame;
266   int gfx_frame;
267   int gfx_random;
268 };
269
270 static struct GamePanelControlInfo game_panel_controls[] =
271 {
272   {
273     GAME_PANEL_LEVEL_NUMBER,
274     &game.panel.level_number,
275     TYPE_INTEGER,
276   },
277   {
278     GAME_PANEL_GEMS,
279     &game.panel.gems,
280     TYPE_INTEGER,
281   },
282   {
283     GAME_PANEL_INVENTORY_COUNT,
284     &game.panel.inventory_count,
285     TYPE_INTEGER,
286   },
287   {
288     GAME_PANEL_INVENTORY_FIRST_1,
289     &game.panel.inventory_first[0],
290     TYPE_ELEMENT,
291   },
292   {
293     GAME_PANEL_INVENTORY_FIRST_2,
294     &game.panel.inventory_first[1],
295     TYPE_ELEMENT,
296   },
297   {
298     GAME_PANEL_INVENTORY_FIRST_3,
299     &game.panel.inventory_first[2],
300     TYPE_ELEMENT,
301   },
302   {
303     GAME_PANEL_INVENTORY_FIRST_4,
304     &game.panel.inventory_first[3],
305     TYPE_ELEMENT,
306   },
307   {
308     GAME_PANEL_INVENTORY_FIRST_5,
309     &game.panel.inventory_first[4],
310     TYPE_ELEMENT,
311   },
312   {
313     GAME_PANEL_INVENTORY_FIRST_6,
314     &game.panel.inventory_first[5],
315     TYPE_ELEMENT,
316   },
317   {
318     GAME_PANEL_INVENTORY_FIRST_7,
319     &game.panel.inventory_first[6],
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_PANEL_INVENTORY_FIRST_8,
324     &game.panel.inventory_first[7],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_PANEL_INVENTORY_LAST_1,
329     &game.panel.inventory_last[0],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_PANEL_INVENTORY_LAST_2,
334     &game.panel.inventory_last[1],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_PANEL_INVENTORY_LAST_3,
339     &game.panel.inventory_last[2],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_PANEL_INVENTORY_LAST_4,
344     &game.panel.inventory_last[3],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_PANEL_INVENTORY_LAST_5,
349     &game.panel.inventory_last[4],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_PANEL_INVENTORY_LAST_6,
354     &game.panel.inventory_last[5],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_PANEL_INVENTORY_LAST_7,
359     &game.panel.inventory_last[6],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_PANEL_INVENTORY_LAST_8,
364     &game.panel.inventory_last[7],
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_PANEL_KEY_1,
369     &game.panel.key[0],
370     TYPE_ELEMENT,
371   },
372   {
373     GAME_PANEL_KEY_2,
374     &game.panel.key[1],
375     TYPE_ELEMENT,
376   },
377   {
378     GAME_PANEL_KEY_3,
379     &game.panel.key[2],
380     TYPE_ELEMENT,
381   },
382   {
383     GAME_PANEL_KEY_4,
384     &game.panel.key[3],
385     TYPE_ELEMENT,
386   },
387   {
388     GAME_PANEL_KEY_5,
389     &game.panel.key[4],
390     TYPE_ELEMENT,
391   },
392   {
393     GAME_PANEL_KEY_6,
394     &game.panel.key[5],
395     TYPE_ELEMENT,
396   },
397   {
398     GAME_PANEL_KEY_7,
399     &game.panel.key[6],
400     TYPE_ELEMENT,
401   },
402   {
403     GAME_PANEL_KEY_8,
404     &game.panel.key[7],
405     TYPE_ELEMENT,
406   },
407   {
408     GAME_PANEL_KEY_WHITE,
409     &game.panel.key_white,
410     TYPE_ELEMENT,
411   },
412   {
413     GAME_PANEL_KEY_WHITE_COUNT,
414     &game.panel.key_white_count,
415     TYPE_INTEGER,
416   },
417   {
418     GAME_PANEL_SCORE,
419     &game.panel.score,
420     TYPE_INTEGER,
421   },
422   {
423     GAME_PANEL_TIME,
424     &game.panel.time,
425     TYPE_INTEGER,
426   },
427   {
428     GAME_PANEL_TIME_HH,
429     &game.panel.time_hh,
430     TYPE_INTEGER,
431   },
432   {
433     GAME_PANEL_TIME_MM,
434     &game.panel.time_mm,
435     TYPE_INTEGER,
436   },
437   {
438     GAME_PANEL_TIME_SS,
439     &game.panel.time_ss,
440     TYPE_INTEGER,
441   },
442   {
443     GAME_PANEL_SHIELD_NORMAL,
444     &game.panel.shield_normal,
445     TYPE_ELEMENT,
446   },
447   {
448     GAME_PANEL_SHIELD_NORMAL_TIME,
449     &game.panel.shield_normal_time,
450     TYPE_INTEGER,
451   },
452   {
453     GAME_PANEL_SHIELD_DEADLY,
454     &game.panel.shield_deadly,
455     TYPE_ELEMENT,
456   },
457   {
458     GAME_PANEL_SHIELD_DEADLY_TIME,
459     &game.panel.shield_deadly_time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_PANEL_EXIT,
464     &game.panel.exit,
465     TYPE_ELEMENT,
466   },
467   {
468     GAME_PANEL_EMC_MAGIC_BALL,
469     &game.panel.emc_magic_ball,
470     TYPE_ELEMENT,
471   },
472   {
473     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
474     &game.panel.emc_magic_ball_switch,
475     TYPE_ELEMENT,
476   },
477   {
478     GAME_PANEL_LIGHT_SWITCH,
479     &game.panel.light_switch,
480     TYPE_ELEMENT,
481   },
482   {
483     GAME_PANEL_LIGHT_SWITCH_TIME,
484     &game.panel.light_switch_time,
485     TYPE_INTEGER,
486   },
487   {
488     GAME_PANEL_TIMEGATE_SWITCH,
489     &game.panel.timegate_switch,
490     TYPE_ELEMENT,
491   },
492   {
493     GAME_PANEL_TIMEGATE_SWITCH_TIME,
494     &game.panel.timegate_switch_time,
495     TYPE_INTEGER,
496   },
497   {
498     GAME_PANEL_SWITCHGATE_SWITCH,
499     &game.panel.switchgate_switch,
500     TYPE_ELEMENT,
501   },
502   {
503     GAME_PANEL_EMC_LENSES,
504     &game.panel.emc_lenses,
505     TYPE_ELEMENT,
506   },
507   {
508     GAME_PANEL_EMC_LENSES_TIME,
509     &game.panel.emc_lenses_time,
510     TYPE_INTEGER,
511   },
512   {
513     GAME_PANEL_EMC_MAGNIFIER,
514     &game.panel.emc_magnifier,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_PANEL_EMC_MAGNIFIER_TIME,
519     &game.panel.emc_magnifier_time,
520     TYPE_INTEGER,
521   },
522   {
523     GAME_PANEL_BALLOON_SWITCH,
524     &game.panel.balloon_switch,
525     TYPE_ELEMENT,
526   },
527   {
528     GAME_PANEL_DYNABOMB_NUMBER,
529     &game.panel.dynabomb_number,
530     TYPE_INTEGER,
531   },
532   {
533     GAME_PANEL_DYNABOMB_SIZE,
534     &game.panel.dynabomb_size,
535     TYPE_INTEGER,
536   },
537   {
538     GAME_PANEL_DYNABOMB_POWER,
539     &game.panel.dynabomb_power,
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_PANEL_PENGUINS,
544     &game.panel.penguins,
545     TYPE_INTEGER,
546   },
547   {
548     GAME_PANEL_SOKOBAN_OBJECTS,
549     &game.panel.sokoban_objects,
550     TYPE_INTEGER,
551   },
552   {
553     GAME_PANEL_SOKOBAN_FIELDS,
554     &game.panel.sokoban_fields,
555     TYPE_INTEGER,
556   },
557   {
558     GAME_PANEL_ROBOT_WHEEL,
559     &game.panel.robot_wheel,
560     TYPE_ELEMENT,
561   },
562   {
563     GAME_PANEL_CONVEYOR_BELT_1,
564     &game.panel.conveyor_belt[0],
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_PANEL_CONVEYOR_BELT_2,
569     &game.panel.conveyor_belt[1],
570     TYPE_ELEMENT,
571   },
572   {
573     GAME_PANEL_CONVEYOR_BELT_3,
574     &game.panel.conveyor_belt[2],
575     TYPE_ELEMENT,
576   },
577   {
578     GAME_PANEL_CONVEYOR_BELT_4,
579     &game.panel.conveyor_belt[3],
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
584     &game.panel.conveyor_belt_switch[0],
585     TYPE_ELEMENT,
586   },
587   {
588     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
589     &game.panel.conveyor_belt_switch[1],
590     TYPE_ELEMENT,
591   },
592   {
593     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
594     &game.panel.conveyor_belt_switch[2],
595     TYPE_ELEMENT,
596   },
597   {
598     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
599     &game.panel.conveyor_belt_switch[3],
600     TYPE_ELEMENT,
601   },
602   {
603     GAME_PANEL_MAGIC_WALL,
604     &game.panel.magic_wall,
605     TYPE_ELEMENT,
606   },
607   {
608     GAME_PANEL_MAGIC_WALL_TIME,
609     &game.panel.magic_wall_time,
610     TYPE_INTEGER,
611   },
612   {
613     GAME_PANEL_GRAVITY_STATE,
614     &game.panel.gravity_state,
615     TYPE_STRING,
616   },
617   {
618     GAME_PANEL_GRAPHIC_1,
619     &game.panel.graphic[0],
620     TYPE_ELEMENT,
621   },
622   {
623     GAME_PANEL_GRAPHIC_2,
624     &game.panel.graphic[1],
625     TYPE_ELEMENT,
626   },
627   {
628     GAME_PANEL_GRAPHIC_3,
629     &game.panel.graphic[2],
630     TYPE_ELEMENT,
631   },
632   {
633     GAME_PANEL_GRAPHIC_4,
634     &game.panel.graphic[3],
635     TYPE_ELEMENT,
636   },
637   {
638     GAME_PANEL_GRAPHIC_5,
639     &game.panel.graphic[4],
640     TYPE_ELEMENT,
641   },
642   {
643     GAME_PANEL_GRAPHIC_6,
644     &game.panel.graphic[5],
645     TYPE_ELEMENT,
646   },
647   {
648     GAME_PANEL_GRAPHIC_7,
649     &game.panel.graphic[6],
650     TYPE_ELEMENT,
651   },
652   {
653     GAME_PANEL_GRAPHIC_8,
654     &game.panel.graphic[7],
655     TYPE_ELEMENT,
656   },
657   {
658     GAME_PANEL_ELEMENT_1,
659     &game.panel.element[0],
660     TYPE_ELEMENT,
661   },
662   {
663     GAME_PANEL_ELEMENT_2,
664     &game.panel.element[1],
665     TYPE_ELEMENT,
666   },
667   {
668     GAME_PANEL_ELEMENT_3,
669     &game.panel.element[2],
670     TYPE_ELEMENT,
671   },
672   {
673     GAME_PANEL_ELEMENT_4,
674     &game.panel.element[3],
675     TYPE_ELEMENT,
676   },
677   {
678     GAME_PANEL_ELEMENT_5,
679     &game.panel.element[4],
680     TYPE_ELEMENT,
681   },
682   {
683     GAME_PANEL_ELEMENT_6,
684     &game.panel.element[5],
685     TYPE_ELEMENT,
686   },
687   {
688     GAME_PANEL_ELEMENT_7,
689     &game.panel.element[6],
690     TYPE_ELEMENT,
691   },
692   {
693     GAME_PANEL_ELEMENT_8,
694     &game.panel.element[7],
695     TYPE_ELEMENT,
696   },
697   {
698     GAME_PANEL_ELEMENT_COUNT_1,
699     &game.panel.element_count[0],
700     TYPE_INTEGER,
701   },
702   {
703     GAME_PANEL_ELEMENT_COUNT_2,
704     &game.panel.element_count[1],
705     TYPE_INTEGER,
706   },
707   {
708     GAME_PANEL_ELEMENT_COUNT_3,
709     &game.panel.element_count[2],
710     TYPE_INTEGER,
711   },
712   {
713     GAME_PANEL_ELEMENT_COUNT_4,
714     &game.panel.element_count[3],
715     TYPE_INTEGER,
716   },
717   {
718     GAME_PANEL_ELEMENT_COUNT_5,
719     &game.panel.element_count[4],
720     TYPE_INTEGER,
721   },
722   {
723     GAME_PANEL_ELEMENT_COUNT_6,
724     &game.panel.element_count[5],
725     TYPE_INTEGER,
726   },
727   {
728     GAME_PANEL_ELEMENT_COUNT_7,
729     &game.panel.element_count[6],
730     TYPE_INTEGER,
731   },
732   {
733     GAME_PANEL_ELEMENT_COUNT_8,
734     &game.panel.element_count[7],
735     TYPE_INTEGER,
736   },
737   {
738     GAME_PANEL_CE_SCORE_1,
739     &game.panel.ce_score[0],
740     TYPE_INTEGER,
741   },
742   {
743     GAME_PANEL_CE_SCORE_2,
744     &game.panel.ce_score[1],
745     TYPE_INTEGER,
746   },
747   {
748     GAME_PANEL_CE_SCORE_3,
749     &game.panel.ce_score[2],
750     TYPE_INTEGER,
751   },
752   {
753     GAME_PANEL_CE_SCORE_4,
754     &game.panel.ce_score[3],
755     TYPE_INTEGER,
756   },
757   {
758     GAME_PANEL_CE_SCORE_5,
759     &game.panel.ce_score[4],
760     TYPE_INTEGER,
761   },
762   {
763     GAME_PANEL_CE_SCORE_6,
764     &game.panel.ce_score[5],
765     TYPE_INTEGER,
766   },
767   {
768     GAME_PANEL_CE_SCORE_7,
769     &game.panel.ce_score[6],
770     TYPE_INTEGER,
771   },
772   {
773     GAME_PANEL_CE_SCORE_8,
774     &game.panel.ce_score[7],
775     TYPE_INTEGER,
776   },
777   {
778     GAME_PANEL_CE_SCORE_1_ELEMENT,
779     &game.panel.ce_score_element[0],
780     TYPE_ELEMENT,
781   },
782   {
783     GAME_PANEL_CE_SCORE_2_ELEMENT,
784     &game.panel.ce_score_element[1],
785     TYPE_ELEMENT,
786   },
787   {
788     GAME_PANEL_CE_SCORE_3_ELEMENT,
789     &game.panel.ce_score_element[2],
790     TYPE_ELEMENT,
791   },
792   {
793     GAME_PANEL_CE_SCORE_4_ELEMENT,
794     &game.panel.ce_score_element[3],
795     TYPE_ELEMENT,
796   },
797   {
798     GAME_PANEL_CE_SCORE_5_ELEMENT,
799     &game.panel.ce_score_element[4],
800     TYPE_ELEMENT,
801   },
802   {
803     GAME_PANEL_CE_SCORE_6_ELEMENT,
804     &game.panel.ce_score_element[5],
805     TYPE_ELEMENT,
806   },
807   {
808     GAME_PANEL_CE_SCORE_7_ELEMENT,
809     &game.panel.ce_score_element[6],
810     TYPE_ELEMENT,
811   },
812   {
813     GAME_PANEL_CE_SCORE_8_ELEMENT,
814     &game.panel.ce_score_element[7],
815     TYPE_ELEMENT,
816   },
817   {
818     GAME_PANEL_PLAYER_NAME,
819     &game.panel.player_name,
820     TYPE_STRING,
821   },
822   {
823     GAME_PANEL_LEVEL_NAME,
824     &game.panel.level_name,
825     TYPE_STRING,
826   },
827   {
828     GAME_PANEL_LEVEL_AUTHOR,
829     &game.panel.level_author,
830     TYPE_STRING,
831   },
832
833   {
834     -1,
835     NULL,
836     -1,
837   }
838 };
839 #endif
840
841
842 /* values for delayed check of falling and moving elements and for collision */
843 #define CHECK_DELAY_MOVING      3
844 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
845 #define CHECK_DELAY_COLLISION   2
846 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
847
848 /* values for initial player move delay (initial delay counter value) */
849 #define INITIAL_MOVE_DELAY_OFF  -1
850 #define INITIAL_MOVE_DELAY_ON   0
851
852 /* values for player movement speed (which is in fact a delay value) */
853 #define MOVE_DELAY_MIN_SPEED    32
854 #define MOVE_DELAY_NORMAL_SPEED 8
855 #define MOVE_DELAY_HIGH_SPEED   4
856 #define MOVE_DELAY_MAX_SPEED    1
857
858 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
859 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
860
861 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
862 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
900         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
901          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
902          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
903          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
904          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
905          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
906          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
907          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
908          (e))
909
910 #define CAN_GROW_INTO(e)                                                \
911         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
912
913 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
914                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
915                                         (condition)))
916
917 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
918                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
919                                         (CAN_MOVE_INTO_ACID(e) &&       \
920                                          Feld[x][y] == EL_ACID) ||      \
921                                         (condition)))
922
923 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
924                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
925                                         (CAN_MOVE_INTO_ACID(e) &&       \
926                                          Feld[x][y] == EL_ACID) ||      \
927                                         (condition)))
928
929 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
930                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
931                                         (condition) ||                  \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Feld[x][y] == EL_ACID) ||      \
934                                         (DONT_COLLIDE_WITH(e) &&        \
935                                          IS_PLAYER(x, y) &&             \
936                                          !PLAYER_ENEMY_PROTECTED(x, y))))
937
938 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
939         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
940
941 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
942         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
943
944 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
946
947 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
948         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
949                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
950
951 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
953
954 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
955         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
956
957 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
959
960 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
962
963 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
964         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
965
966 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
968                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
969                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
970                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
971                                                  IS_FOOD_PENGUIN(Feld[x][y])))
972 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
973         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
974
975 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
977
978 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
980
981 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
982         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
983                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
984
985 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
986
987 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
988                 (!IS_PLAYER(x, y) &&                                    \
989                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
990
991 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
992         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
993
994 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
995 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
996
997 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
998 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
999 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1000 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1001
1002 /* game button identifiers */
1003 #define GAME_CTRL_ID_STOP               0
1004 #define GAME_CTRL_ID_PAUSE              1
1005 #define GAME_CTRL_ID_PLAY               2
1006 #define SOUND_CTRL_ID_MUSIC             3
1007 #define SOUND_CTRL_ID_LOOPS             4
1008 #define SOUND_CTRL_ID_SIMPLE            5
1009
1010 #define NUM_GAME_BUTTONS                6
1011
1012
1013 /* forward declaration for internal use */
1014
1015 static void CreateField(int, int, int);
1016
1017 static void ResetGfxAnimation(int, int);
1018
1019 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1020 static void AdvanceFrameAndPlayerCounters(int);
1021
1022 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1023 static boolean MovePlayer(struct PlayerInfo *, int, int);
1024 static void ScrollPlayer(struct PlayerInfo *, int);
1025 static void ScrollScreen(struct PlayerInfo *, int);
1026
1027 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1028
1029 static void InitBeltMovement(void);
1030 static void CloseAllOpenTimegates(void);
1031 static void CheckGravityMovement(struct PlayerInfo *);
1032 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1033 static void KillPlayerUnlessEnemyProtected(int, int);
1034 static void KillPlayerUnlessExplosionProtected(int, int);
1035
1036 static void TestIfPlayerTouchesCustomElement(int, int);
1037 static void TestIfElementTouchesCustomElement(int, int);
1038 static void TestIfElementHitsCustomElement(int, int, int);
1039 #if 0
1040 static void TestIfElementSmashesCustomElement(int, int, int);
1041 #endif
1042
1043 static void HandleElementChange(int, int, int);
1044 static void ExecuteCustomElementAction(int, int, int, int);
1045 static boolean ChangeElement(int, int, int, int);
1046
1047 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1048 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1049         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1050 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1051         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1052 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1053         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1054 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1055         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1056
1057 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1058 #define CheckElementChange(x, y, e, te, ev)                             \
1059         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1060 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1061         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1062 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1063         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1064
1065 static void PlayLevelSound(int, int, int);
1066 static void PlayLevelSoundNearest(int, int, int);
1067 static void PlayLevelSoundAction(int, int, int);
1068 static void PlayLevelSoundElementAction(int, int, int, int);
1069 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1070 static void PlayLevelSoundActionIfLoop(int, int, int);
1071 static void StopLevelSoundActionIfLoop(int, int, int);
1072 static void PlayLevelMusic();
1073
1074 static void MapGameButtons();
1075 static void HandleGameButtons(struct GadgetInfo *);
1076
1077 int AmoebeNachbarNr(int, int);
1078 void AmoebeUmwandeln(int, int);
1079 void ContinueMoving(int, int);
1080 void Bang(int, int);
1081 void InitMovDir(int, int);
1082 void InitAmoebaNr(int, int);
1083 int NewHiScore(void);
1084
1085 void TestIfGoodThingHitsBadThing(int, int, int);
1086 void TestIfBadThingHitsGoodThing(int, int, int);
1087 void TestIfPlayerTouchesBadThing(int, int);
1088 void TestIfPlayerRunsIntoBadThing(int, int, int);
1089 void TestIfBadThingTouchesPlayer(int, int);
1090 void TestIfBadThingRunsIntoPlayer(int, int, int);
1091 void TestIfFriendTouchesBadThing(int, int);
1092 void TestIfBadThingTouchesFriend(int, int);
1093 void TestIfBadThingTouchesOtherBadThing(int, int);
1094
1095 void KillPlayer(struct PlayerInfo *);
1096 void BuryPlayer(struct PlayerInfo *);
1097 void RemovePlayer(struct PlayerInfo *);
1098
1099 boolean SnapField(struct PlayerInfo *, int, int);
1100 boolean DropElement(struct PlayerInfo *);
1101
1102 static int getInvisibleActiveFromInvisibleElement(int);
1103 static int getInvisibleFromInvisibleActiveElement(int);
1104
1105 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1106
1107 /* for detection of endless loops, caused by custom element programming */
1108 /* (using maximal playfield width x 10 is just a rough approximation) */
1109 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1110
1111 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1112 {                                                                       \
1113   if (recursion_loop_detected)                                          \
1114     return (rc);                                                        \
1115                                                                         \
1116   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1117   {                                                                     \
1118     recursion_loop_detected = TRUE;                                     \
1119     recursion_loop_element = (e);                                       \
1120   }                                                                     \
1121                                                                         \
1122   recursion_loop_depth++;                                               \
1123 }
1124
1125 #define RECURSION_LOOP_DETECTION_END()                                  \
1126 {                                                                       \
1127   recursion_loop_depth--;                                               \
1128 }
1129
1130 static int recursion_loop_depth;
1131 static boolean recursion_loop_detected;
1132 static boolean recursion_loop_element;
1133
1134
1135 /* ------------------------------------------------------------------------- */
1136 /* definition of elements that automatically change to other elements after  */
1137 /* a specified time, eventually calling a function when changing             */
1138 /* ------------------------------------------------------------------------- */
1139
1140 /* forward declaration for changer functions */
1141 static void InitBuggyBase(int, int);
1142 static void WarnBuggyBase(int, int);
1143
1144 static void InitTrap(int, int);
1145 static void ActivateTrap(int, int);
1146 static void ChangeActiveTrap(int, int);
1147
1148 static void InitRobotWheel(int, int);
1149 static void RunRobotWheel(int, int);
1150 static void StopRobotWheel(int, int);
1151
1152 static void InitTimegateWheel(int, int);
1153 static void RunTimegateWheel(int, int);
1154
1155 static void InitMagicBallDelay(int, int);
1156 static void ActivateMagicBall(int, int);
1157
1158 struct ChangingElementInfo
1159 {
1160   int element;
1161   int target_element;
1162   int change_delay;
1163   void (*pre_change_function)(int x, int y);
1164   void (*change_function)(int x, int y);
1165   void (*post_change_function)(int x, int y);
1166 };
1167
1168 static struct ChangingElementInfo change_delay_list[] =
1169 {
1170   {
1171     EL_NUT_BREAKING,
1172     EL_EMERALD,
1173     6,
1174     NULL,
1175     NULL,
1176     NULL
1177   },
1178   {
1179     EL_PEARL_BREAKING,
1180     EL_EMPTY,
1181     8,
1182     NULL,
1183     NULL,
1184     NULL
1185   },
1186   {
1187     EL_EXIT_OPENING,
1188     EL_EXIT_OPEN,
1189     29,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_EXIT_CLOSING,
1196     EL_EXIT_CLOSED,
1197     29,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_STEEL_EXIT_OPENING,
1204     EL_STEEL_EXIT_OPEN,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_STEEL_EXIT_CLOSING,
1212     EL_STEEL_EXIT_CLOSED,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EM_EXIT_OPENING,
1220     EL_EM_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EM_EXIT_CLOSING,
1228 #if 1
1229     EL_EMPTY,
1230 #else
1231     EL_EM_EXIT_CLOSED,
1232 #endif
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_EM_STEEL_EXIT_OPENING,
1240     EL_EM_STEEL_EXIT_OPEN,
1241     29,
1242     NULL,
1243     NULL,
1244     NULL
1245   },
1246   {
1247     EL_EM_STEEL_EXIT_CLOSING,
1248 #if 1
1249     EL_STEELWALL,
1250 #else
1251     EL_EM_STEEL_EXIT_CLOSED,
1252 #endif
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_SP_EXIT_OPENING,
1260     EL_SP_EXIT_OPEN,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_SP_EXIT_CLOSING,
1268     EL_SP_EXIT_CLOSED,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_SWITCHGATE_OPENING,
1276     EL_SWITCHGATE_OPEN,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SWITCHGATE_CLOSING,
1284     EL_SWITCHGATE_CLOSED,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_TIMEGATE_OPENING,
1292     EL_TIMEGATE_OPEN,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_TIMEGATE_CLOSING,
1300     EL_TIMEGATE_CLOSED,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306
1307   {
1308     EL_ACID_SPLASH_LEFT,
1309     EL_EMPTY,
1310     8,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315   {
1316     EL_ACID_SPLASH_RIGHT,
1317     EL_EMPTY,
1318     8,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_SP_BUGGY_BASE,
1325     EL_SP_BUGGY_BASE_ACTIVATING,
1326     0,
1327     InitBuggyBase,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_SP_BUGGY_BASE_ACTIVATING,
1333     EL_SP_BUGGY_BASE_ACTIVE,
1334     0,
1335     InitBuggyBase,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_SP_BUGGY_BASE_ACTIVE,
1341     EL_SP_BUGGY_BASE,
1342     0,
1343     InitBuggyBase,
1344     WarnBuggyBase,
1345     NULL
1346   },
1347   {
1348     EL_TRAP,
1349     EL_TRAP_ACTIVE,
1350     0,
1351     InitTrap,
1352     NULL,
1353     ActivateTrap
1354   },
1355   {
1356     EL_TRAP_ACTIVE,
1357     EL_TRAP,
1358     31,
1359     NULL,
1360     ChangeActiveTrap,
1361     NULL
1362   },
1363   {
1364     EL_ROBOT_WHEEL_ACTIVE,
1365     EL_ROBOT_WHEEL,
1366     0,
1367     InitRobotWheel,
1368     RunRobotWheel,
1369     StopRobotWheel
1370   },
1371   {
1372     EL_TIMEGATE_SWITCH_ACTIVE,
1373     EL_TIMEGATE_SWITCH,
1374     0,
1375     InitTimegateWheel,
1376     RunTimegateWheel,
1377     NULL
1378   },
1379   {
1380     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1381     EL_DC_TIMEGATE_SWITCH,
1382     0,
1383     InitTimegateWheel,
1384     RunTimegateWheel,
1385     NULL
1386   },
1387   {
1388     EL_EMC_MAGIC_BALL_ACTIVE,
1389     EL_EMC_MAGIC_BALL_ACTIVE,
1390     0,
1391     InitMagicBallDelay,
1392     NULL,
1393     ActivateMagicBall
1394   },
1395   {
1396     EL_EMC_SPRING_BUMPER_ACTIVE,
1397     EL_EMC_SPRING_BUMPER,
1398     8,
1399     NULL,
1400     NULL,
1401     NULL
1402   },
1403   {
1404     EL_DIAGONAL_SHRINKING,
1405     EL_UNDEFINED,
1406     0,
1407     NULL,
1408     NULL,
1409     NULL
1410   },
1411   {
1412     EL_DIAGONAL_GROWING,
1413     EL_UNDEFINED,
1414     0,
1415     NULL,
1416     NULL,
1417     NULL,
1418   },
1419
1420   {
1421     EL_UNDEFINED,
1422     EL_UNDEFINED,
1423     -1,
1424     NULL,
1425     NULL,
1426     NULL
1427   }
1428 };
1429
1430 struct
1431 {
1432   int element;
1433   int push_delay_fixed, push_delay_random;
1434 }
1435 push_delay_list[] =
1436 {
1437   { EL_SPRING,                  0, 0 },
1438   { EL_BALLOON,                 0, 0 },
1439
1440   { EL_SOKOBAN_OBJECT,          2, 0 },
1441   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1442   { EL_SATELLITE,               2, 0 },
1443   { EL_SP_DISK_YELLOW,          2, 0 },
1444
1445   { EL_UNDEFINED,               0, 0 },
1446 };
1447
1448 struct
1449 {
1450   int element;
1451   int move_stepsize;
1452 }
1453 move_stepsize_list[] =
1454 {
1455   { EL_AMOEBA_DROP,             2 },
1456   { EL_AMOEBA_DROPPING,         2 },
1457   { EL_QUICKSAND_FILLING,       1 },
1458   { EL_QUICKSAND_EMPTYING,      1 },
1459   { EL_QUICKSAND_FAST_FILLING,  2 },
1460   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1461   { EL_MAGIC_WALL_FILLING,      2 },
1462   { EL_MAGIC_WALL_EMPTYING,     2 },
1463   { EL_BD_MAGIC_WALL_FILLING,   2 },
1464   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1465   { EL_DC_MAGIC_WALL_FILLING,   2 },
1466   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1467
1468   { EL_UNDEFINED,               0 },
1469 };
1470
1471 struct
1472 {
1473   int element;
1474   int count;
1475 }
1476 collect_count_list[] =
1477 {
1478   { EL_EMERALD,                 1 },
1479   { EL_BD_DIAMOND,              1 },
1480   { EL_EMERALD_YELLOW,          1 },
1481   { EL_EMERALD_RED,             1 },
1482   { EL_EMERALD_PURPLE,          1 },
1483   { EL_DIAMOND,                 3 },
1484   { EL_SP_INFOTRON,             1 },
1485   { EL_PEARL,                   5 },
1486   { EL_CRYSTAL,                 8 },
1487
1488   { EL_UNDEFINED,               0 },
1489 };
1490
1491 struct
1492 {
1493   int element;
1494   int direction;
1495 }
1496 access_direction_list[] =
1497 {
1498   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1499   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1500   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1501   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1502   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1503   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1504   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1505   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1506   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1507   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1508   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1509
1510   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1511   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1512   { EL_SP_PORT_UP,                                                   MV_DOWN },
1513   { EL_SP_PORT_DOWN,                                         MV_UP           },
1514   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1515   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1516   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1517   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1518   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1519   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1520   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1521   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1522   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1523   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1524   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1525   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1526   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1527   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1528   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1529
1530   { EL_UNDEFINED,                       MV_NONE                              }
1531 };
1532
1533 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1534
1535 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1536 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1537 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1538                                  IS_JUST_CHANGING(x, y))
1539
1540 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1541
1542 /* static variables for playfield scan mode (scanning forward or backward) */
1543 static int playfield_scan_start_x = 0;
1544 static int playfield_scan_start_y = 0;
1545 static int playfield_scan_delta_x = 1;
1546 static int playfield_scan_delta_y = 1;
1547
1548 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1549                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1550                                      (y) += playfield_scan_delta_y)     \
1551                                 for ((x) = playfield_scan_start_x;      \
1552                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1553                                      (x) += playfield_scan_delta_x)
1554
1555 #ifdef DEBUG
1556 void DEBUG_SetMaximumDynamite()
1557 {
1558   int i;
1559
1560   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1561     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1562       local_player->inventory_element[local_player->inventory_size++] =
1563         EL_DYNAMITE;
1564 }
1565 #endif
1566
1567 static void InitPlayfieldScanModeVars()
1568 {
1569   if (game.use_reverse_scan_direction)
1570   {
1571     playfield_scan_start_x = lev_fieldx - 1;
1572     playfield_scan_start_y = lev_fieldy - 1;
1573
1574     playfield_scan_delta_x = -1;
1575     playfield_scan_delta_y = -1;
1576   }
1577   else
1578   {
1579     playfield_scan_start_x = 0;
1580     playfield_scan_start_y = 0;
1581
1582     playfield_scan_delta_x = 1;
1583     playfield_scan_delta_y = 1;
1584   }
1585 }
1586
1587 static void InitPlayfieldScanMode(int mode)
1588 {
1589   game.use_reverse_scan_direction =
1590     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1591
1592   InitPlayfieldScanModeVars();
1593 }
1594
1595 static int get_move_delay_from_stepsize(int move_stepsize)
1596 {
1597   move_stepsize =
1598     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1599
1600   /* make sure that stepsize value is always a power of 2 */
1601   move_stepsize = (1 << log_2(move_stepsize));
1602
1603   return TILEX / move_stepsize;
1604 }
1605
1606 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1607                                boolean init_game)
1608 {
1609   int player_nr = player->index_nr;
1610   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1611   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1612
1613   /* do no immediately change move delay -- the player might just be moving */
1614   player->move_delay_value_next = move_delay;
1615
1616   /* information if player can move must be set separately */
1617   player->cannot_move = cannot_move;
1618
1619   if (init_game)
1620   {
1621     player->move_delay       = game.initial_move_delay[player_nr];
1622     player->move_delay_value = game.initial_move_delay_value[player_nr];
1623
1624     player->move_delay_value_next = -1;
1625
1626     player->move_delay_reset_counter = 0;
1627   }
1628 }
1629
1630 void GetPlayerConfig()
1631 {
1632   GameFrameDelay = setup.game_frame_delay;
1633
1634   if (!audio.sound_available)
1635     setup.sound_simple = FALSE;
1636
1637   if (!audio.loops_available)
1638     setup.sound_loops = FALSE;
1639
1640   if (!audio.music_available)
1641     setup.sound_music = FALSE;
1642
1643   if (!video.fullscreen_available)
1644     setup.fullscreen = FALSE;
1645
1646   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1647
1648   SetAudioMode(setup.sound);
1649   InitJoysticks();
1650 }
1651
1652 int GetElementFromGroupElement(int element)
1653 {
1654   if (IS_GROUP_ELEMENT(element))
1655   {
1656     struct ElementGroupInfo *group = element_info[element].group;
1657     int last_anim_random_frame = gfx.anim_random_frame;
1658     int element_pos;
1659
1660     if (group->choice_mode == ANIM_RANDOM)
1661       gfx.anim_random_frame = RND(group->num_elements_resolved);
1662
1663     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1664                                     group->choice_mode, 0,
1665                                     group->choice_pos);
1666
1667     if (group->choice_mode == ANIM_RANDOM)
1668       gfx.anim_random_frame = last_anim_random_frame;
1669
1670     group->choice_pos++;
1671
1672     element = group->element_resolved[element_pos];
1673   }
1674
1675   return element;
1676 }
1677
1678 static void InitPlayerField(int x, int y, int element, boolean init_game)
1679 {
1680   if (element == EL_SP_MURPHY)
1681   {
1682     if (init_game)
1683     {
1684       if (stored_player[0].present)
1685       {
1686         Feld[x][y] = EL_SP_MURPHY_CLONE;
1687
1688         return;
1689       }
1690       else
1691       {
1692         stored_player[0].use_murphy = TRUE;
1693
1694         if (!level.use_artwork_element[0])
1695           stored_player[0].artwork_element = EL_SP_MURPHY;
1696       }
1697
1698       Feld[x][y] = EL_PLAYER_1;
1699     }
1700   }
1701
1702   if (init_game)
1703   {
1704     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1705     int jx = player->jx, jy = player->jy;
1706
1707     player->present = TRUE;
1708
1709     player->block_last_field = (element == EL_SP_MURPHY ?
1710                                 level.sp_block_last_field :
1711                                 level.block_last_field);
1712
1713     /* ---------- initialize player's last field block delay --------------- */
1714
1715     /* always start with reliable default value (no adjustment needed) */
1716     player->block_delay_adjustment = 0;
1717
1718     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1719     if (player->block_last_field && element == EL_SP_MURPHY)
1720       player->block_delay_adjustment = 1;
1721
1722     /* special case 2: in game engines before 3.1.1, blocking was different */
1723     if (game.use_block_last_field_bug)
1724       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1725
1726     if (!options.network || player->connected)
1727     {
1728       player->active = TRUE;
1729
1730       /* remove potentially duplicate players */
1731       if (StorePlayer[jx][jy] == Feld[x][y])
1732         StorePlayer[jx][jy] = 0;
1733
1734       StorePlayer[x][y] = Feld[x][y];
1735
1736       if (options.debug)
1737       {
1738         printf("Player %d activated.\n", player->element_nr);
1739         printf("[Local player is %d and currently %s.]\n",
1740                local_player->element_nr,
1741                local_player->active ? "active" : "not active");
1742       }
1743     }
1744
1745     Feld[x][y] = EL_EMPTY;
1746
1747     player->jx = player->last_jx = x;
1748     player->jy = player->last_jy = y;
1749   }
1750 }
1751
1752 static void InitField(int x, int y, boolean init_game)
1753 {
1754   int element = Feld[x][y];
1755
1756   switch (element)
1757   {
1758     case EL_SP_MURPHY:
1759     case EL_PLAYER_1:
1760     case EL_PLAYER_2:
1761     case EL_PLAYER_3:
1762     case EL_PLAYER_4:
1763       InitPlayerField(x, y, element, init_game);
1764       break;
1765
1766     case EL_SOKOBAN_FIELD_PLAYER:
1767       element = Feld[x][y] = EL_PLAYER_1;
1768       InitField(x, y, init_game);
1769
1770       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1771       InitField(x, y, init_game);
1772       break;
1773
1774     case EL_SOKOBAN_FIELD_EMPTY:
1775       local_player->sokobanfields_still_needed++;
1776       break;
1777
1778     case EL_STONEBLOCK:
1779       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1780         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1781       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1782         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1783       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1784         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1785       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1786         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1787       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1788         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1789       break;
1790
1791     case EL_BUG:
1792     case EL_BUG_RIGHT:
1793     case EL_BUG_UP:
1794     case EL_BUG_LEFT:
1795     case EL_BUG_DOWN:
1796     case EL_SPACESHIP:
1797     case EL_SPACESHIP_RIGHT:
1798     case EL_SPACESHIP_UP:
1799     case EL_SPACESHIP_LEFT:
1800     case EL_SPACESHIP_DOWN:
1801     case EL_BD_BUTTERFLY:
1802     case EL_BD_BUTTERFLY_RIGHT:
1803     case EL_BD_BUTTERFLY_UP:
1804     case EL_BD_BUTTERFLY_LEFT:
1805     case EL_BD_BUTTERFLY_DOWN:
1806     case EL_BD_FIREFLY:
1807     case EL_BD_FIREFLY_RIGHT:
1808     case EL_BD_FIREFLY_UP:
1809     case EL_BD_FIREFLY_LEFT:
1810     case EL_BD_FIREFLY_DOWN:
1811     case EL_PACMAN_RIGHT:
1812     case EL_PACMAN_UP:
1813     case EL_PACMAN_LEFT:
1814     case EL_PACMAN_DOWN:
1815     case EL_YAMYAM:
1816     case EL_YAMYAM_LEFT:
1817     case EL_YAMYAM_RIGHT:
1818     case EL_YAMYAM_UP:
1819     case EL_YAMYAM_DOWN:
1820     case EL_DARK_YAMYAM:
1821     case EL_ROBOT:
1822     case EL_PACMAN:
1823     case EL_SP_SNIKSNAK:
1824     case EL_SP_ELECTRON:
1825     case EL_MOLE:
1826     case EL_MOLE_LEFT:
1827     case EL_MOLE_RIGHT:
1828     case EL_MOLE_UP:
1829     case EL_MOLE_DOWN:
1830       InitMovDir(x, y);
1831       break;
1832
1833     case EL_AMOEBA_FULL:
1834     case EL_BD_AMOEBA:
1835       InitAmoebaNr(x, y);
1836       break;
1837
1838     case EL_AMOEBA_DROP:
1839       if (y == lev_fieldy - 1)
1840       {
1841         Feld[x][y] = EL_AMOEBA_GROWING;
1842         Store[x][y] = EL_AMOEBA_WET;
1843       }
1844       break;
1845
1846     case EL_DYNAMITE_ACTIVE:
1847     case EL_SP_DISK_RED_ACTIVE:
1848     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1849     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1850     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1851     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1852       MovDelay[x][y] = 96;
1853       break;
1854
1855     case EL_EM_DYNAMITE_ACTIVE:
1856       MovDelay[x][y] = 32;
1857       break;
1858
1859     case EL_LAMP:
1860       local_player->lights_still_needed++;
1861       break;
1862
1863     case EL_PENGUIN:
1864       local_player->friends_still_needed++;
1865       break;
1866
1867     case EL_PIG:
1868     case EL_DRAGON:
1869       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1870       break;
1871
1872     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1873     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1874     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1875     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1876     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1877     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1878     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1879     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1880     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1881     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1882     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1883     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1884       if (init_game)
1885       {
1886         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1887         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1888         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1889
1890         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1891         {
1892           game.belt_dir[belt_nr] = belt_dir;
1893           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1894         }
1895         else    /* more than one switch -- set it like the first switch */
1896         {
1897           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1898         }
1899       }
1900       break;
1901
1902 #if !USE_BOTH_SWITCHGATE_SWITCHES
1903     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1904       if (init_game)
1905         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1906       break;
1907
1908     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1909       if (init_game)
1910         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1911       break;
1912 #endif
1913
1914     case EL_LIGHT_SWITCH_ACTIVE:
1915       if (init_game)
1916         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1917       break;
1918
1919     case EL_INVISIBLE_STEELWALL:
1920     case EL_INVISIBLE_WALL:
1921     case EL_INVISIBLE_SAND:
1922       if (game.light_time_left > 0 ||
1923           game.lenses_time_left > 0)
1924         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1925       break;
1926
1927     case EL_EMC_MAGIC_BALL:
1928       if (game.ball_state)
1929         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1930       break;
1931
1932     case EL_EMC_MAGIC_BALL_SWITCH:
1933       if (game.ball_state)
1934         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1935       break;
1936
1937     default:
1938       if (IS_CUSTOM_ELEMENT(element))
1939       {
1940         if (CAN_MOVE(element))
1941           InitMovDir(x, y);
1942
1943 #if USE_NEW_CUSTOM_VALUE
1944         if (!element_info[element].use_last_ce_value || init_game)
1945           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1946 #endif
1947       }
1948       else if (IS_GROUP_ELEMENT(element))
1949       {
1950         Feld[x][y] = GetElementFromGroupElement(element);
1951
1952         InitField(x, y, init_game);
1953       }
1954
1955       break;
1956   }
1957
1958   if (!init_game)
1959     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1960 }
1961
1962 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1963 {
1964   InitField(x, y, init_game);
1965
1966   /* not needed to call InitMovDir() -- already done by InitField()! */
1967   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1968       CAN_MOVE(Feld[x][y]))
1969     InitMovDir(x, y);
1970 }
1971
1972 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1973 {
1974   int old_element = Feld[x][y];
1975
1976   InitField(x, y, init_game);
1977
1978   /* not needed to call InitMovDir() -- already done by InitField()! */
1979   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1980       CAN_MOVE(old_element) &&
1981       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1982     InitMovDir(x, y);
1983
1984   /* this case is in fact a combination of not less than three bugs:
1985      first, it calls InitMovDir() for elements that can move, although this is
1986      already done by InitField(); then, it checks the element that was at this
1987      field _before_ the call to InitField() (which can change it); lastly, it
1988      was not called for "mole with direction" elements, which were treated as
1989      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1990   */
1991 }
1992
1993 #if 1
1994
1995 static int get_key_element_from_nr(int key_nr)
1996 {
1997   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1998                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1999                           EL_EM_KEY_1 : EL_KEY_1);
2000
2001   return key_base_element + key_nr;
2002 }
2003
2004 static int get_next_dropped_element(struct PlayerInfo *player)
2005 {
2006   return (player->inventory_size > 0 ?
2007           player->inventory_element[player->inventory_size - 1] :
2008           player->inventory_infinite_element != EL_UNDEFINED ?
2009           player->inventory_infinite_element :
2010           player->dynabombs_left > 0 ?
2011           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2012           EL_UNDEFINED);
2013 }
2014
2015 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2016 {
2017   /* pos >= 0: get element from bottom of the stack;
2018      pos <  0: get element from top of the stack */
2019
2020   if (pos < 0)
2021   {
2022     int min_inventory_size = -pos;
2023     int inventory_pos = player->inventory_size - min_inventory_size;
2024     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2025
2026     return (player->inventory_size >= min_inventory_size ?
2027             player->inventory_element[inventory_pos] :
2028             player->inventory_infinite_element != EL_UNDEFINED ?
2029             player->inventory_infinite_element :
2030             player->dynabombs_left >= min_dynabombs_left ?
2031             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032             EL_UNDEFINED);
2033   }
2034   else
2035   {
2036     int min_dynabombs_left = pos + 1;
2037     int min_inventory_size = pos + 1 - player->dynabombs_left;
2038     int inventory_pos = pos - player->dynabombs_left;
2039
2040     return (player->inventory_infinite_element != EL_UNDEFINED ?
2041             player->inventory_infinite_element :
2042             player->dynabombs_left >= min_dynabombs_left ?
2043             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2044             player->inventory_size >= min_inventory_size ?
2045             player->inventory_element[inventory_pos] :
2046             EL_UNDEFINED);
2047   }
2048 }
2049
2050 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2051 {
2052   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2053   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2054   int compare_result;
2055
2056   if (gpo1->sort_priority != gpo2->sort_priority)
2057     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2058   else
2059     compare_result = gpo1->nr - gpo2->nr;
2060
2061   return compare_result;
2062 }
2063
2064 void InitGameControlValues()
2065 {
2066   int i;
2067
2068   for (i = 0; game_panel_controls[i].nr != -1; i++)
2069   {
2070     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2071     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2072     struct TextPosInfo *pos = gpc->pos;
2073     int nr = gpc->nr;
2074     int type = gpc->type;
2075
2076     if (nr != i)
2077     {
2078       Error(ERR_INFO, "'game_panel_controls' structure corrupted");
2079       Error(ERR_EXIT, "this should not happen -- please debug");
2080     }
2081
2082     /* force update of game controls after initialization */
2083     gpc->value = gpc->last_value = -1;
2084     gpc->frame = gpc->last_frame = -1;
2085     gpc->gfx_frame = -1;
2086
2087     /* determine panel value width for later calculation of alignment */
2088     if (type == TYPE_INTEGER || type == TYPE_STRING)
2089     {
2090       pos->width = pos->size * getFontWidth(pos->font);
2091       pos->height = getFontHeight(pos->font);
2092     }
2093     else if (type == TYPE_ELEMENT)
2094     {
2095       pos->width = pos->size;
2096       pos->height = pos->size;
2097     }
2098
2099     /* fill structure for game panel draw order */
2100     gpo->nr = gpc->nr;
2101     gpo->sort_priority = pos->sort_priority;
2102   }
2103
2104   /* sort game panel controls according to sort_priority and control number */
2105   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2106         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2107 }
2108
2109 void UpdatePlayfieldElementCount()
2110 {
2111   int i, j, x, y;
2112
2113   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2114     element_info[i].element_count = 0;
2115
2116   SCAN_PLAYFIELD(x, y)
2117   {
2118     element_info[Feld[x][y]].element_count++;
2119   }
2120
2121   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2122     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2123       if (IS_IN_GROUP(j, i))
2124         element_info[EL_GROUP_START + i].element_count +=
2125           element_info[j].element_count;
2126 }
2127
2128 void UpdateGameControlValues()
2129 {
2130   int i, k;
2131   int time = (local_player->LevelSolved ?
2132               local_player->LevelSolved_CountingTime :
2133               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2134               level.native_em_level->lev->time :
2135               level.time == 0 ? TimePlayed : TimeLeft);
2136   int score = (local_player->LevelSolved ?
2137                local_player->LevelSolved_CountingScore :
2138                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2139                level.native_em_level->lev->score :
2140                local_player->score);
2141   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142               level.native_em_level->lev->required :
2143               local_player->gems_still_needed);
2144   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2145                      level.native_em_level->lev->required > 0 :
2146                      local_player->gems_still_needed > 0 ||
2147                      local_player->sokobanfields_still_needed > 0 ||
2148                      local_player->lights_still_needed > 0);
2149
2150   UpdatePlayfieldElementCount();
2151
2152   /* update game panel control values */
2153
2154   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2155   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2156
2157   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2158   for (i = 0; i < MAX_NUM_KEYS; i++)
2159     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2160   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2161   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2162
2163   if (game.centered_player_nr == -1)
2164   {
2165     for (i = 0; i < MAX_PLAYERS; i++)
2166     {
2167       for (k = 0; k < MAX_NUM_KEYS; k++)
2168       {
2169         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2170         {
2171           if (level.native_em_level->ply[i]->keys & (1 << k))
2172             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2173               get_key_element_from_nr(k);
2174         }
2175         else if (stored_player[i].key[k])
2176           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2177             get_key_element_from_nr(k);
2178       }
2179
2180       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2181         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2182           level.native_em_level->ply[i]->dynamite;
2183       else
2184         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2185           stored_player[i].inventory_size;
2186
2187       if (stored_player[i].num_white_keys > 0)
2188         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2189           EL_DC_KEY_WHITE;
2190
2191       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2192         stored_player[i].num_white_keys;
2193     }
2194   }
2195   else
2196   {
2197     int player_nr = game.centered_player_nr;
2198
2199     for (k = 0; k < MAX_NUM_KEYS; k++)
2200     {
2201       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2202       {
2203         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2204           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2205             get_key_element_from_nr(k);
2206       }
2207       else if (stored_player[player_nr].key[k])
2208         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2209           get_key_element_from_nr(k);
2210     }
2211
2212     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2213       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2214         level.native_em_level->ply[player_nr]->dynamite;
2215     else
2216       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2217         stored_player[player_nr].inventory_size;
2218
2219     if (stored_player[player_nr].num_white_keys > 0)
2220       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2221
2222     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2223       stored_player[player_nr].num_white_keys;
2224   }
2225
2226   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2227   {
2228     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2229       get_inventory_element_from_pos(local_player, i);
2230     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2231       get_inventory_element_from_pos(local_player, -i - 1);
2232   }
2233
2234   game_panel_controls[GAME_PANEL_SCORE].value = score;
2235
2236   game_panel_controls[GAME_PANEL_TIME].value = time;
2237
2238   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2239   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2240   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2241
2242   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2243     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2244      EL_EMPTY);
2245   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2246     local_player->shield_normal_time_left;
2247   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2248     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2249      EL_EMPTY);
2250   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2251     local_player->shield_deadly_time_left;
2252
2253   game_panel_controls[GAME_PANEL_EXIT].value =
2254     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2255
2256   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2257     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2258   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2259     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2260      EL_EMC_MAGIC_BALL_SWITCH);
2261
2262   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2263     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2264   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2265     game.light_time_left;
2266
2267   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2268     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2269   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2270     game.timegate_time_left;
2271
2272   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2273     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2274
2275   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2276     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2277   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2278     game.lenses_time_left;
2279
2280   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2281     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2282   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2283     game.magnify_time_left;
2284
2285   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2286     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2287      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2288      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2289      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2290      EL_BALLOON_SWITCH_NONE);
2291
2292   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2293     local_player->dynabomb_count;
2294   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2295     local_player->dynabomb_size;
2296   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2297     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2298
2299   game_panel_controls[GAME_PANEL_PENGUINS].value =
2300     local_player->friends_still_needed;
2301
2302   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2303     local_player->sokobanfields_still_needed;
2304   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2305     local_player->sokobanfields_still_needed;
2306
2307   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2308     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2309
2310   for (i = 0; i < NUM_BELTS; i++)
2311   {
2312     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2313       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2314        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2315     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2316       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2317   }
2318
2319   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2320     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2321   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2322     game.magic_wall_time_left;
2323
2324 #if USE_PLAYER_GRAVITY
2325   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2326     local_player->gravity;
2327 #else
2328   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2329 #endif
2330
2331   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2332     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2333
2334   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2335     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2336       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2337        game.panel.element[i].id : EL_UNDEFINED);
2338
2339   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2340     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2341       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2342        element_info[game.panel.element_count[i].id].element_count :
2343        EL_UNDEFINED);
2344
2345   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2346     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2347       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2348        element_info[game.panel.ce_score[i].id].collect_score : 0);
2349
2350   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2351     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2352       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2353        element_info[game.panel.ce_score_element[i].id].collect_score :
2354        EL_UNDEFINED);
2355
2356   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2357   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2358   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2359
2360   /* update game panel control frames */
2361
2362   for (i = 0; game_panel_controls[i].nr != -1; i++)
2363   {
2364     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2365
2366     if (gpc->type == TYPE_ELEMENT)
2367     {
2368       int last_anim_random_frame = gfx.anim_random_frame;
2369       int element = gpc->value;
2370       int graphic = el2panelimg(element);
2371
2372       if (gpc->value != gpc->last_value)
2373       {
2374         gpc->gfx_frame = 0;
2375         gpc->gfx_random = INIT_GFX_RANDOM();
2376       }
2377       else
2378       {
2379         gpc->gfx_frame++;
2380
2381         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2382             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2383           gpc->gfx_random = INIT_GFX_RANDOM();
2384       }
2385
2386       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2387         gfx.anim_random_frame = gpc->gfx_random;
2388
2389       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2390         gpc->gfx_frame = element_info[element].collect_score;
2391
2392       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2393                                             gpc->gfx_frame);
2394
2395       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2396         gfx.anim_random_frame = last_anim_random_frame;
2397     }
2398   }
2399 }
2400
2401 void DisplayGameControlValues()
2402 {
2403   boolean redraw_panel = FALSE;
2404   int i;
2405
2406   for (i = 0; game_panel_controls[i].nr != -1; i++)
2407   {
2408     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2409
2410     if (PANEL_DEACTIVATED(gpc->pos))
2411       continue;
2412
2413     if (gpc->value == gpc->last_value &&
2414         gpc->frame == gpc->last_frame)
2415       continue;
2416
2417     redraw_panel = TRUE;
2418   }
2419
2420   if (!redraw_panel)
2421     return;
2422
2423   /* copy default game door content to main double buffer */
2424   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2425              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2426
2427   /* redraw game control buttons */
2428 #if 1
2429   RedrawGameButtons();
2430 #else
2431   UnmapGameButtons();
2432   MapGameButtons();
2433 #endif
2434
2435   game_status = GAME_MODE_PSEUDO_PANEL;
2436
2437 #if 1
2438   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2439 #else
2440   for (i = 0; game_panel_controls[i].nr != -1; i++)
2441 #endif
2442   {
2443 #if 1
2444     int nr = game_panel_order[i].nr;
2445     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2446 #else
2447     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2448     int nr = gpc->nr;
2449 #endif
2450     struct TextPosInfo *pos = gpc->pos;
2451     int type = gpc->type;
2452     int value = gpc->value;
2453     int frame = gpc->frame;
2454 #if 0
2455     int last_value = gpc->last_value;
2456     int last_frame = gpc->last_frame;
2457 #endif
2458     int size = pos->size;
2459     int font = pos->font;
2460     boolean draw_masked = pos->draw_masked;
2461     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2462
2463     if (PANEL_DEACTIVATED(pos))
2464       continue;
2465
2466 #if 0
2467     if (value == last_value && frame == last_frame)
2468       continue;
2469 #endif
2470
2471     gpc->last_value = value;
2472     gpc->last_frame = frame;
2473
2474 #if 0
2475     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2476 #endif
2477
2478     if (type == TYPE_INTEGER)
2479     {
2480       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2481           nr == GAME_PANEL_TIME)
2482       {
2483         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2484
2485         if (use_dynamic_size)           /* use dynamic number of digits */
2486         {
2487           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2488           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2489           int size2 = size1 + 1;
2490           int font1 = pos->font;
2491           int font2 = pos->font_alt;
2492
2493           size = (value < value_change ? size1 : size2);
2494           font = (value < value_change ? font1 : font2);
2495
2496 #if 0
2497           /* clear background if value just changed its size (dynamic digits) */
2498           if ((last_value < value_change) != (value < value_change))
2499           {
2500             int width1 = size1 * getFontWidth(font1);
2501             int width2 = size2 * getFontWidth(font2);
2502             int max_width = MAX(width1, width2);
2503             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2504
2505             pos->width = max_width;
2506
2507             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2508                                        max_width, max_height);
2509           }
2510 #endif
2511         }
2512       }
2513
2514 #if 1
2515       /* correct text size if "digits" is zero or less */
2516       if (size <= 0)
2517         size = strlen(int2str(value, size));
2518
2519       /* dynamically correct text alignment */
2520       pos->width = size * getFontWidth(font);
2521 #endif
2522
2523       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2524                   int2str(value, size), font, mask_mode);
2525     }
2526     else if (type == TYPE_ELEMENT)
2527     {
2528       int element, graphic;
2529       Bitmap *src_bitmap;
2530       int src_x, src_y;
2531       int width, height;
2532       int dst_x = PANEL_XPOS(pos);
2533       int dst_y = PANEL_YPOS(pos);
2534
2535 #if 1
2536       if (value != EL_UNDEFINED && value != EL_EMPTY)
2537       {
2538         element = value;
2539         graphic = el2panelimg(value);
2540
2541         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2542
2543 #if 1
2544         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2545           size = TILESIZE;
2546 #endif
2547
2548         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2549                               &src_x, &src_y);
2550
2551         width  = graphic_info[graphic].width  * size / TILESIZE;
2552         height = graphic_info[graphic].height * size / TILESIZE;
2553
2554         if (draw_masked)
2555         {
2556           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2557                         dst_x - src_x, dst_y - src_y);
2558           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2559                            dst_x, dst_y);
2560         }
2561         else
2562         {
2563           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2564                      dst_x, dst_y);
2565         }
2566       }
2567 #else
2568       if (value == EL_UNDEFINED || value == EL_EMPTY)
2569       {
2570         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2571         graphic = el2panelimg(element);
2572
2573         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2574         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2575         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2576       }
2577       else
2578       {
2579         element = value;
2580         graphic = el2panelimg(value);
2581
2582         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2583       }
2584
2585       width  = graphic_info[graphic].width  * size / TILESIZE;
2586       height = graphic_info[graphic].height * size / TILESIZE;
2587
2588       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2589 #endif
2590     }
2591     else if (type == TYPE_STRING)
2592     {
2593       boolean active = (value != 0);
2594       char *state_normal = "off";
2595       char *state_active = "on";
2596       char *state = (active ? state_active : state_normal);
2597       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2598                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2599                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2600                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2601
2602       if (nr == GAME_PANEL_GRAVITY_STATE)
2603       {
2604         int font1 = pos->font;          /* (used for normal state) */
2605         int font2 = pos->font_alt;      /* (used for active state) */
2606 #if 0
2607         int size1 = strlen(state_normal);
2608         int size2 = strlen(state_active);
2609         int width1 = size1 * getFontWidth(font1);
2610         int width2 = size2 * getFontWidth(font2);
2611         int max_width = MAX(width1, width2);
2612         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2613
2614         pos->width = max_width;
2615
2616         /* clear background for values that may have changed its size */
2617         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2618                                    max_width, max_height);
2619 #endif
2620
2621         font = (active ? font2 : font1);
2622       }
2623
2624       if (s != NULL)
2625       {
2626         char *s_cut;
2627
2628 #if 1
2629         if (size <= 0)
2630         {
2631           /* don't truncate output if "chars" is zero or less */
2632           size = strlen(s);
2633
2634           /* dynamically correct text alignment */
2635           pos->width = size * getFontWidth(font);
2636         }
2637 #endif
2638
2639         s_cut = getStringCopyN(s, size);
2640
2641         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2642                     s_cut, font, mask_mode);
2643
2644         free(s_cut);
2645       }
2646     }
2647
2648     redraw_mask |= REDRAW_DOOR_1;
2649   }
2650
2651   game_status = GAME_MODE_PLAYING;
2652 }
2653
2654 void DrawGameValue_Emeralds(int value)
2655 {
2656   struct TextPosInfo *pos = &game.panel.gems;
2657 #if 1
2658   int font_nr = pos->font;
2659 #else
2660   int font_nr = FONT_TEXT_2;
2661 #endif
2662   int font_width = getFontWidth(font_nr);
2663   int chars = pos->size;
2664
2665 #if 1
2666   return;       /* !!! USE NEW STUFF !!! */
2667 #endif
2668
2669   if (PANEL_DEACTIVATED(pos))
2670     return;
2671
2672   pos->width = chars * font_width;
2673
2674   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2675 }
2676
2677 void DrawGameValue_Dynamite(int value)
2678 {
2679   struct TextPosInfo *pos = &game.panel.inventory_count;
2680 #if 1
2681   int font_nr = pos->font;
2682 #else
2683   int font_nr = FONT_TEXT_2;
2684 #endif
2685   int font_width = getFontWidth(font_nr);
2686   int chars = pos->size;
2687
2688 #if 1
2689   return;       /* !!! USE NEW STUFF !!! */
2690 #endif
2691
2692   if (PANEL_DEACTIVATED(pos))
2693     return;
2694
2695   pos->width = chars * font_width;
2696
2697   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2698 }
2699
2700 void DrawGameValue_Score(int value)
2701 {
2702   struct TextPosInfo *pos = &game.panel.score;
2703 #if 1
2704   int font_nr = pos->font;
2705 #else
2706   int font_nr = FONT_TEXT_2;
2707 #endif
2708   int font_width = getFontWidth(font_nr);
2709   int chars = pos->size;
2710
2711 #if 1
2712   return;       /* !!! USE NEW STUFF !!! */
2713 #endif
2714
2715   if (PANEL_DEACTIVATED(pos))
2716     return;
2717
2718   pos->width = chars * font_width;
2719
2720   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2721 }
2722
2723 void DrawGameValue_Time(int value)
2724 {
2725   struct TextPosInfo *pos = &game.panel.time;
2726   static int last_value = -1;
2727   int chars1 = 3;
2728   int chars2 = 4;
2729   int chars = pos->size;
2730 #if 1
2731   int font1_nr = pos->font;
2732   int font2_nr = pos->font_alt;
2733 #else
2734   int font1_nr = FONT_TEXT_2;
2735   int font2_nr = FONT_TEXT_1;
2736 #endif
2737   int font_nr = font1_nr;
2738   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2739
2740 #if 1
2741   return;       /* !!! USE NEW STUFF !!! */
2742 #endif
2743
2744   if (PANEL_DEACTIVATED(pos))
2745     return;
2746
2747   if (use_dynamic_chars)                /* use dynamic number of chars */
2748   {
2749     chars   = (value < 1000 ? chars1   : chars2);
2750     font_nr = (value < 1000 ? font1_nr : font2_nr);
2751   }
2752
2753   /* clear background if value just changed its size (dynamic chars only) */
2754   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2755   {
2756     int width1 = chars1 * getFontWidth(font1_nr);
2757     int width2 = chars2 * getFontWidth(font2_nr);
2758     int max_width = MAX(width1, width2);
2759     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2760
2761     pos->width = max_width;
2762
2763     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2764                                max_width, max_height);
2765   }
2766
2767   pos->width = chars * getFontWidth(font_nr);
2768
2769   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2770
2771   last_value = value;
2772 }
2773
2774 void DrawGameValue_Level(int value)
2775 {
2776   struct TextPosInfo *pos = &game.panel.level_number;
2777   int chars1 = 2;
2778   int chars2 = 3;
2779   int chars = pos->size;
2780 #if 1
2781   int font1_nr = pos->font;
2782   int font2_nr = pos->font_alt;
2783 #else
2784   int font1_nr = FONT_TEXT_2;
2785   int font2_nr = FONT_TEXT_1;
2786 #endif
2787   int font_nr = font1_nr;
2788   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2789
2790 #if 1
2791   return;       /* !!! USE NEW STUFF !!! */
2792 #endif
2793
2794   if (PANEL_DEACTIVATED(pos))
2795     return;
2796
2797   if (use_dynamic_chars)                /* use dynamic number of chars */
2798   {
2799     chars   = (level_nr < 100 ? chars1   : chars2);
2800     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2801   }
2802
2803   pos->width = chars * getFontWidth(font_nr);
2804
2805   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2806 }
2807
2808 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2809 {
2810 #if 0
2811   struct TextPosInfo *pos = &game.panel.keys;
2812 #endif
2813 #if 0
2814   int base_key_graphic = EL_KEY_1;
2815 #endif
2816   int i;
2817
2818 #if 1
2819   return;       /* !!! USE NEW STUFF !!! */
2820 #endif
2821
2822 #if 0
2823   if (PANEL_DEACTIVATED(pos))
2824     return;
2825 #endif
2826
2827 #if 0
2828   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2829     base_key_graphic = EL_EM_KEY_1;
2830 #endif
2831
2832 #if 0
2833   pos->width = 4 * MINI_TILEX;
2834 #endif
2835
2836 #if 1
2837   for (i = 0; i < MAX_NUM_KEYS; i++)
2838 #else
2839   /* currently only 4 of 8 possible keys are displayed */
2840   for (i = 0; i < STD_NUM_KEYS; i++)
2841 #endif
2842   {
2843 #if 1
2844     struct TextPosInfo *pos = &game.panel.key[i];
2845 #endif
2846     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2847     int src_y = DOOR_GFX_PAGEY1 + 123;
2848 #if 1
2849     int dst_x = PANEL_XPOS(pos);
2850     int dst_y = PANEL_YPOS(pos);
2851 #else
2852     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2853     int dst_y = PANEL_YPOS(pos);
2854 #endif
2855
2856 #if 1
2857     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2858                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2859                    EL_KEY_1) + i;
2860     int graphic = el2edimg(element);
2861 #endif
2862
2863 #if 1
2864     if (PANEL_DEACTIVATED(pos))
2865       continue;
2866 #endif
2867
2868 #if 0
2869     /* masked blit with tiles from half-size scaled bitmap does not work yet
2870        (no mask bitmap created for these sizes after loading and scaling) --
2871        solution: load without creating mask, scale, then create final mask */
2872
2873     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2874                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2875
2876     if (key[i])
2877     {
2878 #if 0
2879       int graphic = el2edimg(base_key_graphic + i);
2880 #endif
2881       Bitmap *src_bitmap;
2882       int src_x, src_y;
2883
2884       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2885
2886       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2887                     dst_x - src_x, dst_y - src_y);
2888       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2889                        dst_x, dst_y);
2890     }
2891 #else
2892 #if 1
2893     if (key[i])
2894       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2895     else
2896       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2897                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2898 #else
2899     if (key[i])
2900       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2901     else
2902       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2903                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2904 #endif
2905 #endif
2906   }
2907 }
2908
2909 #else
2910
2911 void DrawGameValue_Emeralds(int value)
2912 {
2913   int font_nr = FONT_TEXT_2;
2914   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2915
2916   if (PANEL_DEACTIVATED(game.panel.gems))
2917     return;
2918
2919   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2920 }
2921
2922 void DrawGameValue_Dynamite(int value)
2923 {
2924   int font_nr = FONT_TEXT_2;
2925   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2926
2927   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2928     return;
2929
2930   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2931 }
2932
2933 void DrawGameValue_Score(int value)
2934 {
2935   int font_nr = FONT_TEXT_2;
2936   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2937
2938   if (PANEL_DEACTIVATED(game.panel.score))
2939     return;
2940
2941   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2942 }
2943
2944 void DrawGameValue_Time(int value)
2945 {
2946   int font1_nr = FONT_TEXT_2;
2947 #if 1
2948   int font2_nr = FONT_TEXT_1;
2949 #else
2950   int font2_nr = FONT_LEVEL_NUMBER;
2951 #endif
2952   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2953   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2954
2955   if (PANEL_DEACTIVATED(game.panel.time))
2956     return;
2957
2958   /* clear background if value just changed its size */
2959   if (value == 999 || value == 1000)
2960     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2961
2962   if (value < 1000)
2963     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2964   else
2965     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2966 }
2967
2968 void DrawGameValue_Level(int value)
2969 {
2970   int font1_nr = FONT_TEXT_2;
2971 #if 1
2972   int font2_nr = FONT_TEXT_1;
2973 #else
2974   int font2_nr = FONT_LEVEL_NUMBER;
2975 #endif
2976
2977   if (PANEL_DEACTIVATED(game.panel.level))
2978     return;
2979
2980   if (level_nr < 100)
2981     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2982   else
2983     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2984 }
2985
2986 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2987 {
2988   int base_key_graphic = EL_KEY_1;
2989   int i;
2990
2991   if (PANEL_DEACTIVATED(game.panel.keys))
2992     return;
2993
2994   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2995     base_key_graphic = EL_EM_KEY_1;
2996
2997   /* currently only 4 of 8 possible keys are displayed */
2998   for (i = 0; i < STD_NUM_KEYS; i++)
2999   {
3000     int x = XX_KEYS + i * MINI_TILEX;
3001     int y = YY_KEYS;
3002
3003     if (key[i])
3004       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3005     else
3006       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3007                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3008   }
3009 }
3010
3011 #endif
3012
3013 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3014                        int key_bits)
3015 {
3016   int key[MAX_NUM_KEYS];
3017   int i;
3018
3019   /* prevent EM engine from updating time/score values parallel to GameWon() */
3020   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3021       local_player->LevelSolved)
3022     return;
3023
3024   for (i = 0; i < MAX_NUM_KEYS; i++)
3025     key[i] = key_bits & (1 << i);
3026
3027   DrawGameValue_Level(level_nr);
3028
3029   DrawGameValue_Emeralds(emeralds);
3030   DrawGameValue_Dynamite(dynamite);
3031   DrawGameValue_Score(score);
3032   DrawGameValue_Time(time);
3033
3034   DrawGameValue_Keys(key);
3035 }
3036
3037 void UpdateGameDoorValues()
3038 {
3039   UpdateGameControlValues();
3040 }
3041
3042 void DrawGameDoorValues()
3043 {
3044   DisplayGameControlValues();
3045 }
3046
3047 void DrawGameDoorValues_OLD()
3048 {
3049   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3050   int dynamite_value = 0;
3051   int score_value = (local_player->LevelSolved ? local_player->score_final :
3052                      local_player->score);
3053   int gems_value = local_player->gems_still_needed;
3054   int key_bits = 0;
3055   int i, j;
3056
3057   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3058   {
3059     DrawGameDoorValues_EM();
3060
3061     return;
3062   }
3063
3064   if (game.centered_player_nr == -1)
3065   {
3066     for (i = 0; i < MAX_PLAYERS; i++)
3067     {
3068       for (j = 0; j < MAX_NUM_KEYS; j++)
3069         if (stored_player[i].key[j])
3070           key_bits |= (1 << j);
3071
3072       dynamite_value += stored_player[i].inventory_size;
3073     }
3074   }
3075   else
3076   {
3077     int player_nr = game.centered_player_nr;
3078
3079     for (i = 0; i < MAX_NUM_KEYS; i++)
3080       if (stored_player[player_nr].key[i])
3081         key_bits |= (1 << i);
3082
3083     dynamite_value = stored_player[player_nr].inventory_size;
3084   }
3085
3086   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3087                     key_bits);
3088 }
3089
3090
3091 /*
3092   =============================================================================
3093   InitGameEngine()
3094   -----------------------------------------------------------------------------
3095   initialize game engine due to level / tape version number
3096   =============================================================================
3097 */
3098
3099 static void InitGameEngine()
3100 {
3101   int i, j, k, l, x, y;
3102
3103   /* set game engine from tape file when re-playing, else from level file */
3104   game.engine_version = (tape.playing ? tape.engine_version :
3105                          level.game_version);
3106
3107   /* ---------------------------------------------------------------------- */
3108   /* set flags for bugs and changes according to active game engine version */
3109   /* ---------------------------------------------------------------------- */
3110
3111   /*
3112     Summary of bugfix/change:
3113     Fixed handling for custom elements that change when pushed by the player.
3114
3115     Fixed/changed in version:
3116     3.1.0
3117
3118     Description:
3119     Before 3.1.0, custom elements that "change when pushing" changed directly
3120     after the player started pushing them (until then handled in "DigField()").
3121     Since 3.1.0, these custom elements are not changed until the "pushing"
3122     move of the element is finished (now handled in "ContinueMoving()").
3123
3124     Affected levels/tapes:
3125     The first condition is generally needed for all levels/tapes before version
3126     3.1.0, which might use the old behaviour before it was changed; known tapes
3127     that are affected are some tapes from the level set "Walpurgis Gardens" by
3128     Jamie Cullen.
3129     The second condition is an exception from the above case and is needed for
3130     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3131     above (including some development versions of 3.1.0), but before it was
3132     known that this change would break tapes like the above and was fixed in
3133     3.1.1, so that the changed behaviour was active although the engine version
3134     while recording maybe was before 3.1.0. There is at least one tape that is
3135     affected by this exception, which is the tape for the one-level set "Bug
3136     Machine" by Juergen Bonhagen.
3137   */
3138
3139   game.use_change_when_pushing_bug =
3140     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3141      !(tape.playing &&
3142        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3143        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3144
3145   /*
3146     Summary of bugfix/change:
3147     Fixed handling for blocking the field the player leaves when moving.
3148
3149     Fixed/changed in version:
3150     3.1.1
3151
3152     Description:
3153     Before 3.1.1, when "block last field when moving" was enabled, the field
3154     the player is leaving when moving was blocked for the time of the move,
3155     and was directly unblocked afterwards. This resulted in the last field
3156     being blocked for exactly one less than the number of frames of one player
3157     move. Additionally, even when blocking was disabled, the last field was
3158     blocked for exactly one frame.
3159     Since 3.1.1, due to changes in player movement handling, the last field
3160     is not blocked at all when blocking is disabled. When blocking is enabled,
3161     the last field is blocked for exactly the number of frames of one player
3162     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3163     last field is blocked for exactly one more than the number of frames of
3164     one player move.
3165
3166     Affected levels/tapes:
3167     (!!! yet to be determined -- probably many !!!)
3168   */
3169
3170   game.use_block_last_field_bug =
3171     (game.engine_version < VERSION_IDENT(3,1,1,0));
3172
3173   /*
3174     Summary of bugfix/change:
3175     Changed behaviour of CE changes with multiple changes per single frame.
3176
3177     Fixed/changed in version:
3178     3.2.0-6
3179
3180     Description:
3181     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3182     This resulted in race conditions where CEs seem to behave strange in some
3183     situations (where triggered CE changes were just skipped because there was
3184     already a CE change on that tile in the playfield in that engine frame).
3185     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3186     (The number of changes per frame must be limited in any case, because else
3187     it is easily possible to define CE changes that would result in an infinite
3188     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3189     should be set large enough so that it would only be reached in cases where
3190     the corresponding CE change conditions run into a loop. Therefore, it seems
3191     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3192     maximal number of change pages for custom elements.)
3193
3194     Affected levels/tapes:
3195     Probably many.
3196   */
3197
3198 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3199   game.max_num_changes_per_frame = 1;
3200 #else
3201   game.max_num_changes_per_frame =
3202     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3203 #endif
3204
3205   /* ---------------------------------------------------------------------- */
3206
3207   /* default scan direction: scan playfield from top/left to bottom/right */
3208   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3209
3210   /* dynamically adjust element properties according to game engine version */
3211   InitElementPropertiesEngine(game.engine_version);
3212
3213 #if 0
3214   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3215   printf("          tape version == %06d [%s] [file: %06d]\n",
3216          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3217          tape.file_version);
3218   printf("       => game.engine_version == %06d\n", game.engine_version);
3219 #endif
3220
3221   /* ---------- initialize player's initial move delay --------------------- */
3222
3223   /* dynamically adjust player properties according to level information */
3224   for (i = 0; i < MAX_PLAYERS; i++)
3225     game.initial_move_delay_value[i] =
3226       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3227
3228   /* dynamically adjust player properties according to game engine version */
3229   for (i = 0; i < MAX_PLAYERS; i++)
3230     game.initial_move_delay[i] =
3231       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3232        game.initial_move_delay_value[i] : 0);
3233
3234   /* ---------- initialize player's initial push delay --------------------- */
3235
3236   /* dynamically adjust player properties according to game engine version */
3237   game.initial_push_delay_value =
3238     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3239
3240   /* ---------- initialize changing elements ------------------------------- */
3241
3242   /* initialize changing elements information */
3243   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3244   {
3245     struct ElementInfo *ei = &element_info[i];
3246
3247     /* this pointer might have been changed in the level editor */
3248     ei->change = &ei->change_page[0];
3249
3250     if (!IS_CUSTOM_ELEMENT(i))
3251     {
3252       ei->change->target_element = EL_EMPTY_SPACE;
3253       ei->change->delay_fixed = 0;
3254       ei->change->delay_random = 0;
3255       ei->change->delay_frames = 1;
3256     }
3257
3258     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3259     {
3260       ei->has_change_event[j] = FALSE;
3261
3262       ei->event_page_nr[j] = 0;
3263       ei->event_page[j] = &ei->change_page[0];
3264     }
3265   }
3266
3267   /* add changing elements from pre-defined list */
3268   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3269   {
3270     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3271     struct ElementInfo *ei = &element_info[ch_delay->element];
3272
3273     ei->change->target_element       = ch_delay->target_element;
3274     ei->change->delay_fixed          = ch_delay->change_delay;
3275
3276     ei->change->pre_change_function  = ch_delay->pre_change_function;
3277     ei->change->change_function      = ch_delay->change_function;
3278     ei->change->post_change_function = ch_delay->post_change_function;
3279
3280     ei->change->can_change = TRUE;
3281     ei->change->can_change_or_has_action = TRUE;
3282
3283     ei->has_change_event[CE_DELAY] = TRUE;
3284
3285     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3286     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3287   }
3288
3289   /* ---------- initialize internal run-time variables ------------- */
3290
3291   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3292   {
3293     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3294
3295     for (j = 0; j < ei->num_change_pages; j++)
3296     {
3297       ei->change_page[j].can_change_or_has_action =
3298         (ei->change_page[j].can_change |
3299          ei->change_page[j].has_action);
3300     }
3301   }
3302
3303   /* add change events from custom element configuration */
3304   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3305   {
3306     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3307
3308     for (j = 0; j < ei->num_change_pages; j++)
3309     {
3310       if (!ei->change_page[j].can_change_or_has_action)
3311         continue;
3312
3313       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3314       {
3315         /* only add event page for the first page found with this event */
3316         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3317         {
3318           ei->has_change_event[k] = TRUE;
3319
3320           ei->event_page_nr[k] = j;
3321           ei->event_page[k] = &ei->change_page[j];
3322         }
3323       }
3324     }
3325   }
3326
3327   /* ---------- initialize run-time trigger player and element ------------- */
3328
3329   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3330   {
3331     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3332
3333     for (j = 0; j < ei->num_change_pages; j++)
3334     {
3335       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3336       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3337       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3338       ei->change_page[j].actual_trigger_ce_value = 0;
3339       ei->change_page[j].actual_trigger_ce_score = 0;
3340     }
3341   }
3342
3343   /* ---------- initialize trigger events ---------------------------------- */
3344
3345   /* initialize trigger events information */
3346   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3347     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3348       trigger_events[i][j] = FALSE;
3349
3350   /* add trigger events from element change event properties */
3351   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3352   {
3353     struct ElementInfo *ei = &element_info[i];
3354
3355     for (j = 0; j < ei->num_change_pages; j++)
3356     {
3357       if (!ei->change_page[j].can_change_or_has_action)
3358         continue;
3359
3360       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3361       {
3362         int trigger_element = ei->change_page[j].trigger_element;
3363
3364         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3365         {
3366           if (ei->change_page[j].has_event[k])
3367           {
3368             if (IS_GROUP_ELEMENT(trigger_element))
3369             {
3370               struct ElementGroupInfo *group =
3371                 element_info[trigger_element].group;
3372
3373               for (l = 0; l < group->num_elements_resolved; l++)
3374                 trigger_events[group->element_resolved[l]][k] = TRUE;
3375             }
3376             else if (trigger_element == EL_ANY_ELEMENT)
3377               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3378                 trigger_events[l][k] = TRUE;
3379             else
3380               trigger_events[trigger_element][k] = TRUE;
3381           }
3382         }
3383       }
3384     }
3385   }
3386
3387   /* ---------- initialize push delay -------------------------------------- */
3388
3389   /* initialize push delay values to default */
3390   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3391   {
3392     if (!IS_CUSTOM_ELEMENT(i))
3393     {
3394       /* set default push delay values (corrected since version 3.0.7-1) */
3395       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3396       {
3397         element_info[i].push_delay_fixed = 2;
3398         element_info[i].push_delay_random = 8;
3399       }
3400       else
3401       {
3402         element_info[i].push_delay_fixed = 8;
3403         element_info[i].push_delay_random = 8;
3404       }
3405     }
3406   }
3407
3408   /* set push delay value for certain elements from pre-defined list */
3409   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3410   {
3411     int e = push_delay_list[i].element;
3412
3413     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3414     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3415   }
3416
3417   /* set push delay value for Supaplex elements for newer engine versions */
3418   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3419   {
3420     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3421     {
3422       if (IS_SP_ELEMENT(i))
3423       {
3424         /* set SP push delay to just enough to push under a falling zonk */
3425         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3426
3427         element_info[i].push_delay_fixed  = delay;
3428         element_info[i].push_delay_random = 0;
3429       }
3430     }
3431   }
3432
3433   /* ---------- initialize move stepsize ----------------------------------- */
3434
3435   /* initialize move stepsize values to default */
3436   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3437     if (!IS_CUSTOM_ELEMENT(i))
3438       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3439
3440   /* set move stepsize value for certain elements from pre-defined list */
3441   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3442   {
3443     int e = move_stepsize_list[i].element;
3444
3445     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3446   }
3447
3448   /* ---------- initialize collect score ----------------------------------- */
3449
3450   /* initialize collect score values for custom elements from initial value */
3451   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3452     if (IS_CUSTOM_ELEMENT(i))
3453       element_info[i].collect_score = element_info[i].collect_score_initial;
3454
3455   /* ---------- initialize collect count ----------------------------------- */
3456
3457   /* initialize collect count values for non-custom elements */
3458   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3459     if (!IS_CUSTOM_ELEMENT(i))
3460       element_info[i].collect_count_initial = 0;
3461
3462   /* add collect count values for all elements from pre-defined list */
3463   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3464     element_info[collect_count_list[i].element].collect_count_initial =
3465       collect_count_list[i].count;
3466
3467   /* ---------- initialize access direction -------------------------------- */
3468
3469   /* initialize access direction values to default (access from every side) */
3470   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3471     if (!IS_CUSTOM_ELEMENT(i))
3472       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3473
3474   /* set access direction value for certain elements from pre-defined list */
3475   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3476     element_info[access_direction_list[i].element].access_direction =
3477       access_direction_list[i].direction;
3478
3479   /* ---------- initialize explosion content ------------------------------- */
3480   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3481   {
3482     if (IS_CUSTOM_ELEMENT(i))
3483       continue;
3484
3485     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3486     {
3487       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3488
3489       element_info[i].content.e[x][y] =
3490         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3491          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3492          i == EL_PLAYER_3 ? EL_EMERALD :
3493          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3494          i == EL_MOLE ? EL_EMERALD_RED :
3495          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3496          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3497          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3498          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3499          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3500          i == EL_WALL_EMERALD ? EL_EMERALD :
3501          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3502          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3503          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3504          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3505          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3506          i == EL_WALL_PEARL ? EL_PEARL :
3507          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3508          EL_EMPTY);
3509     }
3510   }
3511
3512   /* ---------- initialize recursion detection ------------------------------ */
3513   recursion_loop_depth = 0;
3514   recursion_loop_detected = FALSE;
3515   recursion_loop_element = EL_UNDEFINED;
3516
3517   /* ---------- initialize graphics engine ---------------------------------- */
3518   game.scroll_delay_value =
3519     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3520      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3521   game.scroll_delay_value =
3522     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3523 }
3524
3525 int get_num_special_action(int element, int action_first, int action_last)
3526 {
3527   int num_special_action = 0;
3528   int i, j;
3529
3530   for (i = action_first; i <= action_last; i++)
3531   {
3532     boolean found = FALSE;
3533
3534     for (j = 0; j < NUM_DIRECTIONS; j++)
3535       if (el_act_dir2img(element, i, j) !=
3536           el_act_dir2img(element, ACTION_DEFAULT, j))
3537         found = TRUE;
3538
3539     if (found)
3540       num_special_action++;
3541     else
3542       break;
3543   }
3544
3545   return num_special_action;
3546 }
3547
3548
3549 /*
3550   =============================================================================
3551   InitGame()
3552   -----------------------------------------------------------------------------
3553   initialize and start new game
3554   =============================================================================
3555 */
3556
3557 void InitGame()
3558 {
3559   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3560   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3561   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3562 #if 0
3563   boolean do_fading = (game_status == GAME_MODE_MAIN);
3564 #endif
3565   int i, j, x, y;
3566
3567   game_status = GAME_MODE_PLAYING;
3568
3569   InitGameEngine();
3570   InitGameControlValues();
3571
3572   /* don't play tapes over network */
3573   network_playing = (options.network && !tape.playing);
3574
3575   for (i = 0; i < MAX_PLAYERS; i++)
3576   {
3577     struct PlayerInfo *player = &stored_player[i];
3578
3579     player->index_nr = i;
3580     player->index_bit = (1 << i);
3581     player->element_nr = EL_PLAYER_1 + i;
3582
3583     player->present = FALSE;
3584     player->active = FALSE;
3585     player->killed = FALSE;
3586
3587     player->action = 0;
3588     player->effective_action = 0;
3589     player->programmed_action = 0;
3590
3591     player->score = 0;
3592     player->score_final = 0;
3593
3594     player->gems_still_needed = level.gems_needed;
3595     player->sokobanfields_still_needed = 0;
3596     player->lights_still_needed = 0;
3597     player->friends_still_needed = 0;
3598
3599     for (j = 0; j < MAX_NUM_KEYS; j++)
3600       player->key[j] = FALSE;
3601
3602     player->num_white_keys = 0;
3603
3604     player->dynabomb_count = 0;
3605     player->dynabomb_size = 1;
3606     player->dynabombs_left = 0;
3607     player->dynabomb_xl = FALSE;
3608
3609     player->MovDir = MV_NONE;
3610     player->MovPos = 0;
3611     player->GfxPos = 0;
3612     player->GfxDir = MV_NONE;
3613     player->GfxAction = ACTION_DEFAULT;
3614     player->Frame = 0;
3615     player->StepFrame = 0;
3616
3617     player->use_murphy = FALSE;
3618     player->artwork_element =
3619       (level.use_artwork_element[i] ? level.artwork_element[i] :
3620        player->element_nr);
3621
3622     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3623     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3624
3625     player->gravity = level.initial_player_gravity[i];
3626
3627     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3628
3629     player->actual_frame_counter = 0;
3630
3631     player->step_counter = 0;
3632
3633     player->last_move_dir = MV_NONE;
3634
3635     player->is_active = FALSE;
3636
3637     player->is_waiting = FALSE;
3638     player->is_moving = FALSE;
3639     player->is_auto_moving = FALSE;
3640     player->is_digging = FALSE;
3641     player->is_snapping = FALSE;
3642     player->is_collecting = FALSE;
3643     player->is_pushing = FALSE;
3644     player->is_switching = FALSE;
3645     player->is_dropping = FALSE;
3646     player->is_dropping_pressed = FALSE;
3647
3648     player->is_bored = FALSE;
3649     player->is_sleeping = FALSE;
3650
3651     player->frame_counter_bored = -1;
3652     player->frame_counter_sleeping = -1;
3653
3654     player->anim_delay_counter = 0;
3655     player->post_delay_counter = 0;
3656
3657     player->dir_waiting = MV_NONE;
3658     player->action_waiting = ACTION_DEFAULT;
3659     player->last_action_waiting = ACTION_DEFAULT;
3660     player->special_action_bored = ACTION_DEFAULT;
3661     player->special_action_sleeping = ACTION_DEFAULT;
3662
3663     player->switch_x = -1;
3664     player->switch_y = -1;
3665
3666     player->drop_x = -1;
3667     player->drop_y = -1;
3668
3669     player->show_envelope = 0;
3670
3671     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3672
3673     player->push_delay       = -1;      /* initialized when pushing starts */
3674     player->push_delay_value = game.initial_push_delay_value;
3675
3676     player->drop_delay = 0;
3677     player->drop_pressed_delay = 0;
3678
3679     player->last_jx = -1;
3680     player->last_jy = -1;
3681     player->jx = -1;
3682     player->jy = -1;
3683
3684     player->shield_normal_time_left = 0;
3685     player->shield_deadly_time_left = 0;
3686
3687     player->inventory_infinite_element = EL_UNDEFINED;
3688     player->inventory_size = 0;
3689
3690     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3691     SnapField(player, 0, 0);
3692
3693     player->LevelSolved = FALSE;
3694     player->GameOver = FALSE;
3695
3696     player->LevelSolved_GameWon = FALSE;
3697     player->LevelSolved_GameEnd = FALSE;
3698     player->LevelSolved_PanelOff = FALSE;
3699     player->LevelSolved_SaveTape = FALSE;
3700     player->LevelSolved_SaveScore = FALSE;
3701     player->LevelSolved_CountingTime = 0;
3702     player->LevelSolved_CountingScore = 0;
3703   }
3704
3705   network_player_action_received = FALSE;
3706
3707 #if defined(NETWORK_AVALIABLE)
3708   /* initial null action */
3709   if (network_playing)
3710     SendToServer_MovePlayer(MV_NONE);
3711 #endif
3712
3713   ZX = ZY = -1;
3714   ExitX = ExitY = -1;
3715
3716   FrameCounter = 0;
3717   TimeFrames = 0;
3718   TimePlayed = 0;
3719   TimeLeft = level.time;
3720   TapeTime = 0;
3721
3722   ScreenMovDir = MV_NONE;
3723   ScreenMovPos = 0;
3724   ScreenGfxPos = 0;
3725
3726   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3727
3728   AllPlayersGone = FALSE;
3729
3730   game.yamyam_content_nr = 0;
3731   game.robot_wheel_active = FALSE;
3732   game.magic_wall_active = FALSE;
3733   game.magic_wall_time_left = 0;
3734   game.light_time_left = 0;
3735   game.timegate_time_left = 0;
3736   game.switchgate_pos = 0;
3737   game.wind_direction = level.wind_direction_initial;
3738
3739 #if !USE_PLAYER_GRAVITY
3740   game.gravity = FALSE;
3741   game.explosions_delayed = TRUE;
3742 #endif
3743
3744   game.lenses_time_left = 0;
3745   game.magnify_time_left = 0;
3746
3747   game.ball_state = level.ball_state_initial;
3748   game.ball_content_nr = 0;
3749
3750   game.envelope_active = FALSE;
3751
3752   /* set focus to local player for network games, else to all players */
3753   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3754   game.centered_player_nr_next = game.centered_player_nr;
3755   game.set_centered_player = FALSE;
3756
3757   if (network_playing && tape.recording)
3758   {
3759     /* store client dependent player focus when recording network games */
3760     tape.centered_player_nr_next = game.centered_player_nr_next;
3761     tape.set_centered_player = TRUE;
3762   }
3763
3764   for (i = 0; i < NUM_BELTS; i++)
3765   {
3766     game.belt_dir[i] = MV_NONE;
3767     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3768   }
3769
3770   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3771     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3772
3773   SCAN_PLAYFIELD(x, y)
3774   {
3775     Feld[x][y] = level.field[x][y];
3776     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3777     ChangeDelay[x][y] = 0;
3778     ChangePage[x][y] = -1;
3779 #if USE_NEW_CUSTOM_VALUE
3780     CustomValue[x][y] = 0;              /* initialized in InitField() */
3781 #endif
3782     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3783     AmoebaNr[x][y] = 0;
3784     WasJustMoving[x][y] = 0;
3785     WasJustFalling[x][y] = 0;
3786     CheckCollision[x][y] = 0;
3787     CheckImpact[x][y] = 0;
3788     Stop[x][y] = FALSE;
3789     Pushed[x][y] = FALSE;
3790
3791     ChangeCount[x][y] = 0;
3792     ChangeEvent[x][y] = -1;
3793
3794     ExplodePhase[x][y] = 0;
3795     ExplodeDelay[x][y] = 0;
3796     ExplodeField[x][y] = EX_TYPE_NONE;
3797
3798     RunnerVisit[x][y] = 0;
3799     PlayerVisit[x][y] = 0;
3800
3801     GfxFrame[x][y] = 0;
3802     GfxRandom[x][y] = INIT_GFX_RANDOM();
3803     GfxElement[x][y] = EL_UNDEFINED;
3804     GfxAction[x][y] = ACTION_DEFAULT;
3805     GfxDir[x][y] = MV_NONE;
3806   }
3807
3808   SCAN_PLAYFIELD(x, y)
3809   {
3810     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3811       emulate_bd = FALSE;
3812     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3813       emulate_sb = FALSE;
3814     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3815       emulate_sp = FALSE;
3816
3817     InitField(x, y, TRUE);
3818
3819     ResetGfxAnimation(x, y);
3820   }
3821
3822   InitBeltMovement();
3823
3824   for (i = 0; i < MAX_PLAYERS; i++)
3825   {
3826     struct PlayerInfo *player = &stored_player[i];
3827
3828     /* set number of special actions for bored and sleeping animation */
3829     player->num_special_action_bored =
3830       get_num_special_action(player->artwork_element,
3831                              ACTION_BORING_1, ACTION_BORING_LAST);
3832     player->num_special_action_sleeping =
3833       get_num_special_action(player->artwork_element,
3834                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3835   }
3836
3837   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3838                     emulate_sb ? EMU_SOKOBAN :
3839                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3840
3841 #if USE_NEW_ALL_SLIPPERY
3842   /* initialize type of slippery elements */
3843   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3844   {
3845     if (!IS_CUSTOM_ELEMENT(i))
3846     {
3847       /* default: elements slip down either to the left or right randomly */
3848       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3849
3850       /* SP style elements prefer to slip down on the left side */
3851       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3852         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3853
3854       /* BD style elements prefer to slip down on the left side */
3855       if (game.emulation == EMU_BOULDERDASH)
3856         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3857     }
3858   }
3859 #endif
3860
3861   /* initialize explosion and ignition delay */
3862   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3863   {
3864     if (!IS_CUSTOM_ELEMENT(i))
3865     {
3866       int num_phase = 8;
3867       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3868                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3869                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3870       int last_phase = (num_phase + 1) * delay;
3871       int half_phase = (num_phase / 2) * delay;
3872
3873       element_info[i].explosion_delay = last_phase - 1;
3874       element_info[i].ignition_delay = half_phase;
3875
3876       if (i == EL_BLACK_ORB)
3877         element_info[i].ignition_delay = 1;
3878     }
3879
3880 #if 0
3881     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3882       element_info[i].explosion_delay = 1;
3883
3884     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3885       element_info[i].ignition_delay = 1;
3886 #endif
3887   }
3888
3889   /* correct non-moving belts to start moving left */
3890   for (i = 0; i < NUM_BELTS; i++)
3891     if (game.belt_dir[i] == MV_NONE)
3892       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3893
3894   /* check if any connected player was not found in playfield */
3895   for (i = 0; i < MAX_PLAYERS; i++)
3896   {
3897     struct PlayerInfo *player = &stored_player[i];
3898
3899     if (player->connected && !player->present)
3900     {
3901       for (j = 0; j < MAX_PLAYERS; j++)
3902       {
3903         struct PlayerInfo *some_player = &stored_player[j];
3904         int jx = some_player->jx, jy = some_player->jy;
3905
3906         /* assign first free player found that is present in the playfield */
3907         if (some_player->present && !some_player->connected)
3908         {
3909           player->present = TRUE;
3910           player->active = TRUE;
3911
3912           some_player->present = FALSE;
3913           some_player->active = FALSE;
3914
3915           player->artwork_element = some_player->artwork_element;
3916
3917           player->block_last_field       = some_player->block_last_field;
3918           player->block_delay_adjustment = some_player->block_delay_adjustment;
3919
3920           StorePlayer[jx][jy] = player->element_nr;
3921           player->jx = player->last_jx = jx;
3922           player->jy = player->last_jy = jy;
3923
3924           break;
3925         }
3926       }
3927     }
3928   }
3929
3930   if (tape.playing)
3931   {
3932     /* when playing a tape, eliminate all players who do not participate */
3933
3934     for (i = 0; i < MAX_PLAYERS; i++)
3935     {
3936       if (stored_player[i].active && !tape.player_participates[i])
3937       {
3938         struct PlayerInfo *player = &stored_player[i];
3939         int jx = player->jx, jy = player->jy;
3940
3941         player->active = FALSE;
3942         StorePlayer[jx][jy] = 0;
3943         Feld[jx][jy] = EL_EMPTY;
3944       }
3945     }
3946   }
3947   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3948   {
3949     /* when in single player mode, eliminate all but the first active player */
3950
3951     for (i = 0; i < MAX_PLAYERS; i++)
3952     {
3953       if (stored_player[i].active)
3954       {
3955         for (j = i + 1; j < MAX_PLAYERS; j++)
3956         {
3957           if (stored_player[j].active)
3958           {
3959             struct PlayerInfo *player = &stored_player[j];
3960             int jx = player->jx, jy = player->jy;
3961
3962             player->active = FALSE;
3963             player->present = FALSE;
3964
3965             StorePlayer[jx][jy] = 0;
3966             Feld[jx][jy] = EL_EMPTY;
3967           }
3968         }
3969       }
3970     }
3971   }
3972
3973   /* when recording the game, store which players take part in the game */
3974   if (tape.recording)
3975   {
3976     for (i = 0; i < MAX_PLAYERS; i++)
3977       if (stored_player[i].active)
3978         tape.player_participates[i] = TRUE;
3979   }
3980
3981   if (options.debug)
3982   {
3983     for (i = 0; i < MAX_PLAYERS; i++)
3984     {
3985       struct PlayerInfo *player = &stored_player[i];
3986
3987       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3988              i+1,
3989              player->present,
3990              player->connected,
3991              player->active);
3992       if (local_player == player)
3993         printf("Player  %d is local player.\n", i+1);
3994     }
3995   }
3996
3997   if (BorderElement == EL_EMPTY)
3998   {
3999     SBX_Left = 0;
4000     SBX_Right = lev_fieldx - SCR_FIELDX;
4001     SBY_Upper = 0;
4002     SBY_Lower = lev_fieldy - SCR_FIELDY;
4003   }
4004   else
4005   {
4006     SBX_Left = -1;
4007     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4008     SBY_Upper = -1;
4009     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4010   }
4011
4012   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4013     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4014
4015   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4016     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4017
4018   /* if local player not found, look for custom element that might create
4019      the player (make some assumptions about the right custom element) */
4020   if (!local_player->present)
4021   {
4022     int start_x = 0, start_y = 0;
4023     int found_rating = 0;
4024     int found_element = EL_UNDEFINED;
4025     int player_nr = local_player->index_nr;
4026
4027     SCAN_PLAYFIELD(x, y)
4028     {
4029       int element = Feld[x][y];
4030       int content;
4031       int xx, yy;
4032       boolean is_player;
4033
4034       if (level.use_start_element[player_nr] &&
4035           level.start_element[player_nr] == element &&
4036           found_rating < 4)
4037       {
4038         start_x = x;
4039         start_y = y;
4040
4041         found_rating = 4;
4042         found_element = element;
4043       }
4044
4045       if (!IS_CUSTOM_ELEMENT(element))
4046         continue;
4047
4048       if (CAN_CHANGE(element))
4049       {
4050         for (i = 0; i < element_info[element].num_change_pages; i++)
4051         {
4052           /* check for player created from custom element as single target */
4053           content = element_info[element].change_page[i].target_element;
4054           is_player = ELEM_IS_PLAYER(content);
4055
4056           if (is_player && (found_rating < 3 ||
4057                             (found_rating == 3 && element < found_element)))
4058           {
4059             start_x = x;
4060             start_y = y;
4061
4062             found_rating = 3;
4063             found_element = element;
4064           }
4065         }
4066       }
4067
4068       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4069       {
4070         /* check for player created from custom element as explosion content */
4071         content = element_info[element].content.e[xx][yy];
4072         is_player = ELEM_IS_PLAYER(content);
4073
4074         if (is_player && (found_rating < 2 ||
4075                           (found_rating == 2 && element < found_element)))
4076         {
4077           start_x = x + xx - 1;
4078           start_y = y + yy - 1;
4079
4080           found_rating = 2;
4081           found_element = element;
4082         }
4083
4084         if (!CAN_CHANGE(element))
4085           continue;
4086
4087         for (i = 0; i < element_info[element].num_change_pages; i++)
4088         {
4089           /* check for player created from custom element as extended target */
4090           content =
4091             element_info[element].change_page[i].target_content.e[xx][yy];
4092
4093           is_player = ELEM_IS_PLAYER(content);
4094
4095           if (is_player && (found_rating < 1 ||
4096                             (found_rating == 1 && element < found_element)))
4097           {
4098             start_x = x + xx - 1;
4099             start_y = y + yy - 1;
4100
4101             found_rating = 1;
4102             found_element = element;
4103           }
4104         }
4105       }
4106     }
4107
4108     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4109                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4110                 start_x - MIDPOSX);
4111
4112     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4113                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4114                 start_y - MIDPOSY);
4115   }
4116   else
4117   {
4118     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4119                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4120                 local_player->jx - MIDPOSX);
4121
4122     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4123                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4124                 local_player->jy - MIDPOSY);
4125   }
4126
4127 #if 0
4128   /* do not use PLAYING mask for fading out from main screen */
4129   game_status = GAME_MODE_MAIN;
4130 #endif
4131
4132   StopAnimation();
4133
4134   if (!game.restart_level)
4135     CloseDoor(DOOR_CLOSE_1);
4136
4137 #if 1
4138   if (level_editor_test_game)
4139     FadeSkipNextFadeIn();
4140   else
4141     FadeSetEnterScreen();
4142 #else
4143   if (level_editor_test_game)
4144     fading = fading_none;
4145   else
4146     fading = menu.destination;
4147 #endif
4148
4149 #if 1
4150   FadeOut(REDRAW_FIELD);
4151 #else
4152   if (do_fading)
4153     FadeOut(REDRAW_FIELD);
4154 #endif
4155
4156 #if 0
4157   game_status = GAME_MODE_PLAYING;
4158 #endif
4159
4160   /* !!! FIX THIS (START) !!! */
4161   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4162   {
4163     InitGameEngine_EM();
4164
4165     /* blit playfield from scroll buffer to normal back buffer for fading in */
4166     BlitScreenToBitmap_EM(backbuffer);
4167   }
4168   else
4169   {
4170     DrawLevel();
4171     DrawAllPlayers();
4172
4173     /* after drawing the level, correct some elements */
4174     if (game.timegate_time_left == 0)
4175       CloseAllOpenTimegates();
4176
4177     /* blit playfield from scroll buffer to normal back buffer for fading in */
4178     if (setup.soft_scrolling)
4179       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4180
4181     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4182   }
4183   /* !!! FIX THIS (END) !!! */
4184
4185 #if 1
4186   FadeIn(REDRAW_FIELD);
4187 #else
4188   if (do_fading)
4189     FadeIn(REDRAW_FIELD);
4190
4191   BackToFront();
4192 #endif
4193
4194   if (!game.restart_level)
4195   {
4196     /* copy default game door content to main double buffer */
4197     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4198                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4199   }
4200
4201   SetPanelBackground();
4202   SetDrawBackgroundMask(REDRAW_DOOR_1);
4203
4204   UpdateGameDoorValues();
4205   DrawGameDoorValues();
4206
4207   if (!game.restart_level)
4208   {
4209     UnmapGameButtons();
4210     UnmapTapeButtons();
4211     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4212     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4213     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4214     MapGameButtons();
4215     MapTapeButtons();
4216
4217     /* copy actual game door content to door double buffer for OpenDoor() */
4218     BlitBitmap(drawto, bitmap_db_door,
4219                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4220
4221     OpenDoor(DOOR_OPEN_ALL);
4222
4223     PlaySound(SND_GAME_STARTING);
4224
4225     if (setup.sound_music)
4226       PlayLevelMusic();
4227
4228     KeyboardAutoRepeatOffUnlessAutoplay();
4229
4230     if (options.debug)
4231     {
4232       for (i = 0; i < MAX_PLAYERS; i++)
4233         printf("Player %d %sactive.\n",
4234                i + 1, (stored_player[i].active ? "" : "not "));
4235     }
4236   }
4237
4238 #if 1
4239   UnmapAllGadgets();
4240
4241   MapGameButtons();
4242   MapTapeButtons();
4243 #endif
4244
4245   game.restart_level = FALSE;
4246 }
4247
4248 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4249 {
4250   /* this is used for non-R'n'D game engines to update certain engine values */
4251
4252   /* needed to determine if sounds are played within the visible screen area */
4253   scroll_x = actual_scroll_x;
4254   scroll_y = actual_scroll_y;
4255 }
4256
4257 void InitMovDir(int x, int y)
4258 {
4259   int i, element = Feld[x][y];
4260   static int xy[4][2] =
4261   {
4262     {  0, +1 },
4263     { +1,  0 },
4264     {  0, -1 },
4265     { -1,  0 }
4266   };
4267   static int direction[3][4] =
4268   {
4269     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4270     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4271     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4272   };
4273
4274   switch (element)
4275   {
4276     case EL_BUG_RIGHT:
4277     case EL_BUG_UP:
4278     case EL_BUG_LEFT:
4279     case EL_BUG_DOWN:
4280       Feld[x][y] = EL_BUG;
4281       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4282       break;
4283
4284     case EL_SPACESHIP_RIGHT:
4285     case EL_SPACESHIP_UP:
4286     case EL_SPACESHIP_LEFT:
4287     case EL_SPACESHIP_DOWN:
4288       Feld[x][y] = EL_SPACESHIP;
4289       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4290       break;
4291
4292     case EL_BD_BUTTERFLY_RIGHT:
4293     case EL_BD_BUTTERFLY_UP:
4294     case EL_BD_BUTTERFLY_LEFT:
4295     case EL_BD_BUTTERFLY_DOWN:
4296       Feld[x][y] = EL_BD_BUTTERFLY;
4297       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4298       break;
4299
4300     case EL_BD_FIREFLY_RIGHT:
4301     case EL_BD_FIREFLY_UP:
4302     case EL_BD_FIREFLY_LEFT:
4303     case EL_BD_FIREFLY_DOWN:
4304       Feld[x][y] = EL_BD_FIREFLY;
4305       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4306       break;
4307
4308     case EL_PACMAN_RIGHT:
4309     case EL_PACMAN_UP:
4310     case EL_PACMAN_LEFT:
4311     case EL_PACMAN_DOWN:
4312       Feld[x][y] = EL_PACMAN;
4313       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4314       break;
4315
4316     case EL_YAMYAM_LEFT:
4317     case EL_YAMYAM_RIGHT:
4318     case EL_YAMYAM_UP:
4319     case EL_YAMYAM_DOWN:
4320       Feld[x][y] = EL_YAMYAM;
4321       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4322       break;
4323
4324     case EL_SP_SNIKSNAK:
4325       MovDir[x][y] = MV_UP;
4326       break;
4327
4328     case EL_SP_ELECTRON:
4329       MovDir[x][y] = MV_LEFT;
4330       break;
4331
4332     case EL_MOLE_LEFT:
4333     case EL_MOLE_RIGHT:
4334     case EL_MOLE_UP:
4335     case EL_MOLE_DOWN:
4336       Feld[x][y] = EL_MOLE;
4337       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4338       break;
4339
4340     default:
4341       if (IS_CUSTOM_ELEMENT(element))
4342       {
4343         struct ElementInfo *ei = &element_info[element];
4344         int move_direction_initial = ei->move_direction_initial;
4345         int move_pattern = ei->move_pattern;
4346
4347         if (move_direction_initial == MV_START_PREVIOUS)
4348         {
4349           if (MovDir[x][y] != MV_NONE)
4350             return;
4351
4352           move_direction_initial = MV_START_AUTOMATIC;
4353         }
4354
4355         if (move_direction_initial == MV_START_RANDOM)
4356           MovDir[x][y] = 1 << RND(4);
4357         else if (move_direction_initial & MV_ANY_DIRECTION)
4358           MovDir[x][y] = move_direction_initial;
4359         else if (move_pattern == MV_ALL_DIRECTIONS ||
4360                  move_pattern == MV_TURNING_LEFT ||
4361                  move_pattern == MV_TURNING_RIGHT ||
4362                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4363                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4364                  move_pattern == MV_TURNING_RANDOM)
4365           MovDir[x][y] = 1 << RND(4);
4366         else if (move_pattern == MV_HORIZONTAL)
4367           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4368         else if (move_pattern == MV_VERTICAL)
4369           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4370         else if (move_pattern & MV_ANY_DIRECTION)
4371           MovDir[x][y] = element_info[element].move_pattern;
4372         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4373                  move_pattern == MV_ALONG_RIGHT_SIDE)
4374         {
4375           /* use random direction as default start direction */
4376           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4377             MovDir[x][y] = 1 << RND(4);
4378
4379           for (i = 0; i < NUM_DIRECTIONS; i++)
4380           {
4381             int x1 = x + xy[i][0];
4382             int y1 = y + xy[i][1];
4383
4384             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4385             {
4386               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4387                 MovDir[x][y] = direction[0][i];
4388               else
4389                 MovDir[x][y] = direction[1][i];
4390
4391               break;
4392             }
4393           }
4394         }                
4395       }
4396       else
4397       {
4398         MovDir[x][y] = 1 << RND(4);
4399
4400         if (element != EL_BUG &&
4401             element != EL_SPACESHIP &&
4402             element != EL_BD_BUTTERFLY &&
4403             element != EL_BD_FIREFLY)
4404           break;
4405
4406         for (i = 0; i < NUM_DIRECTIONS; i++)
4407         {
4408           int x1 = x + xy[i][0];
4409           int y1 = y + xy[i][1];
4410
4411           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4412           {
4413             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4414             {
4415               MovDir[x][y] = direction[0][i];
4416               break;
4417             }
4418             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4419                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4420             {
4421               MovDir[x][y] = direction[1][i];
4422               break;
4423             }
4424           }
4425         }
4426       }
4427       break;
4428   }
4429
4430   GfxDir[x][y] = MovDir[x][y];
4431 }
4432
4433 void InitAmoebaNr(int x, int y)
4434 {
4435   int i;
4436   int group_nr = AmoebeNachbarNr(x, y);
4437
4438   if (group_nr == 0)
4439   {
4440     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4441     {
4442       if (AmoebaCnt[i] == 0)
4443       {
4444         group_nr = i;
4445         break;
4446       }
4447     }
4448   }
4449
4450   AmoebaNr[x][y] = group_nr;
4451   AmoebaCnt[group_nr]++;
4452   AmoebaCnt2[group_nr]++;
4453 }
4454
4455 static void PlayerWins(struct PlayerInfo *player)
4456 {
4457   player->LevelSolved = TRUE;
4458   player->GameOver = TRUE;
4459
4460   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4461                          level.native_em_level->lev->score : player->score);
4462
4463   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4464   player->LevelSolved_CountingScore = player->score_final;
4465 }
4466
4467 void GameWon()
4468 {
4469   static int time, time_final;
4470   static int score, score_final;
4471   static int game_over_delay_1 = 0;
4472   static int game_over_delay_2 = 0;
4473   int game_over_delay_value_1 = 50;
4474   int game_over_delay_value_2 = 50;
4475
4476   if (!local_player->LevelSolved_GameWon)
4477   {
4478     int i;
4479
4480     /* do not start end game actions before the player stops moving (to exit) */
4481     if (local_player->MovPos)
4482       return;
4483
4484     local_player->LevelSolved_GameWon = TRUE;
4485     local_player->LevelSolved_SaveTape = tape.recording;
4486     local_player->LevelSolved_SaveScore = !tape.playing;
4487
4488     if (tape.auto_play)         /* tape might already be stopped here */
4489       tape.auto_play_level_solved = TRUE;
4490
4491 #if 1
4492     TapeStop();
4493 #endif
4494
4495     game_over_delay_1 = game_over_delay_value_1;
4496     game_over_delay_2 = game_over_delay_value_2;
4497
4498     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4499     score = score_final = local_player->score_final;
4500
4501     if (TimeLeft > 0)
4502     {
4503       time_final = 0;
4504       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4505     }
4506     else if (level.time == 0 && TimePlayed < 999)
4507     {
4508       time_final = 999;
4509       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4510     }
4511
4512     local_player->score_final = score_final;
4513
4514     if (level_editor_test_game)
4515     {
4516       time = time_final;
4517       score = score_final;
4518
4519 #if 1
4520       local_player->LevelSolved_CountingTime = time;
4521       local_player->LevelSolved_CountingScore = score;
4522
4523       game_panel_controls[GAME_PANEL_TIME].value = time;
4524       game_panel_controls[GAME_PANEL_SCORE].value = score;
4525
4526       DisplayGameControlValues();
4527 #else
4528       DrawGameValue_Time(time);
4529       DrawGameValue_Score(score);
4530 #endif
4531     }
4532
4533     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4534     {
4535       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4536       {
4537         /* close exit door after last player */
4538         if ((AllPlayersGone &&
4539              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4540               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4541               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4542             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4543             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4544         {
4545           int element = Feld[ExitX][ExitY];
4546
4547 #if 0
4548           if (element == EL_EM_EXIT_OPEN ||
4549               element == EL_EM_STEEL_EXIT_OPEN)
4550           {
4551             Bang(ExitX, ExitY);
4552           }
4553           else
4554 #endif
4555           {
4556             Feld[ExitX][ExitY] =
4557               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4558                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4559                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4560                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4561                EL_EM_STEEL_EXIT_CLOSING);
4562
4563             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4564           }
4565         }
4566
4567         /* player disappears */
4568         DrawLevelField(ExitX, ExitY);
4569       }
4570
4571       for (i = 0; i < MAX_PLAYERS; i++)
4572       {
4573         struct PlayerInfo *player = &stored_player[i];
4574
4575         if (player->present)
4576         {
4577           RemovePlayer(player);
4578
4579           /* player disappears */
4580           DrawLevelField(player->jx, player->jy);
4581         }
4582       }
4583     }
4584
4585     PlaySound(SND_GAME_WINNING);
4586   }
4587
4588   if (game_over_delay_1 > 0)
4589   {
4590     game_over_delay_1--;
4591
4592     return;
4593   }
4594
4595   if (time != time_final)
4596   {
4597     int time_to_go = ABS(time_final - time);
4598     int time_count_dir = (time < time_final ? +1 : -1);
4599     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4600
4601     time  += time_count_steps * time_count_dir;
4602     score += time_count_steps * level.score[SC_TIME_BONUS];
4603
4604 #if 1
4605     local_player->LevelSolved_CountingTime = time;
4606     local_player->LevelSolved_CountingScore = score;
4607
4608     game_panel_controls[GAME_PANEL_TIME].value = time;
4609     game_panel_controls[GAME_PANEL_SCORE].value = score;
4610
4611     DisplayGameControlValues();
4612 #else
4613     DrawGameValue_Time(time);
4614     DrawGameValue_Score(score);
4615 #endif
4616
4617     if (time == time_final)
4618       StopSound(SND_GAME_LEVELTIME_BONUS);
4619     else if (setup.sound_loops)
4620       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4621     else
4622       PlaySound(SND_GAME_LEVELTIME_BONUS);
4623
4624     return;
4625   }
4626
4627   local_player->LevelSolved_PanelOff = TRUE;
4628
4629   if (game_over_delay_2 > 0)
4630   {
4631     game_over_delay_2--;
4632
4633     return;
4634   }
4635
4636 #if 1
4637   GameEnd();
4638 #endif
4639 }
4640
4641 void GameEnd()
4642 {
4643   int hi_pos;
4644   boolean raise_level = FALSE;
4645
4646   local_player->LevelSolved_GameEnd = TRUE;
4647
4648   CloseDoor(DOOR_CLOSE_1);
4649
4650   if (local_player->LevelSolved_SaveTape)
4651   {
4652 #if 0
4653     TapeStop();
4654 #endif
4655
4656 #if 1
4657     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4658 #else
4659     SaveTape(tape.level_nr);            /* ask to save tape */
4660 #endif
4661   }
4662
4663   if (level_editor_test_game)
4664   {
4665     game_status = GAME_MODE_MAIN;
4666
4667 #if 1
4668     DrawAndFadeInMainMenu(REDRAW_FIELD);
4669 #else
4670     DrawMainMenu();
4671 #endif
4672
4673     return;
4674   }
4675
4676   if (!local_player->LevelSolved_SaveScore)
4677   {
4678 #if 1
4679     FadeOut(REDRAW_FIELD);
4680 #endif
4681
4682     game_status = GAME_MODE_MAIN;
4683
4684     DrawAndFadeInMainMenu(REDRAW_FIELD);
4685
4686     return;
4687   }
4688
4689   if (level_nr == leveldir_current->handicap_level)
4690   {
4691     leveldir_current->handicap_level++;
4692     SaveLevelSetup_SeriesInfo();
4693   }
4694
4695   if (level_nr < leveldir_current->last_level)
4696     raise_level = TRUE;                 /* advance to next level */
4697
4698   if ((hi_pos = NewHiScore()) >= 0) 
4699   {
4700     game_status = GAME_MODE_SCORES;
4701
4702     DrawHallOfFame(hi_pos);
4703
4704     if (raise_level)
4705     {
4706       level_nr++;
4707       TapeErase();
4708     }
4709   }
4710   else
4711   {
4712 #if 1
4713     FadeOut(REDRAW_FIELD);
4714 #endif
4715
4716     game_status = GAME_MODE_MAIN;
4717
4718     if (raise_level)
4719     {
4720       level_nr++;
4721       TapeErase();
4722     }
4723
4724     DrawAndFadeInMainMenu(REDRAW_FIELD);
4725   }
4726 }
4727
4728 int NewHiScore()
4729 {
4730   int k, l;
4731   int position = -1;
4732
4733   LoadScore(level_nr);
4734
4735   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4736       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4737     return -1;
4738
4739   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4740   {
4741     if (local_player->score_final > highscore[k].Score)
4742     {
4743       /* player has made it to the hall of fame */
4744
4745       if (k < MAX_SCORE_ENTRIES - 1)
4746       {
4747         int m = MAX_SCORE_ENTRIES - 1;
4748
4749 #ifdef ONE_PER_NAME
4750         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4751           if (strEqual(setup.player_name, highscore[l].Name))
4752             m = l;
4753         if (m == k)     /* player's new highscore overwrites his old one */
4754           goto put_into_list;
4755 #endif
4756
4757         for (l = m; l > k; l--)
4758         {
4759           strcpy(highscore[l].Name, highscore[l - 1].Name);
4760           highscore[l].Score = highscore[l - 1].Score;
4761         }
4762       }
4763
4764 #ifdef ONE_PER_NAME
4765       put_into_list:
4766 #endif
4767       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4768       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4769       highscore[k].Score = local_player->score_final; 
4770       position = k;
4771       break;
4772     }
4773
4774 #ifdef ONE_PER_NAME
4775     else if (!strncmp(setup.player_name, highscore[k].Name,
4776                       MAX_PLAYER_NAME_LEN))
4777       break;    /* player already there with a higher score */
4778 #endif
4779
4780   }
4781
4782   if (position >= 0) 
4783     SaveScore(level_nr);
4784
4785   return position;
4786 }
4787
4788 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4789 {
4790   int element = Feld[x][y];
4791   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4792   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4793   int horiz_move = (dx != 0);
4794   int sign = (horiz_move ? dx : dy);
4795   int step = sign * element_info[element].move_stepsize;
4796
4797   /* special values for move stepsize for spring and things on conveyor belt */
4798   if (horiz_move)
4799   {
4800     if (CAN_FALL(element) &&
4801         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4802       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4803     else if (element == EL_SPRING)
4804       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4805   }
4806
4807   return step;
4808 }
4809
4810 inline static int getElementMoveStepsize(int x, int y)
4811 {
4812   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4813 }
4814
4815 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4816 {
4817   if (player->GfxAction != action || player->GfxDir != dir)
4818   {
4819 #if 0
4820     printf("Player frame reset! (%d => %d, %d => %d)\n",
4821            player->GfxAction, action, player->GfxDir, dir);
4822 #endif
4823
4824     player->GfxAction = action;
4825     player->GfxDir = dir;
4826     player->Frame = 0;
4827     player->StepFrame = 0;
4828   }
4829 }
4830
4831 #if USE_GFX_RESET_GFX_ANIMATION
4832 static void ResetGfxFrame(int x, int y, boolean redraw)
4833 {
4834   int element = Feld[x][y];
4835   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4836   int last_gfx_frame = GfxFrame[x][y];
4837
4838   if (graphic_info[graphic].anim_global_sync)
4839     GfxFrame[x][y] = FrameCounter;
4840   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4841     GfxFrame[x][y] = CustomValue[x][y];
4842   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4843     GfxFrame[x][y] = element_info[element].collect_score;
4844   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4845     GfxFrame[x][y] = ChangeDelay[x][y];
4846
4847   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4848     DrawLevelGraphicAnimation(x, y, graphic);
4849 }
4850 #endif
4851
4852 static void ResetGfxAnimation(int x, int y)
4853 {
4854   GfxAction[x][y] = ACTION_DEFAULT;
4855   GfxDir[x][y] = MovDir[x][y];
4856   GfxFrame[x][y] = 0;
4857
4858 #if USE_GFX_RESET_GFX_ANIMATION
4859   ResetGfxFrame(x, y, FALSE);
4860 #endif
4861 }
4862
4863 static void ResetRandomAnimationValue(int x, int y)
4864 {
4865   GfxRandom[x][y] = INIT_GFX_RANDOM();
4866 }
4867
4868 void InitMovingField(int x, int y, int direction)
4869 {
4870   int element = Feld[x][y];
4871   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4872   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4873   int newx = x + dx;
4874   int newy = y + dy;
4875   boolean is_moving_before, is_moving_after;
4876 #if 0
4877   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4878 #endif
4879
4880   /* check if element was/is moving or being moved before/after mode change */
4881 #if 1
4882 #if 1
4883   is_moving_before = (WasJustMoving[x][y] != 0);
4884 #else
4885   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4886   is_moving_before = WasJustMoving[x][y];
4887 #endif
4888 #else
4889   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4890 #endif
4891   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4892
4893   /* reset animation only for moving elements which change direction of moving
4894      or which just started or stopped moving
4895      (else CEs with property "can move" / "not moving" are reset each frame) */
4896 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4897 #if 1
4898   if (is_moving_before != is_moving_after ||
4899       direction != MovDir[x][y])
4900     ResetGfxAnimation(x, y);
4901 #else
4902   if ((is_moving_before || is_moving_after) && !continues_moving)
4903     ResetGfxAnimation(x, y);
4904 #endif
4905 #else
4906   if (!continues_moving)
4907     ResetGfxAnimation(x, y);
4908 #endif
4909
4910   MovDir[x][y] = direction;
4911   GfxDir[x][y] = direction;
4912
4913 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4914   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4915                      direction == MV_DOWN && CAN_FALL(element) ?
4916                      ACTION_FALLING : ACTION_MOVING);
4917 #else
4918   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4919                      ACTION_FALLING : ACTION_MOVING);
4920 #endif
4921
4922   /* this is needed for CEs with property "can move" / "not moving" */
4923
4924   if (is_moving_after)
4925   {
4926     if (Feld[newx][newy] == EL_EMPTY)
4927       Feld[newx][newy] = EL_BLOCKED;
4928
4929     MovDir[newx][newy] = MovDir[x][y];
4930
4931 #if USE_NEW_CUSTOM_VALUE
4932     CustomValue[newx][newy] = CustomValue[x][y];
4933 #endif
4934
4935     GfxFrame[newx][newy] = GfxFrame[x][y];
4936     GfxRandom[newx][newy] = GfxRandom[x][y];
4937     GfxAction[newx][newy] = GfxAction[x][y];
4938     GfxDir[newx][newy] = GfxDir[x][y];
4939   }
4940 }
4941
4942 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4943 {
4944   int direction = MovDir[x][y];
4945   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4946   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4947
4948   *goes_to_x = newx;
4949   *goes_to_y = newy;
4950 }
4951
4952 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4953 {
4954   int oldx = x, oldy = y;
4955   int direction = MovDir[x][y];
4956
4957   if (direction == MV_LEFT)
4958     oldx++;
4959   else if (direction == MV_RIGHT)
4960     oldx--;
4961   else if (direction == MV_UP)
4962     oldy++;
4963   else if (direction == MV_DOWN)
4964     oldy--;
4965
4966   *comes_from_x = oldx;
4967   *comes_from_y = oldy;
4968 }
4969
4970 int MovingOrBlocked2Element(int x, int y)
4971 {
4972   int element = Feld[x][y];
4973
4974   if (element == EL_BLOCKED)
4975   {
4976     int oldx, oldy;
4977
4978     Blocked2Moving(x, y, &oldx, &oldy);
4979     return Feld[oldx][oldy];
4980   }
4981   else
4982     return element;
4983 }
4984
4985 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4986 {
4987   /* like MovingOrBlocked2Element(), but if element is moving
4988      and (x,y) is the field the moving element is just leaving,
4989      return EL_BLOCKED instead of the element value */
4990   int element = Feld[x][y];
4991
4992   if (IS_MOVING(x, y))
4993   {
4994     if (element == EL_BLOCKED)
4995     {
4996       int oldx, oldy;
4997
4998       Blocked2Moving(x, y, &oldx, &oldy);
4999       return Feld[oldx][oldy];
5000     }
5001     else
5002       return EL_BLOCKED;
5003   }
5004   else
5005     return element;
5006 }
5007
5008 static void RemoveField(int x, int y)
5009 {
5010   Feld[x][y] = EL_EMPTY;
5011
5012   MovPos[x][y] = 0;
5013   MovDir[x][y] = 0;
5014   MovDelay[x][y] = 0;
5015
5016 #if USE_NEW_CUSTOM_VALUE
5017   CustomValue[x][y] = 0;
5018 #endif
5019
5020   AmoebaNr[x][y] = 0;
5021   ChangeDelay[x][y] = 0;
5022   ChangePage[x][y] = -1;
5023   Pushed[x][y] = FALSE;
5024
5025 #if 0
5026   ExplodeField[x][y] = EX_TYPE_NONE;
5027 #endif
5028
5029   GfxElement[x][y] = EL_UNDEFINED;
5030   GfxAction[x][y] = ACTION_DEFAULT;
5031   GfxDir[x][y] = MV_NONE;
5032 }
5033
5034 void RemoveMovingField(int x, int y)
5035 {
5036   int oldx = x, oldy = y, newx = x, newy = y;
5037   int element = Feld[x][y];
5038   int next_element = EL_UNDEFINED;
5039
5040   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5041     return;
5042
5043   if (IS_MOVING(x, y))
5044   {
5045     Moving2Blocked(x, y, &newx, &newy);
5046
5047     if (Feld[newx][newy] != EL_BLOCKED)
5048     {
5049       /* element is moving, but target field is not free (blocked), but
5050          already occupied by something different (example: acid pool);
5051          in this case, only remove the moving field, but not the target */
5052
5053       RemoveField(oldx, oldy);
5054
5055       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5056
5057       DrawLevelField(oldx, oldy);
5058
5059       return;
5060     }
5061   }
5062   else if (element == EL_BLOCKED)
5063   {
5064     Blocked2Moving(x, y, &oldx, &oldy);
5065     if (!IS_MOVING(oldx, oldy))
5066       return;
5067   }
5068
5069   if (element == EL_BLOCKED &&
5070       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5071        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5072        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5073        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5074        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5075        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5076     next_element = get_next_element(Feld[oldx][oldy]);
5077
5078   RemoveField(oldx, oldy);
5079   RemoveField(newx, newy);
5080
5081   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5082
5083   if (next_element != EL_UNDEFINED)
5084     Feld[oldx][oldy] = next_element;
5085
5086   DrawLevelField(oldx, oldy);
5087   DrawLevelField(newx, newy);
5088 }
5089
5090 void DrawDynamite(int x, int y)
5091 {
5092   int sx = SCREENX(x), sy = SCREENY(y);
5093   int graphic = el2img(Feld[x][y]);
5094   int frame;
5095
5096   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5097     return;
5098
5099   if (IS_WALKABLE_INSIDE(Back[x][y]))
5100     return;
5101
5102   if (Back[x][y])
5103     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5104   else if (Store[x][y])
5105     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5106
5107   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5108
5109   if (Back[x][y] || Store[x][y])
5110     DrawGraphicThruMask(sx, sy, graphic, frame);
5111   else
5112     DrawGraphic(sx, sy, graphic, frame);
5113 }
5114
5115 void CheckDynamite(int x, int y)
5116 {
5117   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5118   {
5119     MovDelay[x][y]--;
5120
5121     if (MovDelay[x][y] != 0)
5122     {
5123       DrawDynamite(x, y);
5124       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5125
5126       return;
5127     }
5128   }
5129
5130   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5131
5132   Bang(x, y);
5133 }
5134
5135 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5136 {
5137   boolean num_checked_players = 0;
5138   int i;
5139
5140   for (i = 0; i < MAX_PLAYERS; i++)
5141   {
5142     if (stored_player[i].active)
5143     {
5144       int sx = stored_player[i].jx;
5145       int sy = stored_player[i].jy;
5146
5147       if (num_checked_players == 0)
5148       {
5149         *sx1 = *sx2 = sx;
5150         *sy1 = *sy2 = sy;
5151       }
5152       else
5153       {
5154         *sx1 = MIN(*sx1, sx);
5155         *sy1 = MIN(*sy1, sy);
5156         *sx2 = MAX(*sx2, sx);
5157         *sy2 = MAX(*sy2, sy);
5158       }
5159
5160       num_checked_players++;
5161     }
5162   }
5163 }
5164
5165 static boolean checkIfAllPlayersFitToScreen_RND()
5166 {
5167   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5168
5169   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5170
5171   return (sx2 - sx1 < SCR_FIELDX &&
5172           sy2 - sy1 < SCR_FIELDY);
5173 }
5174
5175 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5176 {
5177   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5178
5179   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5180
5181   *sx = (sx1 + sx2) / 2;
5182   *sy = (sy1 + sy2) / 2;
5183 }
5184
5185 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5186                         boolean center_screen, boolean quick_relocation)
5187 {
5188   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5189   boolean no_delay = (tape.warp_forward);
5190   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5191   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5192
5193   if (quick_relocation)
5194   {
5195     int offset = game.scroll_delay_value;
5196
5197     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5198     {
5199       if (!level.shifted_relocation || center_screen)
5200       {
5201         /* quick relocation (without scrolling), with centering of screen */
5202
5203         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5204                     x > SBX_Right + MIDPOSX ? SBX_Right :
5205                     x - MIDPOSX);
5206
5207         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5208                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5209                     y - MIDPOSY);
5210       }
5211       else
5212       {
5213         /* quick relocation (without scrolling), but do not center screen */
5214
5215         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5216                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5217                                old_x - MIDPOSX);
5218
5219         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5220                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5221                                old_y - MIDPOSY);
5222
5223         int offset_x = x + (scroll_x - center_scroll_x);
5224         int offset_y = y + (scroll_y - center_scroll_y);
5225
5226         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5227                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5228                     offset_x - MIDPOSX);
5229
5230         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5231                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5232                     offset_y - MIDPOSY);
5233       }
5234     }
5235     else
5236     {
5237       /* quick relocation (without scrolling), inside visible screen area */
5238
5239       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5240           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5241         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5242
5243       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5244           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5245         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5246
5247       /* don't scroll over playfield boundaries */
5248       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5249         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5250
5251       /* don't scroll over playfield boundaries */
5252       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5253         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5254     }
5255
5256     RedrawPlayfield(TRUE, 0,0,0,0);
5257   }
5258   else
5259   {
5260 #if 1
5261     int scroll_xx, scroll_yy;
5262
5263     if (!level.shifted_relocation || center_screen)
5264     {
5265       /* visible relocation (with scrolling), with centering of screen */
5266
5267       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5268                    x > SBX_Right + MIDPOSX ? SBX_Right :
5269                    x - MIDPOSX);
5270
5271       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5272                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5273                    y - MIDPOSY);
5274     }
5275     else
5276     {
5277       /* visible relocation (with scrolling), but do not center screen */
5278
5279       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5280                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5281                              old_x - MIDPOSX);
5282
5283       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5284                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5285                              old_y - MIDPOSY);
5286
5287       int offset_x = x + (scroll_x - center_scroll_x);
5288       int offset_y = y + (scroll_y - center_scroll_y);
5289
5290       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5291                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5292                    offset_x - MIDPOSX);
5293
5294       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5295                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5296                    offset_y - MIDPOSY);
5297     }
5298
5299 #else
5300
5301     /* visible relocation (with scrolling), with centering of screen */
5302
5303     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5304                      x > SBX_Right + MIDPOSX ? SBX_Right :
5305                      x - MIDPOSX);
5306
5307     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5308                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5309                      y - MIDPOSY);
5310 #endif
5311
5312     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5313
5314     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5315     {
5316       int dx = 0, dy = 0;
5317       int fx = FX, fy = FY;
5318
5319       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5320       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5321
5322       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5323         break;
5324
5325       scroll_x -= dx;
5326       scroll_y -= dy;
5327
5328       fx += dx * TILEX / 2;
5329       fy += dy * TILEY / 2;
5330
5331       ScrollLevel(dx, dy);
5332       DrawAllPlayers();
5333
5334       /* scroll in two steps of half tile size to make things smoother */
5335       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5336       FlushDisplay();
5337       Delay(wait_delay_value);
5338
5339       /* scroll second step to align at full tile size */
5340       BackToFront();
5341       Delay(wait_delay_value);
5342     }
5343
5344     DrawAllPlayers();
5345     BackToFront();
5346     Delay(wait_delay_value);
5347   }
5348 }
5349
5350 void RelocatePlayer(int jx, int jy, int el_player_raw)
5351 {
5352   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5353   int player_nr = GET_PLAYER_NR(el_player);
5354   struct PlayerInfo *player = &stored_player[player_nr];
5355   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5356   boolean no_delay = (tape.warp_forward);
5357   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5358   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5359   int old_jx = player->jx;
5360   int old_jy = player->jy;
5361   int old_element = Feld[old_jx][old_jy];
5362   int element = Feld[jx][jy];
5363   boolean player_relocated = (old_jx != jx || old_jy != jy);
5364
5365   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5366   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5367   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5368   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5369   int leave_side_horiz = move_dir_horiz;
5370   int leave_side_vert  = move_dir_vert;
5371   int enter_side = enter_side_horiz | enter_side_vert;
5372   int leave_side = leave_side_horiz | leave_side_vert;
5373
5374   if (player->GameOver)         /* do not reanimate dead player */
5375     return;
5376
5377   if (!player_relocated)        /* no need to relocate the player */
5378     return;
5379
5380   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5381   {
5382     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5383     DrawLevelField(jx, jy);
5384   }
5385
5386   if (player->present)
5387   {
5388     while (player->MovPos)
5389     {
5390       ScrollPlayer(player, SCROLL_GO_ON);
5391       ScrollScreen(NULL, SCROLL_GO_ON);
5392
5393       AdvanceFrameAndPlayerCounters(player->index_nr);
5394
5395       DrawPlayer(player);
5396
5397       BackToFront();
5398       Delay(wait_delay_value);
5399     }
5400
5401     DrawPlayer(player);         /* needed here only to cleanup last field */
5402     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5403
5404     player->is_moving = FALSE;
5405   }
5406
5407   if (IS_CUSTOM_ELEMENT(old_element))
5408     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5409                                CE_LEFT_BY_PLAYER,
5410                                player->index_bit, leave_side);
5411
5412   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5413                                       CE_PLAYER_LEAVES_X,
5414                                       player->index_bit, leave_side);
5415
5416   Feld[jx][jy] = el_player;
5417   InitPlayerField(jx, jy, el_player, TRUE);
5418
5419   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5420   {
5421     Feld[jx][jy] = element;
5422     InitField(jx, jy, FALSE);
5423   }
5424
5425   /* only visually relocate centered player */
5426   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5427                      FALSE, level.instant_relocation);
5428
5429   TestIfPlayerTouchesBadThing(jx, jy);
5430   TestIfPlayerTouchesCustomElement(jx, jy);
5431
5432   if (IS_CUSTOM_ELEMENT(element))
5433     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5434                                player->index_bit, enter_side);
5435
5436   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5437                                       player->index_bit, enter_side);
5438 }
5439
5440 void Explode(int ex, int ey, int phase, int mode)
5441 {
5442   int x, y;
5443   int last_phase;
5444   int border_element;
5445
5446   /* !!! eliminate this variable !!! */
5447   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5448
5449   if (game.explosions_delayed)
5450   {
5451     ExplodeField[ex][ey] = mode;
5452     return;
5453   }
5454
5455   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5456   {
5457     int center_element = Feld[ex][ey];
5458     int artwork_element, explosion_element;     /* set these values later */
5459
5460 #if 0
5461     /* --- This is only really needed (and now handled) in "Impact()". --- */
5462     /* do not explode moving elements that left the explode field in time */
5463     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5464         center_element == EL_EMPTY &&
5465         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5466       return;
5467 #endif
5468
5469 #if 0
5470     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5471     if (mode == EX_TYPE_NORMAL ||
5472         mode == EX_TYPE_CENTER ||
5473         mode == EX_TYPE_CROSS)
5474       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5475 #endif
5476
5477     /* remove things displayed in background while burning dynamite */
5478     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5479       Back[ex][ey] = 0;
5480
5481     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5482     {
5483       /* put moving element to center field (and let it explode there) */
5484       center_element = MovingOrBlocked2Element(ex, ey);
5485       RemoveMovingField(ex, ey);
5486       Feld[ex][ey] = center_element;
5487     }
5488
5489     /* now "center_element" is finally determined -- set related values now */
5490     artwork_element = center_element;           /* for custom player artwork */
5491     explosion_element = center_element;         /* for custom player artwork */
5492
5493     if (IS_PLAYER(ex, ey))
5494     {
5495       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5496
5497       artwork_element = stored_player[player_nr].artwork_element;
5498
5499       if (level.use_explosion_element[player_nr])
5500       {
5501         explosion_element = level.explosion_element[player_nr];
5502         artwork_element = explosion_element;
5503       }
5504     }
5505
5506 #if 1
5507     if (mode == EX_TYPE_NORMAL ||
5508         mode == EX_TYPE_CENTER ||
5509         mode == EX_TYPE_CROSS)
5510       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5511 #endif
5512
5513     last_phase = element_info[explosion_element].explosion_delay + 1;
5514
5515     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5516     {
5517       int xx = x - ex + 1;
5518       int yy = y - ey + 1;
5519       int element;
5520
5521       if (!IN_LEV_FIELD(x, y) ||
5522           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5523           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5524         continue;
5525
5526       element = Feld[x][y];
5527
5528       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5529       {
5530         element = MovingOrBlocked2Element(x, y);
5531
5532         if (!IS_EXPLOSION_PROOF(element))
5533           RemoveMovingField(x, y);
5534       }
5535
5536       /* indestructible elements can only explode in center (but not flames) */
5537       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5538                                            mode == EX_TYPE_BORDER)) ||
5539           element == EL_FLAMES)
5540         continue;
5541
5542       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5543          behaviour, for example when touching a yamyam that explodes to rocks
5544          with active deadly shield, a rock is created under the player !!! */
5545       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5546 #if 0
5547       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5548           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5549            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5550 #else
5551       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5552 #endif
5553       {
5554         if (IS_ACTIVE_BOMB(element))
5555         {
5556           /* re-activate things under the bomb like gate or penguin */
5557           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5558           Back[x][y] = 0;
5559         }
5560
5561         continue;
5562       }
5563
5564       /* save walkable background elements while explosion on same tile */
5565       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5566           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5567         Back[x][y] = element;
5568
5569       /* ignite explodable elements reached by other explosion */
5570       if (element == EL_EXPLOSION)
5571         element = Store2[x][y];
5572
5573       if (AmoebaNr[x][y] &&
5574           (element == EL_AMOEBA_FULL ||
5575            element == EL_BD_AMOEBA ||
5576            element == EL_AMOEBA_GROWING))
5577       {
5578         AmoebaCnt[AmoebaNr[x][y]]--;
5579         AmoebaCnt2[AmoebaNr[x][y]]--;
5580       }
5581
5582       RemoveField(x, y);
5583
5584       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5585       {
5586         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5587
5588         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5589
5590         if (PLAYERINFO(ex, ey)->use_murphy)
5591           Store[x][y] = EL_EMPTY;
5592       }
5593
5594       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5595          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5596       else if (ELEM_IS_PLAYER(center_element))
5597         Store[x][y] = EL_EMPTY;
5598       else if (center_element == EL_YAMYAM)
5599         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5600       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5601         Store[x][y] = element_info[center_element].content.e[xx][yy];
5602 #if 1
5603       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5604          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5605          otherwise) -- FIX THIS !!! */
5606       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5607         Store[x][y] = element_info[element].content.e[1][1];
5608 #else
5609       else if (!CAN_EXPLODE(element))
5610         Store[x][y] = element_info[element].content.e[1][1];
5611 #endif
5612       else
5613         Store[x][y] = EL_EMPTY;
5614
5615       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5616           center_element == EL_AMOEBA_TO_DIAMOND)
5617         Store2[x][y] = element;
5618
5619       Feld[x][y] = EL_EXPLOSION;
5620       GfxElement[x][y] = artwork_element;
5621
5622       ExplodePhase[x][y] = 1;
5623       ExplodeDelay[x][y] = last_phase;
5624
5625       Stop[x][y] = TRUE;
5626     }
5627
5628     if (center_element == EL_YAMYAM)
5629       game.yamyam_content_nr =
5630         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5631
5632     return;
5633   }
5634
5635   if (Stop[ex][ey])
5636     return;
5637
5638   x = ex;
5639   y = ey;
5640
5641   if (phase == 1)
5642     GfxFrame[x][y] = 0;         /* restart explosion animation */
5643
5644   last_phase = ExplodeDelay[x][y];
5645
5646   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5647
5648 #ifdef DEBUG
5649
5650   /* activate this even in non-DEBUG version until cause for crash in
5651      getGraphicAnimationFrame() (see below) is found and eliminated */
5652
5653 #endif
5654 #if 1
5655
5656 #if 1
5657   /* this can happen if the player leaves an explosion just in time */
5658   if (GfxElement[x][y] == EL_UNDEFINED)
5659     GfxElement[x][y] = EL_EMPTY;
5660 #else
5661   if (GfxElement[x][y] == EL_UNDEFINED)
5662   {
5663     printf("\n\n");
5664     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5665     printf("Explode(): This should never happen!\n");
5666     printf("\n\n");
5667
5668     GfxElement[x][y] = EL_EMPTY;
5669   }
5670 #endif
5671
5672 #endif
5673
5674   border_element = Store2[x][y];
5675   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5676     border_element = StorePlayer[x][y];
5677
5678   if (phase == element_info[border_element].ignition_delay ||
5679       phase == last_phase)
5680   {
5681     boolean border_explosion = FALSE;
5682
5683     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5684         !PLAYER_EXPLOSION_PROTECTED(x, y))
5685     {
5686       KillPlayerUnlessExplosionProtected(x, y);
5687       border_explosion = TRUE;
5688     }
5689     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5690     {
5691       Feld[x][y] = Store2[x][y];
5692       Store2[x][y] = 0;
5693       Bang(x, y);
5694       border_explosion = TRUE;
5695     }
5696     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5697     {
5698       AmoebeUmwandeln(x, y);
5699       Store2[x][y] = 0;
5700       border_explosion = TRUE;
5701     }
5702
5703     /* if an element just explodes due to another explosion (chain-reaction),
5704        do not immediately end the new explosion when it was the last frame of
5705        the explosion (as it would be done in the following "if"-statement!) */
5706     if (border_explosion && phase == last_phase)
5707       return;
5708   }
5709
5710   if (phase == last_phase)
5711   {
5712     int element;
5713
5714     element = Feld[x][y] = Store[x][y];
5715     Store[x][y] = Store2[x][y] = 0;
5716     GfxElement[x][y] = EL_UNDEFINED;
5717
5718     /* player can escape from explosions and might therefore be still alive */
5719     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5720         element <= EL_PLAYER_IS_EXPLODING_4)
5721     {
5722       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5723       int explosion_element = EL_PLAYER_1 + player_nr;
5724       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5725       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5726
5727       if (level.use_explosion_element[player_nr])
5728         explosion_element = level.explosion_element[player_nr];
5729
5730       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5731                     element_info[explosion_element].content.e[xx][yy]);
5732     }
5733
5734     /* restore probably existing indestructible background element */
5735     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5736       element = Feld[x][y] = Back[x][y];
5737     Back[x][y] = 0;
5738
5739     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5740     GfxDir[x][y] = MV_NONE;
5741     ChangeDelay[x][y] = 0;
5742     ChangePage[x][y] = -1;
5743
5744 #if USE_NEW_CUSTOM_VALUE
5745     CustomValue[x][y] = 0;
5746 #endif
5747
5748     InitField_WithBug2(x, y, FALSE);
5749
5750     DrawLevelField(x, y);
5751
5752     TestIfElementTouchesCustomElement(x, y);
5753
5754     if (GFX_CRUMBLED(element))
5755       DrawLevelFieldCrumbledSandNeighbours(x, y);
5756
5757     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5758       StorePlayer[x][y] = 0;
5759
5760     if (ELEM_IS_PLAYER(element))
5761       RelocatePlayer(x, y, element);
5762   }
5763   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5764   {
5765     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5766     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5767
5768     if (phase == delay)
5769       DrawLevelFieldCrumbledSand(x, y);
5770
5771     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5772     {
5773       DrawLevelElement(x, y, Back[x][y]);
5774       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5775     }
5776     else if (IS_WALKABLE_UNDER(Back[x][y]))
5777     {
5778       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5779       DrawLevelElementThruMask(x, y, Back[x][y]);
5780     }
5781     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5782       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5783   }
5784 }
5785
5786 void DynaExplode(int ex, int ey)
5787 {
5788   int i, j;
5789   int dynabomb_element = Feld[ex][ey];
5790   int dynabomb_size = 1;
5791   boolean dynabomb_xl = FALSE;
5792   struct PlayerInfo *player;
5793   static int xy[4][2] =
5794   {
5795     { 0, -1 },
5796     { -1, 0 },
5797     { +1, 0 },
5798     { 0, +1 }
5799   };
5800
5801   if (IS_ACTIVE_BOMB(dynabomb_element))
5802   {
5803     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5804     dynabomb_size = player->dynabomb_size;
5805     dynabomb_xl = player->dynabomb_xl;
5806     player->dynabombs_left++;
5807   }
5808
5809   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5810
5811   for (i = 0; i < NUM_DIRECTIONS; i++)
5812   {
5813     for (j = 1; j <= dynabomb_size; j++)
5814     {
5815       int x = ex + j * xy[i][0];
5816       int y = ey + j * xy[i][1];
5817       int element;
5818
5819       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5820         break;
5821
5822       element = Feld[x][y];
5823
5824       /* do not restart explosions of fields with active bombs */
5825       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5826         continue;
5827
5828       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5829
5830       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5831           !IS_DIGGABLE(element) && !dynabomb_xl)
5832         break;
5833     }
5834   }
5835 }
5836
5837 void Bang(int x, int y)
5838 {
5839   int element = MovingOrBlocked2Element(x, y);
5840   int explosion_type = EX_TYPE_NORMAL;
5841
5842   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5843   {
5844     struct PlayerInfo *player = PLAYERINFO(x, y);
5845
5846     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5847                             player->element_nr);
5848
5849     if (level.use_explosion_element[player->index_nr])
5850     {
5851       int explosion_element = level.explosion_element[player->index_nr];
5852
5853       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5854         explosion_type = EX_TYPE_CROSS;
5855       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5856         explosion_type = EX_TYPE_CENTER;
5857     }
5858   }
5859
5860   switch (element)
5861   {
5862     case EL_BUG:
5863     case EL_SPACESHIP:
5864     case EL_BD_BUTTERFLY:
5865     case EL_BD_FIREFLY:
5866     case EL_YAMYAM:
5867     case EL_DARK_YAMYAM:
5868     case EL_ROBOT:
5869     case EL_PACMAN:
5870     case EL_MOLE:
5871       RaiseScoreElement(element);
5872       break;
5873
5874     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5875     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5876     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5877     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5878     case EL_DYNABOMB_INCREASE_NUMBER:
5879     case EL_DYNABOMB_INCREASE_SIZE:
5880     case EL_DYNABOMB_INCREASE_POWER:
5881       explosion_type = EX_TYPE_DYNA;
5882       break;
5883
5884     case EL_DC_LANDMINE:
5885 #if 0
5886     case EL_EM_EXIT_OPEN:
5887     case EL_EM_STEEL_EXIT_OPEN:
5888 #endif
5889       explosion_type = EX_TYPE_CENTER;
5890       break;
5891
5892     case EL_PENGUIN:
5893     case EL_LAMP:
5894     case EL_LAMP_ACTIVE:
5895     case EL_AMOEBA_TO_DIAMOND:
5896       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5897         explosion_type = EX_TYPE_CENTER;
5898       break;
5899
5900     default:
5901       if (element_info[element].explosion_type == EXPLODES_CROSS)
5902         explosion_type = EX_TYPE_CROSS;
5903       else if (element_info[element].explosion_type == EXPLODES_1X1)
5904         explosion_type = EX_TYPE_CENTER;
5905       break;
5906   }
5907
5908   if (explosion_type == EX_TYPE_DYNA)
5909     DynaExplode(x, y);
5910   else
5911     Explode(x, y, EX_PHASE_START, explosion_type);
5912
5913   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5914 }
5915
5916 void SplashAcid(int x, int y)
5917 {
5918   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5919       (!IN_LEV_FIELD(x - 1, y - 2) ||
5920        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5921     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5922
5923   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5924       (!IN_LEV_FIELD(x + 1, y - 2) ||
5925        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5926     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5927
5928   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5929 }
5930
5931 static void InitBeltMovement()
5932 {
5933   static int belt_base_element[4] =
5934   {
5935     EL_CONVEYOR_BELT_1_LEFT,
5936     EL_CONVEYOR_BELT_2_LEFT,
5937     EL_CONVEYOR_BELT_3_LEFT,
5938     EL_CONVEYOR_BELT_4_LEFT
5939   };
5940   static int belt_base_active_element[4] =
5941   {
5942     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5943     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5944     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5945     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5946   };
5947
5948   int x, y, i, j;
5949
5950   /* set frame order for belt animation graphic according to belt direction */
5951   for (i = 0; i < NUM_BELTS; i++)
5952   {
5953     int belt_nr = i;
5954
5955     for (j = 0; j < NUM_BELT_PARTS; j++)
5956     {
5957       int element = belt_base_active_element[belt_nr] + j;
5958       int graphic_1 = el2img(element);
5959       int graphic_2 = el2panelimg(element);
5960
5961       if (game.belt_dir[i] == MV_LEFT)
5962       {
5963         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5964         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5965       }
5966       else
5967       {
5968         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5969         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5970       }
5971     }
5972   }
5973
5974   SCAN_PLAYFIELD(x, y)
5975   {
5976     int element = Feld[x][y];
5977
5978     for (i = 0; i < NUM_BELTS; i++)
5979     {
5980       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5981       {
5982         int e_belt_nr = getBeltNrFromBeltElement(element);
5983         int belt_nr = i;
5984
5985         if (e_belt_nr == belt_nr)
5986         {
5987           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5988
5989           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5990         }
5991       }
5992     }
5993   }
5994 }
5995
5996 static void ToggleBeltSwitch(int x, int y)
5997 {
5998   static int belt_base_element[4] =
5999   {
6000     EL_CONVEYOR_BELT_1_LEFT,
6001     EL_CONVEYOR_BELT_2_LEFT,
6002     EL_CONVEYOR_BELT_3_LEFT,
6003     EL_CONVEYOR_BELT_4_LEFT
6004   };
6005   static int belt_base_active_element[4] =
6006   {
6007     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6008     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6009     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6010     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6011   };
6012   static int belt_base_switch_element[4] =
6013   {
6014     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6015     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6016     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6017     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6018   };
6019   static int belt_move_dir[4] =
6020   {
6021     MV_LEFT,
6022     MV_NONE,
6023     MV_RIGHT,
6024     MV_NONE,
6025   };
6026
6027   int element = Feld[x][y];
6028   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6029   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6030   int belt_dir = belt_move_dir[belt_dir_nr];
6031   int xx, yy, i;
6032
6033   if (!IS_BELT_SWITCH(element))
6034     return;
6035
6036   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6037   game.belt_dir[belt_nr] = belt_dir;
6038
6039   if (belt_dir_nr == 3)
6040     belt_dir_nr = 1;
6041
6042   /* set frame order for belt animation graphic according to belt direction */
6043   for (i = 0; i < NUM_BELT_PARTS; i++)
6044   {
6045     int element = belt_base_active_element[belt_nr] + i;
6046     int graphic_1 = el2img(element);
6047     int graphic_2 = el2panelimg(element);
6048
6049     if (belt_dir == MV_LEFT)
6050     {
6051       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6052       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6053     }
6054     else
6055     {
6056       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6057       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6058     }
6059   }
6060
6061   SCAN_PLAYFIELD(xx, yy)
6062   {
6063     int element = Feld[xx][yy];
6064
6065     if (IS_BELT_SWITCH(element))
6066     {
6067       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6068
6069       if (e_belt_nr == belt_nr)
6070       {
6071         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6072         DrawLevelField(xx, yy);
6073       }
6074     }
6075     else if (IS_BELT(element) && belt_dir != MV_NONE)
6076     {
6077       int e_belt_nr = getBeltNrFromBeltElement(element);
6078
6079       if (e_belt_nr == belt_nr)
6080       {
6081         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6082
6083         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6084         DrawLevelField(xx, yy);
6085       }
6086     }
6087     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6088     {
6089       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6090
6091       if (e_belt_nr == belt_nr)
6092       {
6093         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6094
6095         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6096         DrawLevelField(xx, yy);
6097       }
6098     }
6099   }
6100 }
6101
6102 static void ToggleSwitchgateSwitch(int x, int y)
6103 {
6104   int xx, yy;
6105
6106   game.switchgate_pos = !game.switchgate_pos;
6107
6108   SCAN_PLAYFIELD(xx, yy)
6109   {
6110     int element = Feld[xx][yy];
6111
6112 #if !USE_BOTH_SWITCHGATE_SWITCHES
6113     if (element == EL_SWITCHGATE_SWITCH_UP ||
6114         element == EL_SWITCHGATE_SWITCH_DOWN)
6115     {
6116       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6117       DrawLevelField(xx, yy);
6118     }
6119     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6120              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6121     {
6122       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6123       DrawLevelField(xx, yy);
6124     }
6125 #else
6126     if (element == EL_SWITCHGATE_SWITCH_UP)
6127     {
6128       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6129       DrawLevelField(xx, yy);
6130     }
6131     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6132     {
6133       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6134       DrawLevelField(xx, yy);
6135     }
6136     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6137     {
6138       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6139       DrawLevelField(xx, yy);
6140     }
6141     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6142     {
6143       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6144       DrawLevelField(xx, yy);
6145     }
6146 #endif
6147     else if (element == EL_SWITCHGATE_OPEN ||
6148              element == EL_SWITCHGATE_OPENING)
6149     {
6150       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6151
6152       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6153     }
6154     else if (element == EL_SWITCHGATE_CLOSED ||
6155              element == EL_SWITCHGATE_CLOSING)
6156     {
6157       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6158
6159       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6160     }
6161   }
6162 }
6163
6164 static int getInvisibleActiveFromInvisibleElement(int element)
6165 {
6166   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6167           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6168           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6169           element);
6170 }
6171
6172 static int getInvisibleFromInvisibleActiveElement(int element)
6173 {
6174   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6175           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6176           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6177           element);
6178 }
6179
6180 static void RedrawAllLightSwitchesAndInvisibleElements()
6181 {
6182   int x, y;
6183
6184   SCAN_PLAYFIELD(x, y)
6185   {
6186     int element = Feld[x][y];
6187
6188     if (element == EL_LIGHT_SWITCH &&
6189         game.light_time_left > 0)
6190     {
6191       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6192       DrawLevelField(x, y);
6193     }
6194     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6195              game.light_time_left == 0)
6196     {
6197       Feld[x][y] = EL_LIGHT_SWITCH;
6198       DrawLevelField(x, y);
6199     }
6200     else if (element == EL_EMC_DRIPPER &&
6201              game.light_time_left > 0)
6202     {
6203       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6204       DrawLevelField(x, y);
6205     }
6206     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6207              game.light_time_left == 0)
6208     {
6209       Feld[x][y] = EL_EMC_DRIPPER;
6210       DrawLevelField(x, y);
6211     }
6212     else if (element == EL_INVISIBLE_STEELWALL ||
6213              element == EL_INVISIBLE_WALL ||
6214              element == EL_INVISIBLE_SAND)
6215     {
6216       if (game.light_time_left > 0)
6217         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6218
6219       DrawLevelField(x, y);
6220
6221       /* uncrumble neighbour fields, if needed */
6222       if (element == EL_INVISIBLE_SAND)
6223         DrawLevelFieldCrumbledSandNeighbours(x, y);
6224     }
6225     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6226              element == EL_INVISIBLE_WALL_ACTIVE ||
6227              element == EL_INVISIBLE_SAND_ACTIVE)
6228     {
6229       if (game.light_time_left == 0)
6230         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6231
6232       DrawLevelField(x, y);
6233
6234       /* re-crumble neighbour fields, if needed */
6235       if (element == EL_INVISIBLE_SAND)
6236         DrawLevelFieldCrumbledSandNeighbours(x, y);
6237     }
6238   }
6239 }
6240
6241 static void RedrawAllInvisibleElementsForLenses()
6242 {
6243   int x, y;
6244
6245   SCAN_PLAYFIELD(x, y)
6246   {
6247     int element = Feld[x][y];
6248
6249     if (element == EL_EMC_DRIPPER &&
6250         game.lenses_time_left > 0)
6251     {
6252       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6253       DrawLevelField(x, y);
6254     }
6255     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6256              game.lenses_time_left == 0)
6257     {
6258       Feld[x][y] = EL_EMC_DRIPPER;
6259       DrawLevelField(x, y);
6260     }
6261     else if (element == EL_INVISIBLE_STEELWALL ||
6262              element == EL_INVISIBLE_WALL ||
6263              element == EL_INVISIBLE_SAND)
6264     {
6265       if (game.lenses_time_left > 0)
6266         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6267
6268       DrawLevelField(x, y);
6269
6270       /* uncrumble neighbour fields, if needed */
6271       if (element == EL_INVISIBLE_SAND)
6272         DrawLevelFieldCrumbledSandNeighbours(x, y);
6273     }
6274     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6275              element == EL_INVISIBLE_WALL_ACTIVE ||
6276              element == EL_INVISIBLE_SAND_ACTIVE)
6277     {
6278       if (game.lenses_time_left == 0)
6279         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6280
6281       DrawLevelField(x, y);
6282
6283       /* re-crumble neighbour fields, if needed */
6284       if (element == EL_INVISIBLE_SAND)
6285         DrawLevelFieldCrumbledSandNeighbours(x, y);
6286     }
6287   }
6288 }
6289
6290 static void RedrawAllInvisibleElementsForMagnifier()
6291 {
6292   int x, y;
6293
6294   SCAN_PLAYFIELD(x, y)
6295   {
6296     int element = Feld[x][y];
6297
6298     if (element == EL_EMC_FAKE_GRASS &&
6299         game.magnify_time_left > 0)
6300     {
6301       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6302       DrawLevelField(x, y);
6303     }
6304     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6305              game.magnify_time_left == 0)
6306     {
6307       Feld[x][y] = EL_EMC_FAKE_GRASS;
6308       DrawLevelField(x, y);
6309     }
6310     else if (IS_GATE_GRAY(element) &&
6311              game.magnify_time_left > 0)
6312     {
6313       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6314                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6315                     IS_EM_GATE_GRAY(element) ?
6316                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6317                     IS_EMC_GATE_GRAY(element) ?
6318                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6319                     element);
6320       DrawLevelField(x, y);
6321     }
6322     else if (IS_GATE_GRAY_ACTIVE(element) &&
6323              game.magnify_time_left == 0)
6324     {
6325       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6326                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6327                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6328                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6329                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6330                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6331                     element);
6332       DrawLevelField(x, y);
6333     }
6334   }
6335 }
6336
6337 static void ToggleLightSwitch(int x, int y)
6338 {
6339   int element = Feld[x][y];
6340
6341   game.light_time_left =
6342     (element == EL_LIGHT_SWITCH ?
6343      level.time_light * FRAMES_PER_SECOND : 0);
6344
6345   RedrawAllLightSwitchesAndInvisibleElements();
6346 }
6347
6348 static void ActivateTimegateSwitch(int x, int y)
6349 {
6350   int xx, yy;
6351
6352   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6353
6354   SCAN_PLAYFIELD(xx, yy)
6355   {
6356     int element = Feld[xx][yy];
6357
6358     if (element == EL_TIMEGATE_CLOSED ||
6359         element == EL_TIMEGATE_CLOSING)
6360     {
6361       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6362       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6363     }
6364
6365     /*
6366     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6367     {
6368       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6369       DrawLevelField(xx, yy);
6370     }
6371     */
6372
6373   }
6374
6375 #if 1
6376   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6377                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6378 #else
6379   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6380 #endif
6381 }
6382
6383 void Impact(int x, int y)
6384 {
6385   boolean last_line = (y == lev_fieldy - 1);
6386   boolean object_hit = FALSE;
6387   boolean impact = (last_line || object_hit);
6388   int element = Feld[x][y];
6389   int smashed = EL_STEELWALL;
6390
6391   if (!last_line)       /* check if element below was hit */
6392   {
6393     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6394       return;
6395
6396     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6397                                          MovDir[x][y + 1] != MV_DOWN ||
6398                                          MovPos[x][y + 1] <= TILEY / 2));
6399
6400     /* do not smash moving elements that left the smashed field in time */
6401     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6402         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6403       object_hit = FALSE;
6404
6405 #if USE_QUICKSAND_IMPACT_BUGFIX
6406     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6407     {
6408       RemoveMovingField(x, y + 1);
6409       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6410       Feld[x][y + 2] = EL_ROCK;
6411       DrawLevelField(x, y + 2);
6412
6413       object_hit = TRUE;
6414     }
6415
6416     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6417     {
6418       RemoveMovingField(x, y + 1);
6419       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6420       Feld[x][y + 2] = EL_ROCK;
6421       DrawLevelField(x, y + 2);
6422
6423       object_hit = TRUE;
6424     }
6425 #endif
6426
6427     if (object_hit)
6428       smashed = MovingOrBlocked2Element(x, y + 1);
6429
6430     impact = (last_line || object_hit);
6431   }
6432
6433   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6434   {
6435     SplashAcid(x, y + 1);
6436     return;
6437   }
6438
6439   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6440   /* only reset graphic animation if graphic really changes after impact */
6441   if (impact &&
6442       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6443   {
6444     ResetGfxAnimation(x, y);
6445     DrawLevelField(x, y);
6446   }
6447
6448   if (impact && CAN_EXPLODE_IMPACT(element))
6449   {
6450     Bang(x, y);
6451     return;
6452   }
6453   else if (impact && element == EL_PEARL &&
6454            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6455   {
6456     ResetGfxAnimation(x, y);
6457
6458     Feld[x][y] = EL_PEARL_BREAKING;
6459     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6460     return;
6461   }
6462   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6463   {
6464     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6465
6466     return;
6467   }
6468
6469   if (impact && element == EL_AMOEBA_DROP)
6470   {
6471     if (object_hit && IS_PLAYER(x, y + 1))
6472       KillPlayerUnlessEnemyProtected(x, y + 1);
6473     else if (object_hit && smashed == EL_PENGUIN)
6474       Bang(x, y + 1);
6475     else
6476     {
6477       Feld[x][y] = EL_AMOEBA_GROWING;
6478       Store[x][y] = EL_AMOEBA_WET;
6479
6480       ResetRandomAnimationValue(x, y);
6481     }
6482     return;
6483   }
6484
6485   if (object_hit)               /* check which object was hit */
6486   {
6487     if ((CAN_PASS_MAGIC_WALL(element) && 
6488          (smashed == EL_MAGIC_WALL ||
6489           smashed == EL_BD_MAGIC_WALL)) ||
6490         (CAN_PASS_DC_MAGIC_WALL(element) &&
6491          smashed == EL_DC_MAGIC_WALL))
6492     {
6493       int xx, yy;
6494       int activated_magic_wall =
6495         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6496          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6497          EL_DC_MAGIC_WALL_ACTIVE);
6498
6499       /* activate magic wall / mill */
6500       SCAN_PLAYFIELD(xx, yy)
6501       {
6502         if (Feld[xx][yy] == smashed)
6503           Feld[xx][yy] = activated_magic_wall;
6504       }
6505
6506       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6507       game.magic_wall_active = TRUE;
6508
6509       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6510                             SND_MAGIC_WALL_ACTIVATING :
6511                             smashed == EL_BD_MAGIC_WALL ?
6512                             SND_BD_MAGIC_WALL_ACTIVATING :
6513                             SND_DC_MAGIC_WALL_ACTIVATING));
6514     }
6515
6516     if (IS_PLAYER(x, y + 1))
6517     {
6518       if (CAN_SMASH_PLAYER(element))
6519       {
6520         KillPlayerUnlessEnemyProtected(x, y + 1);
6521         return;
6522       }
6523     }
6524     else if (smashed == EL_PENGUIN)
6525     {
6526       if (CAN_SMASH_PLAYER(element))
6527       {
6528         Bang(x, y + 1);
6529         return;
6530       }
6531     }
6532     else if (element == EL_BD_DIAMOND)
6533     {
6534       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6535       {
6536         Bang(x, y + 1);
6537         return;
6538       }
6539     }
6540     else if (((element == EL_SP_INFOTRON ||
6541                element == EL_SP_ZONK) &&
6542               (smashed == EL_SP_SNIKSNAK ||
6543                smashed == EL_SP_ELECTRON ||
6544                smashed == EL_SP_DISK_ORANGE)) ||
6545              (element == EL_SP_INFOTRON &&
6546               smashed == EL_SP_DISK_YELLOW))
6547     {
6548       Bang(x, y + 1);
6549       return;
6550     }
6551     else if (CAN_SMASH_EVERYTHING(element))
6552     {
6553       if (IS_CLASSIC_ENEMY(smashed) ||
6554           CAN_EXPLODE_SMASHED(smashed))
6555       {
6556         Bang(x, y + 1);
6557         return;
6558       }
6559       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6560       {
6561         if (smashed == EL_LAMP ||
6562             smashed == EL_LAMP_ACTIVE)
6563         {
6564           Bang(x, y + 1);
6565           return;
6566         }
6567         else if (smashed == EL_NUT)
6568         {
6569           Feld[x][y + 1] = EL_NUT_BREAKING;
6570           PlayLevelSound(x, y, SND_NUT_BREAKING);
6571           RaiseScoreElement(EL_NUT);
6572           return;
6573         }
6574         else if (smashed == EL_PEARL)
6575         {
6576           ResetGfxAnimation(x, y);
6577
6578           Feld[x][y + 1] = EL_PEARL_BREAKING;
6579           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6580           return;
6581         }
6582         else if (smashed == EL_DIAMOND)
6583         {
6584           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6585           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6586           return;
6587         }
6588         else if (IS_BELT_SWITCH(smashed))
6589         {
6590           ToggleBeltSwitch(x, y + 1);
6591         }
6592         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6593                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6594                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6595                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6596         {
6597           ToggleSwitchgateSwitch(x, y + 1);
6598         }
6599         else if (smashed == EL_LIGHT_SWITCH ||
6600                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6601         {
6602           ToggleLightSwitch(x, y + 1);
6603         }
6604         else
6605         {
6606 #if 0
6607           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6608 #endif
6609
6610           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6611
6612           CheckElementChangeBySide(x, y + 1, smashed, element,
6613                                    CE_SWITCHED, CH_SIDE_TOP);
6614           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6615                                             CH_SIDE_TOP);
6616         }
6617       }
6618       else
6619       {
6620         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6621       }
6622     }
6623   }
6624
6625   /* play sound of magic wall / mill */
6626   if (!last_line &&
6627       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6628        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6629        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6630   {
6631     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6632       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6633     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6634       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6635     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6636       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6637
6638     return;
6639   }
6640
6641   /* play sound of object that hits the ground */
6642   if (last_line || object_hit)
6643     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6644 }
6645
6646 inline static void TurnRoundExt(int x, int y)
6647 {
6648   static struct
6649   {
6650     int dx, dy;
6651   } move_xy[] =
6652   {
6653     {  0,  0 },
6654     { -1,  0 },
6655     { +1,  0 },
6656     {  0,  0 },
6657     {  0, -1 },
6658     {  0,  0 }, { 0, 0 }, { 0, 0 },
6659     {  0, +1 }
6660   };
6661   static struct
6662   {
6663     int left, right, back;
6664   } turn[] =
6665   {
6666     { 0,        0,              0        },
6667     { MV_DOWN,  MV_UP,          MV_RIGHT },
6668     { MV_UP,    MV_DOWN,        MV_LEFT  },
6669     { 0,        0,              0        },
6670     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6671     { 0,        0,              0        },
6672     { 0,        0,              0        },
6673     { 0,        0,              0        },
6674     { MV_RIGHT, MV_LEFT,        MV_UP    }
6675   };
6676
6677   int element = Feld[x][y];
6678   int move_pattern = element_info[element].move_pattern;
6679
6680   int old_move_dir = MovDir[x][y];
6681   int left_dir  = turn[old_move_dir].left;
6682   int right_dir = turn[old_move_dir].right;
6683   int back_dir  = turn[old_move_dir].back;
6684
6685   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6686   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6687   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6688   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6689
6690   int left_x  = x + left_dx,  left_y  = y + left_dy;
6691   int right_x = x + right_dx, right_y = y + right_dy;
6692   int move_x  = x + move_dx,  move_y  = y + move_dy;
6693
6694   int xx, yy;
6695
6696   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6697   {
6698     TestIfBadThingTouchesOtherBadThing(x, y);
6699
6700     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6701       MovDir[x][y] = right_dir;
6702     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6703       MovDir[x][y] = left_dir;
6704
6705     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6706       MovDelay[x][y] = 9;
6707     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6708       MovDelay[x][y] = 1;
6709   }
6710   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6711   {
6712     TestIfBadThingTouchesOtherBadThing(x, y);
6713
6714     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6715       MovDir[x][y] = left_dir;
6716     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6717       MovDir[x][y] = right_dir;
6718
6719     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6720       MovDelay[x][y] = 9;
6721     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6722       MovDelay[x][y] = 1;
6723   }
6724   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6725   {
6726     TestIfBadThingTouchesOtherBadThing(x, y);
6727
6728     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6729       MovDir[x][y] = left_dir;
6730     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6731       MovDir[x][y] = right_dir;
6732
6733     if (MovDir[x][y] != old_move_dir)
6734       MovDelay[x][y] = 9;
6735   }
6736   else if (element == EL_YAMYAM)
6737   {
6738     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6739     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6740
6741     if (can_turn_left && can_turn_right)
6742       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6743     else if (can_turn_left)
6744       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6745     else if (can_turn_right)
6746       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6747     else
6748       MovDir[x][y] = back_dir;
6749
6750     MovDelay[x][y] = 16 + 16 * RND(3);
6751   }
6752   else if (element == EL_DARK_YAMYAM)
6753   {
6754     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6755                                                          left_x, left_y);
6756     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6757                                                          right_x, right_y);
6758
6759     if (can_turn_left && can_turn_right)
6760       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6761     else if (can_turn_left)
6762       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6763     else if (can_turn_right)
6764       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6765     else
6766       MovDir[x][y] = back_dir;
6767
6768     MovDelay[x][y] = 16 + 16 * RND(3);
6769   }
6770   else if (element == EL_PACMAN)
6771   {
6772     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6773     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6774
6775     if (can_turn_left && can_turn_right)
6776       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6777     else if (can_turn_left)
6778       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6779     else if (can_turn_right)
6780       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6781     else
6782       MovDir[x][y] = back_dir;
6783
6784     MovDelay[x][y] = 6 + RND(40);
6785   }
6786   else if (element == EL_PIG)
6787   {
6788     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6789     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6790     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6791     boolean should_turn_left, should_turn_right, should_move_on;
6792     int rnd_value = 24;
6793     int rnd = RND(rnd_value);
6794
6795     should_turn_left = (can_turn_left &&
6796                         (!can_move_on ||
6797                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6798                                                    y + back_dy + left_dy)));
6799     should_turn_right = (can_turn_right &&
6800                          (!can_move_on ||
6801                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6802                                                     y + back_dy + right_dy)));
6803     should_move_on = (can_move_on &&
6804                       (!can_turn_left ||
6805                        !can_turn_right ||
6806                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6807                                                  y + move_dy + left_dy) ||
6808                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6809                                                  y + move_dy + right_dy)));
6810
6811     if (should_turn_left || should_turn_right || should_move_on)
6812     {
6813       if (should_turn_left && should_turn_right && should_move_on)
6814         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6815                         rnd < 2 * rnd_value / 3 ? right_dir :
6816                         old_move_dir);
6817       else if (should_turn_left && should_turn_right)
6818         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6819       else if (should_turn_left && should_move_on)
6820         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6821       else if (should_turn_right && should_move_on)
6822         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6823       else if (should_turn_left)
6824         MovDir[x][y] = left_dir;
6825       else if (should_turn_right)
6826         MovDir[x][y] = right_dir;
6827       else if (should_move_on)
6828         MovDir[x][y] = old_move_dir;
6829     }
6830     else if (can_move_on && rnd > rnd_value / 8)
6831       MovDir[x][y] = old_move_dir;
6832     else if (can_turn_left && can_turn_right)
6833       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6834     else if (can_turn_left && rnd > rnd_value / 8)
6835       MovDir[x][y] = left_dir;
6836     else if (can_turn_right && rnd > rnd_value/8)
6837       MovDir[x][y] = right_dir;
6838     else
6839       MovDir[x][y] = back_dir;
6840
6841     xx = x + move_xy[MovDir[x][y]].dx;
6842     yy = y + move_xy[MovDir[x][y]].dy;
6843
6844     if (!IN_LEV_FIELD(xx, yy) ||
6845         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6846       MovDir[x][y] = old_move_dir;
6847
6848     MovDelay[x][y] = 0;
6849   }
6850   else if (element == EL_DRAGON)
6851   {
6852     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6853     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6854     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6855     int rnd_value = 24;
6856     int rnd = RND(rnd_value);
6857
6858     if (can_move_on && rnd > rnd_value / 8)
6859       MovDir[x][y] = old_move_dir;
6860     else if (can_turn_left && can_turn_right)
6861       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6862     else if (can_turn_left && rnd > rnd_value / 8)
6863       MovDir[x][y] = left_dir;
6864     else if (can_turn_right && rnd > rnd_value / 8)
6865       MovDir[x][y] = right_dir;
6866     else
6867       MovDir[x][y] = back_dir;
6868
6869     xx = x + move_xy[MovDir[x][y]].dx;
6870     yy = y + move_xy[MovDir[x][y]].dy;
6871
6872     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6873       MovDir[x][y] = old_move_dir;
6874
6875     MovDelay[x][y] = 0;
6876   }
6877   else if (element == EL_MOLE)
6878   {
6879     boolean can_move_on =
6880       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6881                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6882                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6883     if (!can_move_on)
6884     {
6885       boolean can_turn_left =
6886         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6887                               IS_AMOEBOID(Feld[left_x][left_y])));
6888
6889       boolean can_turn_right =
6890         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6891                               IS_AMOEBOID(Feld[right_x][right_y])));
6892
6893       if (can_turn_left && can_turn_right)
6894         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6895       else if (can_turn_left)
6896         MovDir[x][y] = left_dir;
6897       else
6898         MovDir[x][y] = right_dir;
6899     }
6900
6901     if (MovDir[x][y] != old_move_dir)
6902       MovDelay[x][y] = 9;
6903   }
6904   else if (element == EL_BALLOON)
6905   {
6906     MovDir[x][y] = game.wind_direction;
6907     MovDelay[x][y] = 0;
6908   }
6909   else if (element == EL_SPRING)
6910   {
6911 #if USE_NEW_SPRING_BUMPER
6912     if (MovDir[x][y] & MV_HORIZONTAL)
6913     {
6914       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6915           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6916       {
6917         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6918         ResetGfxAnimation(move_x, move_y);
6919         DrawLevelField(move_x, move_y);
6920
6921         MovDir[x][y] = back_dir;
6922       }
6923       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6924                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6925         MovDir[x][y] = MV_NONE;
6926     }
6927 #else
6928     if (MovDir[x][y] & MV_HORIZONTAL &&
6929         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6930          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6931       MovDir[x][y] = MV_NONE;
6932 #endif
6933
6934     MovDelay[x][y] = 0;
6935   }
6936   else if (element == EL_ROBOT ||
6937            element == EL_SATELLITE ||
6938            element == EL_PENGUIN ||
6939            element == EL_EMC_ANDROID)
6940   {
6941     int attr_x = -1, attr_y = -1;
6942
6943     if (AllPlayersGone)
6944     {
6945       attr_x = ExitX;
6946       attr_y = ExitY;
6947     }
6948     else
6949     {
6950       int i;
6951
6952       for (i = 0; i < MAX_PLAYERS; i++)
6953       {
6954         struct PlayerInfo *player = &stored_player[i];
6955         int jx = player->jx, jy = player->jy;
6956
6957         if (!player->active)
6958           continue;
6959
6960         if (attr_x == -1 ||
6961             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6962         {
6963           attr_x = jx;
6964           attr_y = jy;
6965         }
6966       }
6967     }
6968
6969     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6970         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6971          game.engine_version < VERSION_IDENT(3,1,0,0)))
6972     {
6973       attr_x = ZX;
6974       attr_y = ZY;
6975     }
6976
6977     if (element == EL_PENGUIN)
6978     {
6979       int i;
6980       static int xy[4][2] =
6981       {
6982         { 0, -1 },
6983         { -1, 0 },
6984         { +1, 0 },
6985         { 0, +1 }
6986       };
6987
6988       for (i = 0; i < NUM_DIRECTIONS; i++)
6989       {
6990         int ex = x + xy[i][0];
6991         int ey = y + xy[i][1];
6992
6993         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6994                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6995                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6996                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6997         {
6998           attr_x = ex;
6999           attr_y = ey;
7000           break;
7001         }
7002       }
7003     }
7004
7005     MovDir[x][y] = MV_NONE;
7006     if (attr_x < x)
7007       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7008     else if (attr_x > x)
7009       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7010     if (attr_y < y)
7011       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7012     else if (attr_y > y)
7013       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7014
7015     if (element == EL_ROBOT)
7016     {
7017       int newx, newy;
7018
7019       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7020         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7021       Moving2Blocked(x, y, &newx, &newy);
7022
7023       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7024         MovDelay[x][y] = 8 + 8 * !RND(3);
7025       else
7026         MovDelay[x][y] = 16;
7027     }
7028     else if (element == EL_PENGUIN)
7029     {
7030       int newx, newy;
7031
7032       MovDelay[x][y] = 1;
7033
7034       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7035       {
7036         boolean first_horiz = RND(2);
7037         int new_move_dir = MovDir[x][y];
7038
7039         MovDir[x][y] =
7040           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7041         Moving2Blocked(x, y, &newx, &newy);
7042
7043         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7044           return;
7045
7046         MovDir[x][y] =
7047           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7048         Moving2Blocked(x, y, &newx, &newy);
7049
7050         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7051           return;
7052
7053         MovDir[x][y] = old_move_dir;
7054         return;
7055       }
7056     }
7057     else if (element == EL_SATELLITE)
7058     {
7059       int newx, newy;
7060
7061       MovDelay[x][y] = 1;
7062
7063       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7064       {
7065         boolean first_horiz = RND(2);
7066         int new_move_dir = MovDir[x][y];
7067
7068         MovDir[x][y] =
7069           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7070         Moving2Blocked(x, y, &newx, &newy);
7071
7072         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7073           return;
7074
7075         MovDir[x][y] =
7076           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7077         Moving2Blocked(x, y, &newx, &newy);
7078
7079         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7080           return;
7081
7082         MovDir[x][y] = old_move_dir;
7083         return;
7084       }
7085     }
7086     else if (element == EL_EMC_ANDROID)
7087     {
7088       static int check_pos[16] =
7089       {
7090         -1,             /*  0 => (invalid)          */
7091         7,              /*  1 => MV_LEFT            */
7092         3,              /*  2 => MV_RIGHT           */
7093         -1,             /*  3 => (invalid)          */
7094         1,              /*  4 =>            MV_UP   */
7095         0,              /*  5 => MV_LEFT  | MV_UP   */
7096         2,              /*  6 => MV_RIGHT | MV_UP   */
7097         -1,             /*  7 => (invalid)          */
7098         5,              /*  8 =>            MV_DOWN */
7099         6,              /*  9 => MV_LEFT  | MV_DOWN */
7100         4,              /* 10 => MV_RIGHT | MV_DOWN */
7101         -1,             /* 11 => (invalid)          */
7102         -1,             /* 12 => (invalid)          */
7103         -1,             /* 13 => (invalid)          */
7104         -1,             /* 14 => (invalid)          */
7105         -1,             /* 15 => (invalid)          */
7106       };
7107       static struct
7108       {
7109         int dx, dy;
7110         int dir;
7111       } check_xy[8] =
7112       {
7113         { -1, -1,       MV_LEFT  | MV_UP   },
7114         {  0, -1,                  MV_UP   },
7115         { +1, -1,       MV_RIGHT | MV_UP   },
7116         { +1,  0,       MV_RIGHT           },
7117         { +1, +1,       MV_RIGHT | MV_DOWN },
7118         {  0, +1,                  MV_DOWN },
7119         { -1, +1,       MV_LEFT  | MV_DOWN },
7120         { -1,  0,       MV_LEFT            },
7121       };
7122       int start_pos, check_order;
7123       boolean can_clone = FALSE;
7124       int i;
7125
7126       /* check if there is any free field around current position */
7127       for (i = 0; i < 8; i++)
7128       {
7129         int newx = x + check_xy[i].dx;
7130         int newy = y + check_xy[i].dy;
7131
7132         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7133         {
7134           can_clone = TRUE;
7135
7136           break;
7137         }
7138       }
7139
7140       if (can_clone)            /* randomly find an element to clone */
7141       {
7142         can_clone = FALSE;
7143
7144         start_pos = check_pos[RND(8)];
7145         check_order = (RND(2) ? -1 : +1);
7146
7147         for (i = 0; i < 8; i++)
7148         {
7149           int pos_raw = start_pos + i * check_order;
7150           int pos = (pos_raw + 8) % 8;
7151           int newx = x + check_xy[pos].dx;
7152           int newy = y + check_xy[pos].dy;
7153
7154           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7155           {
7156             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7157             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7158
7159             Store[x][y] = Feld[newx][newy];
7160
7161             can_clone = TRUE;
7162
7163             break;
7164           }
7165         }
7166       }
7167
7168       if (can_clone)            /* randomly find a direction to move */
7169       {
7170         can_clone = FALSE;
7171
7172         start_pos = check_pos[RND(8)];
7173         check_order = (RND(2) ? -1 : +1);
7174
7175         for (i = 0; i < 8; i++)
7176         {
7177           int pos_raw = start_pos + i * check_order;
7178           int pos = (pos_raw + 8) % 8;
7179           int newx = x + check_xy[pos].dx;
7180           int newy = y + check_xy[pos].dy;
7181           int new_move_dir = check_xy[pos].dir;
7182
7183           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7184           {
7185             MovDir[x][y] = new_move_dir;
7186             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7187
7188             can_clone = TRUE;
7189
7190             break;
7191           }
7192         }
7193       }
7194
7195       if (can_clone)            /* cloning and moving successful */
7196         return;
7197
7198       /* cannot clone -- try to move towards player */
7199
7200       start_pos = check_pos[MovDir[x][y] & 0x0f];
7201       check_order = (RND(2) ? -1 : +1);
7202
7203       for (i = 0; i < 3; i++)
7204       {
7205         /* first check start_pos, then previous/next or (next/previous) pos */
7206         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7207         int pos = (pos_raw + 8) % 8;
7208         int newx = x + check_xy[pos].dx;
7209         int newy = y + check_xy[pos].dy;
7210         int new_move_dir = check_xy[pos].dir;
7211
7212         if (IS_PLAYER(newx, newy))
7213           break;
7214
7215         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7216         {
7217           MovDir[x][y] = new_move_dir;
7218           MovDelay[x][y] = level.android_move_time * 8 + 1;
7219
7220           break;
7221         }
7222       }
7223     }
7224   }
7225   else if (move_pattern == MV_TURNING_LEFT ||
7226            move_pattern == MV_TURNING_RIGHT ||
7227            move_pattern == MV_TURNING_LEFT_RIGHT ||
7228            move_pattern == MV_TURNING_RIGHT_LEFT ||
7229            move_pattern == MV_TURNING_RANDOM ||
7230            move_pattern == MV_ALL_DIRECTIONS)
7231   {
7232     boolean can_turn_left =
7233       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7234     boolean can_turn_right =
7235       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7236
7237     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7238       return;
7239
7240     if (move_pattern == MV_TURNING_LEFT)
7241       MovDir[x][y] = left_dir;
7242     else if (move_pattern == MV_TURNING_RIGHT)
7243       MovDir[x][y] = right_dir;
7244     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7245       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7246     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7247       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7248     else if (move_pattern == MV_TURNING_RANDOM)
7249       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7250                       can_turn_right && !can_turn_left ? right_dir :
7251                       RND(2) ? left_dir : right_dir);
7252     else if (can_turn_left && can_turn_right)
7253       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7254     else if (can_turn_left)
7255       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7256     else if (can_turn_right)
7257       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7258     else
7259       MovDir[x][y] = back_dir;
7260
7261     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7262   }
7263   else if (move_pattern == MV_HORIZONTAL ||
7264            move_pattern == MV_VERTICAL)
7265   {
7266     if (move_pattern & old_move_dir)
7267       MovDir[x][y] = back_dir;
7268     else if (move_pattern == MV_HORIZONTAL)
7269       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7270     else if (move_pattern == MV_VERTICAL)
7271       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7272
7273     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7274   }
7275   else if (move_pattern & MV_ANY_DIRECTION)
7276   {
7277     MovDir[x][y] = move_pattern;
7278     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7279   }
7280   else if (move_pattern & MV_WIND_DIRECTION)
7281   {
7282     MovDir[x][y] = game.wind_direction;
7283     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7284   }
7285   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7286   {
7287     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7288       MovDir[x][y] = left_dir;
7289     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7290       MovDir[x][y] = right_dir;
7291
7292     if (MovDir[x][y] != old_move_dir)
7293       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7294   }
7295   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7296   {
7297     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7298       MovDir[x][y] = right_dir;
7299     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7300       MovDir[x][y] = left_dir;
7301
7302     if (MovDir[x][y] != old_move_dir)
7303       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7304   }
7305   else if (move_pattern == MV_TOWARDS_PLAYER ||
7306            move_pattern == MV_AWAY_FROM_PLAYER)
7307   {
7308     int attr_x = -1, attr_y = -1;
7309     int newx, newy;
7310     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7311
7312     if (AllPlayersGone)
7313     {
7314       attr_x = ExitX;
7315       attr_y = ExitY;
7316     }
7317     else
7318     {
7319       int i;
7320
7321       for (i = 0; i < MAX_PLAYERS; i++)
7322       {
7323         struct PlayerInfo *player = &stored_player[i];
7324         int jx = player->jx, jy = player->jy;
7325
7326         if (!player->active)
7327           continue;
7328
7329         if (attr_x == -1 ||
7330             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7331         {
7332           attr_x = jx;
7333           attr_y = jy;
7334         }
7335       }
7336     }
7337
7338     MovDir[x][y] = MV_NONE;
7339     if (attr_x < x)
7340       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7341     else if (attr_x > x)
7342       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7343     if (attr_y < y)
7344       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7345     else if (attr_y > y)
7346       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7347
7348     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7349
7350     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7351     {
7352       boolean first_horiz = RND(2);
7353       int new_move_dir = MovDir[x][y];
7354
7355       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7356       {
7357         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7358         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7359
7360         return;
7361       }
7362
7363       MovDir[x][y] =
7364         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7365       Moving2Blocked(x, y, &newx, &newy);
7366
7367       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7368         return;
7369
7370       MovDir[x][y] =
7371         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7372       Moving2Blocked(x, y, &newx, &newy);
7373
7374       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7375         return;
7376
7377       MovDir[x][y] = old_move_dir;
7378     }
7379   }
7380   else if (move_pattern == MV_WHEN_PUSHED ||
7381            move_pattern == MV_WHEN_DROPPED)
7382   {
7383     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7384       MovDir[x][y] = MV_NONE;
7385
7386     MovDelay[x][y] = 0;
7387   }
7388   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7389   {
7390     static int test_xy[7][2] =
7391     {
7392       { 0, -1 },
7393       { -1, 0 },
7394       { +1, 0 },
7395       { 0, +1 },
7396       { 0, -1 },
7397       { -1, 0 },
7398       { +1, 0 },
7399     };
7400     static int test_dir[7] =
7401     {
7402       MV_UP,
7403       MV_LEFT,
7404       MV_RIGHT,
7405       MV_DOWN,
7406       MV_UP,
7407       MV_LEFT,
7408       MV_RIGHT,
7409     };
7410     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7411     int move_preference = -1000000;     /* start with very low preference */
7412     int new_move_dir = MV_NONE;
7413     int start_test = RND(4);
7414     int i;
7415
7416     for (i = 0; i < NUM_DIRECTIONS; i++)
7417     {
7418       int move_dir = test_dir[start_test + i];
7419       int move_dir_preference;
7420
7421       xx = x + test_xy[start_test + i][0];
7422       yy = y + test_xy[start_test + i][1];
7423
7424       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7425           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7426       {
7427         new_move_dir = move_dir;
7428
7429         break;
7430       }
7431
7432       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7433         continue;
7434
7435       move_dir_preference = -1 * RunnerVisit[xx][yy];
7436       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7437         move_dir_preference = PlayerVisit[xx][yy];
7438
7439       if (move_dir_preference > move_preference)
7440       {
7441         /* prefer field that has not been visited for the longest time */
7442         move_preference = move_dir_preference;
7443         new_move_dir = move_dir;
7444       }
7445       else if (move_dir_preference == move_preference &&
7446                move_dir == old_move_dir)
7447       {
7448         /* prefer last direction when all directions are preferred equally */
7449         move_preference = move_dir_preference;
7450         new_move_dir = move_dir;
7451       }
7452     }
7453
7454     MovDir[x][y] = new_move_dir;
7455     if (old_move_dir != new_move_dir)
7456       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7457   }
7458 }
7459
7460 static void TurnRound(int x, int y)
7461 {
7462   int direction = MovDir[x][y];
7463
7464   TurnRoundExt(x, y);
7465
7466   GfxDir[x][y] = MovDir[x][y];
7467
7468   if (direction != MovDir[x][y])
7469     GfxFrame[x][y] = 0;
7470
7471   if (MovDelay[x][y])
7472     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7473
7474   ResetGfxFrame(x, y, FALSE);
7475 }
7476
7477 static boolean JustBeingPushed(int x, int y)
7478 {
7479   int i;
7480
7481   for (i = 0; i < MAX_PLAYERS; i++)
7482   {
7483     struct PlayerInfo *player = &stored_player[i];
7484
7485     if (player->active && player->is_pushing && player->MovPos)
7486     {
7487       int next_jx = player->jx + (player->jx - player->last_jx);
7488       int next_jy = player->jy + (player->jy - player->last_jy);
7489
7490       if (x == next_jx && y == next_jy)
7491         return TRUE;
7492     }
7493   }
7494
7495   return FALSE;
7496 }
7497
7498 void StartMoving(int x, int y)
7499 {
7500   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7501   int element = Feld[x][y];
7502
7503   if (Stop[x][y])
7504     return;
7505
7506   if (MovDelay[x][y] == 0)
7507     GfxAction[x][y] = ACTION_DEFAULT;
7508
7509   if (CAN_FALL(element) && y < lev_fieldy - 1)
7510   {
7511     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7512         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7513       if (JustBeingPushed(x, y))
7514         return;
7515
7516     if (element == EL_QUICKSAND_FULL)
7517     {
7518       if (IS_FREE(x, y + 1))
7519       {
7520         InitMovingField(x, y, MV_DOWN);
7521         started_moving = TRUE;
7522
7523         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7524 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7525         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7526           Store[x][y] = EL_ROCK;
7527 #else
7528         Store[x][y] = EL_ROCK;
7529 #endif
7530
7531         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7532       }
7533       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7534       {
7535         if (!MovDelay[x][y])
7536           MovDelay[x][y] = TILEY + 1;
7537
7538         if (MovDelay[x][y])
7539         {
7540           MovDelay[x][y]--;
7541           if (MovDelay[x][y])
7542             return;
7543         }
7544
7545         Feld[x][y] = EL_QUICKSAND_EMPTY;
7546         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7547         Store[x][y + 1] = Store[x][y];
7548         Store[x][y] = 0;
7549
7550         PlayLevelSoundAction(x, y, ACTION_FILLING);
7551       }
7552     }
7553     else if (element == EL_QUICKSAND_FAST_FULL)
7554     {
7555       if (IS_FREE(x, y + 1))
7556       {
7557         InitMovingField(x, y, MV_DOWN);
7558         started_moving = TRUE;
7559
7560         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7561 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7562         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7563           Store[x][y] = EL_ROCK;
7564 #else
7565         Store[x][y] = EL_ROCK;
7566 #endif
7567
7568         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7569       }
7570       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7571       {
7572         if (!MovDelay[x][y])
7573           MovDelay[x][y] = TILEY + 1;
7574
7575         if (MovDelay[x][y])
7576         {
7577           MovDelay[x][y]--;
7578           if (MovDelay[x][y])
7579             return;
7580         }
7581
7582         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7583         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7584         Store[x][y + 1] = Store[x][y];
7585         Store[x][y] = 0;
7586
7587         PlayLevelSoundAction(x, y, ACTION_FILLING);
7588       }
7589     }
7590     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7591              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7592     {
7593       InitMovingField(x, y, MV_DOWN);
7594       started_moving = TRUE;
7595
7596       Feld[x][y] = EL_QUICKSAND_FILLING;
7597       Store[x][y] = element;
7598
7599       PlayLevelSoundAction(x, y, ACTION_FILLING);
7600     }
7601     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7602              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7603     {
7604       InitMovingField(x, y, MV_DOWN);
7605       started_moving = TRUE;
7606
7607       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7608       Store[x][y] = element;
7609
7610       PlayLevelSoundAction(x, y, ACTION_FILLING);
7611     }
7612     else if (element == EL_MAGIC_WALL_FULL)
7613     {
7614       if (IS_FREE(x, y + 1))
7615       {
7616         InitMovingField(x, y, MV_DOWN);
7617         started_moving = TRUE;
7618
7619         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7620         Store[x][y] = EL_CHANGED(Store[x][y]);
7621       }
7622       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7623       {
7624         if (!MovDelay[x][y])
7625           MovDelay[x][y] = TILEY/4 + 1;
7626
7627         if (MovDelay[x][y])
7628         {
7629           MovDelay[x][y]--;
7630           if (MovDelay[x][y])
7631             return;
7632         }
7633
7634         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7635         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7636         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7637         Store[x][y] = 0;
7638       }
7639     }
7640     else if (element == EL_BD_MAGIC_WALL_FULL)
7641     {
7642       if (IS_FREE(x, y + 1))
7643       {
7644         InitMovingField(x, y, MV_DOWN);
7645         started_moving = TRUE;
7646
7647         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7648         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7649       }
7650       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7651       {
7652         if (!MovDelay[x][y])
7653           MovDelay[x][y] = TILEY/4 + 1;
7654
7655         if (MovDelay[x][y])
7656         {
7657           MovDelay[x][y]--;
7658           if (MovDelay[x][y])
7659             return;
7660         }
7661
7662         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7663         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7664         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7665         Store[x][y] = 0;
7666       }
7667     }
7668     else if (element == EL_DC_MAGIC_WALL_FULL)
7669     {
7670       if (IS_FREE(x, y + 1))
7671       {
7672         InitMovingField(x, y, MV_DOWN);
7673         started_moving = TRUE;
7674
7675         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7676         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7677       }
7678       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7679       {
7680         if (!MovDelay[x][y])
7681           MovDelay[x][y] = TILEY/4 + 1;
7682
7683         if (MovDelay[x][y])
7684         {
7685           MovDelay[x][y]--;
7686           if (MovDelay[x][y])
7687             return;
7688         }
7689
7690         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7691         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7692         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7693         Store[x][y] = 0;
7694       }
7695     }
7696     else if ((CAN_PASS_MAGIC_WALL(element) &&
7697               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7698                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7699              (CAN_PASS_DC_MAGIC_WALL(element) &&
7700               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7701
7702     {
7703       InitMovingField(x, y, MV_DOWN);
7704       started_moving = TRUE;
7705
7706       Feld[x][y] =
7707         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7708          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7709          EL_DC_MAGIC_WALL_FILLING);
7710       Store[x][y] = element;
7711     }
7712     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7713     {
7714       SplashAcid(x, y + 1);
7715
7716       InitMovingField(x, y, MV_DOWN);
7717       started_moving = TRUE;
7718
7719       Store[x][y] = EL_ACID;
7720     }
7721     else if (
7722 #if USE_FIX_IMPACT_COLLISION
7723              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7724               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7725 #else
7726              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7727               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7728 #endif
7729              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7730               CAN_FALL(element) && WasJustFalling[x][y] &&
7731               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7732
7733              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7734               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7735               (Feld[x][y + 1] == EL_BLOCKED)))
7736     {
7737       /* this is needed for a special case not covered by calling "Impact()"
7738          from "ContinueMoving()": if an element moves to a tile directly below
7739          another element which was just falling on that tile (which was empty
7740          in the previous frame), the falling element above would just stop
7741          instead of smashing the element below (in previous version, the above
7742          element was just checked for "moving" instead of "falling", resulting
7743          in incorrect smashes caused by horizontal movement of the above
7744          element; also, the case of the player being the element to smash was
7745          simply not covered here... :-/ ) */
7746
7747       CheckCollision[x][y] = 0;
7748       CheckImpact[x][y] = 0;
7749
7750       Impact(x, y);
7751     }
7752     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7753     {
7754       if (MovDir[x][y] == MV_NONE)
7755       {
7756         InitMovingField(x, y, MV_DOWN);
7757         started_moving = TRUE;
7758       }
7759     }
7760     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7761     {
7762       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7763         MovDir[x][y] = MV_DOWN;
7764
7765       InitMovingField(x, y, MV_DOWN);
7766       started_moving = TRUE;
7767     }
7768     else if (element == EL_AMOEBA_DROP)
7769     {
7770       Feld[x][y] = EL_AMOEBA_GROWING;
7771       Store[x][y] = EL_AMOEBA_WET;
7772     }
7773     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7774               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7775              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7776              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7777     {
7778       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7779                                 (IS_FREE(x - 1, y + 1) ||
7780                                  Feld[x - 1][y + 1] == EL_ACID));
7781       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7782                                 (IS_FREE(x + 1, y + 1) ||
7783                                  Feld[x + 1][y + 1] == EL_ACID));
7784       boolean can_fall_any  = (can_fall_left || can_fall_right);
7785       boolean can_fall_both = (can_fall_left && can_fall_right);
7786       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7787
7788 #if USE_NEW_ALL_SLIPPERY
7789       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7790       {
7791         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7792           can_fall_right = FALSE;
7793         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7794           can_fall_left = FALSE;
7795         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7796           can_fall_right = FALSE;
7797         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7798           can_fall_left = FALSE;
7799
7800         can_fall_any  = (can_fall_left || can_fall_right);
7801         can_fall_both = FALSE;
7802       }
7803 #else
7804       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7805       {
7806         if (slippery_type == SLIPPERY_ONLY_LEFT)
7807           can_fall_right = FALSE;
7808         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7809           can_fall_left = FALSE;
7810         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7811           can_fall_right = FALSE;
7812         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7813           can_fall_left = FALSE;
7814
7815         can_fall_any  = (can_fall_left || can_fall_right);
7816         can_fall_both = (can_fall_left && can_fall_right);
7817       }
7818 #endif
7819
7820 #if USE_NEW_ALL_SLIPPERY
7821 #else
7822 #if USE_NEW_SP_SLIPPERY
7823       /* !!! better use the same properties as for custom elements here !!! */
7824       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7825                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7826       {
7827         can_fall_right = FALSE;         /* slip down on left side */
7828         can_fall_both = FALSE;
7829       }
7830 #endif
7831 #endif
7832
7833 #if USE_NEW_ALL_SLIPPERY
7834       if (can_fall_both)
7835       {
7836         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7837           can_fall_right = FALSE;       /* slip down on left side */
7838         else
7839           can_fall_left = !(can_fall_right = RND(2));
7840
7841         can_fall_both = FALSE;
7842       }
7843 #else
7844       if (can_fall_both)
7845       {
7846         if (game.emulation == EMU_BOULDERDASH ||
7847             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7848           can_fall_right = FALSE;       /* slip down on left side */
7849         else
7850           can_fall_left = !(can_fall_right = RND(2));
7851
7852         can_fall_both = FALSE;
7853       }
7854 #endif
7855
7856       if (can_fall_any)
7857       {
7858         /* if not determined otherwise, prefer left side for slipping down */
7859         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7860         started_moving = TRUE;
7861       }
7862     }
7863 #if 0
7864     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7865 #else
7866     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7867 #endif
7868     {
7869       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7870       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7871       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7872       int belt_dir = game.belt_dir[belt_nr];
7873
7874       if ((belt_dir == MV_LEFT  && left_is_free) ||
7875           (belt_dir == MV_RIGHT && right_is_free))
7876       {
7877         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7878
7879         InitMovingField(x, y, belt_dir);
7880         started_moving = TRUE;
7881
7882         Pushed[x][y] = TRUE;
7883         Pushed[nextx][y] = TRUE;
7884
7885         GfxAction[x][y] = ACTION_DEFAULT;
7886       }
7887       else
7888       {
7889         MovDir[x][y] = 0;       /* if element was moving, stop it */
7890       }
7891     }
7892   }
7893
7894   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7895 #if 0
7896   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7897 #else
7898   if (CAN_MOVE(element) && !started_moving)
7899 #endif
7900   {
7901     int move_pattern = element_info[element].move_pattern;
7902     int newx, newy;
7903
7904 #if 0
7905 #if DEBUG
7906     if (MovDir[x][y] == MV_NONE)
7907     {
7908       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7909              x, y, element, element_info[element].token_name);
7910       printf("StartMoving(): This should never happen!\n");
7911     }
7912 #endif
7913 #endif
7914
7915     Moving2Blocked(x, y, &newx, &newy);
7916
7917     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7918       return;
7919
7920     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7921         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7922     {
7923       WasJustMoving[x][y] = 0;
7924       CheckCollision[x][y] = 0;
7925
7926       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7927
7928       if (Feld[x][y] != element)        /* element has changed */
7929         return;
7930     }
7931
7932     if (!MovDelay[x][y])        /* start new movement phase */
7933     {
7934       /* all objects that can change their move direction after each step
7935          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7936
7937       if (element != EL_YAMYAM &&
7938           element != EL_DARK_YAMYAM &&
7939           element != EL_PACMAN &&
7940           !(move_pattern & MV_ANY_DIRECTION) &&
7941           move_pattern != MV_TURNING_LEFT &&
7942           move_pattern != MV_TURNING_RIGHT &&
7943           move_pattern != MV_TURNING_LEFT_RIGHT &&
7944           move_pattern != MV_TURNING_RIGHT_LEFT &&
7945           move_pattern != MV_TURNING_RANDOM)
7946       {
7947         TurnRound(x, y);
7948
7949         if (MovDelay[x][y] && (element == EL_BUG ||
7950                                element == EL_SPACESHIP ||
7951                                element == EL_SP_SNIKSNAK ||
7952                                element == EL_SP_ELECTRON ||
7953                                element == EL_MOLE))
7954           DrawLevelField(x, y);
7955       }
7956     }
7957
7958     if (MovDelay[x][y])         /* wait some time before next movement */
7959     {
7960       MovDelay[x][y]--;
7961
7962       if (element == EL_ROBOT ||
7963           element == EL_YAMYAM ||
7964           element == EL_DARK_YAMYAM)
7965       {
7966         DrawLevelElementAnimationIfNeeded(x, y, element);
7967         PlayLevelSoundAction(x, y, ACTION_WAITING);
7968       }
7969       else if (element == EL_SP_ELECTRON)
7970         DrawLevelElementAnimationIfNeeded(x, y, element);
7971       else if (element == EL_DRAGON)
7972       {
7973         int i;
7974         int dir = MovDir[x][y];
7975         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7976         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7977         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7978                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7979                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7980                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7981         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7982
7983         GfxAction[x][y] = ACTION_ATTACKING;
7984
7985         if (IS_PLAYER(x, y))
7986           DrawPlayerField(x, y);
7987         else
7988           DrawLevelField(x, y);
7989
7990         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7991
7992         for (i = 1; i <= 3; i++)
7993         {
7994           int xx = x + i * dx;
7995           int yy = y + i * dy;
7996           int sx = SCREENX(xx);
7997           int sy = SCREENY(yy);
7998           int flame_graphic = graphic + (i - 1);
7999
8000           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8001             break;
8002
8003           if (MovDelay[x][y])
8004           {
8005             int flamed = MovingOrBlocked2Element(xx, yy);
8006
8007             /* !!! */
8008 #if 0
8009             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8010               Bang(xx, yy);
8011             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8012               RemoveMovingField(xx, yy);
8013             else
8014               RemoveField(xx, yy);
8015 #else
8016             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8017               Bang(xx, yy);
8018             else
8019               RemoveMovingField(xx, yy);
8020 #endif
8021
8022             ChangeDelay[xx][yy] = 0;
8023
8024             Feld[xx][yy] = EL_FLAMES;
8025
8026             if (IN_SCR_FIELD(sx, sy))
8027             {
8028               DrawLevelFieldCrumbledSand(xx, yy);
8029               DrawGraphic(sx, sy, flame_graphic, frame);
8030             }
8031           }
8032           else
8033           {
8034             if (Feld[xx][yy] == EL_FLAMES)
8035               Feld[xx][yy] = EL_EMPTY;
8036             DrawLevelField(xx, yy);
8037           }
8038         }
8039       }
8040
8041       if (MovDelay[x][y])       /* element still has to wait some time */
8042       {
8043         PlayLevelSoundAction(x, y, ACTION_WAITING);
8044
8045         return;
8046       }
8047     }
8048
8049     /* now make next step */
8050
8051     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8052
8053     if (DONT_COLLIDE_WITH(element) &&
8054         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8055         !PLAYER_ENEMY_PROTECTED(newx, newy))
8056     {
8057       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8058
8059       return;
8060     }
8061
8062     else if (CAN_MOVE_INTO_ACID(element) &&
8063              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8064              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8065              (MovDir[x][y] == MV_DOWN ||
8066               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8067     {
8068       SplashAcid(newx, newy);
8069       Store[x][y] = EL_ACID;
8070     }
8071     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8072     {
8073       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8074           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8075           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8076           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8077       {
8078         RemoveField(x, y);
8079         DrawLevelField(x, y);
8080
8081         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8082         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8083           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8084
8085         local_player->friends_still_needed--;
8086         if (!local_player->friends_still_needed &&
8087             !local_player->GameOver && AllPlayersGone)
8088           PlayerWins(local_player);
8089
8090         return;
8091       }
8092       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8093       {
8094         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8095           DrawLevelField(newx, newy);
8096         else
8097           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8098       }
8099       else if (!IS_FREE(newx, newy))
8100       {
8101         GfxAction[x][y] = ACTION_WAITING;
8102
8103         if (IS_PLAYER(x, y))
8104           DrawPlayerField(x, y);
8105         else
8106           DrawLevelField(x, y);
8107
8108         return;
8109       }
8110     }
8111     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8112     {
8113       if (IS_FOOD_PIG(Feld[newx][newy]))
8114       {
8115         if (IS_MOVING(newx, newy))
8116           RemoveMovingField(newx, newy);
8117         else
8118         {
8119           Feld[newx][newy] = EL_EMPTY;
8120           DrawLevelField(newx, newy);
8121         }
8122
8123         PlayLevelSound(x, y, SND_PIG_DIGGING);
8124       }
8125       else if (!IS_FREE(newx, newy))
8126       {
8127         if (IS_PLAYER(x, y))
8128           DrawPlayerField(x, y);
8129         else
8130           DrawLevelField(x, y);
8131
8132         return;
8133       }
8134     }
8135     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8136     {
8137       if (Store[x][y] != EL_EMPTY)
8138       {
8139         boolean can_clone = FALSE;
8140         int xx, yy;
8141
8142         /* check if element to clone is still there */
8143         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8144         {
8145           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8146           {
8147             can_clone = TRUE;
8148
8149             break;
8150           }
8151         }
8152
8153         /* cannot clone or target field not free anymore -- do not clone */
8154         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8155           Store[x][y] = EL_EMPTY;
8156       }
8157
8158       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8159       {
8160         if (IS_MV_DIAGONAL(MovDir[x][y]))
8161         {
8162           int diagonal_move_dir = MovDir[x][y];
8163           int stored = Store[x][y];
8164           int change_delay = 8;
8165           int graphic;
8166
8167           /* android is moving diagonally */
8168
8169           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8170
8171           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8172           GfxElement[x][y] = EL_EMC_ANDROID;
8173           GfxAction[x][y] = ACTION_SHRINKING;
8174           GfxDir[x][y] = diagonal_move_dir;
8175           ChangeDelay[x][y] = change_delay;
8176
8177           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8178                                    GfxDir[x][y]);
8179
8180           DrawLevelGraphicAnimation(x, y, graphic);
8181           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8182
8183           if (Feld[newx][newy] == EL_ACID)
8184           {
8185             SplashAcid(newx, newy);
8186
8187             return;
8188           }
8189
8190           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8191
8192           Store[newx][newy] = EL_EMC_ANDROID;
8193           GfxElement[newx][newy] = EL_EMC_ANDROID;
8194           GfxAction[newx][newy] = ACTION_GROWING;
8195           GfxDir[newx][newy] = diagonal_move_dir;
8196           ChangeDelay[newx][newy] = change_delay;
8197
8198           graphic = el_act_dir2img(GfxElement[newx][newy],
8199                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8200
8201           DrawLevelGraphicAnimation(newx, newy, graphic);
8202           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8203
8204           return;
8205         }
8206         else
8207         {
8208           Feld[newx][newy] = EL_EMPTY;
8209           DrawLevelField(newx, newy);
8210
8211           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8212         }
8213       }
8214       else if (!IS_FREE(newx, newy))
8215       {
8216 #if 0
8217         if (IS_PLAYER(x, y))
8218           DrawPlayerField(x, y);
8219         else
8220           DrawLevelField(x, y);
8221 #endif
8222
8223         return;
8224       }
8225     }
8226     else if (IS_CUSTOM_ELEMENT(element) &&
8227              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8228     {
8229       int new_element = Feld[newx][newy];
8230
8231       if (!IS_FREE(newx, newy))
8232       {
8233         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8234                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8235                       ACTION_BREAKING);
8236
8237         /* no element can dig solid indestructible elements */
8238         if (IS_INDESTRUCTIBLE(new_element) &&
8239             !IS_DIGGABLE(new_element) &&
8240             !IS_COLLECTIBLE(new_element))
8241           return;
8242
8243         if (AmoebaNr[newx][newy] &&
8244             (new_element == EL_AMOEBA_FULL ||
8245              new_element == EL_BD_AMOEBA ||
8246              new_element == EL_AMOEBA_GROWING))
8247         {
8248           AmoebaCnt[AmoebaNr[newx][newy]]--;
8249           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8250         }
8251
8252         if (IS_MOVING(newx, newy))
8253           RemoveMovingField(newx, newy);
8254         else
8255         {
8256           RemoveField(newx, newy);
8257           DrawLevelField(newx, newy);
8258         }
8259
8260         /* if digged element was about to explode, prevent the explosion */
8261         ExplodeField[newx][newy] = EX_TYPE_NONE;
8262
8263         PlayLevelSoundAction(x, y, action);
8264       }
8265
8266       Store[newx][newy] = EL_EMPTY;
8267 #if 1
8268       /* this makes it possible to leave the removed element again */
8269       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8270         Store[newx][newy] = new_element;
8271 #else
8272       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8273       {
8274         int move_leave_element = element_info[element].move_leave_element;
8275
8276         /* this makes it possible to leave the removed element again */
8277         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8278                              new_element : move_leave_element);
8279       }
8280 #endif
8281
8282       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8283       {
8284         RunnerVisit[x][y] = FrameCounter;
8285         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8286       }
8287     }
8288     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8289     {
8290       if (!IS_FREE(newx, newy))
8291       {
8292         if (IS_PLAYER(x, y))
8293           DrawPlayerField(x, y);
8294         else
8295           DrawLevelField(x, y);
8296
8297         return;
8298       }
8299       else
8300       {
8301         boolean wanna_flame = !RND(10);
8302         int dx = newx - x, dy = newy - y;
8303         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8304         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8305         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8306                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8307         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8308                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8309
8310         if ((wanna_flame ||
8311              IS_CLASSIC_ENEMY(element1) ||
8312              IS_CLASSIC_ENEMY(element2)) &&
8313             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8314             element1 != EL_FLAMES && element2 != EL_FLAMES)
8315         {
8316           ResetGfxAnimation(x, y);
8317           GfxAction[x][y] = ACTION_ATTACKING;
8318
8319           if (IS_PLAYER(x, y))
8320             DrawPlayerField(x, y);
8321           else
8322             DrawLevelField(x, y);
8323
8324           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8325
8326           MovDelay[x][y] = 50;
8327
8328           /* !!! */
8329 #if 0
8330           RemoveField(newx, newy);
8331 #endif
8332           Feld[newx][newy] = EL_FLAMES;
8333           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8334           {
8335 #if 0
8336             RemoveField(newx1, newy1);
8337 #endif
8338             Feld[newx1][newy1] = EL_FLAMES;
8339           }
8340           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8341           {
8342 #if 0
8343             RemoveField(newx2, newy2);
8344 #endif
8345             Feld[newx2][newy2] = EL_FLAMES;
8346           }
8347
8348           return;
8349         }
8350       }
8351     }
8352     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8353              Feld[newx][newy] == EL_DIAMOND)
8354     {
8355       if (IS_MOVING(newx, newy))
8356         RemoveMovingField(newx, newy);
8357       else
8358       {
8359         Feld[newx][newy] = EL_EMPTY;
8360         DrawLevelField(newx, newy);
8361       }
8362
8363       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8364     }
8365     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8366              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8367     {
8368       if (AmoebaNr[newx][newy])
8369       {
8370         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8371         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8372             Feld[newx][newy] == EL_BD_AMOEBA)
8373           AmoebaCnt[AmoebaNr[newx][newy]]--;
8374       }
8375
8376 #if 0
8377       /* !!! test !!! */
8378       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8379       {
8380         RemoveMovingField(newx, newy);
8381       }
8382 #else
8383       if (IS_MOVING(newx, newy))
8384       {
8385         RemoveMovingField(newx, newy);
8386       }
8387 #endif
8388       else
8389       {
8390         Feld[newx][newy] = EL_EMPTY;
8391         DrawLevelField(newx, newy);
8392       }
8393
8394       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8395     }
8396     else if ((element == EL_PACMAN || element == EL_MOLE)
8397              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8398     {
8399       if (AmoebaNr[newx][newy])
8400       {
8401         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8402         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8403             Feld[newx][newy] == EL_BD_AMOEBA)
8404           AmoebaCnt[AmoebaNr[newx][newy]]--;
8405       }
8406
8407       if (element == EL_MOLE)
8408       {
8409         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8410         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8411
8412         ResetGfxAnimation(x, y);
8413         GfxAction[x][y] = ACTION_DIGGING;
8414         DrawLevelField(x, y);
8415
8416         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8417
8418         return;                         /* wait for shrinking amoeba */
8419       }
8420       else      /* element == EL_PACMAN */
8421       {
8422         Feld[newx][newy] = EL_EMPTY;
8423         DrawLevelField(newx, newy);
8424         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8425       }
8426     }
8427     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8428              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8429               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8430     {
8431       /* wait for shrinking amoeba to completely disappear */
8432       return;
8433     }
8434     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8435     {
8436       /* object was running against a wall */
8437
8438       TurnRound(x, y);
8439
8440 #if 0
8441       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8442       if (move_pattern & MV_ANY_DIRECTION &&
8443           move_pattern == MovDir[x][y])
8444       {
8445         int blocking_element =
8446           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8447
8448         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8449                                  MovDir[x][y]);
8450
8451         element = Feld[x][y];   /* element might have changed */
8452       }
8453 #endif
8454
8455       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8456         DrawLevelElementAnimation(x, y, element);
8457
8458       if (DONT_TOUCH(element))
8459         TestIfBadThingTouchesPlayer(x, y);
8460
8461       return;
8462     }
8463
8464     InitMovingField(x, y, MovDir[x][y]);
8465
8466     PlayLevelSoundAction(x, y, ACTION_MOVING);
8467   }
8468
8469   if (MovDir[x][y])
8470     ContinueMoving(x, y);
8471 }
8472
8473 void ContinueMoving(int x, int y)
8474 {
8475   int element = Feld[x][y];
8476   struct ElementInfo *ei = &element_info[element];
8477   int direction = MovDir[x][y];
8478   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8479   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8480   int newx = x + dx, newy = y + dy;
8481   int stored = Store[x][y];
8482   int stored_new = Store[newx][newy];
8483   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8484   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8485   boolean last_line = (newy == lev_fieldy - 1);
8486
8487   MovPos[x][y] += getElementMoveStepsize(x, y);
8488
8489   if (pushed_by_player) /* special case: moving object pushed by player */
8490     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8491
8492   if (ABS(MovPos[x][y]) < TILEX)
8493   {
8494 #if 0
8495     int ee = Feld[x][y];
8496     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8497     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8498
8499     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8500            x, y, ABS(MovPos[x][y]),
8501            ee, gg, ff,
8502            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8503 #endif
8504
8505     DrawLevelField(x, y);
8506
8507     return;     /* element is still moving */
8508   }
8509
8510   /* element reached destination field */
8511
8512   Feld[x][y] = EL_EMPTY;
8513   Feld[newx][newy] = element;
8514   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8515
8516   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8517   {
8518     element = Feld[newx][newy] = EL_ACID;
8519   }
8520   else if (element == EL_MOLE)
8521   {
8522     Feld[x][y] = EL_SAND;
8523
8524     DrawLevelFieldCrumbledSandNeighbours(x, y);
8525   }
8526   else if (element == EL_QUICKSAND_FILLING)
8527   {
8528     element = Feld[newx][newy] = get_next_element(element);
8529     Store[newx][newy] = Store[x][y];
8530   }
8531   else if (element == EL_QUICKSAND_EMPTYING)
8532   {
8533     Feld[x][y] = get_next_element(element);
8534     element = Feld[newx][newy] = Store[x][y];
8535   }
8536   else if (element == EL_QUICKSAND_FAST_FILLING)
8537   {
8538     element = Feld[newx][newy] = get_next_element(element);
8539     Store[newx][newy] = Store[x][y];
8540   }
8541   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8542   {
8543     Feld[x][y] = get_next_element(element);
8544     element = Feld[newx][newy] = Store[x][y];
8545   }
8546   else if (element == EL_MAGIC_WALL_FILLING)
8547   {
8548     element = Feld[newx][newy] = get_next_element(element);
8549     if (!game.magic_wall_active)
8550       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8551     Store[newx][newy] = Store[x][y];
8552   }
8553   else if (element == EL_MAGIC_WALL_EMPTYING)
8554   {
8555     Feld[x][y] = get_next_element(element);
8556     if (!game.magic_wall_active)
8557       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8558     element = Feld[newx][newy] = Store[x][y];
8559
8560 #if USE_NEW_CUSTOM_VALUE
8561     InitField(newx, newy, FALSE);
8562 #endif
8563   }
8564   else if (element == EL_BD_MAGIC_WALL_FILLING)
8565   {
8566     element = Feld[newx][newy] = get_next_element(element);
8567     if (!game.magic_wall_active)
8568       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8569     Store[newx][newy] = Store[x][y];
8570   }
8571   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8572   {
8573     Feld[x][y] = get_next_element(element);
8574     if (!game.magic_wall_active)
8575       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8576     element = Feld[newx][newy] = Store[x][y];
8577
8578 #if USE_NEW_CUSTOM_VALUE
8579     InitField(newx, newy, FALSE);
8580 #endif
8581   }
8582   else if (element == EL_DC_MAGIC_WALL_FILLING)
8583   {
8584     element = Feld[newx][newy] = get_next_element(element);
8585     if (!game.magic_wall_active)
8586       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8587     Store[newx][newy] = Store[x][y];
8588   }
8589   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8590   {
8591     Feld[x][y] = get_next_element(element);
8592     if (!game.magic_wall_active)
8593       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8594     element = Feld[newx][newy] = Store[x][y];
8595
8596 #if USE_NEW_CUSTOM_VALUE
8597     InitField(newx, newy, FALSE);
8598 #endif
8599   }
8600   else if (element == EL_AMOEBA_DROPPING)
8601   {
8602     Feld[x][y] = get_next_element(element);
8603     element = Feld[newx][newy] = Store[x][y];
8604   }
8605   else if (element == EL_SOKOBAN_OBJECT)
8606   {
8607     if (Back[x][y])
8608       Feld[x][y] = Back[x][y];
8609
8610     if (Back[newx][newy])
8611       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8612
8613     Back[x][y] = Back[newx][newy] = 0;
8614   }
8615
8616   Store[x][y] = EL_EMPTY;
8617   MovPos[x][y] = 0;
8618   MovDir[x][y] = 0;
8619   MovDelay[x][y] = 0;
8620
8621   MovDelay[newx][newy] = 0;
8622
8623   if (CAN_CHANGE_OR_HAS_ACTION(element))
8624   {
8625     /* copy element change control values to new field */
8626     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8627     ChangePage[newx][newy]  = ChangePage[x][y];
8628     ChangeCount[newx][newy] = ChangeCount[x][y];
8629     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8630   }
8631
8632 #if USE_NEW_CUSTOM_VALUE
8633     CustomValue[newx][newy] = CustomValue[x][y];
8634 #endif
8635
8636   ChangeDelay[x][y] = 0;
8637   ChangePage[x][y] = -1;
8638   ChangeCount[x][y] = 0;
8639   ChangeEvent[x][y] = -1;
8640
8641 #if USE_NEW_CUSTOM_VALUE
8642   CustomValue[x][y] = 0;
8643 #endif
8644
8645   /* copy animation control values to new field */
8646   GfxFrame[newx][newy]  = GfxFrame[x][y];
8647   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8648   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8649   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8650
8651   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8652
8653   /* some elements can leave other elements behind after moving */
8654 #if 1
8655   if (ei->move_leave_element != EL_EMPTY &&
8656       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8657       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8658 #else
8659   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8660       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8661       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8662 #endif
8663   {
8664     int move_leave_element = ei->move_leave_element;
8665
8666 #if 1
8667 #if 1
8668     /* this makes it possible to leave the removed element again */
8669     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8670       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8671 #else
8672     /* this makes it possible to leave the removed element again */
8673     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8674       move_leave_element = stored;
8675 #endif
8676 #else
8677     /* this makes it possible to leave the removed element again */
8678     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8679         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8680       move_leave_element = stored;
8681 #endif
8682
8683     Feld[x][y] = move_leave_element;
8684
8685     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8686       MovDir[x][y] = direction;
8687
8688     InitField(x, y, FALSE);
8689
8690     if (GFX_CRUMBLED(Feld[x][y]))
8691       DrawLevelFieldCrumbledSandNeighbours(x, y);
8692
8693     if (ELEM_IS_PLAYER(move_leave_element))
8694       RelocatePlayer(x, y, move_leave_element);
8695   }
8696
8697   /* do this after checking for left-behind element */
8698   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8699
8700   if (!CAN_MOVE(element) ||
8701       (CAN_FALL(element) && direction == MV_DOWN &&
8702        (element == EL_SPRING ||
8703         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8704         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8705     GfxDir[x][y] = MovDir[newx][newy] = 0;
8706
8707   DrawLevelField(x, y);
8708   DrawLevelField(newx, newy);
8709
8710   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8711
8712   /* prevent pushed element from moving on in pushed direction */
8713   if (pushed_by_player && CAN_MOVE(element) &&
8714       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8715       !(element_info[element].move_pattern & direction))
8716     TurnRound(newx, newy);
8717
8718   /* prevent elements on conveyor belt from moving on in last direction */
8719   if (pushed_by_conveyor && CAN_FALL(element) &&
8720       direction & MV_HORIZONTAL)
8721     MovDir[newx][newy] = 0;
8722
8723   if (!pushed_by_player)
8724   {
8725     int nextx = newx + dx, nexty = newy + dy;
8726     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8727
8728     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8729
8730     if (CAN_FALL(element) && direction == MV_DOWN)
8731       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8732
8733     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8734       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8735
8736 #if USE_FIX_IMPACT_COLLISION
8737     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8738       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8739 #endif
8740   }
8741
8742   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8743   {
8744     TestIfBadThingTouchesPlayer(newx, newy);
8745     TestIfBadThingTouchesFriend(newx, newy);
8746
8747     if (!IS_CUSTOM_ELEMENT(element))
8748       TestIfBadThingTouchesOtherBadThing(newx, newy);
8749   }
8750   else if (element == EL_PENGUIN)
8751     TestIfFriendTouchesBadThing(newx, newy);
8752
8753   /* give the player one last chance (one more frame) to move away */
8754   if (CAN_FALL(element) && direction == MV_DOWN &&
8755       (last_line || (!IS_FREE(x, newy + 1) &&
8756                      (!IS_PLAYER(x, newy + 1) ||
8757                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8758     Impact(x, newy);
8759
8760   if (pushed_by_player && !game.use_change_when_pushing_bug)
8761   {
8762     int push_side = MV_DIR_OPPOSITE(direction);
8763     struct PlayerInfo *player = PLAYERINFO(x, y);
8764
8765     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8766                                player->index_bit, push_side);
8767     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8768                                         player->index_bit, push_side);
8769   }
8770
8771   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8772     MovDelay[newx][newy] = 1;
8773
8774   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8775
8776   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8777
8778 #if 0
8779   if (ChangePage[newx][newy] != -1)             /* delayed change */
8780   {
8781     int page = ChangePage[newx][newy];
8782     struct ElementChangeInfo *change = &ei->change_page[page];
8783
8784     ChangePage[newx][newy] = -1;
8785
8786     if (change->can_change)
8787     {
8788       if (ChangeElement(newx, newy, element, page))
8789       {
8790         if (change->post_change_function)
8791           change->post_change_function(newx, newy);
8792       }
8793     }
8794
8795     if (change->has_action)
8796       ExecuteCustomElementAction(newx, newy, element, page);
8797   }
8798 #endif
8799
8800   TestIfElementHitsCustomElement(newx, newy, direction);
8801   TestIfPlayerTouchesCustomElement(newx, newy);
8802   TestIfElementTouchesCustomElement(newx, newy);
8803
8804   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8805       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8806     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8807                              MV_DIR_OPPOSITE(direction));
8808 }
8809
8810 int AmoebeNachbarNr(int ax, int ay)
8811 {
8812   int i;
8813   int element = Feld[ax][ay];
8814   int group_nr = 0;
8815   static int xy[4][2] =
8816   {
8817     { 0, -1 },
8818     { -1, 0 },
8819     { +1, 0 },
8820     { 0, +1 }
8821   };
8822
8823   for (i = 0; i < NUM_DIRECTIONS; i++)
8824   {
8825     int x = ax + xy[i][0];
8826     int y = ay + xy[i][1];
8827
8828     if (!IN_LEV_FIELD(x, y))
8829       continue;
8830
8831     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8832       group_nr = AmoebaNr[x][y];
8833   }
8834
8835   return group_nr;
8836 }
8837
8838 void AmoebenVereinigen(int ax, int ay)
8839 {
8840   int i, x, y, xx, yy;
8841   int new_group_nr = AmoebaNr[ax][ay];
8842   static int xy[4][2] =
8843   {
8844     { 0, -1 },
8845     { -1, 0 },
8846     { +1, 0 },
8847     { 0, +1 }
8848   };
8849
8850   if (new_group_nr == 0)
8851     return;
8852
8853   for (i = 0; i < NUM_DIRECTIONS; i++)
8854   {
8855     x = ax + xy[i][0];
8856     y = ay + xy[i][1];
8857
8858     if (!IN_LEV_FIELD(x, y))
8859       continue;
8860
8861     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8862          Feld[x][y] == EL_BD_AMOEBA ||
8863          Feld[x][y] == EL_AMOEBA_DEAD) &&
8864         AmoebaNr[x][y] != new_group_nr)
8865     {
8866       int old_group_nr = AmoebaNr[x][y];
8867
8868       if (old_group_nr == 0)
8869         return;
8870
8871       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8872       AmoebaCnt[old_group_nr] = 0;
8873       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8874       AmoebaCnt2[old_group_nr] = 0;
8875
8876       SCAN_PLAYFIELD(xx, yy)
8877       {
8878         if (AmoebaNr[xx][yy] == old_group_nr)
8879           AmoebaNr[xx][yy] = new_group_nr;
8880       }
8881     }
8882   }
8883 }
8884
8885 void AmoebeUmwandeln(int ax, int ay)
8886 {
8887   int i, x, y;
8888
8889   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8890   {
8891     int group_nr = AmoebaNr[ax][ay];
8892
8893 #ifdef DEBUG
8894     if (group_nr == 0)
8895     {
8896       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8897       printf("AmoebeUmwandeln(): This should never happen!\n");
8898       return;
8899     }
8900 #endif
8901
8902     SCAN_PLAYFIELD(x, y)
8903     {
8904       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8905       {
8906         AmoebaNr[x][y] = 0;
8907         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8908       }
8909     }
8910
8911     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8912                             SND_AMOEBA_TURNING_TO_GEM :
8913                             SND_AMOEBA_TURNING_TO_ROCK));
8914     Bang(ax, ay);
8915   }
8916   else
8917   {
8918     static int xy[4][2] =
8919     {
8920       { 0, -1 },
8921       { -1, 0 },
8922       { +1, 0 },
8923       { 0, +1 }
8924     };
8925
8926     for (i = 0; i < NUM_DIRECTIONS; i++)
8927     {
8928       x = ax + xy[i][0];
8929       y = ay + xy[i][1];
8930
8931       if (!IN_LEV_FIELD(x, y))
8932         continue;
8933
8934       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8935       {
8936         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8937                               SND_AMOEBA_TURNING_TO_GEM :
8938                               SND_AMOEBA_TURNING_TO_ROCK));
8939         Bang(x, y);
8940       }
8941     }
8942   }
8943 }
8944
8945 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8946 {
8947   int x, y;
8948   int group_nr = AmoebaNr[ax][ay];
8949   boolean done = FALSE;
8950
8951 #ifdef DEBUG
8952   if (group_nr == 0)
8953   {
8954     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8955     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8956     return;
8957   }
8958 #endif
8959
8960   SCAN_PLAYFIELD(x, y)
8961   {
8962     if (AmoebaNr[x][y] == group_nr &&
8963         (Feld[x][y] == EL_AMOEBA_DEAD ||
8964          Feld[x][y] == EL_BD_AMOEBA ||
8965          Feld[x][y] == EL_AMOEBA_GROWING))
8966     {
8967       AmoebaNr[x][y] = 0;
8968       Feld[x][y] = new_element;
8969       InitField(x, y, FALSE);
8970       DrawLevelField(x, y);
8971       done = TRUE;
8972     }
8973   }
8974
8975   if (done)
8976     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8977                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8978                             SND_BD_AMOEBA_TURNING_TO_GEM));
8979 }
8980
8981 void AmoebeWaechst(int x, int y)
8982 {
8983   static unsigned long sound_delay = 0;
8984   static unsigned long sound_delay_value = 0;
8985
8986   if (!MovDelay[x][y])          /* start new growing cycle */
8987   {
8988     MovDelay[x][y] = 7;
8989
8990     if (DelayReached(&sound_delay, sound_delay_value))
8991     {
8992       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8993       sound_delay_value = 30;
8994     }
8995   }
8996
8997   if (MovDelay[x][y])           /* wait some time before growing bigger */
8998   {
8999     MovDelay[x][y]--;
9000     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9001     {
9002       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9003                                            6 - MovDelay[x][y]);
9004
9005       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9006     }
9007
9008     if (!MovDelay[x][y])
9009     {
9010       Feld[x][y] = Store[x][y];
9011       Store[x][y] = 0;
9012       DrawLevelField(x, y);
9013     }
9014   }
9015 }
9016
9017 void AmoebaDisappearing(int x, int y)
9018 {
9019   static unsigned long sound_delay = 0;
9020   static unsigned long sound_delay_value = 0;
9021
9022   if (!MovDelay[x][y])          /* start new shrinking cycle */
9023   {
9024     MovDelay[x][y] = 7;
9025
9026     if (DelayReached(&sound_delay, sound_delay_value))
9027       sound_delay_value = 30;
9028   }
9029
9030   if (MovDelay[x][y])           /* wait some time before shrinking */
9031   {
9032     MovDelay[x][y]--;
9033     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9034     {
9035       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9036                                            6 - MovDelay[x][y]);
9037
9038       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9039     }
9040
9041     if (!MovDelay[x][y])
9042     {
9043       Feld[x][y] = EL_EMPTY;
9044       DrawLevelField(x, y);
9045
9046       /* don't let mole enter this field in this cycle;
9047          (give priority to objects falling to this field from above) */
9048       Stop[x][y] = TRUE;
9049     }
9050   }
9051 }
9052
9053 void AmoebeAbleger(int ax, int ay)
9054 {
9055   int i;
9056   int element = Feld[ax][ay];
9057   int graphic = el2img(element);
9058   int newax = ax, neway = ay;
9059   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9060   static int xy[4][2] =
9061   {
9062     { 0, -1 },
9063     { -1, 0 },
9064     { +1, 0 },
9065     { 0, +1 }
9066   };
9067
9068   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9069   {
9070     Feld[ax][ay] = EL_AMOEBA_DEAD;
9071     DrawLevelField(ax, ay);
9072     return;
9073   }
9074
9075   if (IS_ANIMATED(graphic))
9076     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9077
9078   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9079     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9080
9081   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9082   {
9083     MovDelay[ax][ay]--;
9084     if (MovDelay[ax][ay])
9085       return;
9086   }
9087
9088   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9089   {
9090     int start = RND(4);
9091     int x = ax + xy[start][0];
9092     int y = ay + xy[start][1];
9093
9094     if (!IN_LEV_FIELD(x, y))
9095       return;
9096
9097     if (IS_FREE(x, y) ||
9098         CAN_GROW_INTO(Feld[x][y]) ||
9099         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9100         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9101     {
9102       newax = x;
9103       neway = y;
9104     }
9105
9106     if (newax == ax && neway == ay)
9107       return;
9108   }
9109   else                          /* normal or "filled" (BD style) amoeba */
9110   {
9111     int start = RND(4);
9112     boolean waiting_for_player = FALSE;
9113
9114     for (i = 0; i < NUM_DIRECTIONS; i++)
9115     {
9116       int j = (start + i) % 4;
9117       int x = ax + xy[j][0];
9118       int y = ay + xy[j][1];
9119
9120       if (!IN_LEV_FIELD(x, y))
9121         continue;
9122
9123       if (IS_FREE(x, y) ||
9124           CAN_GROW_INTO(Feld[x][y]) ||
9125           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9126           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9127       {
9128         newax = x;
9129         neway = y;
9130         break;
9131       }
9132       else if (IS_PLAYER(x, y))
9133         waiting_for_player = TRUE;
9134     }
9135
9136     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9137     {
9138       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9139       {
9140         Feld[ax][ay] = EL_AMOEBA_DEAD;
9141         DrawLevelField(ax, ay);
9142         AmoebaCnt[AmoebaNr[ax][ay]]--;
9143
9144         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9145         {
9146           if (element == EL_AMOEBA_FULL)
9147             AmoebeUmwandeln(ax, ay);
9148           else if (element == EL_BD_AMOEBA)
9149             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9150         }
9151       }
9152       return;
9153     }
9154     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9155     {
9156       /* amoeba gets larger by growing in some direction */
9157
9158       int new_group_nr = AmoebaNr[ax][ay];
9159
9160 #ifdef DEBUG
9161   if (new_group_nr == 0)
9162   {
9163     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9164     printf("AmoebeAbleger(): This should never happen!\n");
9165     return;
9166   }
9167 #endif
9168
9169       AmoebaNr[newax][neway] = new_group_nr;
9170       AmoebaCnt[new_group_nr]++;
9171       AmoebaCnt2[new_group_nr]++;
9172
9173       /* if amoeba touches other amoeba(s) after growing, unify them */
9174       AmoebenVereinigen(newax, neway);
9175
9176       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9177       {
9178         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9179         return;
9180       }
9181     }
9182   }
9183
9184   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9185       (neway == lev_fieldy - 1 && newax != ax))
9186   {
9187     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9188     Store[newax][neway] = element;
9189   }
9190   else if (neway == ay || element == EL_EMC_DRIPPER)
9191   {
9192     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9193
9194     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9195   }
9196   else
9197   {
9198     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9199     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9200     Store[ax][ay] = EL_AMOEBA_DROP;
9201     ContinueMoving(ax, ay);
9202     return;
9203   }
9204
9205   DrawLevelField(newax, neway);
9206 }
9207
9208 void Life(int ax, int ay)
9209 {
9210   int x1, y1, x2, y2;
9211   int life_time = 40;
9212   int element = Feld[ax][ay];
9213   int graphic = el2img(element);
9214   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9215                          level.biomaze);
9216   boolean changed = FALSE;
9217
9218   if (IS_ANIMATED(graphic))
9219     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9220
9221   if (Stop[ax][ay])
9222     return;
9223
9224   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9225     MovDelay[ax][ay] = life_time;
9226
9227   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9228   {
9229     MovDelay[ax][ay]--;
9230     if (MovDelay[ax][ay])
9231       return;
9232   }
9233
9234   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9235   {
9236     int xx = ax+x1, yy = ay+y1;
9237     int nachbarn = 0;
9238
9239     if (!IN_LEV_FIELD(xx, yy))
9240       continue;
9241
9242     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9243     {
9244       int x = xx+x2, y = yy+y2;
9245
9246       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9247         continue;
9248
9249       if (((Feld[x][y] == element ||
9250             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9251            !Stop[x][y]) ||
9252           (IS_FREE(x, y) && Stop[x][y]))
9253         nachbarn++;
9254     }
9255
9256     if (xx == ax && yy == ay)           /* field in the middle */
9257     {
9258       if (nachbarn < life_parameter[0] ||
9259           nachbarn > life_parameter[1])
9260       {
9261         Feld[xx][yy] = EL_EMPTY;
9262         if (!Stop[xx][yy])
9263           DrawLevelField(xx, yy);
9264         Stop[xx][yy] = TRUE;
9265         changed = TRUE;
9266       }
9267     }
9268     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9269     {                                   /* free border field */
9270       if (nachbarn >= life_parameter[2] &&
9271           nachbarn <= life_parameter[3])
9272       {
9273         Feld[xx][yy] = element;
9274         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9275         if (!Stop[xx][yy])
9276           DrawLevelField(xx, yy);
9277         Stop[xx][yy] = TRUE;
9278         changed = TRUE;
9279       }
9280     }
9281   }
9282
9283   if (changed)
9284     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9285                    SND_GAME_OF_LIFE_GROWING);
9286 }
9287
9288 static void InitRobotWheel(int x, int y)
9289 {
9290   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9291 }
9292
9293 static void RunRobotWheel(int x, int y)
9294 {
9295   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9296 }
9297
9298 static void StopRobotWheel(int x, int y)
9299 {
9300   if (ZX == x && ZY == y)
9301   {
9302     ZX = ZY = -1;
9303
9304     game.robot_wheel_active = FALSE;
9305   }
9306 }
9307
9308 static void InitTimegateWheel(int x, int y)
9309 {
9310   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9311 }
9312
9313 static void RunTimegateWheel(int x, int y)
9314 {
9315   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9316 }
9317
9318 static void InitMagicBallDelay(int x, int y)
9319 {
9320 #if 1
9321   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9322 #else
9323   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9324 #endif
9325 }
9326
9327 static void ActivateMagicBall(int bx, int by)
9328 {
9329   int x, y;
9330
9331   if (level.ball_random)
9332   {
9333     int pos_border = RND(8);    /* select one of the eight border elements */
9334     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9335     int xx = pos_content % 3;
9336     int yy = pos_content / 3;
9337
9338     x = bx - 1 + xx;
9339     y = by - 1 + yy;
9340
9341     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9342       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9343   }
9344   else
9345   {
9346     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9347     {
9348       int xx = x - bx + 1;
9349       int yy = y - by + 1;
9350
9351       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9352         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9353     }
9354   }
9355
9356   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9357 }
9358
9359 void CheckExit(int x, int y)
9360 {
9361   if (local_player->gems_still_needed > 0 ||
9362       local_player->sokobanfields_still_needed > 0 ||
9363       local_player->lights_still_needed > 0)
9364   {
9365     int element = Feld[x][y];
9366     int graphic = el2img(element);
9367
9368     if (IS_ANIMATED(graphic))
9369       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9370
9371     return;
9372   }
9373
9374   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9375     return;
9376
9377   Feld[x][y] = EL_EXIT_OPENING;
9378
9379   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9380 }
9381
9382 void CheckExitEM(int x, int y)
9383 {
9384   if (local_player->gems_still_needed > 0 ||
9385       local_player->sokobanfields_still_needed > 0 ||
9386       local_player->lights_still_needed > 0)
9387   {
9388     int element = Feld[x][y];
9389     int graphic = el2img(element);
9390
9391     if (IS_ANIMATED(graphic))
9392       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9393
9394     return;
9395   }
9396
9397   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9398     return;
9399
9400   Feld[x][y] = EL_EM_EXIT_OPENING;
9401
9402   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9403 }
9404
9405 void CheckExitSteel(int x, int y)
9406 {
9407   if (local_player->gems_still_needed > 0 ||
9408       local_player->sokobanfields_still_needed > 0 ||
9409       local_player->lights_still_needed > 0)
9410   {
9411     int element = Feld[x][y];
9412     int graphic = el2img(element);
9413
9414     if (IS_ANIMATED(graphic))
9415       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9416
9417     return;
9418   }
9419
9420   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9421     return;
9422
9423   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9424
9425   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9426 }
9427
9428 void CheckExitSteelEM(int x, int y)
9429 {
9430   if (local_player->gems_still_needed > 0 ||
9431       local_player->sokobanfields_still_needed > 0 ||
9432       local_player->lights_still_needed > 0)
9433   {
9434     int element = Feld[x][y];
9435     int graphic = el2img(element);
9436
9437     if (IS_ANIMATED(graphic))
9438       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9439
9440     return;
9441   }
9442
9443   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9444     return;
9445
9446   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9447
9448   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9449 }
9450
9451 void CheckExitSP(int x, int y)
9452 {
9453   if (local_player->gems_still_needed > 0)
9454   {
9455     int element = Feld[x][y];
9456     int graphic = el2img(element);
9457
9458     if (IS_ANIMATED(graphic))
9459       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9460
9461     return;
9462   }
9463
9464   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9465     return;
9466
9467   Feld[x][y] = EL_SP_EXIT_OPENING;
9468
9469   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9470 }
9471
9472 static void CloseAllOpenTimegates()
9473 {
9474   int x, y;
9475
9476   SCAN_PLAYFIELD(x, y)
9477   {
9478     int element = Feld[x][y];
9479
9480     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9481     {
9482       Feld[x][y] = EL_TIMEGATE_CLOSING;
9483
9484       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9485     }
9486   }
9487 }
9488
9489 void DrawTwinkleOnField(int x, int y)
9490 {
9491   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9492     return;
9493
9494   if (Feld[x][y] == EL_BD_DIAMOND)
9495     return;
9496
9497   if (MovDelay[x][y] == 0)      /* next animation frame */
9498     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9499
9500   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9501   {
9502     MovDelay[x][y]--;
9503
9504     if (setup.direct_draw && MovDelay[x][y])
9505       SetDrawtoField(DRAW_BUFFERED);
9506
9507     DrawLevelElementAnimation(x, y, Feld[x][y]);
9508
9509     if (MovDelay[x][y] != 0)
9510     {
9511       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9512                                            10 - MovDelay[x][y]);
9513
9514       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9515
9516       if (setup.direct_draw)
9517       {
9518         int dest_x, dest_y;
9519
9520         dest_x = FX + SCREENX(x) * TILEX;
9521         dest_y = FY + SCREENY(y) * TILEY;
9522
9523         BlitBitmap(drawto_field, window,
9524                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9525         SetDrawtoField(DRAW_DIRECT);
9526       }
9527     }
9528   }
9529 }
9530
9531 void MauerWaechst(int x, int y)
9532 {
9533   int delay = 6;
9534
9535   if (!MovDelay[x][y])          /* next animation frame */
9536     MovDelay[x][y] = 3 * delay;
9537
9538   if (MovDelay[x][y])           /* wait some time before next frame */
9539   {
9540     MovDelay[x][y]--;
9541
9542     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9543     {
9544       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9545       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9546
9547       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9548     }
9549
9550     if (!MovDelay[x][y])
9551     {
9552       if (MovDir[x][y] == MV_LEFT)
9553       {
9554         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9555           DrawLevelField(x - 1, y);
9556       }
9557       else if (MovDir[x][y] == MV_RIGHT)
9558       {
9559         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9560           DrawLevelField(x + 1, y);
9561       }
9562       else if (MovDir[x][y] == MV_UP)
9563       {
9564         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9565           DrawLevelField(x, y - 1);
9566       }
9567       else
9568       {
9569         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9570           DrawLevelField(x, y + 1);
9571       }
9572
9573       Feld[x][y] = Store[x][y];
9574       Store[x][y] = 0;
9575       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9576       DrawLevelField(x, y);
9577     }
9578   }
9579 }
9580
9581 void MauerAbleger(int ax, int ay)
9582 {
9583   int element = Feld[ax][ay];
9584   int graphic = el2img(element);
9585   boolean oben_frei = FALSE, unten_frei = FALSE;
9586   boolean links_frei = FALSE, rechts_frei = FALSE;
9587   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9588   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9589   boolean new_wall = FALSE;
9590
9591   if (IS_ANIMATED(graphic))
9592     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9593
9594   if (!MovDelay[ax][ay])        /* start building new wall */
9595     MovDelay[ax][ay] = 6;
9596
9597   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9598   {
9599     MovDelay[ax][ay]--;
9600     if (MovDelay[ax][ay])
9601       return;
9602   }
9603
9604   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9605     oben_frei = TRUE;
9606   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9607     unten_frei = TRUE;
9608   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9609     links_frei = TRUE;
9610   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9611     rechts_frei = TRUE;
9612
9613   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9614       element == EL_EXPANDABLE_WALL_ANY)
9615   {
9616     if (oben_frei)
9617     {
9618       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9619       Store[ax][ay-1] = element;
9620       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9621       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9622         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9623                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9624       new_wall = TRUE;
9625     }
9626     if (unten_frei)
9627     {
9628       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9629       Store[ax][ay+1] = element;
9630       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9631       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9632         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9633                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9634       new_wall = TRUE;
9635     }
9636   }
9637
9638   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9639       element == EL_EXPANDABLE_WALL_ANY ||
9640       element == EL_EXPANDABLE_WALL ||
9641       element == EL_BD_EXPANDABLE_WALL)
9642   {
9643     if (links_frei)
9644     {
9645       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9646       Store[ax-1][ay] = element;
9647       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9648       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9649         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9650                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9651       new_wall = TRUE;
9652     }
9653
9654     if (rechts_frei)
9655     {
9656       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9657       Store[ax+1][ay] = element;
9658       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9659       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9660         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9661                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9662       new_wall = TRUE;
9663     }
9664   }
9665
9666   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9667     DrawLevelField(ax, ay);
9668
9669   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9670     oben_massiv = TRUE;
9671   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9672     unten_massiv = TRUE;
9673   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9674     links_massiv = TRUE;
9675   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9676     rechts_massiv = TRUE;
9677
9678   if (((oben_massiv && unten_massiv) ||
9679        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9680        element == EL_EXPANDABLE_WALL) &&
9681       ((links_massiv && rechts_massiv) ||
9682        element == EL_EXPANDABLE_WALL_VERTICAL))
9683     Feld[ax][ay] = EL_WALL;
9684
9685   if (new_wall)
9686     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9687 }
9688
9689 void MauerAblegerStahl(int ax, int ay)
9690 {
9691   int element = Feld[ax][ay];
9692   int graphic = el2img(element);
9693   boolean oben_frei = FALSE, unten_frei = FALSE;
9694   boolean links_frei = FALSE, rechts_frei = FALSE;
9695   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9696   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9697   boolean new_wall = FALSE;
9698
9699   if (IS_ANIMATED(graphic))
9700     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9701
9702   if (!MovDelay[ax][ay])        /* start building new wall */
9703     MovDelay[ax][ay] = 6;
9704
9705   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9706   {
9707     MovDelay[ax][ay]--;
9708     if (MovDelay[ax][ay])
9709       return;
9710   }
9711
9712   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9713     oben_frei = TRUE;
9714   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9715     unten_frei = TRUE;
9716   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9717     links_frei = TRUE;
9718   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9719     rechts_frei = TRUE;
9720
9721   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9722       element == EL_EXPANDABLE_STEELWALL_ANY)
9723   {
9724     if (oben_frei)
9725     {
9726       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9727       Store[ax][ay-1] = element;
9728       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9729       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9730         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9731                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9732       new_wall = TRUE;
9733     }
9734     if (unten_frei)
9735     {
9736       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9737       Store[ax][ay+1] = element;
9738       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9739       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9740         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9741                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9742       new_wall = TRUE;
9743     }
9744   }
9745
9746   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9747       element == EL_EXPANDABLE_STEELWALL_ANY)
9748   {
9749     if (links_frei)
9750     {
9751       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9752       Store[ax-1][ay] = element;
9753       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9754       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9755         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9756                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9757       new_wall = TRUE;
9758     }
9759
9760     if (rechts_frei)
9761     {
9762       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9763       Store[ax+1][ay] = element;
9764       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9765       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9766         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9767                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9768       new_wall = TRUE;
9769     }
9770   }
9771
9772   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9773     oben_massiv = TRUE;
9774   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9775     unten_massiv = TRUE;
9776   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9777     links_massiv = TRUE;
9778   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9779     rechts_massiv = TRUE;
9780
9781   if (((oben_massiv && unten_massiv) ||
9782        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9783       ((links_massiv && rechts_massiv) ||
9784        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9785     Feld[ax][ay] = EL_WALL;
9786
9787   if (new_wall)
9788     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9789 }
9790
9791 void CheckForDragon(int x, int y)
9792 {
9793   int i, j;
9794   boolean dragon_found = FALSE;
9795   static int xy[4][2] =
9796   {
9797     { 0, -1 },
9798     { -1, 0 },
9799     { +1, 0 },
9800     { 0, +1 }
9801   };
9802
9803   for (i = 0; i < NUM_DIRECTIONS; i++)
9804   {
9805     for (j = 0; j < 4; j++)
9806     {
9807       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9808
9809       if (IN_LEV_FIELD(xx, yy) &&
9810           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9811       {
9812         if (Feld[xx][yy] == EL_DRAGON)
9813           dragon_found = TRUE;
9814       }
9815       else
9816         break;
9817     }
9818   }
9819
9820   if (!dragon_found)
9821   {
9822     for (i = 0; i < NUM_DIRECTIONS; i++)
9823     {
9824       for (j = 0; j < 3; j++)
9825       {
9826         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9827   
9828         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9829         {
9830           Feld[xx][yy] = EL_EMPTY;
9831           DrawLevelField(xx, yy);
9832         }
9833         else
9834           break;
9835       }
9836     }
9837   }
9838 }
9839
9840 static void InitBuggyBase(int x, int y)
9841 {
9842   int element = Feld[x][y];
9843   int activating_delay = FRAMES_PER_SECOND / 4;
9844
9845   ChangeDelay[x][y] =
9846     (element == EL_SP_BUGGY_BASE ?
9847      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9848      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9849      activating_delay :
9850      element == EL_SP_BUGGY_BASE_ACTIVE ?
9851      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9852 }
9853
9854 static void WarnBuggyBase(int x, int y)
9855 {
9856   int i;
9857   static int xy[4][2] =
9858   {
9859     { 0, -1 },
9860     { -1, 0 },
9861     { +1, 0 },
9862     { 0, +1 }
9863   };
9864
9865   for (i = 0; i < NUM_DIRECTIONS; i++)
9866   {
9867     int xx = x + xy[i][0];
9868     int yy = y + xy[i][1];
9869
9870     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9871     {
9872       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9873
9874       break;
9875     }
9876   }
9877 }
9878
9879 static void InitTrap(int x, int y)
9880 {
9881   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9882 }
9883
9884 static void ActivateTrap(int x, int y)
9885 {
9886   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9887 }
9888
9889 static void ChangeActiveTrap(int x, int y)
9890 {
9891   int graphic = IMG_TRAP_ACTIVE;
9892
9893   /* if new animation frame was drawn, correct crumbled sand border */
9894   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9895     DrawLevelFieldCrumbledSand(x, y);
9896 }
9897
9898 static int getSpecialActionElement(int element, int number, int base_element)
9899 {
9900   return (element != EL_EMPTY ? element :
9901           number != -1 ? base_element + number - 1 :
9902           EL_EMPTY);
9903 }
9904
9905 static int getModifiedActionNumber(int value_old, int operator, int operand,
9906                                    int value_min, int value_max)
9907 {
9908   int value_new = (operator == CA_MODE_SET      ? operand :
9909                    operator == CA_MODE_ADD      ? value_old + operand :
9910                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9911                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9912                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9913                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9914                    value_old);
9915
9916   return (value_new < value_min ? value_min :
9917           value_new > value_max ? value_max :
9918           value_new);
9919 }
9920
9921 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9922 {
9923   struct ElementInfo *ei = &element_info[element];
9924   struct ElementChangeInfo *change = &ei->change_page[page];
9925   int target_element = change->target_element;
9926   int action_type = change->action_type;
9927   int action_mode = change->action_mode;
9928   int action_arg = change->action_arg;
9929   int i;
9930
9931   if (!change->has_action)
9932     return;
9933
9934   /* ---------- determine action paramater values -------------------------- */
9935
9936   int level_time_value =
9937     (level.time > 0 ? TimeLeft :
9938      TimePlayed);
9939
9940   int action_arg_element =
9941     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9942      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9943      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9944      EL_EMPTY);
9945
9946   int action_arg_direction =
9947     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9948      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9949      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9950      change->actual_trigger_side :
9951      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9952      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9953      MV_NONE);
9954
9955   int action_arg_number_min =
9956     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9957      CA_ARG_MIN);
9958
9959   int action_arg_number_max =
9960     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9961      action_type == CA_SET_LEVEL_GEMS ? 999 :
9962      action_type == CA_SET_LEVEL_TIME ? 9999 :
9963      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9964      action_type == CA_SET_CE_VALUE ? 9999 :
9965      action_type == CA_SET_CE_SCORE ? 9999 :
9966      CA_ARG_MAX);
9967
9968   int action_arg_number_reset =
9969     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9970      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9971      action_type == CA_SET_LEVEL_TIME ? level.time :
9972      action_type == CA_SET_LEVEL_SCORE ? 0 :
9973 #if USE_NEW_CUSTOM_VALUE
9974      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9975 #else
9976      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9977 #endif
9978      action_type == CA_SET_CE_SCORE ? 0 :
9979      0);
9980
9981   int action_arg_number =
9982     (action_arg <= CA_ARG_MAX ? action_arg :
9983      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9984      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9985      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9986      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9987      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9988      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9989 #if USE_NEW_CUSTOM_VALUE
9990      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9991 #else
9992      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9993 #endif
9994      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9995      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9996      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9997      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9998      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9999      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10000      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10001      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10002      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10003      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10004      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10005      -1);
10006
10007   int action_arg_number_old =
10008     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10009      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10010      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10011      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10012      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10013      0);
10014
10015   int action_arg_number_new =
10016     getModifiedActionNumber(action_arg_number_old,
10017                             action_mode, action_arg_number,
10018                             action_arg_number_min, action_arg_number_max);
10019
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
10026   int action_arg_player_bits =
10027     (action_arg >= CA_ARG_PLAYER_1 &&
10028      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10029      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10030      PLAYER_BITS_ANY);
10031
10032   /* ---------- execute action  -------------------------------------------- */
10033
10034   switch (action_type)
10035   {
10036     case CA_NO_ACTION:
10037     {
10038       return;
10039     }
10040
10041     /* ---------- level actions  ------------------------------------------- */
10042
10043     case CA_RESTART_LEVEL:
10044     {
10045       game.restart_level = TRUE;
10046
10047       break;
10048     }
10049
10050     case CA_SHOW_ENVELOPE:
10051     {
10052       int element = getSpecialActionElement(action_arg_element,
10053                                             action_arg_number, EL_ENVELOPE_1);
10054
10055       if (IS_ENVELOPE(element))
10056         local_player->show_envelope = element;
10057
10058       break;
10059     }
10060
10061     case CA_SET_LEVEL_TIME:
10062     {
10063       if (level.time > 0)       /* only modify limited time value */
10064       {
10065         TimeLeft = action_arg_number_new;
10066
10067 #if 1
10068         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10069
10070         DisplayGameControlValues();
10071 #else
10072         DrawGameValue_Time(TimeLeft);
10073 #endif
10074
10075         if (!TimeLeft && setup.time_limit)
10076           for (i = 0; i < MAX_PLAYERS; i++)
10077             KillPlayer(&stored_player[i]);
10078       }
10079
10080       break;
10081     }
10082
10083     case CA_SET_LEVEL_SCORE:
10084     {
10085       local_player->score = action_arg_number_new;
10086
10087 #if 1
10088       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10089
10090       DisplayGameControlValues();
10091 #else
10092       DrawGameValue_Score(local_player->score);
10093 #endif
10094
10095       break;
10096     }
10097
10098     case CA_SET_LEVEL_GEMS:
10099     {
10100       local_player->gems_still_needed = action_arg_number_new;
10101
10102 #if 1
10103       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10104
10105       DisplayGameControlValues();
10106 #else
10107       DrawGameValue_Emeralds(local_player->gems_still_needed);
10108 #endif
10109
10110       break;
10111     }
10112
10113 #if !USE_PLAYER_GRAVITY
10114     case CA_SET_LEVEL_GRAVITY:
10115     {
10116       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10117                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10118                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10119                       game.gravity);
10120       break;
10121     }
10122 #endif
10123
10124     case CA_SET_LEVEL_WIND:
10125     {
10126       game.wind_direction = action_arg_direction;
10127
10128       break;
10129     }
10130
10131     /* ---------- player actions  ------------------------------------------ */
10132
10133     case CA_MOVE_PLAYER:
10134     {
10135       /* automatically move to the next field in specified direction */
10136       for (i = 0; i < MAX_PLAYERS; i++)
10137         if (trigger_player_bits & (1 << i))
10138           stored_player[i].programmed_action = action_arg_direction;
10139
10140       break;
10141     }
10142
10143     case CA_EXIT_PLAYER:
10144     {
10145       for (i = 0; i < MAX_PLAYERS; i++)
10146         if (action_arg_player_bits & (1 << i))
10147           PlayerWins(&stored_player[i]);
10148
10149       break;
10150     }
10151
10152     case CA_KILL_PLAYER:
10153     {
10154       for (i = 0; i < MAX_PLAYERS; i++)
10155         if (action_arg_player_bits & (1 << i))
10156           KillPlayer(&stored_player[i]);
10157
10158       break;
10159     }
10160
10161     case CA_SET_PLAYER_KEYS:
10162     {
10163       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10164       int element = getSpecialActionElement(action_arg_element,
10165                                             action_arg_number, EL_KEY_1);
10166
10167       if (IS_KEY(element))
10168       {
10169         for (i = 0; i < MAX_PLAYERS; i++)
10170         {
10171           if (trigger_player_bits & (1 << i))
10172           {
10173             stored_player[i].key[KEY_NR(element)] = key_state;
10174
10175             DrawGameDoorValues();
10176           }
10177         }
10178       }
10179
10180       break;
10181     }
10182
10183     case CA_SET_PLAYER_SPEED:
10184     {
10185       for (i = 0; i < MAX_PLAYERS; i++)
10186       {
10187         if (trigger_player_bits & (1 << i))
10188         {
10189           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10190
10191           if (action_arg == CA_ARG_SPEED_FASTER &&
10192               stored_player[i].cannot_move)
10193           {
10194             action_arg_number = STEPSIZE_VERY_SLOW;
10195           }
10196           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10197                    action_arg == CA_ARG_SPEED_FASTER)
10198           {
10199             action_arg_number = 2;
10200             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10201                            CA_MODE_MULTIPLY);
10202           }
10203           else if (action_arg == CA_ARG_NUMBER_RESET)
10204           {
10205             action_arg_number = level.initial_player_stepsize[i];
10206           }
10207
10208           move_stepsize =
10209             getModifiedActionNumber(move_stepsize,
10210                                     action_mode,
10211                                     action_arg_number,
10212                                     action_arg_number_min,
10213                                     action_arg_number_max);
10214
10215           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10216         }
10217       }
10218
10219       break;
10220     }
10221
10222     case CA_SET_PLAYER_SHIELD:
10223     {
10224       for (i = 0; i < MAX_PLAYERS; i++)
10225       {
10226         if (trigger_player_bits & (1 << i))
10227         {
10228           if (action_arg == CA_ARG_SHIELD_OFF)
10229           {
10230             stored_player[i].shield_normal_time_left = 0;
10231             stored_player[i].shield_deadly_time_left = 0;
10232           }
10233           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10234           {
10235             stored_player[i].shield_normal_time_left = 999999;
10236           }
10237           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10238           {
10239             stored_player[i].shield_normal_time_left = 999999;
10240             stored_player[i].shield_deadly_time_left = 999999;
10241           }
10242         }
10243       }
10244
10245       break;
10246     }
10247
10248 #if USE_PLAYER_GRAVITY
10249     case CA_SET_PLAYER_GRAVITY:
10250     {
10251       for (i = 0; i < MAX_PLAYERS; i++)
10252       {
10253         if (trigger_player_bits & (1 << i))
10254         {
10255           stored_player[i].gravity =
10256             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10257              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10258              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10259              stored_player[i].gravity);
10260         }
10261       }
10262
10263       break;
10264     }
10265 #endif
10266
10267     case CA_SET_PLAYER_ARTWORK:
10268     {
10269       for (i = 0; i < MAX_PLAYERS; i++)
10270       {
10271         if (trigger_player_bits & (1 << i))
10272         {
10273           int artwork_element = action_arg_element;
10274
10275           if (action_arg == CA_ARG_ELEMENT_RESET)
10276             artwork_element =
10277               (level.use_artwork_element[i] ? level.artwork_element[i] :
10278                stored_player[i].element_nr);
10279
10280 #if USE_GFX_RESET_PLAYER_ARTWORK
10281           if (stored_player[i].artwork_element != artwork_element)
10282             stored_player[i].Frame = 0;
10283 #endif
10284
10285           stored_player[i].artwork_element = artwork_element;
10286
10287           SetPlayerWaiting(&stored_player[i], FALSE);
10288
10289           /* set number of special actions for bored and sleeping animation */
10290           stored_player[i].num_special_action_bored =
10291             get_num_special_action(artwork_element,
10292                                    ACTION_BORING_1, ACTION_BORING_LAST);
10293           stored_player[i].num_special_action_sleeping =
10294             get_num_special_action(artwork_element,
10295                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10296         }
10297       }
10298
10299       break;
10300     }
10301
10302     /* ---------- CE actions  ---------------------------------------------- */
10303
10304     case CA_SET_CE_VALUE:
10305     {
10306 #if USE_NEW_CUSTOM_VALUE
10307       int last_ce_value = CustomValue[x][y];
10308
10309       CustomValue[x][y] = action_arg_number_new;
10310
10311       if (CustomValue[x][y] != last_ce_value)
10312       {
10313         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10314         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10315
10316         if (CustomValue[x][y] == 0)
10317         {
10318           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10319           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10320         }
10321       }
10322 #endif
10323
10324       break;
10325     }
10326
10327     case CA_SET_CE_SCORE:
10328     {
10329 #if USE_NEW_CUSTOM_VALUE
10330       int last_ce_score = ei->collect_score;
10331
10332       ei->collect_score = action_arg_number_new;
10333
10334       if (ei->collect_score != last_ce_score)
10335       {
10336         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10337         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10338
10339         if (ei->collect_score == 0)
10340         {
10341           int xx, yy;
10342
10343           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10344           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10345
10346           /*
10347             This is a very special case that seems to be a mixture between
10348             CheckElementChange() and CheckTriggeredElementChange(): while
10349             the first one only affects single elements that are triggered
10350             directly, the second one affects multiple elements in the playfield
10351             that are triggered indirectly by another element. This is a third
10352             case: Changing the CE score always affects multiple identical CEs,
10353             so every affected CE must be checked, not only the single CE for
10354             which the CE score was changed in the first place (as every instance
10355             of that CE shares the same CE score, and therefore also can change)!
10356           */
10357           SCAN_PLAYFIELD(xx, yy)
10358           {
10359             if (Feld[xx][yy] == element)
10360               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10361                                  CE_SCORE_GETS_ZERO);
10362           }
10363         }
10364       }
10365 #endif
10366
10367       break;
10368     }
10369
10370     /* ---------- engine actions  ------------------------------------------ */
10371
10372     case CA_SET_ENGINE_SCAN_MODE:
10373     {
10374       InitPlayfieldScanMode(action_arg);
10375
10376       break;
10377     }
10378
10379     default:
10380       break;
10381   }
10382 }
10383
10384 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10385 {
10386   int old_element = Feld[x][y];
10387   int new_element = GetElementFromGroupElement(element);
10388   int previous_move_direction = MovDir[x][y];
10389 #if USE_NEW_CUSTOM_VALUE
10390   int last_ce_value = CustomValue[x][y];
10391 #endif
10392   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10393   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10394   boolean add_player_onto_element = (new_element_is_player &&
10395 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10396                                      /* this breaks SnakeBite when a snake is
10397                                         halfway through a door that closes */
10398                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10399                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10400 #endif
10401                                      IS_WALKABLE(old_element));
10402
10403 #if 0
10404   /* check if element under the player changes from accessible to unaccessible
10405      (needed for special case of dropping element which then changes) */
10406   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10407       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10408   {
10409     Bang(x, y);
10410
10411     return;
10412   }
10413 #endif
10414
10415   if (!add_player_onto_element)
10416   {
10417     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10418       RemoveMovingField(x, y);
10419     else
10420       RemoveField(x, y);
10421
10422     Feld[x][y] = new_element;
10423
10424 #if !USE_GFX_RESET_GFX_ANIMATION
10425     ResetGfxAnimation(x, y);
10426     ResetRandomAnimationValue(x, y);
10427 #endif
10428
10429     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10430       MovDir[x][y] = previous_move_direction;
10431
10432 #if USE_NEW_CUSTOM_VALUE
10433     if (element_info[new_element].use_last_ce_value)
10434       CustomValue[x][y] = last_ce_value;
10435 #endif
10436
10437     InitField_WithBug1(x, y, FALSE);
10438
10439     new_element = Feld[x][y];   /* element may have changed */
10440
10441 #if USE_GFX_RESET_GFX_ANIMATION
10442     ResetGfxAnimation(x, y);
10443     ResetRandomAnimationValue(x, y);
10444 #endif
10445
10446     DrawLevelField(x, y);
10447
10448     if (GFX_CRUMBLED(new_element))
10449       DrawLevelFieldCrumbledSandNeighbours(x, y);
10450   }
10451
10452 #if 1
10453   /* check if element under the player changes from accessible to unaccessible
10454      (needed for special case of dropping element which then changes) */
10455   /* (must be checked after creating new element for walkable group elements) */
10456 #if USE_FIX_KILLED_BY_NON_WALKABLE
10457   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10458       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10459   {
10460     Bang(x, y);
10461
10462     return;
10463   }
10464 #else
10465   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10466       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10467   {
10468     Bang(x, y);
10469
10470     return;
10471   }
10472 #endif
10473 #endif
10474
10475   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10476   if (new_element_is_player)
10477     RelocatePlayer(x, y, new_element);
10478
10479   if (is_change)
10480     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10481
10482   TestIfBadThingTouchesPlayer(x, y);
10483   TestIfPlayerTouchesCustomElement(x, y);
10484   TestIfElementTouchesCustomElement(x, y);
10485 }
10486
10487 static void CreateField(int x, int y, int element)
10488 {
10489   CreateFieldExt(x, y, element, FALSE);
10490 }
10491
10492 static void CreateElementFromChange(int x, int y, int element)
10493 {
10494   element = GET_VALID_RUNTIME_ELEMENT(element);
10495
10496 #if USE_STOP_CHANGED_ELEMENTS
10497   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10498   {
10499     int old_element = Feld[x][y];
10500
10501     /* prevent changed element from moving in same engine frame
10502        unless both old and new element can either fall or move */
10503     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10504         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10505       Stop[x][y] = TRUE;
10506   }
10507 #endif
10508
10509   CreateFieldExt(x, y, element, TRUE);
10510 }
10511
10512 static boolean ChangeElement(int x, int y, int element, int page)
10513 {
10514   struct ElementInfo *ei = &element_info[element];
10515   struct ElementChangeInfo *change = &ei->change_page[page];
10516   int ce_value = CustomValue[x][y];
10517   int ce_score = ei->collect_score;
10518   int target_element;
10519   int old_element = Feld[x][y];
10520
10521   /* always use default change event to prevent running into a loop */
10522   if (ChangeEvent[x][y] == -1)
10523     ChangeEvent[x][y] = CE_DELAY;
10524
10525   if (ChangeEvent[x][y] == CE_DELAY)
10526   {
10527     /* reset actual trigger element, trigger player and action element */
10528     change->actual_trigger_element = EL_EMPTY;
10529     change->actual_trigger_player = EL_PLAYER_1;
10530     change->actual_trigger_side = CH_SIDE_NONE;
10531     change->actual_trigger_ce_value = 0;
10532     change->actual_trigger_ce_score = 0;
10533   }
10534
10535   /* do not change elements more than a specified maximum number of changes */
10536   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10537     return FALSE;
10538
10539   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10540
10541   if (change->explode)
10542   {
10543     Bang(x, y);
10544
10545     return TRUE;
10546   }
10547
10548   if (change->use_target_content)
10549   {
10550     boolean complete_replace = TRUE;
10551     boolean can_replace[3][3];
10552     int xx, yy;
10553
10554     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10555     {
10556       boolean is_empty;
10557       boolean is_walkable;
10558       boolean is_diggable;
10559       boolean is_collectible;
10560       boolean is_removable;
10561       boolean is_destructible;
10562       int ex = x + xx - 1;
10563       int ey = y + yy - 1;
10564       int content_element = change->target_content.e[xx][yy];
10565       int e;
10566
10567       can_replace[xx][yy] = TRUE;
10568
10569       if (ex == x && ey == y)   /* do not check changing element itself */
10570         continue;
10571
10572       if (content_element == EL_EMPTY_SPACE)
10573       {
10574         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10575
10576         continue;
10577       }
10578
10579       if (!IN_LEV_FIELD(ex, ey))
10580       {
10581         can_replace[xx][yy] = FALSE;
10582         complete_replace = FALSE;
10583
10584         continue;
10585       }
10586
10587       e = Feld[ex][ey];
10588
10589       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10590         e = MovingOrBlocked2Element(ex, ey);
10591
10592       is_empty = (IS_FREE(ex, ey) ||
10593                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10594
10595       is_walkable     = (is_empty || IS_WALKABLE(e));
10596       is_diggable     = (is_empty || IS_DIGGABLE(e));
10597       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10598       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10599       is_removable    = (is_diggable || is_collectible);
10600
10601       can_replace[xx][yy] =
10602         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10603           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10604           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10605           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10606           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10607           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10608          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10609
10610       if (!can_replace[xx][yy])
10611         complete_replace = FALSE;
10612     }
10613
10614     if (!change->only_if_complete || complete_replace)
10615     {
10616       boolean something_has_changed = FALSE;
10617
10618       if (change->only_if_complete && change->use_random_replace &&
10619           RND(100) < change->random_percentage)
10620         return FALSE;
10621
10622       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10623       {
10624         int ex = x + xx - 1;
10625         int ey = y + yy - 1;
10626         int content_element;
10627
10628         if (can_replace[xx][yy] && (!change->use_random_replace ||
10629                                     RND(100) < change->random_percentage))
10630         {
10631           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10632             RemoveMovingField(ex, ey);
10633
10634           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10635
10636           content_element = change->target_content.e[xx][yy];
10637           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10638                                               ce_value, ce_score);
10639
10640           CreateElementFromChange(ex, ey, target_element);
10641
10642           something_has_changed = TRUE;
10643
10644           /* for symmetry reasons, freeze newly created border elements */
10645           if (ex != x || ey != y)
10646             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10647         }
10648       }
10649
10650       if (something_has_changed)
10651       {
10652         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10653         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10654       }
10655     }
10656   }
10657   else
10658   {
10659     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10660                                         ce_value, ce_score);
10661
10662     if (element == EL_DIAGONAL_GROWING ||
10663         element == EL_DIAGONAL_SHRINKING)
10664     {
10665       target_element = Store[x][y];
10666
10667       Store[x][y] = EL_EMPTY;
10668     }
10669
10670     CreateElementFromChange(x, y, target_element);
10671
10672     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10673     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10674   }
10675
10676   /* this uses direct change before indirect change */
10677   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10678
10679   return TRUE;
10680 }
10681
10682 #if USE_NEW_DELAYED_ACTION
10683
10684 static void HandleElementChange(int x, int y, int page)
10685 {
10686   int element = MovingOrBlocked2Element(x, y);
10687   struct ElementInfo *ei = &element_info[element];
10688   struct ElementChangeInfo *change = &ei->change_page[page];
10689
10690 #ifdef DEBUG
10691   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10692       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10693   {
10694     printf("\n\n");
10695     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10696            x, y, element, element_info[element].token_name);
10697     printf("HandleElementChange(): This should never happen!\n");
10698     printf("\n\n");
10699   }
10700 #endif
10701
10702   /* this can happen with classic bombs on walkable, changing elements */
10703   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10704   {
10705 #if 0
10706     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10707       ChangeDelay[x][y] = 0;
10708 #endif
10709
10710     return;
10711   }
10712
10713   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10714   {
10715     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10716
10717     if (change->can_change)
10718     {
10719 #if 1
10720       /* !!! not clear why graphic animation should be reset at all here !!! */
10721       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10722 #if USE_GFX_RESET_WHEN_NOT_MOVING
10723       /* when a custom element is about to change (for example by change delay),
10724          do not reset graphic animation when the custom element is moving */
10725       if (!IS_MOVING(x, y))
10726 #endif
10727       {
10728         ResetGfxAnimation(x, y);
10729         ResetRandomAnimationValue(x, y);
10730       }
10731 #endif
10732
10733       if (change->pre_change_function)
10734         change->pre_change_function(x, y);
10735     }
10736   }
10737
10738   ChangeDelay[x][y]--;
10739
10740   if (ChangeDelay[x][y] != 0)           /* continue element change */
10741   {
10742     if (change->can_change)
10743     {
10744       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10745
10746       if (IS_ANIMATED(graphic))
10747         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10748
10749       if (change->change_function)
10750         change->change_function(x, y);
10751     }
10752   }
10753   else                                  /* finish element change */
10754   {
10755     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10756     {
10757       page = ChangePage[x][y];
10758       ChangePage[x][y] = -1;
10759
10760       change = &ei->change_page[page];
10761     }
10762
10763     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10764     {
10765       ChangeDelay[x][y] = 1;            /* try change after next move step */
10766       ChangePage[x][y] = page;          /* remember page to use for change */
10767
10768       return;
10769     }
10770
10771     if (change->can_change)
10772     {
10773       if (ChangeElement(x, y, element, page))
10774       {
10775         if (change->post_change_function)
10776           change->post_change_function(x, y);
10777       }
10778     }
10779
10780     if (change->has_action)
10781       ExecuteCustomElementAction(x, y, element, page);
10782   }
10783 }
10784
10785 #else
10786
10787 static void HandleElementChange(int x, int y, int page)
10788 {
10789   int element = MovingOrBlocked2Element(x, y);
10790   struct ElementInfo *ei = &element_info[element];
10791   struct ElementChangeInfo *change = &ei->change_page[page];
10792
10793 #ifdef DEBUG
10794   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10795   {
10796     printf("\n\n");
10797     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10798            x, y, element, element_info[element].token_name);
10799     printf("HandleElementChange(): This should never happen!\n");
10800     printf("\n\n");
10801   }
10802 #endif
10803
10804   /* this can happen with classic bombs on walkable, changing elements */
10805   if (!CAN_CHANGE(element))
10806   {
10807 #if 0
10808     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10809       ChangeDelay[x][y] = 0;
10810 #endif
10811
10812     return;
10813   }
10814
10815   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10816   {
10817     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10818
10819     ResetGfxAnimation(x, y);
10820     ResetRandomAnimationValue(x, y);
10821
10822     if (change->pre_change_function)
10823       change->pre_change_function(x, y);
10824   }
10825
10826   ChangeDelay[x][y]--;
10827
10828   if (ChangeDelay[x][y] != 0)           /* continue element change */
10829   {
10830     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10831
10832     if (IS_ANIMATED(graphic))
10833       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10834
10835     if (change->change_function)
10836       change->change_function(x, y);
10837   }
10838   else                                  /* finish element change */
10839   {
10840     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10841     {
10842       page = ChangePage[x][y];
10843       ChangePage[x][y] = -1;
10844
10845       change = &ei->change_page[page];
10846     }
10847
10848     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10849     {
10850       ChangeDelay[x][y] = 1;            /* try change after next move step */
10851       ChangePage[x][y] = page;          /* remember page to use for change */
10852
10853       return;
10854     }
10855
10856     if (ChangeElement(x, y, element, page))
10857     {
10858       if (change->post_change_function)
10859         change->post_change_function(x, y);
10860     }
10861   }
10862 }
10863
10864 #endif
10865
10866 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10867                                               int trigger_element,
10868                                               int trigger_event,
10869                                               int trigger_player,
10870                                               int trigger_side,
10871                                               int trigger_page)
10872 {
10873   boolean change_done_any = FALSE;
10874   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10875   int i;
10876
10877   if (!(trigger_events[trigger_element][trigger_event]))
10878     return FALSE;
10879
10880 #if 0
10881   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10882          trigger_event, recursion_loop_depth, recursion_loop_detected,
10883          recursion_loop_element, EL_NAME(recursion_loop_element));
10884 #endif
10885
10886   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10887
10888   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10889   {
10890     int element = EL_CUSTOM_START + i;
10891     boolean change_done = FALSE;
10892     int p;
10893
10894     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10895         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10896       continue;
10897
10898     for (p = 0; p < element_info[element].num_change_pages; p++)
10899     {
10900       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10901
10902       if (change->can_change_or_has_action &&
10903           change->has_event[trigger_event] &&
10904           change->trigger_side & trigger_side &&
10905           change->trigger_player & trigger_player &&
10906           change->trigger_page & trigger_page_bits &&
10907           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10908       {
10909         change->actual_trigger_element = trigger_element;
10910         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10911         change->actual_trigger_side = trigger_side;
10912         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10913         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10914
10915         if ((change->can_change && !change_done) || change->has_action)
10916         {
10917           int x, y;
10918
10919           SCAN_PLAYFIELD(x, y)
10920           {
10921             if (Feld[x][y] == element)
10922             {
10923               if (change->can_change && !change_done)
10924               {
10925                 ChangeDelay[x][y] = 1;
10926                 ChangeEvent[x][y] = trigger_event;
10927
10928                 HandleElementChange(x, y, p);
10929               }
10930 #if USE_NEW_DELAYED_ACTION
10931               else if (change->has_action)
10932               {
10933                 ExecuteCustomElementAction(x, y, element, p);
10934                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10935               }
10936 #else
10937               if (change->has_action)
10938               {
10939                 ExecuteCustomElementAction(x, y, element, p);
10940                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10941               }
10942 #endif
10943             }
10944           }
10945
10946           if (change->can_change)
10947           {
10948             change_done = TRUE;
10949             change_done_any = TRUE;
10950           }
10951         }
10952       }
10953     }
10954   }
10955
10956   RECURSION_LOOP_DETECTION_END();
10957
10958   return change_done_any;
10959 }
10960
10961 static boolean CheckElementChangeExt(int x, int y,
10962                                      int element,
10963                                      int trigger_element,
10964                                      int trigger_event,
10965                                      int trigger_player,
10966                                      int trigger_side)
10967 {
10968   boolean change_done = FALSE;
10969   int p;
10970
10971   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10972       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10973     return FALSE;
10974
10975   if (Feld[x][y] == EL_BLOCKED)
10976   {
10977     Blocked2Moving(x, y, &x, &y);
10978     element = Feld[x][y];
10979   }
10980
10981 #if 0
10982   /* check if element has already changed */
10983   if (Feld[x][y] != element)
10984     return FALSE;
10985 #else
10986   /* check if element has already changed or is about to change after moving */
10987   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10988        Feld[x][y] != element) ||
10989
10990       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10991        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10992         ChangePage[x][y] != -1)))
10993     return FALSE;
10994 #endif
10995
10996 #if 0
10997   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10998          trigger_event, recursion_loop_depth, recursion_loop_detected,
10999          recursion_loop_element, EL_NAME(recursion_loop_element));
11000 #endif
11001
11002   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11003
11004   for (p = 0; p < element_info[element].num_change_pages; p++)
11005   {
11006     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11007
11008     /* check trigger element for all events where the element that is checked
11009        for changing interacts with a directly adjacent element -- this is
11010        different to element changes that affect other elements to change on the
11011        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11012     boolean check_trigger_element =
11013       (trigger_event == CE_TOUCHING_X ||
11014        trigger_event == CE_HITTING_X ||
11015        trigger_event == CE_HIT_BY_X ||
11016 #if 1
11017        /* this one was forgotten until 3.2.3 */
11018        trigger_event == CE_DIGGING_X);
11019 #endif
11020
11021     if (change->can_change_or_has_action &&
11022         change->has_event[trigger_event] &&
11023         change->trigger_side & trigger_side &&
11024         change->trigger_player & trigger_player &&
11025         (!check_trigger_element ||
11026          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11027     {
11028       change->actual_trigger_element = trigger_element;
11029       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
11030       change->actual_trigger_side = trigger_side;
11031       change->actual_trigger_ce_value = CustomValue[x][y];
11032       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11033
11034       /* special case: trigger element not at (x,y) position for some events */
11035       if (check_trigger_element)
11036       {
11037         static struct
11038         {
11039           int dx, dy;
11040         } move_xy[] =
11041           {
11042             {  0,  0 },
11043             { -1,  0 },
11044             { +1,  0 },
11045             {  0,  0 },
11046             {  0, -1 },
11047             {  0,  0 }, { 0, 0 }, { 0, 0 },
11048             {  0, +1 }
11049           };
11050
11051         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11052         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11053
11054         change->actual_trigger_ce_value = CustomValue[xx][yy];
11055         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11056       }
11057
11058       if (change->can_change && !change_done)
11059       {
11060         ChangeDelay[x][y] = 1;
11061         ChangeEvent[x][y] = trigger_event;
11062
11063         HandleElementChange(x, y, p);
11064
11065         change_done = TRUE;
11066       }
11067 #if USE_NEW_DELAYED_ACTION
11068       else if (change->has_action)
11069       {
11070         ExecuteCustomElementAction(x, y, element, p);
11071         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11072       }
11073 #else
11074       if (change->has_action)
11075       {
11076         ExecuteCustomElementAction(x, y, element, p);
11077         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11078       }
11079 #endif
11080     }
11081   }
11082
11083   RECURSION_LOOP_DETECTION_END();
11084
11085   return change_done;
11086 }
11087
11088 static void PlayPlayerSound(struct PlayerInfo *player)
11089 {
11090   int jx = player->jx, jy = player->jy;
11091   int sound_element = player->artwork_element;
11092   int last_action = player->last_action_waiting;
11093   int action = player->action_waiting;
11094
11095   if (player->is_waiting)
11096   {
11097     if (action != last_action)
11098       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11099     else
11100       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11101   }
11102   else
11103   {
11104     if (action != last_action)
11105       StopSound(element_info[sound_element].sound[last_action]);
11106
11107     if (last_action == ACTION_SLEEPING)
11108       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11109   }
11110 }
11111
11112 static void PlayAllPlayersSound()
11113 {
11114   int i;
11115
11116   for (i = 0; i < MAX_PLAYERS; i++)
11117     if (stored_player[i].active)
11118       PlayPlayerSound(&stored_player[i]);
11119 }
11120
11121 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11122 {
11123   boolean last_waiting = player->is_waiting;
11124   int move_dir = player->MovDir;
11125
11126   player->dir_waiting = move_dir;
11127   player->last_action_waiting = player->action_waiting;
11128
11129   if (is_waiting)
11130   {
11131     if (!last_waiting)          /* not waiting -> waiting */
11132     {
11133       player->is_waiting = TRUE;
11134
11135       player->frame_counter_bored =
11136         FrameCounter +
11137         game.player_boring_delay_fixed +
11138         GetSimpleRandom(game.player_boring_delay_random);
11139       player->frame_counter_sleeping =
11140         FrameCounter +
11141         game.player_sleeping_delay_fixed +
11142         GetSimpleRandom(game.player_sleeping_delay_random);
11143
11144       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11145     }
11146
11147     if (game.player_sleeping_delay_fixed +
11148         game.player_sleeping_delay_random > 0 &&
11149         player->anim_delay_counter == 0 &&
11150         player->post_delay_counter == 0 &&
11151         FrameCounter >= player->frame_counter_sleeping)
11152       player->is_sleeping = TRUE;
11153     else if (game.player_boring_delay_fixed +
11154              game.player_boring_delay_random > 0 &&
11155              FrameCounter >= player->frame_counter_bored)
11156       player->is_bored = TRUE;
11157
11158     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11159                               player->is_bored ? ACTION_BORING :
11160                               ACTION_WAITING);
11161
11162     if (player->is_sleeping && player->use_murphy)
11163     {
11164       /* special case for sleeping Murphy when leaning against non-free tile */
11165
11166       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11167           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11168            !IS_MOVING(player->jx - 1, player->jy)))
11169         move_dir = MV_LEFT;
11170       else 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_RIGHT;
11174       else
11175         player->is_sleeping = FALSE;
11176
11177       player->dir_waiting = move_dir;
11178     }
11179
11180     if (player->is_sleeping)
11181     {
11182       if (player->num_special_action_sleeping > 0)
11183       {
11184         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11185         {
11186           int last_special_action = player->special_action_sleeping;
11187           int num_special_action = player->num_special_action_sleeping;
11188           int special_action =
11189             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11190              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11191              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11192              last_special_action + 1 : ACTION_SLEEPING);
11193           int special_graphic =
11194             el_act_dir2img(player->artwork_element, special_action, move_dir);
11195
11196           player->anim_delay_counter =
11197             graphic_info[special_graphic].anim_delay_fixed +
11198             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11199           player->post_delay_counter =
11200             graphic_info[special_graphic].post_delay_fixed +
11201             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11202
11203           player->special_action_sleeping = special_action;
11204         }
11205
11206         if (player->anim_delay_counter > 0)
11207         {
11208           player->action_waiting = player->special_action_sleeping;
11209           player->anim_delay_counter--;
11210         }
11211         else if (player->post_delay_counter > 0)
11212         {
11213           player->post_delay_counter--;
11214         }
11215       }
11216     }
11217     else if (player->is_bored)
11218     {
11219       if (player->num_special_action_bored > 0)
11220       {
11221         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11222         {
11223           int special_action =
11224             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11225           int special_graphic =
11226             el_act_dir2img(player->artwork_element, special_action, move_dir);
11227
11228           player->anim_delay_counter =
11229             graphic_info[special_graphic].anim_delay_fixed +
11230             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11231           player->post_delay_counter =
11232             graphic_info[special_graphic].post_delay_fixed +
11233             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11234
11235           player->special_action_bored = special_action;
11236         }
11237
11238         if (player->anim_delay_counter > 0)
11239         {
11240           player->action_waiting = player->special_action_bored;
11241           player->anim_delay_counter--;
11242         }
11243         else if (player->post_delay_counter > 0)
11244         {
11245           player->post_delay_counter--;
11246         }
11247       }
11248     }
11249   }
11250   else if (last_waiting)        /* waiting -> not waiting */
11251   {
11252     player->is_waiting = FALSE;
11253     player->is_bored = FALSE;
11254     player->is_sleeping = FALSE;
11255
11256     player->frame_counter_bored = -1;
11257     player->frame_counter_sleeping = -1;
11258
11259     player->anim_delay_counter = 0;
11260     player->post_delay_counter = 0;
11261
11262     player->dir_waiting = player->MovDir;
11263     player->action_waiting = ACTION_DEFAULT;
11264
11265     player->special_action_bored = ACTION_DEFAULT;
11266     player->special_action_sleeping = ACTION_DEFAULT;
11267   }
11268 }
11269
11270 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11271 {
11272   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11273   int left      = player_action & JOY_LEFT;
11274   int right     = player_action & JOY_RIGHT;
11275   int up        = player_action & JOY_UP;
11276   int down      = player_action & JOY_DOWN;
11277   int button1   = player_action & JOY_BUTTON_1;
11278   int button2   = player_action & JOY_BUTTON_2;
11279   int dx        = (left ? -1 : right ? 1 : 0);
11280   int dy        = (up   ? -1 : down  ? 1 : 0);
11281
11282   if (!player->active || tape.pausing)
11283     return 0;
11284
11285   if (player_action)
11286   {
11287     if (button1)
11288       snapped = SnapField(player, dx, dy);
11289     else
11290     {
11291       if (button2)
11292         dropped = DropElement(player);
11293
11294       moved = MovePlayer(player, dx, dy);
11295     }
11296
11297     if (tape.single_step && tape.recording && !tape.pausing)
11298     {
11299       if (button1 || (dropped && !moved))
11300       {
11301         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11302         SnapField(player, 0, 0);                /* stop snapping */
11303       }
11304     }
11305
11306     SetPlayerWaiting(player, FALSE);
11307
11308     return player_action;
11309   }
11310   else
11311   {
11312     /* no actions for this player (no input at player's configured device) */
11313
11314     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11315     SnapField(player, 0, 0);
11316     CheckGravityMovementWhenNotMoving(player);
11317
11318     if (player->MovPos == 0)
11319       SetPlayerWaiting(player, TRUE);
11320
11321     if (player->MovPos == 0)    /* needed for tape.playing */
11322       player->is_moving = FALSE;
11323
11324     player->is_dropping = FALSE;
11325     player->is_dropping_pressed = FALSE;
11326     player->drop_pressed_delay = 0;
11327
11328     return 0;
11329   }
11330 }
11331
11332 static void CheckLevelTime()
11333 {
11334   int i;
11335
11336   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11337   {
11338     if (level.native_em_level->lev->home == 0)  /* all players at home */
11339     {
11340       PlayerWins(local_player);
11341
11342       AllPlayersGone = TRUE;
11343
11344       level.native_em_level->lev->home = -1;
11345     }
11346
11347     if (level.native_em_level->ply[0]->alive == 0 &&
11348         level.native_em_level->ply[1]->alive == 0 &&
11349         level.native_em_level->ply[2]->alive == 0 &&
11350         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11351       AllPlayersGone = TRUE;
11352   }
11353
11354   if (TimeFrames >= FRAMES_PER_SECOND)
11355   {
11356     TimeFrames = 0;
11357     TapeTime++;
11358
11359     for (i = 0; i < MAX_PLAYERS; i++)
11360     {
11361       struct PlayerInfo *player = &stored_player[i];
11362
11363       if (SHIELD_ON(player))
11364       {
11365         player->shield_normal_time_left--;
11366
11367         if (player->shield_deadly_time_left > 0)
11368           player->shield_deadly_time_left--;
11369       }
11370     }
11371
11372     if (!local_player->LevelSolved && !level.use_step_counter)
11373     {
11374       TimePlayed++;
11375
11376       if (TimeLeft > 0)
11377       {
11378         TimeLeft--;
11379
11380         if (TimeLeft <= 10 && setup.time_limit)
11381           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11382
11383 #if 1
11384         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11385
11386         DisplayGameControlValues();
11387 #else
11388         DrawGameValue_Time(TimeLeft);
11389 #endif
11390
11391         if (!TimeLeft && setup.time_limit)
11392         {
11393           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11394             level.native_em_level->lev->killed_out_of_time = TRUE;
11395           else
11396             for (i = 0; i < MAX_PLAYERS; i++)
11397               KillPlayer(&stored_player[i]);
11398         }
11399       }
11400 #if 1
11401       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11402       {
11403         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11404
11405         DisplayGameControlValues();
11406       }
11407 #else
11408       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11409         DrawGameValue_Time(TimePlayed);
11410 #endif
11411
11412       level.native_em_level->lev->time =
11413         (level.time == 0 ? TimePlayed : TimeLeft);
11414     }
11415
11416     if (tape.recording || tape.playing)
11417       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11418   }
11419
11420   UpdateGameDoorValues();
11421   DrawGameDoorValues();
11422 }
11423
11424 void AdvanceFrameAndPlayerCounters(int player_nr)
11425 {
11426   int i;
11427
11428   /* advance frame counters (global frame counter and time frame counter) */
11429   FrameCounter++;
11430   TimeFrames++;
11431
11432   /* advance player counters (counters for move delay, move animation etc.) */
11433   for (i = 0; i < MAX_PLAYERS; i++)
11434   {
11435     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11436     int move_delay_value = stored_player[i].move_delay_value;
11437     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11438
11439     if (!advance_player_counters)       /* not all players may be affected */
11440       continue;
11441
11442 #if USE_NEW_PLAYER_ANIM
11443     if (move_frames == 0)       /* less than one move per game frame */
11444     {
11445       int stepsize = TILEX / move_delay_value;
11446       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11447       int count = (stored_player[i].is_moving ?
11448                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11449
11450       if (count % delay == 0)
11451         move_frames = 1;
11452     }
11453 #endif
11454
11455     stored_player[i].Frame += move_frames;
11456
11457     if (stored_player[i].MovPos != 0)
11458       stored_player[i].StepFrame += move_frames;
11459
11460     if (stored_player[i].move_delay > 0)
11461       stored_player[i].move_delay--;
11462
11463     /* due to bugs in previous versions, counter must count up, not down */
11464     if (stored_player[i].push_delay != -1)
11465       stored_player[i].push_delay++;
11466
11467     if (stored_player[i].drop_delay > 0)
11468       stored_player[i].drop_delay--;
11469
11470     if (stored_player[i].is_dropping_pressed)
11471       stored_player[i].drop_pressed_delay++;
11472   }
11473 }
11474
11475 void StartGameActions(boolean init_network_game, boolean record_tape,
11476                       long random_seed)
11477 {
11478   unsigned long new_random_seed = InitRND(random_seed);
11479
11480   if (record_tape)
11481     TapeStartRecording(new_random_seed);
11482
11483 #if defined(NETWORK_AVALIABLE)
11484   if (init_network_game)
11485   {
11486     SendToServer_StartPlaying();
11487
11488     return;
11489   }
11490 #endif
11491
11492   InitGame();
11493 }
11494
11495 void GameActions()
11496 {
11497   static unsigned long game_frame_delay = 0;
11498   unsigned long game_frame_delay_value;
11499   byte *recorded_player_action;
11500   byte summarized_player_action = 0;
11501   byte tape_action[MAX_PLAYERS];
11502   int i;
11503
11504   /* detect endless loops, caused by custom element programming */
11505   if (recursion_loop_detected && recursion_loop_depth == 0)
11506   {
11507     char *message = getStringCat3("Internal Error ! Element ",
11508                                   EL_NAME(recursion_loop_element),
11509                                   " caused endless loop ! Quit the game ?");
11510
11511     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11512           EL_NAME(recursion_loop_element));
11513
11514     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11515
11516     recursion_loop_detected = FALSE;    /* if game should be continued */
11517
11518     free(message);
11519
11520     return;
11521   }
11522
11523   if (game.restart_level)
11524     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11525
11526   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11527   {
11528     if (level.native_em_level->lev->home == 0)  /* all players at home */
11529     {
11530       PlayerWins(local_player);
11531
11532       AllPlayersGone = TRUE;
11533
11534       level.native_em_level->lev->home = -1;
11535     }
11536
11537     if (level.native_em_level->ply[0]->alive == 0 &&
11538         level.native_em_level->ply[1]->alive == 0 &&
11539         level.native_em_level->ply[2]->alive == 0 &&
11540         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11541       AllPlayersGone = TRUE;
11542   }
11543
11544   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11545     GameWon();
11546
11547   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11548     TapeStop();
11549
11550   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11551     return;
11552
11553   game_frame_delay_value =
11554     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11555
11556   if (tape.playing && tape.warp_forward && !tape.pausing)
11557     game_frame_delay_value = 0;
11558
11559   /* ---------- main game synchronization point ---------- */
11560
11561   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11562
11563   if (network_playing && !network_player_action_received)
11564   {
11565     /* try to get network player actions in time */
11566
11567 #if defined(NETWORK_AVALIABLE)
11568     /* last chance to get network player actions without main loop delay */
11569     HandleNetworking();
11570 #endif
11571
11572     /* game was quit by network peer */
11573     if (game_status != GAME_MODE_PLAYING)
11574       return;
11575
11576     if (!network_player_action_received)
11577       return;           /* failed to get network player actions in time */
11578
11579     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11580   }
11581
11582   if (tape.pausing)
11583     return;
11584
11585   /* at this point we know that we really continue executing the game */
11586
11587   network_player_action_received = FALSE;
11588
11589   /* when playing tape, read previously recorded player input from tape data */
11590   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11591
11592 #if 1
11593   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11594   if (tape.pausing)
11595     return;
11596 #endif
11597
11598   if (tape.set_centered_player)
11599   {
11600     game.centered_player_nr_next = tape.centered_player_nr_next;
11601     game.set_centered_player = TRUE;
11602   }
11603
11604   for (i = 0; i < MAX_PLAYERS; i++)
11605   {
11606     summarized_player_action |= stored_player[i].action;
11607
11608     if (!network_playing)
11609       stored_player[i].effective_action = stored_player[i].action;
11610   }
11611
11612 #if defined(NETWORK_AVALIABLE)
11613   if (network_playing)
11614     SendToServer_MovePlayer(summarized_player_action);
11615 #endif
11616
11617   if (!options.network && !setup.team_mode)
11618     local_player->effective_action = summarized_player_action;
11619
11620   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11621   {
11622     for (i = 0; i < MAX_PLAYERS; i++)
11623       stored_player[i].effective_action =
11624         (i == game.centered_player_nr ? summarized_player_action : 0);
11625   }
11626
11627   if (recorded_player_action != NULL)
11628     for (i = 0; i < MAX_PLAYERS; i++)
11629       stored_player[i].effective_action = recorded_player_action[i];
11630
11631   for (i = 0; i < MAX_PLAYERS; i++)
11632   {
11633     tape_action[i] = stored_player[i].effective_action;
11634
11635     /* (this can only happen in the R'n'D game engine) */
11636     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11637       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11638   }
11639
11640   /* only record actions from input devices, but not programmed actions */
11641   if (tape.recording)
11642     TapeRecordAction(tape_action);
11643
11644   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11645   {
11646     GameActions_EM_Main();
11647   }
11648   else
11649   {
11650     GameActions_RND();
11651   }
11652 }
11653
11654 void GameActions_EM_Main()
11655 {
11656   byte effective_action[MAX_PLAYERS];
11657   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11658   int i;
11659
11660   for (i = 0; i < MAX_PLAYERS; i++)
11661     effective_action[i] = stored_player[i].effective_action;
11662
11663   GameActions_EM(effective_action, warp_mode);
11664
11665   CheckLevelTime();
11666
11667   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11668 }
11669
11670 void GameActions_RND()
11671 {
11672   int magic_wall_x = 0, magic_wall_y = 0;
11673   int i, x, y, element, graphic;
11674
11675   InitPlayfieldScanModeVars();
11676
11677 #if USE_ONE_MORE_CHANGE_PER_FRAME
11678   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11679   {
11680     SCAN_PLAYFIELD(x, y)
11681     {
11682       ChangeCount[x][y] = 0;
11683       ChangeEvent[x][y] = -1;
11684     }
11685   }
11686 #endif
11687
11688   if (game.set_centered_player)
11689   {
11690     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11691
11692     /* switching to "all players" only possible if all players fit to screen */
11693     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11694     {
11695       game.centered_player_nr_next = game.centered_player_nr;
11696       game.set_centered_player = FALSE;
11697     }
11698
11699     /* do not switch focus to non-existing (or non-active) player */
11700     if (game.centered_player_nr_next >= 0 &&
11701         !stored_player[game.centered_player_nr_next].active)
11702     {
11703       game.centered_player_nr_next = game.centered_player_nr;
11704       game.set_centered_player = FALSE;
11705     }
11706   }
11707
11708   if (game.set_centered_player &&
11709       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11710   {
11711     int sx, sy;
11712
11713     if (game.centered_player_nr_next == -1)
11714     {
11715       setScreenCenteredToAllPlayers(&sx, &sy);
11716     }
11717     else
11718     {
11719       sx = stored_player[game.centered_player_nr_next].jx;
11720       sy = stored_player[game.centered_player_nr_next].jy;
11721     }
11722
11723     game.centered_player_nr = game.centered_player_nr_next;
11724     game.set_centered_player = FALSE;
11725
11726     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11727     DrawGameDoorValues();
11728   }
11729
11730   for (i = 0; i < MAX_PLAYERS; i++)
11731   {
11732     int actual_player_action = stored_player[i].effective_action;
11733
11734 #if 1
11735     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11736        - rnd_equinox_tetrachloride 048
11737        - rnd_equinox_tetrachloride_ii 096
11738        - rnd_emanuel_schmieg 002
11739        - doctor_sloan_ww 001, 020
11740     */
11741     if (stored_player[i].MovPos == 0)
11742       CheckGravityMovement(&stored_player[i]);
11743 #endif
11744
11745     /* overwrite programmed action with tape action */
11746     if (stored_player[i].programmed_action)
11747       actual_player_action = stored_player[i].programmed_action;
11748
11749     PlayerActions(&stored_player[i], actual_player_action);
11750
11751     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11752   }
11753
11754   ScrollScreen(NULL, SCROLL_GO_ON);
11755
11756   /* for backwards compatibility, the following code emulates a fixed bug that
11757      occured when pushing elements (causing elements that just made their last
11758      pushing step to already (if possible) make their first falling step in the
11759      same game frame, which is bad); this code is also needed to use the famous
11760      "spring push bug" which is used in older levels and might be wanted to be
11761      used also in newer levels, but in this case the buggy pushing code is only
11762      affecting the "spring" element and no other elements */
11763
11764   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11765   {
11766     for (i = 0; i < MAX_PLAYERS; i++)
11767     {
11768       struct PlayerInfo *player = &stored_player[i];
11769       int x = player->jx;
11770       int y = player->jy;
11771
11772       if (player->active && player->is_pushing && player->is_moving &&
11773           IS_MOVING(x, y) &&
11774           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11775            Feld[x][y] == EL_SPRING))
11776       {
11777         ContinueMoving(x, y);
11778
11779         /* continue moving after pushing (this is actually a bug) */
11780         if (!IS_MOVING(x, y))
11781           Stop[x][y] = FALSE;
11782       }
11783     }
11784   }
11785
11786 #if 0
11787   debug_print_timestamp(0, "start main loop profiling");
11788 #endif
11789
11790   SCAN_PLAYFIELD(x, y)
11791   {
11792     ChangeCount[x][y] = 0;
11793     ChangeEvent[x][y] = -1;
11794
11795     /* this must be handled before main playfield loop */
11796     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11797     {
11798       MovDelay[x][y]--;
11799       if (MovDelay[x][y] <= 0)
11800         RemoveField(x, y);
11801     }
11802
11803 #if USE_NEW_SNAP_DELAY
11804     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11805     {
11806       MovDelay[x][y]--;
11807       if (MovDelay[x][y] <= 0)
11808       {
11809         RemoveField(x, y);
11810         DrawLevelField(x, y);
11811
11812         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11813       }
11814     }
11815 #endif
11816
11817 #if DEBUG
11818     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11819     {
11820       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11821       printf("GameActions(): This should never happen!\n");
11822
11823       ChangePage[x][y] = -1;
11824     }
11825 #endif
11826
11827     Stop[x][y] = FALSE;
11828     if (WasJustMoving[x][y] > 0)
11829       WasJustMoving[x][y]--;
11830     if (WasJustFalling[x][y] > 0)
11831       WasJustFalling[x][y]--;
11832     if (CheckCollision[x][y] > 0)
11833       CheckCollision[x][y]--;
11834     if (CheckImpact[x][y] > 0)
11835       CheckImpact[x][y]--;
11836
11837     GfxFrame[x][y]++;
11838
11839     /* reset finished pushing action (not done in ContinueMoving() to allow
11840        continuous pushing animation for elements with zero push delay) */
11841     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11842     {
11843       ResetGfxAnimation(x, y);
11844       DrawLevelField(x, y);
11845     }
11846
11847 #if DEBUG
11848     if (IS_BLOCKED(x, y))
11849     {
11850       int oldx, oldy;
11851
11852       Blocked2Moving(x, y, &oldx, &oldy);
11853       if (!IS_MOVING(oldx, oldy))
11854       {
11855         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11856         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11857         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11858         printf("GameActions(): This should never happen!\n");
11859       }
11860     }
11861 #endif
11862   }
11863
11864 #if 0
11865   debug_print_timestamp(0, "- time for pre-main loop:");
11866 #endif
11867
11868 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11869   SCAN_PLAYFIELD(x, y)
11870   {
11871     element = Feld[x][y];
11872     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11873
11874 #if 1
11875     {
11876 #if 1
11877       int element2 = element;
11878       int graphic2 = graphic;
11879 #else
11880       int element2 = Feld[x][y];
11881       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11882 #endif
11883       int last_gfx_frame = GfxFrame[x][y];
11884
11885       if (graphic_info[graphic2].anim_global_sync)
11886         GfxFrame[x][y] = FrameCounter;
11887       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11888         GfxFrame[x][y] = CustomValue[x][y];
11889       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11890         GfxFrame[x][y] = element_info[element2].collect_score;
11891       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11892         GfxFrame[x][y] = ChangeDelay[x][y];
11893
11894       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11895         DrawLevelGraphicAnimation(x, y, graphic2);
11896     }
11897 #else
11898     ResetGfxFrame(x, y, TRUE);
11899 #endif
11900
11901 #if 1
11902     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11903         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11904       ResetRandomAnimationValue(x, y);
11905 #endif
11906
11907 #if 1
11908     SetRandomAnimationValue(x, y);
11909 #endif
11910
11911 #if 1
11912     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11913 #endif
11914   }
11915 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11916
11917 #if 0
11918   debug_print_timestamp(0, "- time for TEST loop:     -->");
11919 #endif
11920
11921   SCAN_PLAYFIELD(x, y)
11922   {
11923     element = Feld[x][y];
11924     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11925
11926     ResetGfxFrame(x, y, TRUE);
11927
11928     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11929         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11930       ResetRandomAnimationValue(x, y);
11931
11932     SetRandomAnimationValue(x, y);
11933
11934     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11935
11936     if (IS_INACTIVE(element))
11937     {
11938       if (IS_ANIMATED(graphic))
11939         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11940
11941       continue;
11942     }
11943
11944     /* this may take place after moving, so 'element' may have changed */
11945     if (IS_CHANGING(x, y) &&
11946         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11947     {
11948       int page = element_info[element].event_page_nr[CE_DELAY];
11949
11950 #if 1
11951       HandleElementChange(x, y, page);
11952 #else
11953       if (CAN_CHANGE(element))
11954         HandleElementChange(x, y, page);
11955
11956       if (HAS_ACTION(element))
11957         ExecuteCustomElementAction(x, y, element, page);
11958 #endif
11959
11960       element = Feld[x][y];
11961       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11962     }
11963
11964 #if 0   // ---------------------------------------------------------------------
11965
11966     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11967     {
11968       StartMoving(x, y);
11969
11970       element = Feld[x][y];
11971       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11972
11973       if (IS_ANIMATED(graphic) &&
11974           !IS_MOVING(x, y) &&
11975           !Stop[x][y])
11976         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11977
11978       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11979         DrawTwinkleOnField(x, y);
11980     }
11981     else if (IS_MOVING(x, y))
11982       ContinueMoving(x, y);
11983     else
11984     {
11985       switch (element)
11986       {
11987         case EL_ACID:
11988         case EL_EXIT_OPEN:
11989         case EL_EM_EXIT_OPEN:
11990         case EL_SP_EXIT_OPEN:
11991         case EL_STEEL_EXIT_OPEN:
11992         case EL_EM_STEEL_EXIT_OPEN:
11993         case EL_SP_TERMINAL:
11994         case EL_SP_TERMINAL_ACTIVE:
11995         case EL_EXTRA_TIME:
11996         case EL_SHIELD_NORMAL:
11997         case EL_SHIELD_DEADLY:
11998           if (IS_ANIMATED(graphic))
11999             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12000           break;
12001
12002         case EL_DYNAMITE_ACTIVE:
12003         case EL_EM_DYNAMITE_ACTIVE:
12004         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12005         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12006         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12007         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12008         case EL_SP_DISK_RED_ACTIVE:
12009           CheckDynamite(x, y);
12010           break;
12011
12012         case EL_AMOEBA_GROWING:
12013           AmoebeWaechst(x, y);
12014           break;
12015
12016         case EL_AMOEBA_SHRINKING:
12017           AmoebaDisappearing(x, y);
12018           break;
12019
12020 #if !USE_NEW_AMOEBA_CODE
12021         case EL_AMOEBA_WET:
12022         case EL_AMOEBA_DRY:
12023         case EL_AMOEBA_FULL:
12024         case EL_BD_AMOEBA:
12025         case EL_EMC_DRIPPER:
12026           AmoebeAbleger(x, y);
12027           break;
12028 #endif
12029
12030         case EL_GAME_OF_LIFE:
12031         case EL_BIOMAZE:
12032           Life(x, y);
12033           break;
12034
12035         case EL_EXIT_CLOSED:
12036           CheckExit(x, y);
12037           break;
12038
12039         case EL_EM_EXIT_CLOSED:
12040           CheckExitEM(x, y);
12041           break;
12042
12043         case EL_STEEL_EXIT_CLOSED:
12044           CheckExitSteel(x, y);
12045           break;
12046
12047         case EL_EM_STEEL_EXIT_CLOSED:
12048           CheckExitSteelEM(x, y);
12049           break;
12050
12051         case EL_SP_EXIT_CLOSED:
12052           CheckExitSP(x, y);
12053           break;
12054
12055         case EL_EXPANDABLE_WALL_GROWING:
12056         case EL_EXPANDABLE_STEELWALL_GROWING:
12057           MauerWaechst(x, y);
12058           break;
12059
12060         case EL_EXPANDABLE_WALL:
12061         case EL_EXPANDABLE_WALL_HORIZONTAL:
12062         case EL_EXPANDABLE_WALL_VERTICAL:
12063         case EL_EXPANDABLE_WALL_ANY:
12064         case EL_BD_EXPANDABLE_WALL:
12065           MauerAbleger(x, y);
12066           break;
12067
12068         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12069         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12070         case EL_EXPANDABLE_STEELWALL_ANY:
12071           MauerAblegerStahl(x, y);
12072           break;
12073
12074         case EL_FLAMES:
12075           CheckForDragon(x, y);
12076           break;
12077
12078         case EL_EXPLOSION:
12079           break;
12080
12081         case EL_ELEMENT_SNAPPING:
12082         case EL_DIAGONAL_SHRINKING:
12083         case EL_DIAGONAL_GROWING:
12084         {
12085           graphic =
12086             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12087
12088           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12089           break;
12090         }
12091
12092         default:
12093           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12094             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12095           break;
12096       }
12097     }
12098
12099 #else   // ---------------------------------------------------------------------
12100
12101     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12102     {
12103       StartMoving(x, y);
12104
12105       element = Feld[x][y];
12106       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12107
12108       if (IS_ANIMATED(graphic) &&
12109           !IS_MOVING(x, y) &&
12110           !Stop[x][y])
12111         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12112
12113       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12114         DrawTwinkleOnField(x, y);
12115     }
12116     else if ((element == EL_ACID ||
12117               element == EL_EXIT_OPEN ||
12118               element == EL_EM_EXIT_OPEN ||
12119               element == EL_SP_EXIT_OPEN ||
12120               element == EL_STEEL_EXIT_OPEN ||
12121               element == EL_EM_STEEL_EXIT_OPEN ||
12122               element == EL_SP_TERMINAL ||
12123               element == EL_SP_TERMINAL_ACTIVE ||
12124               element == EL_EXTRA_TIME ||
12125               element == EL_SHIELD_NORMAL ||
12126               element == EL_SHIELD_DEADLY) &&
12127              IS_ANIMATED(graphic))
12128       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12129     else if (IS_MOVING(x, y))
12130       ContinueMoving(x, y);
12131     else if (IS_ACTIVE_BOMB(element))
12132       CheckDynamite(x, y);
12133     else if (element == EL_AMOEBA_GROWING)
12134       AmoebeWaechst(x, y);
12135     else if (element == EL_AMOEBA_SHRINKING)
12136       AmoebaDisappearing(x, y);
12137
12138 #if !USE_NEW_AMOEBA_CODE
12139     else if (IS_AMOEBALIVE(element))
12140       AmoebeAbleger(x, y);
12141 #endif
12142
12143     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12144       Life(x, y);
12145     else if (element == EL_EXIT_CLOSED)
12146       CheckExit(x, y);
12147     else if (element == EL_EM_EXIT_CLOSED)
12148       CheckExitEM(x, y);
12149     else if (element == EL_STEEL_EXIT_CLOSED)
12150       CheckExitSteel(x, y);
12151     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12152       CheckExitSteelEM(x, y);
12153     else if (element == EL_SP_EXIT_CLOSED)
12154       CheckExitSP(x, y);
12155     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12156              element == EL_EXPANDABLE_STEELWALL_GROWING)
12157       MauerWaechst(x, y);
12158     else if (element == EL_EXPANDABLE_WALL ||
12159              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12160              element == EL_EXPANDABLE_WALL_VERTICAL ||
12161              element == EL_EXPANDABLE_WALL_ANY ||
12162              element == EL_BD_EXPANDABLE_WALL)
12163       MauerAbleger(x, y);
12164     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12165              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12166              element == EL_EXPANDABLE_STEELWALL_ANY)
12167       MauerAblegerStahl(x, y);
12168     else if (element == EL_FLAMES)
12169       CheckForDragon(x, y);
12170     else if (element == EL_EXPLOSION)
12171       ; /* drawing of correct explosion animation is handled separately */
12172     else if (element == EL_ELEMENT_SNAPPING ||
12173              element == EL_DIAGONAL_SHRINKING ||
12174              element == EL_DIAGONAL_GROWING)
12175     {
12176       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12177
12178       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12179     }
12180     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12181       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12182
12183 #endif  // ---------------------------------------------------------------------
12184
12185     if (IS_BELT_ACTIVE(element))
12186       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12187
12188     if (game.magic_wall_active)
12189     {
12190       int jx = local_player->jx, jy = local_player->jy;
12191
12192       /* play the element sound at the position nearest to the player */
12193       if ((element == EL_MAGIC_WALL_FULL ||
12194            element == EL_MAGIC_WALL_ACTIVE ||
12195            element == EL_MAGIC_WALL_EMPTYING ||
12196            element == EL_BD_MAGIC_WALL_FULL ||
12197            element == EL_BD_MAGIC_WALL_ACTIVE ||
12198            element == EL_BD_MAGIC_WALL_EMPTYING ||
12199            element == EL_DC_MAGIC_WALL_FULL ||
12200            element == EL_DC_MAGIC_WALL_ACTIVE ||
12201            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12202           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12203       {
12204         magic_wall_x = x;
12205         magic_wall_y = y;
12206       }
12207     }
12208   }
12209
12210 #if 0
12211   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12212 #endif
12213
12214 #if USE_NEW_AMOEBA_CODE
12215   /* new experimental amoeba growth stuff */
12216   if (!(FrameCounter % 8))
12217   {
12218     static unsigned long random = 1684108901;
12219
12220     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12221     {
12222       x = RND(lev_fieldx);
12223       y = RND(lev_fieldy);
12224       element = Feld[x][y];
12225
12226       if (!IS_PLAYER(x,y) &&
12227           (element == EL_EMPTY ||
12228            CAN_GROW_INTO(element) ||
12229            element == EL_QUICKSAND_EMPTY ||
12230            element == EL_QUICKSAND_FAST_EMPTY ||
12231            element == EL_ACID_SPLASH_LEFT ||
12232            element == EL_ACID_SPLASH_RIGHT))
12233       {
12234         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12235             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12236             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12237             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12238           Feld[x][y] = EL_AMOEBA_DROP;
12239       }
12240
12241       random = random * 129 + 1;
12242     }
12243   }
12244 #endif
12245
12246 #if 0
12247   if (game.explosions_delayed)
12248 #endif
12249   {
12250     game.explosions_delayed = FALSE;
12251
12252     SCAN_PLAYFIELD(x, y)
12253     {
12254       element = Feld[x][y];
12255
12256       if (ExplodeField[x][y])
12257         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12258       else if (element == EL_EXPLOSION)
12259         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12260
12261       ExplodeField[x][y] = EX_TYPE_NONE;
12262     }
12263
12264     game.explosions_delayed = TRUE;
12265   }
12266
12267   if (game.magic_wall_active)
12268   {
12269     if (!(game.magic_wall_time_left % 4))
12270     {
12271       int element = Feld[magic_wall_x][magic_wall_y];
12272
12273       if (element == EL_BD_MAGIC_WALL_FULL ||
12274           element == EL_BD_MAGIC_WALL_ACTIVE ||
12275           element == EL_BD_MAGIC_WALL_EMPTYING)
12276         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12277       else if (element == EL_DC_MAGIC_WALL_FULL ||
12278                element == EL_DC_MAGIC_WALL_ACTIVE ||
12279                element == EL_DC_MAGIC_WALL_EMPTYING)
12280         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12281       else
12282         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12283     }
12284
12285     if (game.magic_wall_time_left > 0)
12286     {
12287       game.magic_wall_time_left--;
12288
12289       if (!game.magic_wall_time_left)
12290       {
12291         SCAN_PLAYFIELD(x, y)
12292         {
12293           element = Feld[x][y];
12294
12295           if (element == EL_MAGIC_WALL_ACTIVE ||
12296               element == EL_MAGIC_WALL_FULL)
12297           {
12298             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12299             DrawLevelField(x, y);
12300           }
12301           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12302                    element == EL_BD_MAGIC_WALL_FULL)
12303           {
12304             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12305             DrawLevelField(x, y);
12306           }
12307           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12308                    element == EL_DC_MAGIC_WALL_FULL)
12309           {
12310             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12311             DrawLevelField(x, y);
12312           }
12313         }
12314
12315         game.magic_wall_active = FALSE;
12316       }
12317     }
12318   }
12319
12320   if (game.light_time_left > 0)
12321   {
12322     game.light_time_left--;
12323
12324     if (game.light_time_left == 0)
12325       RedrawAllLightSwitchesAndInvisibleElements();
12326   }
12327
12328   if (game.timegate_time_left > 0)
12329   {
12330     game.timegate_time_left--;
12331
12332     if (game.timegate_time_left == 0)
12333       CloseAllOpenTimegates();
12334   }
12335
12336   if (game.lenses_time_left > 0)
12337   {
12338     game.lenses_time_left--;
12339
12340     if (game.lenses_time_left == 0)
12341       RedrawAllInvisibleElementsForLenses();
12342   }
12343
12344   if (game.magnify_time_left > 0)
12345   {
12346     game.magnify_time_left--;
12347
12348     if (game.magnify_time_left == 0)
12349       RedrawAllInvisibleElementsForMagnifier();
12350   }
12351
12352   for (i = 0; i < MAX_PLAYERS; i++)
12353   {
12354     struct PlayerInfo *player = &stored_player[i];
12355
12356     if (SHIELD_ON(player))
12357     {
12358       if (player->shield_deadly_time_left)
12359         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12360       else if (player->shield_normal_time_left)
12361         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12362     }
12363   }
12364
12365   CheckLevelTime();
12366
12367   DrawAllPlayers();
12368   PlayAllPlayersSound();
12369
12370   if (options.debug)                    /* calculate frames per second */
12371   {
12372     static unsigned long fps_counter = 0;
12373     static int fps_frames = 0;
12374     unsigned long fps_delay_ms = Counter() - fps_counter;
12375
12376     fps_frames++;
12377
12378     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12379     {
12380       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12381
12382       fps_frames = 0;
12383       fps_counter = Counter();
12384     }
12385
12386     redraw_mask |= REDRAW_FPS;
12387   }
12388
12389   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12390
12391   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12392   {
12393     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12394
12395     local_player->show_envelope = 0;
12396   }
12397
12398 #if 0
12399   debug_print_timestamp(0, "stop main loop profiling ");
12400   printf("----------------------------------------------------------\n");
12401 #endif
12402
12403   /* use random number generator in every frame to make it less predictable */
12404   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12405     RND(1);
12406 }
12407
12408 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12409 {
12410   int min_x = x, min_y = y, max_x = x, max_y = y;
12411   int i;
12412
12413   for (i = 0; i < MAX_PLAYERS; i++)
12414   {
12415     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12416
12417     if (!stored_player[i].active || &stored_player[i] == player)
12418       continue;
12419
12420     min_x = MIN(min_x, jx);
12421     min_y = MIN(min_y, jy);
12422     max_x = MAX(max_x, jx);
12423     max_y = MAX(max_y, jy);
12424   }
12425
12426   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12427 }
12428
12429 static boolean AllPlayersInVisibleScreen()
12430 {
12431   int i;
12432
12433   for (i = 0; i < MAX_PLAYERS; i++)
12434   {
12435     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12436
12437     if (!stored_player[i].active)
12438       continue;
12439
12440     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12441       return FALSE;
12442   }
12443
12444   return TRUE;
12445 }
12446
12447 void ScrollLevel(int dx, int dy)
12448 {
12449 #if 1
12450   static Bitmap *bitmap_db_field2 = NULL;
12451   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12452   int x, y;
12453 #else
12454   int i, x, y;
12455 #endif
12456
12457 #if 0
12458   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12459   /* only horizontal XOR vertical scroll direction allowed */
12460   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12461     return;
12462 #endif
12463
12464 #if 1
12465   if (bitmap_db_field2 == NULL)
12466     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12467
12468   /* needed when blitting directly to same bitmap -- should not be needed with
12469      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12470   BlitBitmap(drawto_field, bitmap_db_field2,
12471              FX + TILEX * (dx == -1) - softscroll_offset,
12472              FY + TILEY * (dy == -1) - softscroll_offset,
12473              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12474              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12475              FX + TILEX * (dx == 1) - softscroll_offset,
12476              FY + TILEY * (dy == 1) - softscroll_offset);
12477   BlitBitmap(bitmap_db_field2, drawto_field,
12478              FX + TILEX * (dx == 1) - softscroll_offset,
12479              FY + TILEY * (dy == 1) - softscroll_offset,
12480              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12481              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12482              FX + TILEX * (dx == 1) - softscroll_offset,
12483              FY + TILEY * (dy == 1) - softscroll_offset);
12484
12485 #else
12486
12487 #if 1
12488   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12489   int xsize = (BX2 - BX1 + 1);
12490   int ysize = (BY2 - BY1 + 1);
12491   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12492   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12493   int step  = (start < end ? +1 : -1);
12494
12495   for (i = start; i != end; i += step)
12496   {
12497     BlitBitmap(drawto_field, drawto_field,
12498                FX + TILEX * (dx != 0 ? i + step : 0),
12499                FY + TILEY * (dy != 0 ? i + step : 0),
12500                TILEX * (dx != 0 ? 1 : xsize),
12501                TILEY * (dy != 0 ? 1 : ysize),
12502                FX + TILEX * (dx != 0 ? i : 0),
12503                FY + TILEY * (dy != 0 ? i : 0));
12504   }
12505
12506 #else
12507
12508   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12509
12510   BlitBitmap(drawto_field, drawto_field,
12511              FX + TILEX * (dx == -1) - softscroll_offset,
12512              FY + TILEY * (dy == -1) - softscroll_offset,
12513              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12514              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12515              FX + TILEX * (dx == 1) - softscroll_offset,
12516              FY + TILEY * (dy == 1) - softscroll_offset);
12517 #endif
12518 #endif
12519
12520   if (dx != 0)
12521   {
12522     x = (dx == 1 ? BX1 : BX2);
12523     for (y = BY1; y <= BY2; y++)
12524       DrawScreenField(x, y);
12525   }
12526
12527   if (dy != 0)
12528   {
12529     y = (dy == 1 ? BY1 : BY2);
12530     for (x = BX1; x <= BX2; x++)
12531       DrawScreenField(x, y);
12532   }
12533
12534   redraw_mask |= REDRAW_FIELD;
12535 }
12536
12537 static boolean canFallDown(struct PlayerInfo *player)
12538 {
12539   int jx = player->jx, jy = player->jy;
12540
12541   return (IN_LEV_FIELD(jx, jy + 1) &&
12542           (IS_FREE(jx, jy + 1) ||
12543            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12544           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12545           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12546 }
12547
12548 static boolean canPassField(int x, int y, int move_dir)
12549 {
12550   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12551   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12552   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12553   int nextx = x + dx;
12554   int nexty = y + dy;
12555   int element = Feld[x][y];
12556
12557   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12558           !CAN_MOVE(element) &&
12559           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12560           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12561           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12562 }
12563
12564 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12565 {
12566   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12567   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12568   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12569   int newx = x + dx;
12570   int newy = y + dy;
12571
12572   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12573           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12574           (IS_DIGGABLE(Feld[newx][newy]) ||
12575            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12576            canPassField(newx, newy, move_dir)));
12577 }
12578
12579 static void CheckGravityMovement(struct PlayerInfo *player)
12580 {
12581 #if USE_PLAYER_GRAVITY
12582   if (player->gravity && !player->programmed_action)
12583 #else
12584   if (game.gravity && !player->programmed_action)
12585 #endif
12586   {
12587     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12588     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12589     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12590     int jx = player->jx, jy = player->jy;
12591     boolean player_is_moving_to_valid_field =
12592       (!player_is_snapping &&
12593        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12594         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12595     boolean player_can_fall_down = canFallDown(player);
12596
12597     if (player_can_fall_down &&
12598         !player_is_moving_to_valid_field)
12599       player->programmed_action = MV_DOWN;
12600   }
12601 }
12602
12603 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12604 {
12605   return CheckGravityMovement(player);
12606
12607 #if USE_PLAYER_GRAVITY
12608   if (player->gravity && !player->programmed_action)
12609 #else
12610   if (game.gravity && !player->programmed_action)
12611 #endif
12612   {
12613     int jx = player->jx, jy = player->jy;
12614     boolean field_under_player_is_free =
12615       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12616     boolean player_is_standing_on_valid_field =
12617       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12618        (IS_WALKABLE(Feld[jx][jy]) &&
12619         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12620
12621     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12622       player->programmed_action = MV_DOWN;
12623   }
12624 }
12625
12626 /*
12627   MovePlayerOneStep()
12628   -----------------------------------------------------------------------------
12629   dx, dy:               direction (non-diagonal) to try to move the player to
12630   real_dx, real_dy:     direction as read from input device (can be diagonal)
12631 */
12632
12633 boolean MovePlayerOneStep(struct PlayerInfo *player,
12634                           int dx, int dy, int real_dx, int real_dy)
12635 {
12636   int jx = player->jx, jy = player->jy;
12637   int new_jx = jx + dx, new_jy = jy + dy;
12638 #if !USE_FIXED_DONT_RUN_INTO
12639   int element;
12640 #endif
12641   int can_move;
12642   boolean player_can_move = !player->cannot_move;
12643
12644   if (!player->active || (!dx && !dy))
12645     return MP_NO_ACTION;
12646
12647   player->MovDir = (dx < 0 ? MV_LEFT :
12648                     dx > 0 ? MV_RIGHT :
12649                     dy < 0 ? MV_UP :
12650                     dy > 0 ? MV_DOWN :  MV_NONE);
12651
12652   if (!IN_LEV_FIELD(new_jx, new_jy))
12653     return MP_NO_ACTION;
12654
12655   if (!player_can_move)
12656   {
12657     if (player->MovPos == 0)
12658     {
12659       player->is_moving = FALSE;
12660       player->is_digging = FALSE;
12661       player->is_collecting = FALSE;
12662       player->is_snapping = FALSE;
12663       player->is_pushing = FALSE;
12664     }
12665   }
12666
12667 #if 1
12668   if (!options.network && game.centered_player_nr == -1 &&
12669       !AllPlayersInSight(player, new_jx, new_jy))
12670     return MP_NO_ACTION;
12671 #else
12672   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12673     return MP_NO_ACTION;
12674 #endif
12675
12676 #if !USE_FIXED_DONT_RUN_INTO
12677   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12678
12679   /* (moved to DigField()) */
12680   if (player_can_move && DONT_RUN_INTO(element))
12681   {
12682     if (element == EL_ACID && dx == 0 && dy == 1)
12683     {
12684       SplashAcid(new_jx, new_jy);
12685       Feld[jx][jy] = EL_PLAYER_1;
12686       InitMovingField(jx, jy, MV_DOWN);
12687       Store[jx][jy] = EL_ACID;
12688       ContinueMoving(jx, jy);
12689       BuryPlayer(player);
12690     }
12691     else
12692       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12693
12694     return MP_MOVING;
12695   }
12696 #endif
12697
12698   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12699   if (can_move != MP_MOVING)
12700     return can_move;
12701
12702   /* check if DigField() has caused relocation of the player */
12703   if (player->jx != jx || player->jy != jy)
12704     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12705
12706   StorePlayer[jx][jy] = 0;
12707   player->last_jx = jx;
12708   player->last_jy = jy;
12709   player->jx = new_jx;
12710   player->jy = new_jy;
12711   StorePlayer[new_jx][new_jy] = player->element_nr;
12712
12713   if (player->move_delay_value_next != -1)
12714   {
12715     player->move_delay_value = player->move_delay_value_next;
12716     player->move_delay_value_next = -1;
12717   }
12718
12719   player->MovPos =
12720     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12721
12722   player->step_counter++;
12723
12724   PlayerVisit[jx][jy] = FrameCounter;
12725
12726 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12727   player->is_moving = TRUE;
12728 #endif
12729
12730 #if 1
12731   /* should better be called in MovePlayer(), but this breaks some tapes */
12732   ScrollPlayer(player, SCROLL_INIT);
12733 #endif
12734
12735   return MP_MOVING;
12736 }
12737
12738 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12739 {
12740   int jx = player->jx, jy = player->jy;
12741   int old_jx = jx, old_jy = jy;
12742   int moved = MP_NO_ACTION;
12743
12744   if (!player->active)
12745     return FALSE;
12746
12747   if (!dx && !dy)
12748   {
12749     if (player->MovPos == 0)
12750     {
12751       player->is_moving = FALSE;
12752       player->is_digging = FALSE;
12753       player->is_collecting = FALSE;
12754       player->is_snapping = FALSE;
12755       player->is_pushing = FALSE;
12756     }
12757
12758     return FALSE;
12759   }
12760
12761   if (player->move_delay > 0)
12762     return FALSE;
12763
12764   player->move_delay = -1;              /* set to "uninitialized" value */
12765
12766   /* store if player is automatically moved to next field */
12767   player->is_auto_moving = (player->programmed_action != MV_NONE);
12768
12769   /* remove the last programmed player action */
12770   player->programmed_action = 0;
12771
12772   if (player->MovPos)
12773   {
12774     /* should only happen if pre-1.2 tape recordings are played */
12775     /* this is only for backward compatibility */
12776
12777     int original_move_delay_value = player->move_delay_value;
12778
12779 #if DEBUG
12780     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12781            tape.counter);
12782 #endif
12783
12784     /* scroll remaining steps with finest movement resolution */
12785     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12786
12787     while (player->MovPos)
12788     {
12789       ScrollPlayer(player, SCROLL_GO_ON);
12790       ScrollScreen(NULL, SCROLL_GO_ON);
12791
12792       AdvanceFrameAndPlayerCounters(player->index_nr);
12793
12794       DrawAllPlayers();
12795       BackToFront();
12796     }
12797
12798     player->move_delay_value = original_move_delay_value;
12799   }
12800
12801   player->is_active = FALSE;
12802
12803   if (player->last_move_dir & MV_HORIZONTAL)
12804   {
12805     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12806       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12807   }
12808   else
12809   {
12810     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12811       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12812   }
12813
12814 #if USE_FIXED_BORDER_RUNNING_GFX
12815   if (!moved && !player->is_active)
12816   {
12817     player->is_moving = FALSE;
12818     player->is_digging = FALSE;
12819     player->is_collecting = FALSE;
12820     player->is_snapping = FALSE;
12821     player->is_pushing = FALSE;
12822   }
12823 #endif
12824
12825   jx = player->jx;
12826   jy = player->jy;
12827
12828 #if 1
12829   if (moved & MP_MOVING && !ScreenMovPos &&
12830       (player->index_nr == game.centered_player_nr ||
12831        game.centered_player_nr == -1))
12832 #else
12833   if (moved & MP_MOVING && !ScreenMovPos &&
12834       (player == local_player || !options.network))
12835 #endif
12836   {
12837     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12838     int offset = game.scroll_delay_value;
12839
12840     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12841     {
12842       /* actual player has left the screen -- scroll in that direction */
12843       if (jx != old_jx)         /* player has moved horizontally */
12844         scroll_x += (jx - old_jx);
12845       else                      /* player has moved vertically */
12846         scroll_y += (jy - old_jy);
12847     }
12848     else
12849     {
12850       if (jx != old_jx)         /* player has moved horizontally */
12851       {
12852         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12853             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12854           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12855
12856         /* don't scroll over playfield boundaries */
12857         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12858           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12859
12860         /* don't scroll more than one field at a time */
12861         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12862
12863         /* don't scroll against the player's moving direction */
12864         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12865             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12866           scroll_x = old_scroll_x;
12867       }
12868       else                      /* player has moved vertically */
12869       {
12870         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12871             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12872           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12873
12874         /* don't scroll over playfield boundaries */
12875         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12876           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12877
12878         /* don't scroll more than one field at a time */
12879         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12880
12881         /* don't scroll against the player's moving direction */
12882         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12883             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12884           scroll_y = old_scroll_y;
12885       }
12886     }
12887
12888     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12889     {
12890 #if 1
12891       if (!options.network && game.centered_player_nr == -1 &&
12892           !AllPlayersInVisibleScreen())
12893       {
12894         scroll_x = old_scroll_x;
12895         scroll_y = old_scroll_y;
12896       }
12897       else
12898 #else
12899       if (!options.network && !AllPlayersInVisibleScreen())
12900       {
12901         scroll_x = old_scroll_x;
12902         scroll_y = old_scroll_y;
12903       }
12904       else
12905 #endif
12906       {
12907         ScrollScreen(player, SCROLL_INIT);
12908         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12909       }
12910     }
12911   }
12912
12913   player->StepFrame = 0;
12914
12915   if (moved & MP_MOVING)
12916   {
12917     if (old_jx != jx && old_jy == jy)
12918       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12919     else if (old_jx == jx && old_jy != jy)
12920       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12921
12922     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12923
12924     player->last_move_dir = player->MovDir;
12925     player->is_moving = TRUE;
12926     player->is_snapping = FALSE;
12927     player->is_switching = FALSE;
12928     player->is_dropping = FALSE;
12929     player->is_dropping_pressed = FALSE;
12930     player->drop_pressed_delay = 0;
12931
12932 #if 0
12933     /* should better be called here than above, but this breaks some tapes */
12934     ScrollPlayer(player, SCROLL_INIT);
12935 #endif
12936   }
12937   else
12938   {
12939     CheckGravityMovementWhenNotMoving(player);
12940
12941     player->is_moving = FALSE;
12942
12943     /* at this point, the player is allowed to move, but cannot move right now
12944        (e.g. because of something blocking the way) -- ensure that the player
12945        is also allowed to move in the next frame (in old versions before 3.1.1,
12946        the player was forced to wait again for eight frames before next try) */
12947
12948     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12949       player->move_delay = 0;   /* allow direct movement in the next frame */
12950   }
12951
12952   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12953     player->move_delay = player->move_delay_value;
12954
12955   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12956   {
12957     TestIfPlayerTouchesBadThing(jx, jy);
12958     TestIfPlayerTouchesCustomElement(jx, jy);
12959   }
12960
12961   if (!player->active)
12962     RemovePlayer(player);
12963
12964   return moved;
12965 }
12966
12967 void ScrollPlayer(struct PlayerInfo *player, int mode)
12968 {
12969   int jx = player->jx, jy = player->jy;
12970   int last_jx = player->last_jx, last_jy = player->last_jy;
12971   int move_stepsize = TILEX / player->move_delay_value;
12972
12973 #if USE_NEW_PLAYER_SPEED
12974   if (!player->active)
12975     return;
12976
12977   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12978     return;
12979 #else
12980   if (!player->active || player->MovPos == 0)
12981     return;
12982 #endif
12983
12984   if (mode == SCROLL_INIT)
12985   {
12986     player->actual_frame_counter = FrameCounter;
12987     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12988
12989     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12990         Feld[last_jx][last_jy] == EL_EMPTY)
12991     {
12992       int last_field_block_delay = 0;   /* start with no blocking at all */
12993       int block_delay_adjustment = player->block_delay_adjustment;
12994
12995       /* if player blocks last field, add delay for exactly one move */
12996       if (player->block_last_field)
12997       {
12998         last_field_block_delay += player->move_delay_value;
12999
13000         /* when blocking enabled, prevent moving up despite gravity */
13001 #if USE_PLAYER_GRAVITY
13002         if (player->gravity && player->MovDir == MV_UP)
13003           block_delay_adjustment = -1;
13004 #else
13005         if (game.gravity && player->MovDir == MV_UP)
13006           block_delay_adjustment = -1;
13007 #endif
13008       }
13009
13010       /* add block delay adjustment (also possible when not blocking) */
13011       last_field_block_delay += block_delay_adjustment;
13012
13013       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13014       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13015     }
13016
13017 #if USE_NEW_PLAYER_SPEED
13018     if (player->MovPos != 0)    /* player has not yet reached destination */
13019       return;
13020 #else
13021     return;
13022 #endif
13023   }
13024   else if (!FrameReached(&player->actual_frame_counter, 1))
13025     return;
13026
13027 #if USE_NEW_PLAYER_SPEED
13028   if (player->MovPos != 0)
13029   {
13030     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13031     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13032
13033     /* before DrawPlayer() to draw correct player graphic for this case */
13034     if (player->MovPos == 0)
13035       CheckGravityMovement(player);
13036   }
13037 #else
13038   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13039   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13040
13041   /* before DrawPlayer() to draw correct player graphic for this case */
13042   if (player->MovPos == 0)
13043     CheckGravityMovement(player);
13044 #endif
13045
13046   if (player->MovPos == 0)      /* player reached destination field */
13047   {
13048     if (player->move_delay_reset_counter > 0)
13049     {
13050       player->move_delay_reset_counter--;
13051
13052       if (player->move_delay_reset_counter == 0)
13053       {
13054         /* continue with normal speed after quickly moving through gate */
13055         HALVE_PLAYER_SPEED(player);
13056
13057         /* be able to make the next move without delay */
13058         player->move_delay = 0;
13059       }
13060     }
13061
13062     player->last_jx = jx;
13063     player->last_jy = jy;
13064
13065     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13066         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13067         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13068         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13069         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13070         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13071     {
13072       DrawPlayer(player);       /* needed here only to cleanup last field */
13073       RemovePlayer(player);
13074
13075       if (local_player->friends_still_needed == 0 ||
13076           IS_SP_ELEMENT(Feld[jx][jy]))
13077         PlayerWins(player);
13078     }
13079
13080     /* this breaks one level: "machine", level 000 */
13081     {
13082       int move_direction = player->MovDir;
13083       int enter_side = MV_DIR_OPPOSITE(move_direction);
13084       int leave_side = move_direction;
13085       int old_jx = last_jx;
13086       int old_jy = last_jy;
13087       int old_element = Feld[old_jx][old_jy];
13088       int new_element = Feld[jx][jy];
13089
13090       if (IS_CUSTOM_ELEMENT(old_element))
13091         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13092                                    CE_LEFT_BY_PLAYER,
13093                                    player->index_bit, leave_side);
13094
13095       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13096                                           CE_PLAYER_LEAVES_X,
13097                                           player->index_bit, leave_side);
13098
13099       if (IS_CUSTOM_ELEMENT(new_element))
13100         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13101                                    player->index_bit, enter_side);
13102
13103       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13104                                           CE_PLAYER_ENTERS_X,
13105                                           player->index_bit, enter_side);
13106
13107       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13108                                         CE_MOVE_OF_X, move_direction);
13109     }
13110
13111     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13112     {
13113       TestIfPlayerTouchesBadThing(jx, jy);
13114       TestIfPlayerTouchesCustomElement(jx, jy);
13115
13116       /* needed because pushed element has not yet reached its destination,
13117          so it would trigger a change event at its previous field location */
13118       if (!player->is_pushing)
13119         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13120
13121       if (!player->active)
13122         RemovePlayer(player);
13123     }
13124
13125     if (!local_player->LevelSolved && level.use_step_counter)
13126     {
13127       int i;
13128
13129       TimePlayed++;
13130
13131       if (TimeLeft > 0)
13132       {
13133         TimeLeft--;
13134
13135         if (TimeLeft <= 10 && setup.time_limit)
13136           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13137
13138 #if 1
13139         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13140
13141         DisplayGameControlValues();
13142 #else
13143         DrawGameValue_Time(TimeLeft);
13144 #endif
13145
13146         if (!TimeLeft && setup.time_limit)
13147           for (i = 0; i < MAX_PLAYERS; i++)
13148             KillPlayer(&stored_player[i]);
13149       }
13150 #if 1
13151       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13152       {
13153         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13154
13155         DisplayGameControlValues();
13156       }
13157 #else
13158       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13159         DrawGameValue_Time(TimePlayed);
13160 #endif
13161     }
13162
13163     if (tape.single_step && tape.recording && !tape.pausing &&
13164         !player->programmed_action)
13165       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13166   }
13167 }
13168
13169 void ScrollScreen(struct PlayerInfo *player, int mode)
13170 {
13171   static unsigned long screen_frame_counter = 0;
13172
13173   if (mode == SCROLL_INIT)
13174   {
13175     /* set scrolling step size according to actual player's moving speed */
13176     ScrollStepSize = TILEX / player->move_delay_value;
13177
13178     screen_frame_counter = FrameCounter;
13179     ScreenMovDir = player->MovDir;
13180     ScreenMovPos = player->MovPos;
13181     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13182     return;
13183   }
13184   else if (!FrameReached(&screen_frame_counter, 1))
13185     return;
13186
13187   if (ScreenMovPos)
13188   {
13189     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13190     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13191     redraw_mask |= REDRAW_FIELD;
13192   }
13193   else
13194     ScreenMovDir = MV_NONE;
13195 }
13196
13197 void TestIfPlayerTouchesCustomElement(int x, int y)
13198 {
13199   static int xy[4][2] =
13200   {
13201     { 0, -1 },
13202     { -1, 0 },
13203     { +1, 0 },
13204     { 0, +1 }
13205   };
13206   static int trigger_sides[4][2] =
13207   {
13208     /* center side       border side */
13209     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13210     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13211     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13212     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13213   };
13214   static int touch_dir[4] =
13215   {
13216     MV_LEFT | MV_RIGHT,
13217     MV_UP   | MV_DOWN,
13218     MV_UP   | MV_DOWN,
13219     MV_LEFT | MV_RIGHT
13220   };
13221   int center_element = Feld[x][y];      /* should always be non-moving! */
13222   int i;
13223
13224   for (i = 0; i < NUM_DIRECTIONS; i++)
13225   {
13226     int xx = x + xy[i][0];
13227     int yy = y + xy[i][1];
13228     int center_side = trigger_sides[i][0];
13229     int border_side = trigger_sides[i][1];
13230     int border_element;
13231
13232     if (!IN_LEV_FIELD(xx, yy))
13233       continue;
13234
13235     if (IS_PLAYER(x, y))
13236     {
13237       struct PlayerInfo *player = PLAYERINFO(x, y);
13238
13239       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13240         border_element = Feld[xx][yy];          /* may be moving! */
13241       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13242         border_element = Feld[xx][yy];
13243       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13244         border_element = MovingOrBlocked2Element(xx, yy);
13245       else
13246         continue;               /* center and border element do not touch */
13247
13248       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13249                                  player->index_bit, border_side);
13250       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13251                                           CE_PLAYER_TOUCHES_X,
13252                                           player->index_bit, border_side);
13253     }
13254     else if (IS_PLAYER(xx, yy))
13255     {
13256       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13257
13258       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13259       {
13260         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13261           continue;             /* center and border element do not touch */
13262       }
13263
13264       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13265                                  player->index_bit, center_side);
13266       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13267                                           CE_PLAYER_TOUCHES_X,
13268                                           player->index_bit, center_side);
13269       break;
13270     }
13271   }
13272 }
13273
13274 #if USE_ELEMENT_TOUCHING_BUGFIX
13275
13276 void TestIfElementTouchesCustomElement(int x, int y)
13277 {
13278   static int xy[4][2] =
13279   {
13280     { 0, -1 },
13281     { -1, 0 },
13282     { +1, 0 },
13283     { 0, +1 }
13284   };
13285   static int trigger_sides[4][2] =
13286   {
13287     /* center side      border side */
13288     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13289     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13290     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13291     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13292   };
13293   static int touch_dir[4] =
13294   {
13295     MV_LEFT | MV_RIGHT,
13296     MV_UP   | MV_DOWN,
13297     MV_UP   | MV_DOWN,
13298     MV_LEFT | MV_RIGHT
13299   };
13300   boolean change_center_element = FALSE;
13301   int center_element = Feld[x][y];      /* should always be non-moving! */
13302   int border_element_old[NUM_DIRECTIONS];
13303   int i;
13304
13305   for (i = 0; i < NUM_DIRECTIONS; i++)
13306   {
13307     int xx = x + xy[i][0];
13308     int yy = y + xy[i][1];
13309     int border_element;
13310
13311     border_element_old[i] = -1;
13312
13313     if (!IN_LEV_FIELD(xx, yy))
13314       continue;
13315
13316     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13317       border_element = Feld[xx][yy];    /* may be moving! */
13318     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13319       border_element = Feld[xx][yy];
13320     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13321       border_element = MovingOrBlocked2Element(xx, yy);
13322     else
13323       continue;                 /* center and border element do not touch */
13324
13325     border_element_old[i] = border_element;
13326   }
13327
13328   for (i = 0; i < NUM_DIRECTIONS; i++)
13329   {
13330     int xx = x + xy[i][0];
13331     int yy = y + xy[i][1];
13332     int center_side = trigger_sides[i][0];
13333     int border_element = border_element_old[i];
13334
13335     if (border_element == -1)
13336       continue;
13337
13338     /* check for change of border element */
13339     CheckElementChangeBySide(xx, yy, border_element, center_element,
13340                              CE_TOUCHING_X, center_side);
13341   }
13342
13343   for (i = 0; i < NUM_DIRECTIONS; i++)
13344   {
13345     int border_side = trigger_sides[i][1];
13346     int border_element = border_element_old[i];
13347
13348     if (border_element == -1)
13349       continue;
13350
13351     /* check for change of center element (but change it only once) */
13352     if (!change_center_element)
13353       change_center_element =
13354         CheckElementChangeBySide(x, y, center_element, border_element,
13355                                  CE_TOUCHING_X, border_side);
13356   }
13357 }
13358
13359 #else
13360
13361 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13362 {
13363   static int xy[4][2] =
13364   {
13365     { 0, -1 },
13366     { -1, 0 },
13367     { +1, 0 },
13368     { 0, +1 }
13369   };
13370   static int trigger_sides[4][2] =
13371   {
13372     /* center side      border side */
13373     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13374     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13375     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13376     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13377   };
13378   static int touch_dir[4] =
13379   {
13380     MV_LEFT | MV_RIGHT,
13381     MV_UP   | MV_DOWN,
13382     MV_UP   | MV_DOWN,
13383     MV_LEFT | MV_RIGHT
13384   };
13385   boolean change_center_element = FALSE;
13386   int center_element = Feld[x][y];      /* should always be non-moving! */
13387   int i;
13388
13389   for (i = 0; i < NUM_DIRECTIONS; i++)
13390   {
13391     int xx = x + xy[i][0];
13392     int yy = y + xy[i][1];
13393     int center_side = trigger_sides[i][0];
13394     int border_side = trigger_sides[i][1];
13395     int border_element;
13396
13397     if (!IN_LEV_FIELD(xx, yy))
13398       continue;
13399
13400     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13401       border_element = Feld[xx][yy];    /* may be moving! */
13402     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13403       border_element = Feld[xx][yy];
13404     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13405       border_element = MovingOrBlocked2Element(xx, yy);
13406     else
13407       continue;                 /* center and border element do not touch */
13408
13409     /* check for change of center element (but change it only once) */
13410     if (!change_center_element)
13411       change_center_element =
13412         CheckElementChangeBySide(x, y, center_element, border_element,
13413                                  CE_TOUCHING_X, border_side);
13414
13415     /* check for change of border element */
13416     CheckElementChangeBySide(xx, yy, border_element, center_element,
13417                              CE_TOUCHING_X, center_side);
13418   }
13419 }
13420
13421 #endif
13422
13423 void TestIfElementHitsCustomElement(int x, int y, int direction)
13424 {
13425   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13426   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13427   int hitx = x + dx, hity = y + dy;
13428   int hitting_element = Feld[x][y];
13429   int touched_element;
13430
13431   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13432     return;
13433
13434   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13435                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13436
13437   if (IN_LEV_FIELD(hitx, hity))
13438   {
13439     int opposite_direction = MV_DIR_OPPOSITE(direction);
13440     int hitting_side = direction;
13441     int touched_side = opposite_direction;
13442     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13443                           MovDir[hitx][hity] != direction ||
13444                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13445
13446     object_hit = TRUE;
13447
13448     if (object_hit)
13449     {
13450       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13451                                CE_HITTING_X, touched_side);
13452
13453       CheckElementChangeBySide(hitx, hity, touched_element,
13454                                hitting_element, CE_HIT_BY_X, hitting_side);
13455
13456       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13457                                CE_HIT_BY_SOMETHING, opposite_direction);
13458     }
13459   }
13460
13461   /* "hitting something" is also true when hitting the playfield border */
13462   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13463                            CE_HITTING_SOMETHING, direction);
13464 }
13465
13466 #if 0
13467 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13468 {
13469   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13470   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13471   int hitx = x + dx, hity = y + dy;
13472   int hitting_element = Feld[x][y];
13473   int touched_element;
13474 #if 0
13475   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13476                         !IS_FREE(hitx, hity) &&
13477                         (!IS_MOVING(hitx, hity) ||
13478                          MovDir[hitx][hity] != direction ||
13479                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13480 #endif
13481
13482   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13483     return;
13484
13485 #if 0
13486   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13487     return;
13488 #endif
13489
13490   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13491                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13492
13493   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13494                            EP_CAN_SMASH_EVERYTHING, direction);
13495
13496   if (IN_LEV_FIELD(hitx, hity))
13497   {
13498     int opposite_direction = MV_DIR_OPPOSITE(direction);
13499     int hitting_side = direction;
13500     int touched_side = opposite_direction;
13501 #if 0
13502     int touched_element = MovingOrBlocked2Element(hitx, hity);
13503 #endif
13504 #if 1
13505     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13506                           MovDir[hitx][hity] != direction ||
13507                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13508
13509     object_hit = TRUE;
13510 #endif
13511
13512     if (object_hit)
13513     {
13514       int i;
13515
13516       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13517                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13518
13519       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13520                                CE_OTHER_IS_SMASHING, touched_side);
13521
13522       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13523                                CE_OTHER_GETS_SMASHED, hitting_side);
13524     }
13525   }
13526 }
13527 #endif
13528
13529 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13530 {
13531   int i, kill_x = -1, kill_y = -1;
13532
13533   int bad_element = -1;
13534   static int test_xy[4][2] =
13535   {
13536     { 0, -1 },
13537     { -1, 0 },
13538     { +1, 0 },
13539     { 0, +1 }
13540   };
13541   static int test_dir[4] =
13542   {
13543     MV_UP,
13544     MV_LEFT,
13545     MV_RIGHT,
13546     MV_DOWN
13547   };
13548
13549   for (i = 0; i < NUM_DIRECTIONS; i++)
13550   {
13551     int test_x, test_y, test_move_dir, test_element;
13552
13553     test_x = good_x + test_xy[i][0];
13554     test_y = good_y + test_xy[i][1];
13555
13556     if (!IN_LEV_FIELD(test_x, test_y))
13557       continue;
13558
13559     test_move_dir =
13560       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13561
13562     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13563
13564     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13565        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13566     */
13567     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13568         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13569     {
13570       kill_x = test_x;
13571       kill_y = test_y;
13572       bad_element = test_element;
13573
13574       break;
13575     }
13576   }
13577
13578   if (kill_x != -1 || kill_y != -1)
13579   {
13580     if (IS_PLAYER(good_x, good_y))
13581     {
13582       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13583
13584       if (player->shield_deadly_time_left > 0 &&
13585           !IS_INDESTRUCTIBLE(bad_element))
13586         Bang(kill_x, kill_y);
13587       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13588         KillPlayer(player);
13589     }
13590     else
13591       Bang(good_x, good_y);
13592   }
13593 }
13594
13595 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13596 {
13597   int i, kill_x = -1, kill_y = -1;
13598   int bad_element = Feld[bad_x][bad_y];
13599   static int test_xy[4][2] =
13600   {
13601     { 0, -1 },
13602     { -1, 0 },
13603     { +1, 0 },
13604     { 0, +1 }
13605   };
13606   static int touch_dir[4] =
13607   {
13608     MV_LEFT | MV_RIGHT,
13609     MV_UP   | MV_DOWN,
13610     MV_UP   | MV_DOWN,
13611     MV_LEFT | MV_RIGHT
13612   };
13613   static int test_dir[4] =
13614   {
13615     MV_UP,
13616     MV_LEFT,
13617     MV_RIGHT,
13618     MV_DOWN
13619   };
13620
13621   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13622     return;
13623
13624   for (i = 0; i < NUM_DIRECTIONS; i++)
13625   {
13626     int test_x, test_y, test_move_dir, test_element;
13627
13628     test_x = bad_x + test_xy[i][0];
13629     test_y = bad_y + test_xy[i][1];
13630     if (!IN_LEV_FIELD(test_x, test_y))
13631       continue;
13632
13633     test_move_dir =
13634       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13635
13636     test_element = Feld[test_x][test_y];
13637
13638     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13639        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13640     */
13641     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13642         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13643     {
13644       /* good thing is player or penguin that does not move away */
13645       if (IS_PLAYER(test_x, test_y))
13646       {
13647         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13648
13649         if (bad_element == EL_ROBOT && player->is_moving)
13650           continue;     /* robot does not kill player if he is moving */
13651
13652         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13653         {
13654           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13655             continue;           /* center and border element do not touch */
13656         }
13657
13658         kill_x = test_x;
13659         kill_y = test_y;
13660         break;
13661       }
13662       else if (test_element == EL_PENGUIN)
13663       {
13664         kill_x = test_x;
13665         kill_y = test_y;
13666         break;
13667       }
13668     }
13669   }
13670
13671   if (kill_x != -1 || kill_y != -1)
13672   {
13673     if (IS_PLAYER(kill_x, kill_y))
13674     {
13675       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13676
13677       if (player->shield_deadly_time_left > 0 &&
13678           !IS_INDESTRUCTIBLE(bad_element))
13679         Bang(bad_x, bad_y);
13680       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13681         KillPlayer(player);
13682     }
13683     else
13684       Bang(kill_x, kill_y);
13685   }
13686 }
13687
13688 void TestIfPlayerTouchesBadThing(int x, int y)
13689 {
13690   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13691 }
13692
13693 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13694 {
13695   TestIfGoodThingHitsBadThing(x, y, move_dir);
13696 }
13697
13698 void TestIfBadThingTouchesPlayer(int x, int y)
13699 {
13700   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13701 }
13702
13703 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13704 {
13705   TestIfBadThingHitsGoodThing(x, y, move_dir);
13706 }
13707
13708 void TestIfFriendTouchesBadThing(int x, int y)
13709 {
13710   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13711 }
13712
13713 void TestIfBadThingTouchesFriend(int x, int y)
13714 {
13715   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13716 }
13717
13718 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13719 {
13720   int i, kill_x = bad_x, kill_y = bad_y;
13721   static int xy[4][2] =
13722   {
13723     { 0, -1 },
13724     { -1, 0 },
13725     { +1, 0 },
13726     { 0, +1 }
13727   };
13728
13729   for (i = 0; i < NUM_DIRECTIONS; i++)
13730   {
13731     int x, y, element;
13732
13733     x = bad_x + xy[i][0];
13734     y = bad_y + xy[i][1];
13735     if (!IN_LEV_FIELD(x, y))
13736       continue;
13737
13738     element = Feld[x][y];
13739     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13740         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13741     {
13742       kill_x = x;
13743       kill_y = y;
13744       break;
13745     }
13746   }
13747
13748   if (kill_x != bad_x || kill_y != bad_y)
13749     Bang(bad_x, bad_y);
13750 }
13751
13752 void KillPlayer(struct PlayerInfo *player)
13753 {
13754   int jx = player->jx, jy = player->jy;
13755
13756   if (!player->active)
13757     return;
13758
13759   /* the following code was introduced to prevent an infinite loop when calling
13760      -> Bang()
13761      -> CheckTriggeredElementChangeExt()
13762      -> ExecuteCustomElementAction()
13763      -> KillPlayer()
13764      -> (infinitely repeating the above sequence of function calls)
13765      which occurs when killing the player while having a CE with the setting
13766      "kill player X when explosion of <player X>"; the solution using a new
13767      field "player->killed" was chosen for backwards compatibility, although
13768      clever use of the fields "player->active" etc. would probably also work */
13769 #if 1
13770   if (player->killed)
13771     return;
13772 #endif
13773
13774   player->killed = TRUE;
13775
13776   /* remove accessible field at the player's position */
13777   Feld[jx][jy] = EL_EMPTY;
13778
13779   /* deactivate shield (else Bang()/Explode() would not work right) */
13780   player->shield_normal_time_left = 0;
13781   player->shield_deadly_time_left = 0;
13782
13783   Bang(jx, jy);
13784   BuryPlayer(player);
13785 }
13786
13787 static void KillPlayerUnlessEnemyProtected(int x, int y)
13788 {
13789   if (!PLAYER_ENEMY_PROTECTED(x, y))
13790     KillPlayer(PLAYERINFO(x, y));
13791 }
13792
13793 static void KillPlayerUnlessExplosionProtected(int x, int y)
13794 {
13795   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13796     KillPlayer(PLAYERINFO(x, y));
13797 }
13798
13799 void BuryPlayer(struct PlayerInfo *player)
13800 {
13801   int jx = player->jx, jy = player->jy;
13802
13803   if (!player->active)
13804     return;
13805
13806   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13807   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13808
13809   player->GameOver = TRUE;
13810   RemovePlayer(player);
13811 }
13812
13813 void RemovePlayer(struct PlayerInfo *player)
13814 {
13815   int jx = player->jx, jy = player->jy;
13816   int i, found = FALSE;
13817
13818   player->present = FALSE;
13819   player->active = FALSE;
13820
13821   if (!ExplodeField[jx][jy])
13822     StorePlayer[jx][jy] = 0;
13823
13824   if (player->is_moving)
13825     DrawLevelField(player->last_jx, player->last_jy);
13826
13827   for (i = 0; i < MAX_PLAYERS; i++)
13828     if (stored_player[i].active)
13829       found = TRUE;
13830
13831   if (!found)
13832     AllPlayersGone = TRUE;
13833
13834   ExitX = ZX = jx;
13835   ExitY = ZY = jy;
13836 }
13837
13838 #if USE_NEW_SNAP_DELAY
13839 static void setFieldForSnapping(int x, int y, int element, int direction)
13840 {
13841   struct ElementInfo *ei = &element_info[element];
13842   int direction_bit = MV_DIR_TO_BIT(direction);
13843   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13844   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13845                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13846
13847   Feld[x][y] = EL_ELEMENT_SNAPPING;
13848   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13849
13850   ResetGfxAnimation(x, y);
13851
13852   GfxElement[x][y] = element;
13853   GfxAction[x][y] = action;
13854   GfxDir[x][y] = direction;
13855   GfxFrame[x][y] = -1;
13856 }
13857 #endif
13858
13859 /*
13860   =============================================================================
13861   checkDiagonalPushing()
13862   -----------------------------------------------------------------------------
13863   check if diagonal input device direction results in pushing of object
13864   (by checking if the alternative direction is walkable, diggable, ...)
13865   =============================================================================
13866 */
13867
13868 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13869                                     int x, int y, int real_dx, int real_dy)
13870 {
13871   int jx, jy, dx, dy, xx, yy;
13872
13873   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13874     return TRUE;
13875
13876   /* diagonal direction: check alternative direction */
13877   jx = player->jx;
13878   jy = player->jy;
13879   dx = x - jx;
13880   dy = y - jy;
13881   xx = jx + (dx == 0 ? real_dx : 0);
13882   yy = jy + (dy == 0 ? real_dy : 0);
13883
13884   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13885 }
13886
13887 /*
13888   =============================================================================
13889   DigField()
13890   -----------------------------------------------------------------------------
13891   x, y:                 field next to player (non-diagonal) to try to dig to
13892   real_dx, real_dy:     direction as read from input device (can be diagonal)
13893   =============================================================================
13894 */
13895
13896 int DigField(struct PlayerInfo *player,
13897              int oldx, int oldy, int x, int y,
13898              int real_dx, int real_dy, int mode)
13899 {
13900   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13901   boolean player_was_pushing = player->is_pushing;
13902   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13903   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13904   int jx = oldx, jy = oldy;
13905   int dx = x - jx, dy = y - jy;
13906   int nextx = x + dx, nexty = y + dy;
13907   int move_direction = (dx == -1 ? MV_LEFT  :
13908                         dx == +1 ? MV_RIGHT :
13909                         dy == -1 ? MV_UP    :
13910                         dy == +1 ? MV_DOWN  : MV_NONE);
13911   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13912   int dig_side = MV_DIR_OPPOSITE(move_direction);
13913   int old_element = Feld[jx][jy];
13914 #if USE_FIXED_DONT_RUN_INTO
13915   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13916 #else
13917   int element;
13918 #endif
13919   int collect_count;
13920
13921   if (is_player)                /* function can also be called by EL_PENGUIN */
13922   {
13923     if (player->MovPos == 0)
13924     {
13925       player->is_digging = FALSE;
13926       player->is_collecting = FALSE;
13927     }
13928
13929     if (player->MovPos == 0)    /* last pushing move finished */
13930       player->is_pushing = FALSE;
13931
13932     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13933     {
13934       player->is_switching = FALSE;
13935       player->push_delay = -1;
13936
13937       return MP_NO_ACTION;
13938     }
13939   }
13940
13941 #if !USE_FIXED_DONT_RUN_INTO
13942   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13943     return MP_NO_ACTION;
13944 #endif
13945
13946   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13947     old_element = Back[jx][jy];
13948
13949   /* in case of element dropped at player position, check background */
13950   else if (Back[jx][jy] != EL_EMPTY &&
13951            game.engine_version >= VERSION_IDENT(2,2,0,0))
13952     old_element = Back[jx][jy];
13953
13954   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13955     return MP_NO_ACTION;        /* field has no opening in this direction */
13956
13957   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13958     return MP_NO_ACTION;        /* field has no opening in this direction */
13959
13960 #if USE_FIXED_DONT_RUN_INTO
13961   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13962   {
13963     SplashAcid(x, y);
13964
13965     Feld[jx][jy] = player->artwork_element;
13966     InitMovingField(jx, jy, MV_DOWN);
13967     Store[jx][jy] = EL_ACID;
13968     ContinueMoving(jx, jy);
13969     BuryPlayer(player);
13970
13971     return MP_DONT_RUN_INTO;
13972   }
13973 #endif
13974
13975 #if USE_FIXED_DONT_RUN_INTO
13976   if (player_can_move && DONT_RUN_INTO(element))
13977   {
13978     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13979
13980     return MP_DONT_RUN_INTO;
13981   }
13982 #endif
13983
13984 #if USE_FIXED_DONT_RUN_INTO
13985   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13986     return MP_NO_ACTION;
13987 #endif
13988
13989 #if !USE_FIXED_DONT_RUN_INTO
13990   element = Feld[x][y];
13991 #endif
13992
13993   collect_count = element_info[element].collect_count_initial;
13994
13995   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13996     return MP_NO_ACTION;
13997
13998   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13999     player_can_move = player_can_move_or_snap;
14000
14001   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14002       game.engine_version >= VERSION_IDENT(2,2,0,0))
14003   {
14004     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14005                                player->index_bit, dig_side);
14006     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14007                                         player->index_bit, dig_side);
14008
14009     if (element == EL_DC_LANDMINE)
14010       Bang(x, y);
14011
14012     if (Feld[x][y] != element)          /* field changed by snapping */
14013       return MP_ACTION;
14014
14015     return MP_NO_ACTION;
14016   }
14017
14018 #if USE_PLAYER_GRAVITY
14019   if (player->gravity && is_player && !player->is_auto_moving &&
14020       canFallDown(player) && move_direction != MV_DOWN &&
14021       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14022     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14023 #else
14024   if (game.gravity && is_player && !player->is_auto_moving &&
14025       canFallDown(player) && move_direction != MV_DOWN &&
14026       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14027     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14028 #endif
14029
14030   if (player_can_move &&
14031       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14032   {
14033     int sound_element = SND_ELEMENT(element);
14034     int sound_action = ACTION_WALKING;
14035
14036     if (IS_RND_GATE(element))
14037     {
14038       if (!player->key[RND_GATE_NR(element)])
14039         return MP_NO_ACTION;
14040     }
14041     else if (IS_RND_GATE_GRAY(element))
14042     {
14043       if (!player->key[RND_GATE_GRAY_NR(element)])
14044         return MP_NO_ACTION;
14045     }
14046     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14047     {
14048       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14049         return MP_NO_ACTION;
14050     }
14051     else if (element == EL_EXIT_OPEN ||
14052              element == EL_EM_EXIT_OPEN ||
14053              element == EL_STEEL_EXIT_OPEN ||
14054              element == EL_EM_STEEL_EXIT_OPEN ||
14055              element == EL_SP_EXIT_OPEN ||
14056              element == EL_SP_EXIT_OPENING)
14057     {
14058       sound_action = ACTION_PASSING;    /* player is passing exit */
14059     }
14060     else if (element == EL_EMPTY)
14061     {
14062       sound_action = ACTION_MOVING;             /* nothing to walk on */
14063     }
14064
14065     /* play sound from background or player, whatever is available */
14066     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14067       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14068     else
14069       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14070   }
14071   else if (player_can_move &&
14072            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14073   {
14074     if (!ACCESS_FROM(element, opposite_direction))
14075       return MP_NO_ACTION;      /* field not accessible from this direction */
14076
14077     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14078       return MP_NO_ACTION;
14079
14080     if (IS_EM_GATE(element))
14081     {
14082       if (!player->key[EM_GATE_NR(element)])
14083         return MP_NO_ACTION;
14084     }
14085     else if (IS_EM_GATE_GRAY(element))
14086     {
14087       if (!player->key[EM_GATE_GRAY_NR(element)])
14088         return MP_NO_ACTION;
14089     }
14090     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14091     {
14092       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14093         return MP_NO_ACTION;
14094     }
14095     else if (IS_EMC_GATE(element))
14096     {
14097       if (!player->key[EMC_GATE_NR(element)])
14098         return MP_NO_ACTION;
14099     }
14100     else if (IS_EMC_GATE_GRAY(element))
14101     {
14102       if (!player->key[EMC_GATE_GRAY_NR(element)])
14103         return MP_NO_ACTION;
14104     }
14105     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14106     {
14107       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14108         return MP_NO_ACTION;
14109     }
14110     else if (element == EL_DC_GATE_WHITE ||
14111              element == EL_DC_GATE_WHITE_GRAY ||
14112              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14113     {
14114       if (player->num_white_keys == 0)
14115         return MP_NO_ACTION;
14116
14117       player->num_white_keys--;
14118     }
14119     else if (IS_SP_PORT(element))
14120     {
14121       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14122           element == EL_SP_GRAVITY_PORT_RIGHT ||
14123           element == EL_SP_GRAVITY_PORT_UP ||
14124           element == EL_SP_GRAVITY_PORT_DOWN)
14125 #if USE_PLAYER_GRAVITY
14126         player->gravity = !player->gravity;
14127 #else
14128         game.gravity = !game.gravity;
14129 #endif
14130       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14131                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14132                element == EL_SP_GRAVITY_ON_PORT_UP ||
14133                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14134 #if USE_PLAYER_GRAVITY
14135         player->gravity = TRUE;
14136 #else
14137         game.gravity = TRUE;
14138 #endif
14139       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14140                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14141                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14142                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14143 #if USE_PLAYER_GRAVITY
14144         player->gravity = FALSE;
14145 #else
14146         game.gravity = FALSE;
14147 #endif
14148     }
14149
14150     /* automatically move to the next field with double speed */
14151     player->programmed_action = move_direction;
14152
14153     if (player->move_delay_reset_counter == 0)
14154     {
14155       player->move_delay_reset_counter = 2;     /* two double speed steps */
14156
14157       DOUBLE_PLAYER_SPEED(player);
14158     }
14159
14160     PlayLevelSoundAction(x, y, ACTION_PASSING);
14161   }
14162   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14163   {
14164     RemoveField(x, y);
14165
14166     if (mode != DF_SNAP)
14167     {
14168       GfxElement[x][y] = GFX_ELEMENT(element);
14169       player->is_digging = TRUE;
14170     }
14171
14172     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14173
14174     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14175                                         player->index_bit, dig_side);
14176
14177     if (mode == DF_SNAP)
14178     {
14179 #if USE_NEW_SNAP_DELAY
14180       if (level.block_snap_field)
14181         setFieldForSnapping(x, y, element, move_direction);
14182       else
14183         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14184 #else
14185       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14186 #endif
14187
14188       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14189                                           player->index_bit, dig_side);
14190     }
14191   }
14192   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14193   {
14194     RemoveField(x, y);
14195
14196     if (is_player && mode != DF_SNAP)
14197     {
14198       GfxElement[x][y] = element;
14199       player->is_collecting = TRUE;
14200     }
14201
14202     if (element == EL_SPEED_PILL)
14203     {
14204       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14205     }
14206     else if (element == EL_EXTRA_TIME && level.time > 0)
14207     {
14208       TimeLeft += level.extra_time;
14209
14210 #if 1
14211       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14212
14213       DisplayGameControlValues();
14214 #else
14215       DrawGameValue_Time(TimeLeft);
14216 #endif
14217     }
14218     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14219     {
14220       player->shield_normal_time_left += level.shield_normal_time;
14221       if (element == EL_SHIELD_DEADLY)
14222         player->shield_deadly_time_left += level.shield_deadly_time;
14223     }
14224     else if (element == EL_DYNAMITE ||
14225              element == EL_EM_DYNAMITE ||
14226              element == EL_SP_DISK_RED)
14227     {
14228       if (player->inventory_size < MAX_INVENTORY_SIZE)
14229         player->inventory_element[player->inventory_size++] = element;
14230
14231       DrawGameDoorValues();
14232     }
14233     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14234     {
14235       player->dynabomb_count++;
14236       player->dynabombs_left++;
14237     }
14238     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14239     {
14240       player->dynabomb_size++;
14241     }
14242     else if (element == EL_DYNABOMB_INCREASE_POWER)
14243     {
14244       player->dynabomb_xl = TRUE;
14245     }
14246     else if (IS_KEY(element))
14247     {
14248       player->key[KEY_NR(element)] = TRUE;
14249
14250       DrawGameDoorValues();
14251     }
14252     else if (element == EL_DC_KEY_WHITE)
14253     {
14254       player->num_white_keys++;
14255
14256       /* display white keys? */
14257       /* DrawGameDoorValues(); */
14258     }
14259     else if (IS_ENVELOPE(element))
14260     {
14261       player->show_envelope = element;
14262     }
14263     else if (element == EL_EMC_LENSES)
14264     {
14265       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14266
14267       RedrawAllInvisibleElementsForLenses();
14268     }
14269     else if (element == EL_EMC_MAGNIFIER)
14270     {
14271       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14272
14273       RedrawAllInvisibleElementsForMagnifier();
14274     }
14275     else if (IS_DROPPABLE(element) ||
14276              IS_THROWABLE(element))     /* can be collected and dropped */
14277     {
14278       int i;
14279
14280       if (collect_count == 0)
14281         player->inventory_infinite_element = element;
14282       else
14283         for (i = 0; i < collect_count; i++)
14284           if (player->inventory_size < MAX_INVENTORY_SIZE)
14285             player->inventory_element[player->inventory_size++] = element;
14286
14287       DrawGameDoorValues();
14288     }
14289     else if (collect_count > 0)
14290     {
14291       local_player->gems_still_needed -= collect_count;
14292       if (local_player->gems_still_needed < 0)
14293         local_player->gems_still_needed = 0;
14294
14295 #if 1
14296       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14297
14298       DisplayGameControlValues();
14299 #else
14300       DrawGameValue_Emeralds(local_player->gems_still_needed);
14301 #endif
14302     }
14303
14304     RaiseScoreElement(element);
14305     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14306
14307     if (is_player)
14308       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14309                                           player->index_bit, dig_side);
14310
14311     if (mode == DF_SNAP)
14312     {
14313 #if USE_NEW_SNAP_DELAY
14314       if (level.block_snap_field)
14315         setFieldForSnapping(x, y, element, move_direction);
14316       else
14317         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14318 #else
14319       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14320 #endif
14321
14322       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14323                                           player->index_bit, dig_side);
14324     }
14325   }
14326   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14327   {
14328     if (mode == DF_SNAP && element != EL_BD_ROCK)
14329       return MP_NO_ACTION;
14330
14331     if (CAN_FALL(element) && dy)
14332       return MP_NO_ACTION;
14333
14334     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14335         !(element == EL_SPRING && level.use_spring_bug))
14336       return MP_NO_ACTION;
14337
14338     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14339         ((move_direction & MV_VERTICAL &&
14340           ((element_info[element].move_pattern & MV_LEFT &&
14341             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14342            (element_info[element].move_pattern & MV_RIGHT &&
14343             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14344          (move_direction & MV_HORIZONTAL &&
14345           ((element_info[element].move_pattern & MV_UP &&
14346             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14347            (element_info[element].move_pattern & MV_DOWN &&
14348             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14349       return MP_NO_ACTION;
14350
14351     /* do not push elements already moving away faster than player */
14352     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14353         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14354       return MP_NO_ACTION;
14355
14356     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14357     {
14358       if (player->push_delay_value == -1 || !player_was_pushing)
14359         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14360     }
14361     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14362     {
14363       if (player->push_delay_value == -1)
14364         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14365     }
14366     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14367     {
14368       if (!player->is_pushing)
14369         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14370     }
14371
14372     player->is_pushing = TRUE;
14373     player->is_active = TRUE;
14374
14375     if (!(IN_LEV_FIELD(nextx, nexty) &&
14376           (IS_FREE(nextx, nexty) ||
14377            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14378             IS_SB_ELEMENT(element)))))
14379       return MP_NO_ACTION;
14380
14381     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14382       return MP_NO_ACTION;
14383
14384     if (player->push_delay == -1)       /* new pushing; restart delay */
14385       player->push_delay = 0;
14386
14387     if (player->push_delay < player->push_delay_value &&
14388         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14389         element != EL_SPRING && element != EL_BALLOON)
14390     {
14391       /* make sure that there is no move delay before next try to push */
14392       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14393         player->move_delay = 0;
14394
14395       return MP_NO_ACTION;
14396     }
14397
14398     if (IS_SB_ELEMENT(element))
14399     {
14400       if (element == EL_SOKOBAN_FIELD_FULL)
14401       {
14402         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14403         local_player->sokobanfields_still_needed++;
14404       }
14405
14406       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14407       {
14408         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14409         local_player->sokobanfields_still_needed--;
14410       }
14411
14412       Feld[x][y] = EL_SOKOBAN_OBJECT;
14413
14414       if (Back[x][y] == Back[nextx][nexty])
14415         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14416       else if (Back[x][y] != 0)
14417         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14418                                     ACTION_EMPTYING);
14419       else
14420         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14421                                     ACTION_FILLING);
14422
14423       if (local_player->sokobanfields_still_needed == 0 &&
14424           game.emulation == EMU_SOKOBAN)
14425       {
14426         PlayerWins(player);
14427
14428         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14429       }
14430     }
14431     else
14432       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14433
14434     InitMovingField(x, y, move_direction);
14435     GfxAction[x][y] = ACTION_PUSHING;
14436
14437     if (mode == DF_SNAP)
14438       ContinueMoving(x, y);
14439     else
14440       MovPos[x][y] = (dx != 0 ? dx : dy);
14441
14442     Pushed[x][y] = TRUE;
14443     Pushed[nextx][nexty] = TRUE;
14444
14445     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14446       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14447     else
14448       player->push_delay_value = -1;    /* get new value later */
14449
14450     /* check for element change _after_ element has been pushed */
14451     if (game.use_change_when_pushing_bug)
14452     {
14453       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14454                                  player->index_bit, dig_side);
14455       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14456                                           player->index_bit, dig_side);
14457     }
14458   }
14459   else if (IS_SWITCHABLE(element))
14460   {
14461     if (PLAYER_SWITCHING(player, x, y))
14462     {
14463       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14464                                           player->index_bit, dig_side);
14465
14466       return MP_ACTION;
14467     }
14468
14469     player->is_switching = TRUE;
14470     player->switch_x = x;
14471     player->switch_y = y;
14472
14473     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14474
14475     if (element == EL_ROBOT_WHEEL)
14476     {
14477       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14478       ZX = x;
14479       ZY = y;
14480
14481       game.robot_wheel_active = TRUE;
14482
14483       DrawLevelField(x, y);
14484     }
14485     else if (element == EL_SP_TERMINAL)
14486     {
14487       int xx, yy;
14488
14489       SCAN_PLAYFIELD(xx, yy)
14490       {
14491         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14492           Bang(xx, yy);
14493         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14494           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14495       }
14496     }
14497     else if (IS_BELT_SWITCH(element))
14498     {
14499       ToggleBeltSwitch(x, y);
14500     }
14501     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14502              element == EL_SWITCHGATE_SWITCH_DOWN ||
14503              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14504              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14505     {
14506       ToggleSwitchgateSwitch(x, y);
14507     }
14508     else if (element == EL_LIGHT_SWITCH ||
14509              element == EL_LIGHT_SWITCH_ACTIVE)
14510     {
14511       ToggleLightSwitch(x, y);
14512     }
14513     else if (element == EL_TIMEGATE_SWITCH ||
14514              element == EL_DC_TIMEGATE_SWITCH)
14515     {
14516       ActivateTimegateSwitch(x, y);
14517     }
14518     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14519              element == EL_BALLOON_SWITCH_RIGHT ||
14520              element == EL_BALLOON_SWITCH_UP    ||
14521              element == EL_BALLOON_SWITCH_DOWN  ||
14522              element == EL_BALLOON_SWITCH_NONE  ||
14523              element == EL_BALLOON_SWITCH_ANY)
14524     {
14525       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14526                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14527                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14528                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14529                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14530                              move_direction);
14531     }
14532     else if (element == EL_LAMP)
14533     {
14534       Feld[x][y] = EL_LAMP_ACTIVE;
14535       local_player->lights_still_needed--;
14536
14537       ResetGfxAnimation(x, y);
14538       DrawLevelField(x, y);
14539     }
14540     else if (element == EL_TIME_ORB_FULL)
14541     {
14542       Feld[x][y] = EL_TIME_ORB_EMPTY;
14543
14544       if (level.time > 0 || level.use_time_orb_bug)
14545       {
14546         TimeLeft += level.time_orb_time;
14547
14548 #if 1
14549         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14550
14551         DisplayGameControlValues();
14552 #else
14553         DrawGameValue_Time(TimeLeft);
14554 #endif
14555       }
14556
14557       ResetGfxAnimation(x, y);
14558       DrawLevelField(x, y);
14559     }
14560     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14561              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14562     {
14563       int xx, yy;
14564
14565       game.ball_state = !game.ball_state;
14566
14567       SCAN_PLAYFIELD(xx, yy)
14568       {
14569         int e = Feld[xx][yy];
14570
14571         if (game.ball_state)
14572         {
14573           if (e == EL_EMC_MAGIC_BALL)
14574             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14575           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14576             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14577         }
14578         else
14579         {
14580           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14581             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14582           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14583             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14584         }
14585       }
14586     }
14587
14588     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14589                                         player->index_bit, dig_side);
14590
14591     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14592                                         player->index_bit, dig_side);
14593
14594     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14595                                         player->index_bit, dig_side);
14596
14597     return MP_ACTION;
14598   }
14599   else
14600   {
14601     if (!PLAYER_SWITCHING(player, x, y))
14602     {
14603       player->is_switching = TRUE;
14604       player->switch_x = x;
14605       player->switch_y = y;
14606
14607       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14608                                  player->index_bit, dig_side);
14609       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14610                                           player->index_bit, dig_side);
14611
14612       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14613                                  player->index_bit, dig_side);
14614       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14615                                           player->index_bit, dig_side);
14616     }
14617
14618     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14619                                player->index_bit, dig_side);
14620     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14621                                         player->index_bit, dig_side);
14622
14623     return MP_NO_ACTION;
14624   }
14625
14626   player->push_delay = -1;
14627
14628   if (is_player)                /* function can also be called by EL_PENGUIN */
14629   {
14630     if (Feld[x][y] != element)          /* really digged/collected something */
14631     {
14632       player->is_collecting = !player->is_digging;
14633       player->is_active = TRUE;
14634     }
14635   }
14636
14637   return MP_MOVING;
14638 }
14639
14640 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14641 {
14642   int jx = player->jx, jy = player->jy;
14643   int x = jx + dx, y = jy + dy;
14644   int snap_direction = (dx == -1 ? MV_LEFT  :
14645                         dx == +1 ? MV_RIGHT :
14646                         dy == -1 ? MV_UP    :
14647                         dy == +1 ? MV_DOWN  : MV_NONE);
14648   boolean can_continue_snapping = (level.continuous_snapping &&
14649                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14650
14651   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14652     return FALSE;
14653
14654   if (!player->active || !IN_LEV_FIELD(x, y))
14655     return FALSE;
14656
14657   if (dx && dy)
14658     return FALSE;
14659
14660   if (!dx && !dy)
14661   {
14662     if (player->MovPos == 0)
14663       player->is_pushing = FALSE;
14664
14665     player->is_snapping = FALSE;
14666
14667     if (player->MovPos == 0)
14668     {
14669       player->is_moving = FALSE;
14670       player->is_digging = FALSE;
14671       player->is_collecting = FALSE;
14672     }
14673
14674     return FALSE;
14675   }
14676
14677 #if USE_NEW_CONTINUOUS_SNAPPING
14678   /* prevent snapping with already pressed snap key when not allowed */
14679   if (player->is_snapping && !can_continue_snapping)
14680     return FALSE;
14681 #else
14682   if (player->is_snapping)
14683     return FALSE;
14684 #endif
14685
14686   player->MovDir = snap_direction;
14687
14688   if (player->MovPos == 0)
14689   {
14690     player->is_moving = FALSE;
14691     player->is_digging = FALSE;
14692     player->is_collecting = FALSE;
14693   }
14694
14695   player->is_dropping = FALSE;
14696   player->is_dropping_pressed = FALSE;
14697   player->drop_pressed_delay = 0;
14698
14699   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14700     return FALSE;
14701
14702   player->is_snapping = TRUE;
14703   player->is_active = TRUE;
14704
14705   if (player->MovPos == 0)
14706   {
14707     player->is_moving = FALSE;
14708     player->is_digging = FALSE;
14709     player->is_collecting = FALSE;
14710   }
14711
14712   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14713     DrawLevelField(player->last_jx, player->last_jy);
14714
14715   DrawLevelField(x, y);
14716
14717   return TRUE;
14718 }
14719
14720 boolean DropElement(struct PlayerInfo *player)
14721 {
14722   int old_element, new_element;
14723   int dropx = player->jx, dropy = player->jy;
14724   int drop_direction = player->MovDir;
14725   int drop_side = drop_direction;
14726 #if 1
14727   int drop_element = get_next_dropped_element(player);
14728 #else
14729   int drop_element = (player->inventory_size > 0 ?
14730                       player->inventory_element[player->inventory_size - 1] :
14731                       player->inventory_infinite_element != EL_UNDEFINED ?
14732                       player->inventory_infinite_element :
14733                       player->dynabombs_left > 0 ?
14734                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14735                       EL_UNDEFINED);
14736 #endif
14737
14738   player->is_dropping_pressed = TRUE;
14739
14740   /* do not drop an element on top of another element; when holding drop key
14741      pressed without moving, dropped element must move away before the next
14742      element can be dropped (this is especially important if the next element
14743      is dynamite, which can be placed on background for historical reasons) */
14744   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14745     return MP_ACTION;
14746
14747   if (IS_THROWABLE(drop_element))
14748   {
14749     dropx += GET_DX_FROM_DIR(drop_direction);
14750     dropy += GET_DY_FROM_DIR(drop_direction);
14751
14752     if (!IN_LEV_FIELD(dropx, dropy))
14753       return FALSE;
14754   }
14755
14756   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14757   new_element = drop_element;           /* default: no change when dropping */
14758
14759   /* check if player is active, not moving and ready to drop */
14760   if (!player->active || player->MovPos || player->drop_delay > 0)
14761     return FALSE;
14762
14763   /* check if player has anything that can be dropped */
14764   if (new_element == EL_UNDEFINED)
14765     return FALSE;
14766
14767   /* check if drop key was pressed long enough for EM style dynamite */
14768   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14769     return FALSE;
14770
14771   /* check if anything can be dropped at the current position */
14772   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14773     return FALSE;
14774
14775   /* collected custom elements can only be dropped on empty fields */
14776   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14777     return FALSE;
14778
14779   if (old_element != EL_EMPTY)
14780     Back[dropx][dropy] = old_element;   /* store old element on this field */
14781
14782   ResetGfxAnimation(dropx, dropy);
14783   ResetRandomAnimationValue(dropx, dropy);
14784
14785   if (player->inventory_size > 0 ||
14786       player->inventory_infinite_element != EL_UNDEFINED)
14787   {
14788     if (player->inventory_size > 0)
14789     {
14790       player->inventory_size--;
14791
14792       DrawGameDoorValues();
14793
14794       if (new_element == EL_DYNAMITE)
14795         new_element = EL_DYNAMITE_ACTIVE;
14796       else if (new_element == EL_EM_DYNAMITE)
14797         new_element = EL_EM_DYNAMITE_ACTIVE;
14798       else if (new_element == EL_SP_DISK_RED)
14799         new_element = EL_SP_DISK_RED_ACTIVE;
14800     }
14801
14802     Feld[dropx][dropy] = new_element;
14803
14804     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14805       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14806                           el2img(Feld[dropx][dropy]), 0);
14807
14808     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14809
14810     /* needed if previous element just changed to "empty" in the last frame */
14811     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14812
14813     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14814                                player->index_bit, drop_side);
14815     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14816                                         CE_PLAYER_DROPS_X,
14817                                         player->index_bit, drop_side);
14818
14819     TestIfElementTouchesCustomElement(dropx, dropy);
14820   }
14821   else          /* player is dropping a dyna bomb */
14822   {
14823     player->dynabombs_left--;
14824
14825     Feld[dropx][dropy] = new_element;
14826
14827     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14828       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14829                           el2img(Feld[dropx][dropy]), 0);
14830
14831     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14832   }
14833
14834   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14835     InitField_WithBug1(dropx, dropy, FALSE);
14836
14837   new_element = Feld[dropx][dropy];     /* element might have changed */
14838
14839   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14840       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14841   {
14842     int move_direction, nextx, nexty;
14843
14844     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14845       MovDir[dropx][dropy] = drop_direction;
14846
14847     move_direction = MovDir[dropx][dropy];
14848     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14849     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14850
14851     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14852
14853 #if USE_FIX_IMPACT_COLLISION
14854     /* do not cause impact style collision by dropping elements that can fall */
14855     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14856 #else
14857     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14858 #endif
14859   }
14860
14861   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14862   player->is_dropping = TRUE;
14863
14864   player->drop_pressed_delay = 0;
14865   player->is_dropping_pressed = FALSE;
14866
14867   player->drop_x = dropx;
14868   player->drop_y = dropy;
14869
14870   return TRUE;
14871 }
14872
14873 /* ------------------------------------------------------------------------- */
14874 /* game sound playing functions                                              */
14875 /* ------------------------------------------------------------------------- */
14876
14877 static int *loop_sound_frame = NULL;
14878 static int *loop_sound_volume = NULL;
14879
14880 void InitPlayLevelSound()
14881 {
14882   int num_sounds = getSoundListSize();
14883
14884   checked_free(loop_sound_frame);
14885   checked_free(loop_sound_volume);
14886
14887   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14888   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14889 }
14890
14891 static void PlayLevelSound(int x, int y, int nr)
14892 {
14893   int sx = SCREENX(x), sy = SCREENY(y);
14894   int volume, stereo_position;
14895   int max_distance = 8;
14896   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14897
14898   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14899       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14900     return;
14901
14902   if (!IN_LEV_FIELD(x, y) ||
14903       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14904       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14905     return;
14906
14907   volume = SOUND_MAX_VOLUME;
14908
14909   if (!IN_SCR_FIELD(sx, sy))
14910   {
14911     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14912     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14913
14914     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14915   }
14916
14917   stereo_position = (SOUND_MAX_LEFT +
14918                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14919                      (SCR_FIELDX + 2 * max_distance));
14920
14921   if (IS_LOOP_SOUND(nr))
14922   {
14923     /* This assures that quieter loop sounds do not overwrite louder ones,
14924        while restarting sound volume comparison with each new game frame. */
14925
14926     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14927       return;
14928
14929     loop_sound_volume[nr] = volume;
14930     loop_sound_frame[nr] = FrameCounter;
14931   }
14932
14933   PlaySoundExt(nr, volume, stereo_position, type);
14934 }
14935
14936 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14937 {
14938   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14939                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14940                  y < LEVELY(BY1) ? LEVELY(BY1) :
14941                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14942                  sound_action);
14943 }
14944
14945 static void PlayLevelSoundAction(int x, int y, int action)
14946 {
14947   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14948 }
14949
14950 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14951 {
14952   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14953
14954   if (sound_effect != SND_UNDEFINED)
14955     PlayLevelSound(x, y, sound_effect);
14956 }
14957
14958 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14959                                               int action)
14960 {
14961   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14962
14963   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14964     PlayLevelSound(x, y, sound_effect);
14965 }
14966
14967 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14968 {
14969   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14970
14971   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14972     PlayLevelSound(x, y, sound_effect);
14973 }
14974
14975 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14976 {
14977   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14978
14979   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14980     StopSound(sound_effect);
14981 }
14982
14983 static void PlayLevelMusic()
14984 {
14985   if (levelset.music[level_nr] != MUS_UNDEFINED)
14986     PlayMusic(levelset.music[level_nr]);        /* from config file */
14987   else
14988     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14989 }
14990
14991 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14992 {
14993   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14994   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14995   int x = xx - 1 - offset;
14996   int y = yy - 1 - offset;
14997
14998   switch (sample)
14999   {
15000     case SAMPLE_blank:
15001       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15002       break;
15003
15004     case SAMPLE_roll:
15005       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15006       break;
15007
15008     case SAMPLE_stone:
15009       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15010       break;
15011
15012     case SAMPLE_nut:
15013       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15014       break;
15015
15016     case SAMPLE_crack:
15017       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15018       break;
15019
15020     case SAMPLE_bug:
15021       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15022       break;
15023
15024     case SAMPLE_tank:
15025       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15026       break;
15027
15028     case SAMPLE_android_clone:
15029       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15030       break;
15031
15032     case SAMPLE_android_move:
15033       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15034       break;
15035
15036     case SAMPLE_spring:
15037       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15038       break;
15039
15040     case SAMPLE_slurp:
15041       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15042       break;
15043
15044     case SAMPLE_eater:
15045       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15046       break;
15047
15048     case SAMPLE_eater_eat:
15049       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15050       break;
15051
15052     case SAMPLE_alien:
15053       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15054       break;
15055
15056     case SAMPLE_collect:
15057       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15058       break;
15059
15060     case SAMPLE_diamond:
15061       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15062       break;
15063
15064     case SAMPLE_squash:
15065       /* !!! CHECK THIS !!! */
15066 #if 1
15067       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15068 #else
15069       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15070 #endif
15071       break;
15072
15073     case SAMPLE_wonderfall:
15074       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15075       break;
15076
15077     case SAMPLE_drip:
15078       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15079       break;
15080
15081     case SAMPLE_push:
15082       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15083       break;
15084
15085     case SAMPLE_dirt:
15086       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15087       break;
15088
15089     case SAMPLE_acid:
15090       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15091       break;
15092
15093     case SAMPLE_ball:
15094       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15095       break;
15096
15097     case SAMPLE_grow:
15098       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15099       break;
15100
15101     case SAMPLE_wonder:
15102       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15103       break;
15104
15105     case SAMPLE_door:
15106       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15107       break;
15108
15109     case SAMPLE_exit_open:
15110       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15111       break;
15112
15113     case SAMPLE_exit_leave:
15114       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15115       break;
15116
15117     case SAMPLE_dynamite:
15118       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15119       break;
15120
15121     case SAMPLE_tick:
15122       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15123       break;
15124
15125     case SAMPLE_press:
15126       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15127       break;
15128
15129     case SAMPLE_wheel:
15130       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15131       break;
15132
15133     case SAMPLE_boom:
15134       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15135       break;
15136
15137     case SAMPLE_die:
15138       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15139       break;
15140
15141     case SAMPLE_time:
15142       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15143       break;
15144
15145     default:
15146       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15147       break;
15148   }
15149 }
15150
15151 #if 0
15152 void ChangeTime(int value)
15153 {
15154   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15155
15156   *time += value;
15157
15158   /* EMC game engine uses value from time counter of RND game engine */
15159   level.native_em_level->lev->time = *time;
15160
15161   DrawGameValue_Time(*time);
15162 }
15163
15164 void RaiseScore(int value)
15165 {
15166   /* EMC game engine and RND game engine have separate score counters */
15167   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15168                 &level.native_em_level->lev->score : &local_player->score);
15169
15170   *score += value;
15171
15172   DrawGameValue_Score(*score);
15173 }
15174 #endif
15175
15176 void RaiseScore(int value)
15177 {
15178   local_player->score += value;
15179
15180 #if 1
15181   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15182
15183   DisplayGameControlValues();
15184 #else
15185   DrawGameValue_Score(local_player->score);
15186 #endif
15187 }
15188
15189 void RaiseScoreElement(int element)
15190 {
15191   switch (element)
15192   {
15193     case EL_EMERALD:
15194     case EL_BD_DIAMOND:
15195     case EL_EMERALD_YELLOW:
15196     case EL_EMERALD_RED:
15197     case EL_EMERALD_PURPLE:
15198     case EL_SP_INFOTRON:
15199       RaiseScore(level.score[SC_EMERALD]);
15200       break;
15201     case EL_DIAMOND:
15202       RaiseScore(level.score[SC_DIAMOND]);
15203       break;
15204     case EL_CRYSTAL:
15205       RaiseScore(level.score[SC_CRYSTAL]);
15206       break;
15207     case EL_PEARL:
15208       RaiseScore(level.score[SC_PEARL]);
15209       break;
15210     case EL_BUG:
15211     case EL_BD_BUTTERFLY:
15212     case EL_SP_ELECTRON:
15213       RaiseScore(level.score[SC_BUG]);
15214       break;
15215     case EL_SPACESHIP:
15216     case EL_BD_FIREFLY:
15217     case EL_SP_SNIKSNAK:
15218       RaiseScore(level.score[SC_SPACESHIP]);
15219       break;
15220     case EL_YAMYAM:
15221     case EL_DARK_YAMYAM:
15222       RaiseScore(level.score[SC_YAMYAM]);
15223       break;
15224     case EL_ROBOT:
15225       RaiseScore(level.score[SC_ROBOT]);
15226       break;
15227     case EL_PACMAN:
15228       RaiseScore(level.score[SC_PACMAN]);
15229       break;
15230     case EL_NUT:
15231       RaiseScore(level.score[SC_NUT]);
15232       break;
15233     case EL_DYNAMITE:
15234     case EL_EM_DYNAMITE:
15235     case EL_SP_DISK_RED:
15236     case EL_DYNABOMB_INCREASE_NUMBER:
15237     case EL_DYNABOMB_INCREASE_SIZE:
15238     case EL_DYNABOMB_INCREASE_POWER:
15239       RaiseScore(level.score[SC_DYNAMITE]);
15240       break;
15241     case EL_SHIELD_NORMAL:
15242     case EL_SHIELD_DEADLY:
15243       RaiseScore(level.score[SC_SHIELD]);
15244       break;
15245     case EL_EXTRA_TIME:
15246       RaiseScore(level.extra_time_score);
15247       break;
15248     case EL_KEY_1:
15249     case EL_KEY_2:
15250     case EL_KEY_3:
15251     case EL_KEY_4:
15252     case EL_EM_KEY_1:
15253     case EL_EM_KEY_2:
15254     case EL_EM_KEY_3:
15255     case EL_EM_KEY_4:
15256     case EL_EMC_KEY_5:
15257     case EL_EMC_KEY_6:
15258     case EL_EMC_KEY_7:
15259     case EL_EMC_KEY_8:
15260     case EL_DC_KEY_WHITE:
15261       RaiseScore(level.score[SC_KEY]);
15262       break;
15263     default:
15264       RaiseScore(element_info[element].collect_score);
15265       break;
15266   }
15267 }
15268
15269 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15270 {
15271   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15272   {
15273 #if defined(NETWORK_AVALIABLE)
15274     if (options.network)
15275       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15276     else
15277 #endif
15278     {
15279       if (quick_quit)
15280       {
15281 #if 1
15282
15283 #if 1
15284         FadeSkipNextFadeIn();
15285 #else
15286         fading = fading_none;
15287 #endif
15288
15289 #else
15290         OpenDoor(DOOR_CLOSE_1);
15291 #endif
15292
15293         game_status = GAME_MODE_MAIN;
15294
15295 #if 1
15296         DrawAndFadeInMainMenu(REDRAW_FIELD);
15297 #else
15298         DrawMainMenu();
15299 #endif
15300       }
15301       else
15302       {
15303 #if 0
15304         FadeOut(REDRAW_FIELD);
15305 #endif
15306
15307         game_status = GAME_MODE_MAIN;
15308
15309         DrawAndFadeInMainMenu(REDRAW_FIELD);
15310       }
15311     }
15312   }
15313   else          /* continue playing the game */
15314   {
15315     if (tape.playing && tape.deactivate_display)
15316       TapeDeactivateDisplayOff(TRUE);
15317
15318     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15319
15320     if (tape.playing && tape.deactivate_display)
15321       TapeDeactivateDisplayOn();
15322   }
15323 }
15324
15325 void RequestQuitGame(boolean ask_if_really_quit)
15326 {
15327   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15328   boolean skip_request = AllPlayersGone || quick_quit;
15329
15330   RequestQuitGameExt(skip_request, quick_quit,
15331                      "Do you really want to quit the game ?");
15332 }
15333
15334
15335 /* ------------------------------------------------------------------------- */
15336 /* random generator functions                                                */
15337 /* ------------------------------------------------------------------------- */
15338
15339 unsigned int InitEngineRandom_RND(long seed)
15340 {
15341   game.num_random_calls = 0;
15342
15343 #if 0
15344   unsigned int rnd_seed = InitEngineRandom(seed);
15345
15346   printf("::: START RND: %d\n", rnd_seed);
15347
15348   return rnd_seed;
15349 #else
15350
15351   return InitEngineRandom(seed);
15352
15353 #endif
15354
15355 }
15356
15357 unsigned int RND(int max)
15358 {
15359   if (max > 0)
15360   {
15361     game.num_random_calls++;
15362
15363     return GetEngineRandom(max);
15364   }
15365
15366   return 0;
15367 }
15368
15369
15370 /* ------------------------------------------------------------------------- */
15371 /* game engine snapshot handling functions                                   */
15372 /* ------------------------------------------------------------------------- */
15373
15374 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15375
15376 struct EngineSnapshotInfo
15377 {
15378   /* runtime values for custom element collect score */
15379   int collect_score[NUM_CUSTOM_ELEMENTS];
15380
15381   /* runtime values for group element choice position */
15382   int choice_pos[NUM_GROUP_ELEMENTS];
15383
15384   /* runtime values for belt position animations */
15385   int belt_graphic[4 * NUM_BELT_PARTS];
15386   int belt_anim_mode[4 * NUM_BELT_PARTS];
15387 };
15388
15389 struct EngineSnapshotNodeInfo
15390 {
15391   void *buffer_orig;
15392   void *buffer_copy;
15393   int size;
15394 };
15395
15396 static struct EngineSnapshotInfo engine_snapshot_rnd;
15397 static ListNode *engine_snapshot_list = NULL;
15398 static char *snapshot_level_identifier = NULL;
15399 static int snapshot_level_nr = -1;
15400
15401 void FreeEngineSnapshot()
15402 {
15403   while (engine_snapshot_list != NULL)
15404     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15405                        checked_free);
15406
15407   setString(&snapshot_level_identifier, NULL);
15408   snapshot_level_nr = -1;
15409 }
15410
15411 static void SaveEngineSnapshotValues_RND()
15412 {
15413   static int belt_base_active_element[4] =
15414   {
15415     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15416     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15417     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15418     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15419   };
15420   int i, j;
15421
15422   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15423   {
15424     int element = EL_CUSTOM_START + i;
15425
15426     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15427   }
15428
15429   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15430   {
15431     int element = EL_GROUP_START + i;
15432
15433     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15434   }
15435
15436   for (i = 0; i < 4; i++)
15437   {
15438     for (j = 0; j < NUM_BELT_PARTS; j++)
15439     {
15440       int element = belt_base_active_element[i] + j;
15441       int graphic = el2img(element);
15442       int anim_mode = graphic_info[graphic].anim_mode;
15443
15444       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15445       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15446     }
15447   }
15448 }
15449
15450 static void LoadEngineSnapshotValues_RND()
15451 {
15452   unsigned long num_random_calls = game.num_random_calls;
15453   int i, j;
15454
15455   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15456   {
15457     int element = EL_CUSTOM_START + i;
15458
15459     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15460   }
15461
15462   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15463   {
15464     int element = EL_GROUP_START + i;
15465
15466     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15467   }
15468
15469   for (i = 0; i < 4; i++)
15470   {
15471     for (j = 0; j < NUM_BELT_PARTS; j++)
15472     {
15473       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15474       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15475
15476       graphic_info[graphic].anim_mode = anim_mode;
15477     }
15478   }
15479
15480   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15481   {
15482     InitRND(tape.random_seed);
15483     for (i = 0; i < num_random_calls; i++)
15484       RND(1);
15485   }
15486
15487   if (game.num_random_calls != num_random_calls)
15488   {
15489     Error(ERR_INFO, "number of random calls out of sync");
15490     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15491     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15492     Error(ERR_EXIT, "this should not happen -- please debug");
15493   }
15494 }
15495
15496 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15497 {
15498   struct EngineSnapshotNodeInfo *bi =
15499     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15500
15501   bi->buffer_orig = buffer;
15502   bi->buffer_copy = checked_malloc(size);
15503   bi->size = size;
15504
15505   memcpy(bi->buffer_copy, buffer, size);
15506
15507   addNodeToList(&engine_snapshot_list, NULL, bi);
15508 }
15509
15510 void SaveEngineSnapshot()
15511 {
15512   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15513
15514   if (level_editor_test_game)   /* do not save snapshots from editor */
15515     return;
15516
15517   /* copy some special values to a structure better suited for the snapshot */
15518
15519   SaveEngineSnapshotValues_RND();
15520   SaveEngineSnapshotValues_EM();
15521
15522   /* save values stored in special snapshot structure */
15523
15524   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15525   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15526
15527   /* save further RND engine values */
15528
15529   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15530   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15531   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15532
15533   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15534   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15535   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15536   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15537
15538   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15539   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15540   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15541   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15542   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15543
15544   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15545   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15546   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15547
15548   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15549
15550   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15551
15552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15553   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15554
15555   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15556   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15558   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15559   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15560   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15561   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15562   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15563   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15565   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15566   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15567   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15570   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15572   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15573
15574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15576
15577   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15579   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15580
15581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15583
15584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15588   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15589
15590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15592
15593   /* save level identification information */
15594
15595   setString(&snapshot_level_identifier, leveldir_current->identifier);
15596   snapshot_level_nr = level_nr;
15597
15598 #if 0
15599   ListNode *node = engine_snapshot_list;
15600   int num_bytes = 0;
15601
15602   while (node != NULL)
15603   {
15604     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15605
15606     node = node->next;
15607   }
15608
15609   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15610 #endif
15611 }
15612
15613 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15614 {
15615   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15616 }
15617
15618 void LoadEngineSnapshot()
15619 {
15620   ListNode *node = engine_snapshot_list;
15621
15622   if (engine_snapshot_list == NULL)
15623     return;
15624
15625   while (node != NULL)
15626   {
15627     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15628
15629     node = node->next;
15630   }
15631
15632   /* restore special values from snapshot structure */
15633
15634   LoadEngineSnapshotValues_RND();
15635   LoadEngineSnapshotValues_EM();
15636 }
15637
15638 boolean CheckEngineSnapshot()
15639 {
15640   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15641           snapshot_level_nr == level_nr);
15642 }
15643
15644
15645 /* ---------- new game button stuff ---------------------------------------- */
15646
15647 /* graphic position values for game buttons */
15648 #define GAME_BUTTON_XSIZE       30
15649 #define GAME_BUTTON_YSIZE       30
15650 #define GAME_BUTTON_XPOS        5
15651 #define GAME_BUTTON_YPOS        215
15652 #define SOUND_BUTTON_XPOS       5
15653 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15654
15655 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15656 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15657 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15658 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15659 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15660 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15661
15662 static struct
15663 {
15664   int *x, *y;
15665   int gd_x, gd_y;
15666   int gadget_id;
15667   char *infotext;
15668 } gamebutton_info[NUM_GAME_BUTTONS] =
15669 {
15670 #if 1
15671   {
15672     &game.button.stop.x,        &game.button.stop.y,
15673     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15674     GAME_CTRL_ID_STOP,
15675     "stop game"
15676   },
15677   {
15678     &game.button.pause.x,       &game.button.pause.y,
15679     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15680     GAME_CTRL_ID_PAUSE,
15681     "pause game"
15682   },
15683   {
15684     &game.button.play.x,        &game.button.play.y,
15685     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15686     GAME_CTRL_ID_PLAY,
15687     "play game"
15688   },
15689   {
15690     &game.button.sound_music.x, &game.button.sound_music.y,
15691     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15692     SOUND_CTRL_ID_MUSIC,
15693     "background music on/off"
15694   },
15695   {
15696     &game.button.sound_loops.x, &game.button.sound_loops.y,
15697     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15698     SOUND_CTRL_ID_LOOPS,
15699     "sound loops on/off"
15700   },
15701   {
15702     &game.button.sound_simple.x,&game.button.sound_simple.y,
15703     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15704     SOUND_CTRL_ID_SIMPLE,
15705     "normal sounds on/off"
15706   }
15707 #else
15708   {
15709     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15710     GAME_CTRL_ID_STOP,
15711     "stop game"
15712   },
15713   {
15714     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15715     GAME_CTRL_ID_PAUSE,
15716     "pause game"
15717   },
15718   {
15719     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15720     GAME_CTRL_ID_PLAY,
15721     "play game"
15722   },
15723   {
15724     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15725     SOUND_CTRL_ID_MUSIC,
15726     "background music on/off"
15727   },
15728   {
15729     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15730     SOUND_CTRL_ID_LOOPS,
15731     "sound loops on/off"
15732   },
15733   {
15734     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15735     SOUND_CTRL_ID_SIMPLE,
15736     "normal sounds on/off"
15737   }
15738 #endif
15739 };
15740
15741 void CreateGameButtons()
15742 {
15743   int i;
15744
15745   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15746   {
15747     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15748     struct GadgetInfo *gi;
15749     int button_type;
15750     boolean checked;
15751     unsigned long event_mask;
15752     int x, y;
15753     int gd_xoffset, gd_yoffset;
15754     int gd_x1, gd_x2, gd_y1, gd_y2;
15755     int id = i;
15756
15757     x = DX + *gamebutton_info[i].x;
15758     y = DY + *gamebutton_info[i].y;
15759     gd_xoffset = gamebutton_info[i].gd_x;
15760     gd_yoffset = gamebutton_info[i].gd_y;
15761     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15762     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15763
15764     if (id == GAME_CTRL_ID_STOP ||
15765         id == GAME_CTRL_ID_PAUSE ||
15766         id == GAME_CTRL_ID_PLAY)
15767     {
15768       button_type = GD_TYPE_NORMAL_BUTTON;
15769       checked = FALSE;
15770       event_mask = GD_EVENT_RELEASED;
15771       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15772       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15773     }
15774     else
15775     {
15776       button_type = GD_TYPE_CHECK_BUTTON;
15777       checked =
15778         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15779          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15780          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15781       event_mask = GD_EVENT_PRESSED;
15782       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15783       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15784     }
15785
15786     gi = CreateGadget(GDI_CUSTOM_ID, id,
15787                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15788 #if 1
15789                       GDI_X, x,
15790                       GDI_Y, y,
15791 #else
15792                       GDI_X, DX + gd_xoffset,
15793                       GDI_Y, DY + gd_yoffset,
15794 #endif
15795                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15796                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15797                       GDI_TYPE, button_type,
15798                       GDI_STATE, GD_BUTTON_UNPRESSED,
15799                       GDI_CHECKED, checked,
15800                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15801                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15802                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15803                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15804                       GDI_DIRECT_DRAW, FALSE,
15805                       GDI_EVENT_MASK, event_mask,
15806                       GDI_CALLBACK_ACTION, HandleGameButtons,
15807                       GDI_END);
15808
15809     if (gi == NULL)
15810       Error(ERR_EXIT, "cannot create gadget");
15811
15812     game_gadget[id] = gi;
15813   }
15814 }
15815
15816 void FreeGameButtons()
15817 {
15818   int i;
15819
15820   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15821     FreeGadget(game_gadget[i]);
15822 }
15823
15824 static void MapGameButtons()
15825 {
15826   int i;
15827
15828   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15829     MapGadget(game_gadget[i]);
15830 }
15831
15832 void UnmapGameButtons()
15833 {
15834   int i;
15835
15836   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15837     UnmapGadget(game_gadget[i]);
15838 }
15839
15840 void RedrawGameButtons()
15841 {
15842   int i;
15843
15844   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15845     RedrawGadget(game_gadget[i]);
15846 }
15847
15848 static void HandleGameButtons(struct GadgetInfo *gi)
15849 {
15850   int id = gi->custom_id;
15851
15852   if (game_status != GAME_MODE_PLAYING)
15853     return;
15854
15855   switch (id)
15856   {
15857     case GAME_CTRL_ID_STOP:
15858       if (tape.playing)
15859         TapeStop();
15860       else
15861         RequestQuitGame(TRUE);
15862       break;
15863
15864     case GAME_CTRL_ID_PAUSE:
15865       if (options.network)
15866       {
15867 #if defined(NETWORK_AVALIABLE)
15868         if (tape.pausing)
15869           SendToServer_ContinuePlaying();
15870         else
15871           SendToServer_PausePlaying();
15872 #endif
15873       }
15874       else
15875         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15876       break;
15877
15878     case GAME_CTRL_ID_PLAY:
15879       if (tape.pausing)
15880       {
15881 #if defined(NETWORK_AVALIABLE)
15882         if (options.network)
15883           SendToServer_ContinuePlaying();
15884         else
15885 #endif
15886         {
15887           tape.pausing = FALSE;
15888           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15889         }
15890       }
15891       break;
15892
15893     case SOUND_CTRL_ID_MUSIC:
15894       if (setup.sound_music)
15895       { 
15896         setup.sound_music = FALSE;
15897         FadeMusic();
15898       }
15899       else if (audio.music_available)
15900       { 
15901         setup.sound = setup.sound_music = TRUE;
15902
15903         SetAudioMode(setup.sound);
15904
15905         PlayLevelMusic();
15906       }
15907       break;
15908
15909     case SOUND_CTRL_ID_LOOPS:
15910       if (setup.sound_loops)
15911         setup.sound_loops = FALSE;
15912       else if (audio.loops_available)
15913       {
15914         setup.sound = setup.sound_loops = TRUE;
15915         SetAudioMode(setup.sound);
15916       }
15917       break;
15918
15919     case SOUND_CTRL_ID_SIMPLE:
15920       if (setup.sound_simple)
15921         setup.sound_simple = FALSE;
15922       else if (audio.sound_available)
15923       {
15924         setup.sound = setup.sound_simple = TRUE;
15925         SetAudioMode(setup.sound);
15926       }
15927       break;
15928
15929     default:
15930       break;
15931   }
15932 }