rnd-20070901-3-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_PANEL_LEVEL_NUMBER                 0
135 #define GAME_PANEL_GEMS                         1
136 #define GAME_PANEL_INVENTORY_COUNT              2
137 #define GAME_PANEL_INVENTORY_FIRST_1            3
138 #define GAME_PANEL_INVENTORY_FIRST_2            4
139 #define GAME_PANEL_INVENTORY_FIRST_3            5
140 #define GAME_PANEL_INVENTORY_FIRST_4            6
141 #define GAME_PANEL_INVENTORY_FIRST_5            7
142 #define GAME_PANEL_INVENTORY_FIRST_6            8
143 #define GAME_PANEL_INVENTORY_FIRST_7            9
144 #define GAME_PANEL_INVENTORY_FIRST_8            10
145 #define GAME_PANEL_INVENTORY_LAST_1             11
146 #define GAME_PANEL_INVENTORY_LAST_2             12
147 #define GAME_PANEL_INVENTORY_LAST_3             13
148 #define GAME_PANEL_INVENTORY_LAST_4             14
149 #define GAME_PANEL_INVENTORY_LAST_5             15
150 #define GAME_PANEL_INVENTORY_LAST_6             16
151 #define GAME_PANEL_INVENTORY_LAST_7             17
152 #define GAME_PANEL_INVENTORY_LAST_8             18
153 #define GAME_PANEL_KEY_1                        19
154 #define GAME_PANEL_KEY_2                        20
155 #define GAME_PANEL_KEY_3                        21
156 #define GAME_PANEL_KEY_4                        22
157 #define GAME_PANEL_KEY_5                        23
158 #define GAME_PANEL_KEY_6                        24
159 #define GAME_PANEL_KEY_7                        25
160 #define GAME_PANEL_KEY_8                        26
161 #define GAME_PANEL_KEY_WHITE                    27
162 #define GAME_PANEL_KEY_WHITE_COUNT              28
163 #define GAME_PANEL_SCORE                        29
164 #define GAME_PANEL_TIME                         30
165 #define GAME_PANEL_TIME_HH                      31
166 #define GAME_PANEL_TIME_MM                      32
167 #define GAME_PANEL_TIME_SS                      33
168 #define GAME_PANEL_SHIELD_NORMAL                34
169 #define GAME_PANEL_SHIELD_NORMAL_TIME           35
170 #define GAME_PANEL_SHIELD_DEADLY                36
171 #define GAME_PANEL_SHIELD_DEADLY_TIME           37
172 #define GAME_PANEL_EXIT                         38
173 #define GAME_PANEL_EMC_MAGIC_BALL               39
174 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        40
175 #define GAME_PANEL_LIGHT_SWITCH                 41
176 #define GAME_PANEL_LIGHT_SWITCH_TIME            42
177 #define GAME_PANEL_TIMEGATE_SWITCH              43
178 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         44
179 #define GAME_PANEL_SWITCHGATE_SWITCH            45
180 #define GAME_PANEL_EMC_LENSES                   46
181 #define GAME_PANEL_EMC_LENSES_TIME              47
182 #define GAME_PANEL_EMC_MAGNIFIER                48
183 #define GAME_PANEL_EMC_MAGNIFIER_TIME           49
184 #define GAME_PANEL_BALLOON_SWITCH               50
185 #define GAME_PANEL_DYNABOMB_NUMBER              51
186 #define GAME_PANEL_DYNABOMB_SIZE                52
187 #define GAME_PANEL_DYNABOMB_POWER               53
188 #define GAME_PANEL_PENGUINS                     54
189 #define GAME_PANEL_SOKOBAN_OBJECTS              55
190 #define GAME_PANEL_SOKOBAN_FIELDS               56
191 #define GAME_PANEL_ROBOT_WHEEL                  57
192 #define GAME_PANEL_CONVEYOR_BELT_1              58
193 #define GAME_PANEL_CONVEYOR_BELT_2              59
194 #define GAME_PANEL_CONVEYOR_BELT_3              60
195 #define GAME_PANEL_CONVEYOR_BELT_4              61
196 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       62
197 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       63
198 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       64
199 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       65
200 #define GAME_PANEL_MAGIC_WALL                   66
201 #define GAME_PANEL_MAGIC_WALL_TIME              67
202 #define GAME_PANEL_GRAVITY_STATE                68
203 #define GAME_PANEL_GRAPHIC_1                    69
204 #define GAME_PANEL_GRAPHIC_2                    70
205 #define GAME_PANEL_GRAPHIC_3                    71
206 #define GAME_PANEL_GRAPHIC_4                    72
207 #define GAME_PANEL_GRAPHIC_5                    73
208 #define GAME_PANEL_GRAPHIC_6                    74
209 #define GAME_PANEL_GRAPHIC_7                    75
210 #define GAME_PANEL_GRAPHIC_8                    76
211 #define GAME_PANEL_ELEMENT_1                    77
212 #define GAME_PANEL_ELEMENT_2                    78
213 #define GAME_PANEL_ELEMENT_3                    79
214 #define GAME_PANEL_ELEMENT_4                    80
215 #define GAME_PANEL_ELEMENT_5                    81
216 #define GAME_PANEL_ELEMENT_6                    82
217 #define GAME_PANEL_ELEMENT_7                    83
218 #define GAME_PANEL_ELEMENT_8                    84
219 #define GAME_PANEL_ELEMENT_COUNT_1              85
220 #define GAME_PANEL_ELEMENT_COUNT_2              86
221 #define GAME_PANEL_ELEMENT_COUNT_3              87
222 #define GAME_PANEL_ELEMENT_COUNT_4              88
223 #define GAME_PANEL_ELEMENT_COUNT_5              89
224 #define GAME_PANEL_ELEMENT_COUNT_6              90
225 #define GAME_PANEL_ELEMENT_COUNT_7              91
226 #define GAME_PANEL_ELEMENT_COUNT_8              92
227 #define GAME_PANEL_CE_SCORE_1                   93
228 #define GAME_PANEL_CE_SCORE_2                   94
229 #define GAME_PANEL_CE_SCORE_3                   95
230 #define GAME_PANEL_CE_SCORE_4                   96
231 #define GAME_PANEL_CE_SCORE_5                   97
232 #define GAME_PANEL_CE_SCORE_6                   98
233 #define GAME_PANEL_CE_SCORE_7                   99
234 #define GAME_PANEL_CE_SCORE_8                   100
235 #define GAME_PANEL_CE_SCORE_1_ELEMENT           101
236 #define GAME_PANEL_CE_SCORE_2_ELEMENT           102
237 #define GAME_PANEL_CE_SCORE_3_ELEMENT           103
238 #define GAME_PANEL_CE_SCORE_4_ELEMENT           104
239 #define GAME_PANEL_CE_SCORE_5_ELEMENT           105
240 #define GAME_PANEL_CE_SCORE_6_ELEMENT           106
241 #define GAME_PANEL_CE_SCORE_7_ELEMENT           107
242 #define GAME_PANEL_CE_SCORE_8_ELEMENT           108
243 #define GAME_PANEL_PLAYER_NAME                  109
244 #define GAME_PANEL_LEVEL_NAME                   110
245 #define GAME_PANEL_LEVEL_AUTHOR                 111
246
247 #define NUM_GAME_PANEL_CONTROLS                 112
248
249 struct GamePanelOrderInfo
250 {
251   int nr;
252   int sort_priority;
253 };
254
255 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
256
257 struct GamePanelControlInfo
258 {
259   int nr;
260
261   struct TextPosInfo *pos;
262   int type;
263
264   int value, last_value;
265   int frame, last_frame;
266   int gfx_frame;
267   int gfx_random;
268 };
269
270 static struct GamePanelControlInfo game_panel_controls[] =
271 {
272   {
273     GAME_PANEL_LEVEL_NUMBER,
274     &game.panel.level_number,
275     TYPE_INTEGER,
276   },
277   {
278     GAME_PANEL_GEMS,
279     &game.panel.gems,
280     TYPE_INTEGER,
281   },
282   {
283     GAME_PANEL_INVENTORY_COUNT,
284     &game.panel.inventory_count,
285     TYPE_INTEGER,
286   },
287   {
288     GAME_PANEL_INVENTORY_FIRST_1,
289     &game.panel.inventory_first[0],
290     TYPE_ELEMENT,
291   },
292   {
293     GAME_PANEL_INVENTORY_FIRST_2,
294     &game.panel.inventory_first[1],
295     TYPE_ELEMENT,
296   },
297   {
298     GAME_PANEL_INVENTORY_FIRST_3,
299     &game.panel.inventory_first[2],
300     TYPE_ELEMENT,
301   },
302   {
303     GAME_PANEL_INVENTORY_FIRST_4,
304     &game.panel.inventory_first[3],
305     TYPE_ELEMENT,
306   },
307   {
308     GAME_PANEL_INVENTORY_FIRST_5,
309     &game.panel.inventory_first[4],
310     TYPE_ELEMENT,
311   },
312   {
313     GAME_PANEL_INVENTORY_FIRST_6,
314     &game.panel.inventory_first[5],
315     TYPE_ELEMENT,
316   },
317   {
318     GAME_PANEL_INVENTORY_FIRST_7,
319     &game.panel.inventory_first[6],
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_PANEL_INVENTORY_FIRST_8,
324     &game.panel.inventory_first[7],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_PANEL_INVENTORY_LAST_1,
329     &game.panel.inventory_last[0],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_PANEL_INVENTORY_LAST_2,
334     &game.panel.inventory_last[1],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_PANEL_INVENTORY_LAST_3,
339     &game.panel.inventory_last[2],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_PANEL_INVENTORY_LAST_4,
344     &game.panel.inventory_last[3],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_PANEL_INVENTORY_LAST_5,
349     &game.panel.inventory_last[4],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_PANEL_INVENTORY_LAST_6,
354     &game.panel.inventory_last[5],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_PANEL_INVENTORY_LAST_7,
359     &game.panel.inventory_last[6],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_PANEL_INVENTORY_LAST_8,
364     &game.panel.inventory_last[7],
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_PANEL_KEY_1,
369     &game.panel.key[0],
370     TYPE_ELEMENT,
371   },
372   {
373     GAME_PANEL_KEY_2,
374     &game.panel.key[1],
375     TYPE_ELEMENT,
376   },
377   {
378     GAME_PANEL_KEY_3,
379     &game.panel.key[2],
380     TYPE_ELEMENT,
381   },
382   {
383     GAME_PANEL_KEY_4,
384     &game.panel.key[3],
385     TYPE_ELEMENT,
386   },
387   {
388     GAME_PANEL_KEY_5,
389     &game.panel.key[4],
390     TYPE_ELEMENT,
391   },
392   {
393     GAME_PANEL_KEY_6,
394     &game.panel.key[5],
395     TYPE_ELEMENT,
396   },
397   {
398     GAME_PANEL_KEY_7,
399     &game.panel.key[6],
400     TYPE_ELEMENT,
401   },
402   {
403     GAME_PANEL_KEY_8,
404     &game.panel.key[7],
405     TYPE_ELEMENT,
406   },
407   {
408     GAME_PANEL_KEY_WHITE,
409     &game.panel.key_white,
410     TYPE_ELEMENT,
411   },
412   {
413     GAME_PANEL_KEY_WHITE_COUNT,
414     &game.panel.key_white_count,
415     TYPE_INTEGER,
416   },
417   {
418     GAME_PANEL_SCORE,
419     &game.panel.score,
420     TYPE_INTEGER,
421   },
422   {
423     GAME_PANEL_TIME,
424     &game.panel.time,
425     TYPE_INTEGER,
426   },
427   {
428     GAME_PANEL_TIME_HH,
429     &game.panel.time_hh,
430     TYPE_INTEGER,
431   },
432   {
433     GAME_PANEL_TIME_MM,
434     &game.panel.time_mm,
435     TYPE_INTEGER,
436   },
437   {
438     GAME_PANEL_TIME_SS,
439     &game.panel.time_ss,
440     TYPE_INTEGER,
441   },
442   {
443     GAME_PANEL_SHIELD_NORMAL,
444     &game.panel.shield_normal,
445     TYPE_ELEMENT,
446   },
447   {
448     GAME_PANEL_SHIELD_NORMAL_TIME,
449     &game.panel.shield_normal_time,
450     TYPE_INTEGER,
451   },
452   {
453     GAME_PANEL_SHIELD_DEADLY,
454     &game.panel.shield_deadly,
455     TYPE_ELEMENT,
456   },
457   {
458     GAME_PANEL_SHIELD_DEADLY_TIME,
459     &game.panel.shield_deadly_time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_PANEL_EXIT,
464     &game.panel.exit,
465     TYPE_ELEMENT,
466   },
467   {
468     GAME_PANEL_EMC_MAGIC_BALL,
469     &game.panel.emc_magic_ball,
470     TYPE_ELEMENT,
471   },
472   {
473     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
474     &game.panel.emc_magic_ball_switch,
475     TYPE_ELEMENT,
476   },
477   {
478     GAME_PANEL_LIGHT_SWITCH,
479     &game.panel.light_switch,
480     TYPE_ELEMENT,
481   },
482   {
483     GAME_PANEL_LIGHT_SWITCH_TIME,
484     &game.panel.light_switch_time,
485     TYPE_INTEGER,
486   },
487   {
488     GAME_PANEL_TIMEGATE_SWITCH,
489     &game.panel.timegate_switch,
490     TYPE_ELEMENT,
491   },
492   {
493     GAME_PANEL_TIMEGATE_SWITCH_TIME,
494     &game.panel.timegate_switch_time,
495     TYPE_INTEGER,
496   },
497   {
498     GAME_PANEL_SWITCHGATE_SWITCH,
499     &game.panel.switchgate_switch,
500     TYPE_ELEMENT,
501   },
502   {
503     GAME_PANEL_EMC_LENSES,
504     &game.panel.emc_lenses,
505     TYPE_ELEMENT,
506   },
507   {
508     GAME_PANEL_EMC_LENSES_TIME,
509     &game.panel.emc_lenses_time,
510     TYPE_INTEGER,
511   },
512   {
513     GAME_PANEL_EMC_MAGNIFIER,
514     &game.panel.emc_magnifier,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_PANEL_EMC_MAGNIFIER_TIME,
519     &game.panel.emc_magnifier_time,
520     TYPE_INTEGER,
521   },
522   {
523     GAME_PANEL_BALLOON_SWITCH,
524     &game.panel.balloon_switch,
525     TYPE_ELEMENT,
526   },
527   {
528     GAME_PANEL_DYNABOMB_NUMBER,
529     &game.panel.dynabomb_number,
530     TYPE_INTEGER,
531   },
532   {
533     GAME_PANEL_DYNABOMB_SIZE,
534     &game.panel.dynabomb_size,
535     TYPE_INTEGER,
536   },
537   {
538     GAME_PANEL_DYNABOMB_POWER,
539     &game.panel.dynabomb_power,
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_PANEL_PENGUINS,
544     &game.panel.penguins,
545     TYPE_INTEGER,
546   },
547   {
548     GAME_PANEL_SOKOBAN_OBJECTS,
549     &game.panel.sokoban_objects,
550     TYPE_INTEGER,
551   },
552   {
553     GAME_PANEL_SOKOBAN_FIELDS,
554     &game.panel.sokoban_fields,
555     TYPE_INTEGER,
556   },
557   {
558     GAME_PANEL_ROBOT_WHEEL,
559     &game.panel.robot_wheel,
560     TYPE_ELEMENT,
561   },
562   {
563     GAME_PANEL_CONVEYOR_BELT_1,
564     &game.panel.conveyor_belt[0],
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_PANEL_CONVEYOR_BELT_2,
569     &game.panel.conveyor_belt[1],
570     TYPE_ELEMENT,
571   },
572   {
573     GAME_PANEL_CONVEYOR_BELT_3,
574     &game.panel.conveyor_belt[2],
575     TYPE_ELEMENT,
576   },
577   {
578     GAME_PANEL_CONVEYOR_BELT_4,
579     &game.panel.conveyor_belt[3],
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
584     &game.panel.conveyor_belt_switch[0],
585     TYPE_ELEMENT,
586   },
587   {
588     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
589     &game.panel.conveyor_belt_switch[1],
590     TYPE_ELEMENT,
591   },
592   {
593     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
594     &game.panel.conveyor_belt_switch[2],
595     TYPE_ELEMENT,
596   },
597   {
598     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
599     &game.panel.conveyor_belt_switch[3],
600     TYPE_ELEMENT,
601   },
602   {
603     GAME_PANEL_MAGIC_WALL,
604     &game.panel.magic_wall,
605     TYPE_ELEMENT,
606   },
607   {
608     GAME_PANEL_MAGIC_WALL_TIME,
609     &game.panel.magic_wall_time,
610     TYPE_INTEGER,
611   },
612   {
613     GAME_PANEL_GRAVITY_STATE,
614     &game.panel.gravity_state,
615     TYPE_STRING,
616   },
617   {
618     GAME_PANEL_GRAPHIC_1,
619     &game.panel.graphic[0],
620     TYPE_ELEMENT,
621   },
622   {
623     GAME_PANEL_GRAPHIC_2,
624     &game.panel.graphic[1],
625     TYPE_ELEMENT,
626   },
627   {
628     GAME_PANEL_GRAPHIC_3,
629     &game.panel.graphic[2],
630     TYPE_ELEMENT,
631   },
632   {
633     GAME_PANEL_GRAPHIC_4,
634     &game.panel.graphic[3],
635     TYPE_ELEMENT,
636   },
637   {
638     GAME_PANEL_GRAPHIC_5,
639     &game.panel.graphic[4],
640     TYPE_ELEMENT,
641   },
642   {
643     GAME_PANEL_GRAPHIC_6,
644     &game.panel.graphic[5],
645     TYPE_ELEMENT,
646   },
647   {
648     GAME_PANEL_GRAPHIC_7,
649     &game.panel.graphic[6],
650     TYPE_ELEMENT,
651   },
652   {
653     GAME_PANEL_GRAPHIC_8,
654     &game.panel.graphic[7],
655     TYPE_ELEMENT,
656   },
657   {
658     GAME_PANEL_ELEMENT_1,
659     &game.panel.element[0],
660     TYPE_ELEMENT,
661   },
662   {
663     GAME_PANEL_ELEMENT_2,
664     &game.panel.element[1],
665     TYPE_ELEMENT,
666   },
667   {
668     GAME_PANEL_ELEMENT_3,
669     &game.panel.element[2],
670     TYPE_ELEMENT,
671   },
672   {
673     GAME_PANEL_ELEMENT_4,
674     &game.panel.element[3],
675     TYPE_ELEMENT,
676   },
677   {
678     GAME_PANEL_ELEMENT_5,
679     &game.panel.element[4],
680     TYPE_ELEMENT,
681   },
682   {
683     GAME_PANEL_ELEMENT_6,
684     &game.panel.element[5],
685     TYPE_ELEMENT,
686   },
687   {
688     GAME_PANEL_ELEMENT_7,
689     &game.panel.element[6],
690     TYPE_ELEMENT,
691   },
692   {
693     GAME_PANEL_ELEMENT_8,
694     &game.panel.element[7],
695     TYPE_ELEMENT,
696   },
697   {
698     GAME_PANEL_ELEMENT_COUNT_1,
699     &game.panel.element_count[0],
700     TYPE_INTEGER,
701   },
702   {
703     GAME_PANEL_ELEMENT_COUNT_2,
704     &game.panel.element_count[1],
705     TYPE_INTEGER,
706   },
707   {
708     GAME_PANEL_ELEMENT_COUNT_3,
709     &game.panel.element_count[2],
710     TYPE_INTEGER,
711   },
712   {
713     GAME_PANEL_ELEMENT_COUNT_4,
714     &game.panel.element_count[3],
715     TYPE_INTEGER,
716   },
717   {
718     GAME_PANEL_ELEMENT_COUNT_5,
719     &game.panel.element_count[4],
720     TYPE_INTEGER,
721   },
722   {
723     GAME_PANEL_ELEMENT_COUNT_6,
724     &game.panel.element_count[5],
725     TYPE_INTEGER,
726   },
727   {
728     GAME_PANEL_ELEMENT_COUNT_7,
729     &game.panel.element_count[6],
730     TYPE_INTEGER,
731   },
732   {
733     GAME_PANEL_ELEMENT_COUNT_8,
734     &game.panel.element_count[7],
735     TYPE_INTEGER,
736   },
737   {
738     GAME_PANEL_CE_SCORE_1,
739     &game.panel.ce_score[0],
740     TYPE_INTEGER,
741   },
742   {
743     GAME_PANEL_CE_SCORE_2,
744     &game.panel.ce_score[1],
745     TYPE_INTEGER,
746   },
747   {
748     GAME_PANEL_CE_SCORE_3,
749     &game.panel.ce_score[2],
750     TYPE_INTEGER,
751   },
752   {
753     GAME_PANEL_CE_SCORE_4,
754     &game.panel.ce_score[3],
755     TYPE_INTEGER,
756   },
757   {
758     GAME_PANEL_CE_SCORE_5,
759     &game.panel.ce_score[4],
760     TYPE_INTEGER,
761   },
762   {
763     GAME_PANEL_CE_SCORE_6,
764     &game.panel.ce_score[5],
765     TYPE_INTEGER,
766   },
767   {
768     GAME_PANEL_CE_SCORE_7,
769     &game.panel.ce_score[6],
770     TYPE_INTEGER,
771   },
772   {
773     GAME_PANEL_CE_SCORE_8,
774     &game.panel.ce_score[7],
775     TYPE_INTEGER,
776   },
777   {
778     GAME_PANEL_CE_SCORE_1_ELEMENT,
779     &game.panel.ce_score_element[0],
780     TYPE_ELEMENT,
781   },
782   {
783     GAME_PANEL_CE_SCORE_2_ELEMENT,
784     &game.panel.ce_score_element[1],
785     TYPE_ELEMENT,
786   },
787   {
788     GAME_PANEL_CE_SCORE_3_ELEMENT,
789     &game.panel.ce_score_element[2],
790     TYPE_ELEMENT,
791   },
792   {
793     GAME_PANEL_CE_SCORE_4_ELEMENT,
794     &game.panel.ce_score_element[3],
795     TYPE_ELEMENT,
796   },
797   {
798     GAME_PANEL_CE_SCORE_5_ELEMENT,
799     &game.panel.ce_score_element[4],
800     TYPE_ELEMENT,
801   },
802   {
803     GAME_PANEL_CE_SCORE_6_ELEMENT,
804     &game.panel.ce_score_element[5],
805     TYPE_ELEMENT,
806   },
807   {
808     GAME_PANEL_CE_SCORE_7_ELEMENT,
809     &game.panel.ce_score_element[6],
810     TYPE_ELEMENT,
811   },
812   {
813     GAME_PANEL_CE_SCORE_8_ELEMENT,
814     &game.panel.ce_score_element[7],
815     TYPE_ELEMENT,
816   },
817   {
818     GAME_PANEL_PLAYER_NAME,
819     &game.panel.player_name,
820     TYPE_STRING,
821   },
822   {
823     GAME_PANEL_LEVEL_NAME,
824     &game.panel.level_name,
825     TYPE_STRING,
826   },
827   {
828     GAME_PANEL_LEVEL_AUTHOR,
829     &game.panel.level_author,
830     TYPE_STRING,
831   },
832
833   {
834     -1,
835     NULL,
836     -1,
837   }
838 };
839 #endif
840
841
842 /* values for delayed check of falling and moving elements and for collision */
843 #define CHECK_DELAY_MOVING      3
844 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
845 #define CHECK_DELAY_COLLISION   2
846 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
847
848 /* values for initial player move delay (initial delay counter value) */
849 #define INITIAL_MOVE_DELAY_OFF  -1
850 #define INITIAL_MOVE_DELAY_ON   0
851
852 /* values for player movement speed (which is in fact a delay value) */
853 #define MOVE_DELAY_MIN_SPEED    32
854 #define MOVE_DELAY_NORMAL_SPEED 8
855 #define MOVE_DELAY_HIGH_SPEED   4
856 #define MOVE_DELAY_MAX_SPEED    1
857
858 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
859 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
860
861 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
862 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP               0
1007 #define GAME_CTRL_ID_PAUSE              1
1008 #define GAME_CTRL_ID_PLAY               2
1009 #define SOUND_CTRL_ID_MUSIC             3
1010 #define SOUND_CTRL_ID_LOOPS             4
1011 #define SOUND_CTRL_ID_SIMPLE            5
1012
1013 #define NUM_GAME_BUTTONS                6
1014
1015
1016 /* forward declaration for internal use */
1017
1018 static void CreateField(int, int, int);
1019
1020 static void ResetGfxAnimation(int, int);
1021
1022 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1023 static void AdvanceFrameAndPlayerCounters(int);
1024
1025 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1026 static boolean MovePlayer(struct PlayerInfo *, int, int);
1027 static void ScrollPlayer(struct PlayerInfo *, int);
1028 static void ScrollScreen(struct PlayerInfo *, int);
1029
1030 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1031
1032 static void InitBeltMovement(void);
1033 static void CloseAllOpenTimegates(void);
1034 static void CheckGravityMovement(struct PlayerInfo *);
1035 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1036 static void KillPlayerUnlessEnemyProtected(int, int);
1037 static void KillPlayerUnlessExplosionProtected(int, int);
1038
1039 static void TestIfPlayerTouchesCustomElement(int, int);
1040 static void TestIfElementTouchesCustomElement(int, int);
1041 static void TestIfElementHitsCustomElement(int, int, int);
1042 #if 0
1043 static void TestIfElementSmashesCustomElement(int, int, int);
1044 #endif
1045
1046 static void HandleElementChange(int, int, int);
1047 static void ExecuteCustomElementAction(int, int, int, int);
1048 static boolean ChangeElement(int, int, int, int);
1049
1050 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1051 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1052         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1053 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1054         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1055 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1056         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1057 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1058         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1059
1060 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1061 #define CheckElementChange(x, y, e, te, ev)                             \
1062         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1063 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1064         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1065 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1066         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1067
1068 static void PlayLevelSound(int, int, int);
1069 static void PlayLevelSoundNearest(int, int, int);
1070 static void PlayLevelSoundAction(int, int, int);
1071 static void PlayLevelSoundElementAction(int, int, int, int);
1072 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1073 static void PlayLevelSoundActionIfLoop(int, int, int);
1074 static void StopLevelSoundActionIfLoop(int, int, int);
1075 static void PlayLevelMusic();
1076
1077 static void MapGameButtons();
1078 static void HandleGameButtons(struct GadgetInfo *);
1079
1080 int AmoebeNachbarNr(int, int);
1081 void AmoebeUmwandeln(int, int);
1082 void ContinueMoving(int, int);
1083 void Bang(int, int);
1084 void InitMovDir(int, int);
1085 void InitAmoebaNr(int, int);
1086 int NewHiScore(void);
1087
1088 void TestIfGoodThingHitsBadThing(int, int, int);
1089 void TestIfBadThingHitsGoodThing(int, int, int);
1090 void TestIfPlayerTouchesBadThing(int, int);
1091 void TestIfPlayerRunsIntoBadThing(int, int, int);
1092 void TestIfBadThingTouchesPlayer(int, int);
1093 void TestIfBadThingRunsIntoPlayer(int, int, int);
1094 void TestIfFriendTouchesBadThing(int, int);
1095 void TestIfBadThingTouchesFriend(int, int);
1096 void TestIfBadThingTouchesOtherBadThing(int, int);
1097
1098 void KillPlayer(struct PlayerInfo *);
1099 void BuryPlayer(struct PlayerInfo *);
1100 void RemovePlayer(struct PlayerInfo *);
1101
1102 boolean SnapField(struct PlayerInfo *, int, int);
1103 boolean DropElement(struct PlayerInfo *);
1104
1105 static int getInvisibleActiveFromInvisibleElement(int);
1106 static int getInvisibleFromInvisibleActiveElement(int);
1107
1108 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1109
1110 /* for detection of endless loops, caused by custom element programming */
1111 /* (using maximal playfield width x 10 is just a rough approximation) */
1112 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1113
1114 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1115 {                                                                       \
1116   if (recursion_loop_detected)                                          \
1117     return (rc);                                                        \
1118                                                                         \
1119   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1120   {                                                                     \
1121     recursion_loop_detected = TRUE;                                     \
1122     recursion_loop_element = (e);                                       \
1123   }                                                                     \
1124                                                                         \
1125   recursion_loop_depth++;                                               \
1126 }
1127
1128 #define RECURSION_LOOP_DETECTION_END()                                  \
1129 {                                                                       \
1130   recursion_loop_depth--;                                               \
1131 }
1132
1133 static int recursion_loop_depth;
1134 static boolean recursion_loop_detected;
1135 static boolean recursion_loop_element;
1136
1137
1138 /* ------------------------------------------------------------------------- */
1139 /* definition of elements that automatically change to other elements after  */
1140 /* a specified time, eventually calling a function when changing             */
1141 /* ------------------------------------------------------------------------- */
1142
1143 /* forward declaration for changer functions */
1144 static void InitBuggyBase(int, int);
1145 static void WarnBuggyBase(int, int);
1146
1147 static void InitTrap(int, int);
1148 static void ActivateTrap(int, int);
1149 static void ChangeActiveTrap(int, int);
1150
1151 static void InitRobotWheel(int, int);
1152 static void RunRobotWheel(int, int);
1153 static void StopRobotWheel(int, int);
1154
1155 static void InitTimegateWheel(int, int);
1156 static void RunTimegateWheel(int, int);
1157
1158 static void InitMagicBallDelay(int, int);
1159 static void ActivateMagicBall(int, int);
1160
1161 struct ChangingElementInfo
1162 {
1163   int element;
1164   int target_element;
1165   int change_delay;
1166   void (*pre_change_function)(int x, int y);
1167   void (*change_function)(int x, int y);
1168   void (*post_change_function)(int x, int y);
1169 };
1170
1171 static struct ChangingElementInfo change_delay_list[] =
1172 {
1173   {
1174     EL_NUT_BREAKING,
1175     EL_EMERALD,
1176     6,
1177     NULL,
1178     NULL,
1179     NULL
1180   },
1181   {
1182     EL_PEARL_BREAKING,
1183     EL_EMPTY,
1184     8,
1185     NULL,
1186     NULL,
1187     NULL
1188   },
1189   {
1190     EL_EXIT_OPENING,
1191     EL_EXIT_OPEN,
1192     29,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_EXIT_CLOSING,
1199     EL_EXIT_CLOSED,
1200     29,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_STEEL_EXIT_OPENING,
1207     EL_STEEL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_STEEL_EXIT_CLOSING,
1215     EL_STEEL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_EM_EXIT_OPENING,
1223     EL_EM_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_EM_EXIT_CLOSING,
1231 #if 1
1232     EL_EMPTY,
1233 #else
1234     EL_EM_EXIT_CLOSED,
1235 #endif
1236     29,
1237     NULL,
1238     NULL,
1239     NULL
1240   },
1241   {
1242     EL_EM_STEEL_EXIT_OPENING,
1243     EL_EM_STEEL_EXIT_OPEN,
1244     29,
1245     NULL,
1246     NULL,
1247     NULL
1248   },
1249   {
1250     EL_EM_STEEL_EXIT_CLOSING,
1251 #if 1
1252     EL_STEELWALL,
1253 #else
1254     EL_EM_STEEL_EXIT_CLOSED,
1255 #endif
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_SP_EXIT_OPENING,
1263     EL_SP_EXIT_OPEN,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_CLOSING,
1271     EL_SP_EXIT_CLOSED,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SWITCHGATE_OPENING,
1279     EL_SWITCHGATE_OPEN,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_CLOSING,
1287     EL_SWITCHGATE_CLOSED,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_TIMEGATE_OPENING,
1295     EL_TIMEGATE_OPEN,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_CLOSING,
1303     EL_TIMEGATE_CLOSED,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309
1310   {
1311     EL_ACID_SPLASH_LEFT,
1312     EL_EMPTY,
1313     8,
1314     NULL,
1315     NULL,
1316     NULL
1317   },
1318   {
1319     EL_ACID_SPLASH_RIGHT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_SP_BUGGY_BASE,
1328     EL_SP_BUGGY_BASE_ACTIVATING,
1329     0,
1330     InitBuggyBase,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE_ACTIVATING,
1336     EL_SP_BUGGY_BASE_ACTIVE,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVE,
1344     EL_SP_BUGGY_BASE,
1345     0,
1346     InitBuggyBase,
1347     WarnBuggyBase,
1348     NULL
1349   },
1350   {
1351     EL_TRAP,
1352     EL_TRAP_ACTIVE,
1353     0,
1354     InitTrap,
1355     NULL,
1356     ActivateTrap
1357   },
1358   {
1359     EL_TRAP_ACTIVE,
1360     EL_TRAP,
1361     31,
1362     NULL,
1363     ChangeActiveTrap,
1364     NULL
1365   },
1366   {
1367     EL_ROBOT_WHEEL_ACTIVE,
1368     EL_ROBOT_WHEEL,
1369     0,
1370     InitRobotWheel,
1371     RunRobotWheel,
1372     StopRobotWheel
1373   },
1374   {
1375     EL_TIMEGATE_SWITCH_ACTIVE,
1376     EL_TIMEGATE_SWITCH,
1377     0,
1378     InitTimegateWheel,
1379     RunTimegateWheel,
1380     NULL
1381   },
1382   {
1383     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1384     EL_DC_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_EMC_MAGIC_BALL_ACTIVE,
1392     EL_EMC_MAGIC_BALL_ACTIVE,
1393     0,
1394     InitMagicBallDelay,
1395     NULL,
1396     ActivateMagicBall
1397   },
1398   {
1399     EL_EMC_SPRING_BUMPER_ACTIVE,
1400     EL_EMC_SPRING_BUMPER,
1401     8,
1402     NULL,
1403     NULL,
1404     NULL
1405   },
1406   {
1407     EL_DIAGONAL_SHRINKING,
1408     EL_UNDEFINED,
1409     0,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_GROWING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL,
1421   },
1422
1423   {
1424     EL_UNDEFINED,
1425     EL_UNDEFINED,
1426     -1,
1427     NULL,
1428     NULL,
1429     NULL
1430   }
1431 };
1432
1433 struct
1434 {
1435   int element;
1436   int push_delay_fixed, push_delay_random;
1437 }
1438 push_delay_list[] =
1439 {
1440   { EL_SPRING,                  0, 0 },
1441   { EL_BALLOON,                 0, 0 },
1442
1443   { EL_SOKOBAN_OBJECT,          2, 0 },
1444   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1445   { EL_SATELLITE,               2, 0 },
1446   { EL_SP_DISK_YELLOW,          2, 0 },
1447
1448   { EL_UNDEFINED,               0, 0 },
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int move_stepsize;
1455 }
1456 move_stepsize_list[] =
1457 {
1458   { EL_AMOEBA_DROP,             2 },
1459   { EL_AMOEBA_DROPPING,         2 },
1460   { EL_QUICKSAND_FILLING,       1 },
1461   { EL_QUICKSAND_EMPTYING,      1 },
1462   { EL_QUICKSAND_FAST_FILLING,  2 },
1463   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1464   { EL_MAGIC_WALL_FILLING,      2 },
1465   { EL_MAGIC_WALL_EMPTYING,     2 },
1466   { EL_BD_MAGIC_WALL_FILLING,   2 },
1467   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1468   { EL_DC_MAGIC_WALL_FILLING,   2 },
1469   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1470
1471   { EL_UNDEFINED,               0 },
1472 };
1473
1474 struct
1475 {
1476   int element;
1477   int count;
1478 }
1479 collect_count_list[] =
1480 {
1481   { EL_EMERALD,                 1 },
1482   { EL_BD_DIAMOND,              1 },
1483   { EL_EMERALD_YELLOW,          1 },
1484   { EL_EMERALD_RED,             1 },
1485   { EL_EMERALD_PURPLE,          1 },
1486   { EL_DIAMOND,                 3 },
1487   { EL_SP_INFOTRON,             1 },
1488   { EL_PEARL,                   5 },
1489   { EL_CRYSTAL,                 8 },
1490
1491   { EL_UNDEFINED,               0 },
1492 };
1493
1494 struct
1495 {
1496   int element;
1497   int direction;
1498 }
1499 access_direction_list[] =
1500 {
1501   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1502   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1503   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1504   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1505   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1506   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1507   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1508   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1509   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1510   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1511   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1512
1513   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1514   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1515   { EL_SP_PORT_UP,                                                   MV_DOWN },
1516   { EL_SP_PORT_DOWN,                                         MV_UP           },
1517   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1518   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1519   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1521   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1522   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1523   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1524   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1525   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1526   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1527   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1528   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1529   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1530   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1531   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1532
1533   { EL_UNDEFINED,                       MV_NONE                              }
1534 };
1535
1536 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1537
1538 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1539 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1540 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1541                                  IS_JUST_CHANGING(x, y))
1542
1543 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1544
1545 /* static variables for playfield scan mode (scanning forward or backward) */
1546 static int playfield_scan_start_x = 0;
1547 static int playfield_scan_start_y = 0;
1548 static int playfield_scan_delta_x = 1;
1549 static int playfield_scan_delta_y = 1;
1550
1551 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1552                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1553                                      (y) += playfield_scan_delta_y)     \
1554                                 for ((x) = playfield_scan_start_x;      \
1555                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1556                                      (x) += playfield_scan_delta_x)
1557
1558 #ifdef DEBUG
1559 void DEBUG_SetMaximumDynamite()
1560 {
1561   int i;
1562
1563   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1564     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1565       local_player->inventory_element[local_player->inventory_size++] =
1566         EL_DYNAMITE;
1567 }
1568 #endif
1569
1570 static void InitPlayfieldScanModeVars()
1571 {
1572   if (game.use_reverse_scan_direction)
1573   {
1574     playfield_scan_start_x = lev_fieldx - 1;
1575     playfield_scan_start_y = lev_fieldy - 1;
1576
1577     playfield_scan_delta_x = -1;
1578     playfield_scan_delta_y = -1;
1579   }
1580   else
1581   {
1582     playfield_scan_start_x = 0;
1583     playfield_scan_start_y = 0;
1584
1585     playfield_scan_delta_x = 1;
1586     playfield_scan_delta_y = 1;
1587   }
1588 }
1589
1590 static void InitPlayfieldScanMode(int mode)
1591 {
1592   game.use_reverse_scan_direction =
1593     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1594
1595   InitPlayfieldScanModeVars();
1596 }
1597
1598 static int get_move_delay_from_stepsize(int move_stepsize)
1599 {
1600   move_stepsize =
1601     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1602
1603   /* make sure that stepsize value is always a power of 2 */
1604   move_stepsize = (1 << log_2(move_stepsize));
1605
1606   return TILEX / move_stepsize;
1607 }
1608
1609 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1610                                boolean init_game)
1611 {
1612   int player_nr = player->index_nr;
1613   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1614   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1615
1616   /* do no immediately change move delay -- the player might just be moving */
1617   player->move_delay_value_next = move_delay;
1618
1619   /* information if player can move must be set separately */
1620   player->cannot_move = cannot_move;
1621
1622   if (init_game)
1623   {
1624     player->move_delay       = game.initial_move_delay[player_nr];
1625     player->move_delay_value = game.initial_move_delay_value[player_nr];
1626
1627     player->move_delay_value_next = -1;
1628
1629     player->move_delay_reset_counter = 0;
1630   }
1631 }
1632
1633 void GetPlayerConfig()
1634 {
1635   GameFrameDelay = setup.game_frame_delay;
1636
1637   if (!audio.sound_available)
1638     setup.sound_simple = FALSE;
1639
1640   if (!audio.loops_available)
1641     setup.sound_loops = FALSE;
1642
1643   if (!audio.music_available)
1644     setup.sound_music = FALSE;
1645
1646   if (!video.fullscreen_available)
1647     setup.fullscreen = FALSE;
1648
1649   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1650
1651   SetAudioMode(setup.sound);
1652   InitJoysticks();
1653 }
1654
1655 int GetElementFromGroupElement(int element)
1656 {
1657   if (IS_GROUP_ELEMENT(element))
1658   {
1659     struct ElementGroupInfo *group = element_info[element].group;
1660     int last_anim_random_frame = gfx.anim_random_frame;
1661     int element_pos;
1662
1663     if (group->choice_mode == ANIM_RANDOM)
1664       gfx.anim_random_frame = RND(group->num_elements_resolved);
1665
1666     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1667                                     group->choice_mode, 0,
1668                                     group->choice_pos);
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = last_anim_random_frame;
1672
1673     group->choice_pos++;
1674
1675     element = group->element_resolved[element_pos];
1676   }
1677
1678   return element;
1679 }
1680
1681 static void InitPlayerField(int x, int y, int element, boolean init_game)
1682 {
1683   if (element == EL_SP_MURPHY)
1684   {
1685     if (init_game)
1686     {
1687       if (stored_player[0].present)
1688       {
1689         Feld[x][y] = EL_SP_MURPHY_CLONE;
1690
1691         return;
1692       }
1693       else
1694       {
1695         stored_player[0].use_murphy = TRUE;
1696
1697         if (!level.use_artwork_element[0])
1698           stored_player[0].artwork_element = EL_SP_MURPHY;
1699       }
1700
1701       Feld[x][y] = EL_PLAYER_1;
1702     }
1703   }
1704
1705   if (init_game)
1706   {
1707     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1708     int jx = player->jx, jy = player->jy;
1709
1710     player->present = TRUE;
1711
1712     player->block_last_field = (element == EL_SP_MURPHY ?
1713                                 level.sp_block_last_field :
1714                                 level.block_last_field);
1715
1716     /* ---------- initialize player's last field block delay --------------- */
1717
1718     /* always start with reliable default value (no adjustment needed) */
1719     player->block_delay_adjustment = 0;
1720
1721     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1722     if (player->block_last_field && element == EL_SP_MURPHY)
1723       player->block_delay_adjustment = 1;
1724
1725     /* special case 2: in game engines before 3.1.1, blocking was different */
1726     if (game.use_block_last_field_bug)
1727       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1728
1729     if (!options.network || player->connected)
1730     {
1731       player->active = TRUE;
1732
1733       /* remove potentially duplicate players */
1734       if (StorePlayer[jx][jy] == Feld[x][y])
1735         StorePlayer[jx][jy] = 0;
1736
1737       StorePlayer[x][y] = Feld[x][y];
1738
1739       if (options.debug)
1740       {
1741         printf("Player %d activated.\n", player->element_nr);
1742         printf("[Local player is %d and currently %s.]\n",
1743                local_player->element_nr,
1744                local_player->active ? "active" : "not active");
1745       }
1746     }
1747
1748     Feld[x][y] = EL_EMPTY;
1749
1750     player->jx = player->last_jx = x;
1751     player->jy = player->last_jy = y;
1752   }
1753 }
1754
1755 static void InitField(int x, int y, boolean init_game)
1756 {
1757   int element = Feld[x][y];
1758
1759   switch (element)
1760   {
1761     case EL_SP_MURPHY:
1762     case EL_PLAYER_1:
1763     case EL_PLAYER_2:
1764     case EL_PLAYER_3:
1765     case EL_PLAYER_4:
1766       InitPlayerField(x, y, element, init_game);
1767       break;
1768
1769     case EL_SOKOBAN_FIELD_PLAYER:
1770       element = Feld[x][y] = EL_PLAYER_1;
1771       InitField(x, y, init_game);
1772
1773       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1774       InitField(x, y, init_game);
1775       break;
1776
1777     case EL_SOKOBAN_FIELD_EMPTY:
1778       local_player->sokobanfields_still_needed++;
1779       break;
1780
1781     case EL_STONEBLOCK:
1782       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1783         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1784       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1785         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1786       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1787         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1788       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1789         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1790       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1791         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1792       break;
1793
1794     case EL_BUG:
1795     case EL_BUG_RIGHT:
1796     case EL_BUG_UP:
1797     case EL_BUG_LEFT:
1798     case EL_BUG_DOWN:
1799     case EL_SPACESHIP:
1800     case EL_SPACESHIP_RIGHT:
1801     case EL_SPACESHIP_UP:
1802     case EL_SPACESHIP_LEFT:
1803     case EL_SPACESHIP_DOWN:
1804     case EL_BD_BUTTERFLY:
1805     case EL_BD_BUTTERFLY_RIGHT:
1806     case EL_BD_BUTTERFLY_UP:
1807     case EL_BD_BUTTERFLY_LEFT:
1808     case EL_BD_BUTTERFLY_DOWN:
1809     case EL_BD_FIREFLY:
1810     case EL_BD_FIREFLY_RIGHT:
1811     case EL_BD_FIREFLY_UP:
1812     case EL_BD_FIREFLY_LEFT:
1813     case EL_BD_FIREFLY_DOWN:
1814     case EL_PACMAN_RIGHT:
1815     case EL_PACMAN_UP:
1816     case EL_PACMAN_LEFT:
1817     case EL_PACMAN_DOWN:
1818     case EL_YAMYAM:
1819     case EL_YAMYAM_LEFT:
1820     case EL_YAMYAM_RIGHT:
1821     case EL_YAMYAM_UP:
1822     case EL_YAMYAM_DOWN:
1823     case EL_DARK_YAMYAM:
1824     case EL_ROBOT:
1825     case EL_PACMAN:
1826     case EL_SP_SNIKSNAK:
1827     case EL_SP_ELECTRON:
1828     case EL_MOLE:
1829     case EL_MOLE_LEFT:
1830     case EL_MOLE_RIGHT:
1831     case EL_MOLE_UP:
1832     case EL_MOLE_DOWN:
1833       InitMovDir(x, y);
1834       break;
1835
1836     case EL_AMOEBA_FULL:
1837     case EL_BD_AMOEBA:
1838       InitAmoebaNr(x, y);
1839       break;
1840
1841     case EL_AMOEBA_DROP:
1842       if (y == lev_fieldy - 1)
1843       {
1844         Feld[x][y] = EL_AMOEBA_GROWING;
1845         Store[x][y] = EL_AMOEBA_WET;
1846       }
1847       break;
1848
1849     case EL_DYNAMITE_ACTIVE:
1850     case EL_SP_DISK_RED_ACTIVE:
1851     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1852     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1853     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1854     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1855       MovDelay[x][y] = 96;
1856       break;
1857
1858     case EL_EM_DYNAMITE_ACTIVE:
1859       MovDelay[x][y] = 32;
1860       break;
1861
1862     case EL_LAMP:
1863       local_player->lights_still_needed++;
1864       break;
1865
1866     case EL_PENGUIN:
1867       local_player->friends_still_needed++;
1868       break;
1869
1870     case EL_PIG:
1871     case EL_DRAGON:
1872       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1873       break;
1874
1875     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1876     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1877     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1878     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1879     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1880     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1881     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1882     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1883     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1884     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1885     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1886     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1887       if (init_game)
1888       {
1889         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1890         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1891         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1892
1893         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1894         {
1895           game.belt_dir[belt_nr] = belt_dir;
1896           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1897         }
1898         else    /* more than one switch -- set it like the first switch */
1899         {
1900           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1901         }
1902       }
1903       break;
1904
1905 #if !USE_BOTH_SWITCHGATE_SWITCHES
1906     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1907       if (init_game)
1908         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1909       break;
1910
1911     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1912       if (init_game)
1913         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1914       break;
1915 #endif
1916
1917     case EL_LIGHT_SWITCH_ACTIVE:
1918       if (init_game)
1919         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1920       break;
1921
1922     case EL_INVISIBLE_STEELWALL:
1923     case EL_INVISIBLE_WALL:
1924     case EL_INVISIBLE_SAND:
1925       if (game.light_time_left > 0 ||
1926           game.lenses_time_left > 0)
1927         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1928       break;
1929
1930     case EL_EMC_MAGIC_BALL:
1931       if (game.ball_state)
1932         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL_SWITCH:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1938       break;
1939
1940     default:
1941       if (IS_CUSTOM_ELEMENT(element))
1942       {
1943         if (CAN_MOVE(element))
1944           InitMovDir(x, y);
1945
1946 #if USE_NEW_CUSTOM_VALUE
1947         if (!element_info[element].use_last_ce_value || init_game)
1948           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1949 #endif
1950       }
1951       else if (IS_GROUP_ELEMENT(element))
1952       {
1953         Feld[x][y] = GetElementFromGroupElement(element);
1954
1955         InitField(x, y, init_game);
1956       }
1957
1958       break;
1959   }
1960
1961   if (!init_game)
1962     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1963 }
1964
1965 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1966 {
1967   InitField(x, y, init_game);
1968
1969   /* not needed to call InitMovDir() -- already done by InitField()! */
1970   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971       CAN_MOVE(Feld[x][y]))
1972     InitMovDir(x, y);
1973 }
1974
1975 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1976 {
1977   int old_element = Feld[x][y];
1978
1979   InitField(x, y, init_game);
1980
1981   /* not needed to call InitMovDir() -- already done by InitField()! */
1982   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1983       CAN_MOVE(old_element) &&
1984       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1985     InitMovDir(x, y);
1986
1987   /* this case is in fact a combination of not less than three bugs:
1988      first, it calls InitMovDir() for elements that can move, although this is
1989      already done by InitField(); then, it checks the element that was at this
1990      field _before_ the call to InitField() (which can change it); lastly, it
1991      was not called for "mole with direction" elements, which were treated as
1992      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1993   */
1994 }
1995
1996 #if 1
1997
1998 static int get_key_element_from_nr(int key_nr)
1999 {
2000   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2001                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2002                           EL_EM_KEY_1 : EL_KEY_1);
2003
2004   return key_base_element + key_nr;
2005 }
2006
2007 static int get_next_dropped_element(struct PlayerInfo *player)
2008 {
2009   return (player->inventory_size > 0 ?
2010           player->inventory_element[player->inventory_size - 1] :
2011           player->inventory_infinite_element != EL_UNDEFINED ?
2012           player->inventory_infinite_element :
2013           player->dynabombs_left > 0 ?
2014           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2015           EL_UNDEFINED);
2016 }
2017
2018 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2019 {
2020   /* pos >= 0: get element from bottom of the stack;
2021      pos <  0: get element from top of the stack */
2022
2023   if (pos < 0)
2024   {
2025     int min_inventory_size = -pos;
2026     int inventory_pos = player->inventory_size - min_inventory_size;
2027     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2028
2029     return (player->inventory_size >= min_inventory_size ?
2030             player->inventory_element[inventory_pos] :
2031             player->inventory_infinite_element != EL_UNDEFINED ?
2032             player->inventory_infinite_element :
2033             player->dynabombs_left >= min_dynabombs_left ?
2034             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2035             EL_UNDEFINED);
2036   }
2037   else
2038   {
2039     int min_dynabombs_left = pos + 1;
2040     int min_inventory_size = pos + 1 - player->dynabombs_left;
2041     int inventory_pos = pos - player->dynabombs_left;
2042
2043     return (player->inventory_infinite_element != EL_UNDEFINED ?
2044             player->inventory_infinite_element :
2045             player->dynabombs_left >= min_dynabombs_left ?
2046             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2047             player->inventory_size >= min_inventory_size ?
2048             player->inventory_element[inventory_pos] :
2049             EL_UNDEFINED);
2050   }
2051 }
2052
2053 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2054 {
2055   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2056   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2057   int compare_result;
2058
2059   if (gpo1->sort_priority != gpo2->sort_priority)
2060     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2061   else
2062     compare_result = gpo1->nr - gpo2->nr;
2063
2064   return compare_result;
2065 }
2066
2067 void InitGameControlValues()
2068 {
2069   int i;
2070
2071   for (i = 0; game_panel_controls[i].nr != -1; i++)
2072   {
2073     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2074     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2075     struct TextPosInfo *pos = gpc->pos;
2076     int nr = gpc->nr;
2077     int type = gpc->type;
2078
2079     if (nr != i)
2080     {
2081       Error(ERR_INFO, "'game_panel_controls' structure corrupted");
2082       Error(ERR_EXIT, "this should not happen -- please debug");
2083     }
2084
2085     /* force update of game controls after initialization */
2086     gpc->value = gpc->last_value = -1;
2087     gpc->frame = gpc->last_frame = -1;
2088     gpc->gfx_frame = -1;
2089
2090     /* determine panel value width for later calculation of alignment */
2091     if (type == TYPE_INTEGER || type == TYPE_STRING)
2092     {
2093       pos->width = pos->size * getFontWidth(pos->font);
2094       pos->height = getFontHeight(pos->font);
2095     }
2096     else if (type == TYPE_ELEMENT)
2097     {
2098       pos->width = pos->size;
2099       pos->height = pos->size;
2100     }
2101
2102     /* fill structure for game panel draw order */
2103     gpo->nr = gpc->nr;
2104     gpo->sort_priority = pos->sort_priority;
2105   }
2106
2107   /* sort game panel controls according to sort_priority and control number */
2108   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2109         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2110 }
2111
2112 void UpdatePlayfieldElementCount()
2113 {
2114   int i, j, x, y;
2115
2116   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2117     element_info[i].element_count = 0;
2118
2119   SCAN_PLAYFIELD(x, y)
2120   {
2121     element_info[Feld[x][y]].element_count++;
2122   }
2123
2124   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2125     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2126       if (IS_IN_GROUP(j, i))
2127         element_info[EL_GROUP_START + i].element_count +=
2128           element_info[j].element_count;
2129 }
2130
2131 void UpdateGameControlValues()
2132 {
2133   int i, k;
2134   int time = (local_player->LevelSolved ?
2135               local_player->LevelSolved_CountingTime :
2136               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2137               level.native_em_level->lev->time :
2138               level.time == 0 ? TimePlayed : TimeLeft);
2139   int score = (local_player->LevelSolved ?
2140                local_player->LevelSolved_CountingScore :
2141                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142                level.native_em_level->lev->score :
2143                local_player->score);
2144   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2145               level.native_em_level->lev->required :
2146               local_player->gems_still_needed);
2147   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2148                      level.native_em_level->lev->required > 0 :
2149                      local_player->gems_still_needed > 0 ||
2150                      local_player->sokobanfields_still_needed > 0 ||
2151                      local_player->lights_still_needed > 0);
2152
2153   UpdatePlayfieldElementCount();
2154
2155   /* update game panel control values */
2156
2157   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2158   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2159
2160   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2161   for (i = 0; i < MAX_NUM_KEYS; i++)
2162     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2163   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2164   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2165
2166   if (game.centered_player_nr == -1)
2167   {
2168     for (i = 0; i < MAX_PLAYERS; i++)
2169     {
2170       for (k = 0; k < MAX_NUM_KEYS; k++)
2171       {
2172         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2173         {
2174           if (level.native_em_level->ply[i]->keys & (1 << k))
2175             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2176               get_key_element_from_nr(k);
2177         }
2178         else if (stored_player[i].key[k])
2179           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2180             get_key_element_from_nr(k);
2181       }
2182
2183       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2184         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2185           level.native_em_level->ply[i]->dynamite;
2186       else
2187         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2188           stored_player[i].inventory_size;
2189
2190       if (stored_player[i].num_white_keys > 0)
2191         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2192           EL_DC_KEY_WHITE;
2193
2194       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2195         stored_player[i].num_white_keys;
2196     }
2197   }
2198   else
2199   {
2200     int player_nr = game.centered_player_nr;
2201
2202     for (k = 0; k < MAX_NUM_KEYS; k++)
2203     {
2204       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2205       {
2206         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2207           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2208             get_key_element_from_nr(k);
2209       }
2210       else if (stored_player[player_nr].key[k])
2211         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2212           get_key_element_from_nr(k);
2213     }
2214
2215     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2216       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2217         level.native_em_level->ply[player_nr]->dynamite;
2218     else
2219       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2220         stored_player[player_nr].inventory_size;
2221
2222     if (stored_player[player_nr].num_white_keys > 0)
2223       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2224
2225     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2226       stored_player[player_nr].num_white_keys;
2227   }
2228
2229   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2230   {
2231     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2232       get_inventory_element_from_pos(local_player, i);
2233     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2234       get_inventory_element_from_pos(local_player, -i - 1);
2235   }
2236
2237   game_panel_controls[GAME_PANEL_SCORE].value = score;
2238
2239   game_panel_controls[GAME_PANEL_TIME].value = time;
2240
2241   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2242   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2243   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2244
2245   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2246     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2247      EL_EMPTY);
2248   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2249     local_player->shield_normal_time_left;
2250   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2251     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2252      EL_EMPTY);
2253   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2254     local_player->shield_deadly_time_left;
2255
2256   game_panel_controls[GAME_PANEL_EXIT].value =
2257     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2258
2259   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2260     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2261   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2262     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2263      EL_EMC_MAGIC_BALL_SWITCH);
2264
2265   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2266     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2267   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2268     game.light_time_left;
2269
2270   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2271     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2272   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2273     game.timegate_time_left;
2274
2275   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2276     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2277
2278   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2279     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2280   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2281     game.lenses_time_left;
2282
2283   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2284     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2285   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2286     game.magnify_time_left;
2287
2288   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2289     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2290      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2291      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2292      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2293      EL_BALLOON_SWITCH_NONE);
2294
2295   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2296     local_player->dynabomb_count;
2297   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2298     local_player->dynabomb_size;
2299   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2300     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2301
2302   game_panel_controls[GAME_PANEL_PENGUINS].value =
2303     local_player->friends_still_needed;
2304
2305   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2306     local_player->sokobanfields_still_needed;
2307   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2308     local_player->sokobanfields_still_needed;
2309
2310   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2311     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2312
2313   for (i = 0; i < NUM_BELTS; i++)
2314   {
2315     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2316       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2317        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2318     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2319       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2320   }
2321
2322   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2323     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2324   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2325     game.magic_wall_time_left;
2326
2327 #if USE_PLAYER_GRAVITY
2328   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2329     local_player->gravity;
2330 #else
2331   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2332 #endif
2333
2334   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2335     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2336
2337   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2338     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2339       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2340        game.panel.element[i].id : EL_UNDEFINED);
2341
2342   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2343     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2344       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2345        element_info[game.panel.element_count[i].id].element_count :
2346        EL_UNDEFINED);
2347
2348   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2349     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2350       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2351        element_info[game.panel.ce_score[i].id].collect_score : 0);
2352
2353   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2354     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2355       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2356        element_info[game.panel.ce_score_element[i].id].collect_score :
2357        EL_UNDEFINED);
2358
2359   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2360   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2361   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2362
2363   /* update game panel control frames */
2364
2365   for (i = 0; game_panel_controls[i].nr != -1; i++)
2366   {
2367     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2368
2369     if (gpc->type == TYPE_ELEMENT)
2370     {
2371       int last_anim_random_frame = gfx.anim_random_frame;
2372       int element = gpc->value;
2373       int graphic = el2panelimg(element);
2374
2375       if (gpc->value != gpc->last_value)
2376       {
2377         gpc->gfx_frame = 0;
2378         gpc->gfx_random = INIT_GFX_RANDOM();
2379       }
2380       else
2381       {
2382         gpc->gfx_frame++;
2383
2384         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2385             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2386           gpc->gfx_random = INIT_GFX_RANDOM();
2387       }
2388
2389       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2390         gfx.anim_random_frame = gpc->gfx_random;
2391
2392       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2393         gpc->gfx_frame = element_info[element].collect_score;
2394
2395       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2396                                             gpc->gfx_frame);
2397
2398       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2399         gfx.anim_random_frame = last_anim_random_frame;
2400     }
2401   }
2402 }
2403
2404 void DisplayGameControlValues()
2405 {
2406   boolean redraw_panel = FALSE;
2407   int i;
2408
2409   for (i = 0; game_panel_controls[i].nr != -1; i++)
2410   {
2411     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2412
2413     if (PANEL_DEACTIVATED(gpc->pos))
2414       continue;
2415
2416     if (gpc->value == gpc->last_value &&
2417         gpc->frame == gpc->last_frame)
2418       continue;
2419
2420     redraw_panel = TRUE;
2421   }
2422
2423   if (!redraw_panel)
2424     return;
2425
2426   /* copy default game door content to main double buffer */
2427   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2428              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2429
2430   /* redraw game control buttons */
2431 #if 1
2432   RedrawGameButtons();
2433 #else
2434   UnmapGameButtons();
2435   MapGameButtons();
2436 #endif
2437
2438   game_status = GAME_MODE_PSEUDO_PANEL;
2439
2440 #if 1
2441   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2442 #else
2443   for (i = 0; game_panel_controls[i].nr != -1; i++)
2444 #endif
2445   {
2446 #if 1
2447     int nr = game_panel_order[i].nr;
2448     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2449 #else
2450     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2451     int nr = gpc->nr;
2452 #endif
2453     struct TextPosInfo *pos = gpc->pos;
2454     int type = gpc->type;
2455     int value = gpc->value;
2456     int frame = gpc->frame;
2457 #if 0
2458     int last_value = gpc->last_value;
2459     int last_frame = gpc->last_frame;
2460 #endif
2461     int size = pos->size;
2462     int font = pos->font;
2463     boolean draw_masked = pos->draw_masked;
2464     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2465
2466     if (PANEL_DEACTIVATED(pos))
2467       continue;
2468
2469 #if 0
2470     if (value == last_value && frame == last_frame)
2471       continue;
2472 #endif
2473
2474     gpc->last_value = value;
2475     gpc->last_frame = frame;
2476
2477 #if 0
2478     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2479 #endif
2480
2481     if (type == TYPE_INTEGER)
2482     {
2483       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2484           nr == GAME_PANEL_TIME)
2485       {
2486         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2487
2488         if (use_dynamic_size)           /* use dynamic number of digits */
2489         {
2490           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2491           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2492           int size2 = size1 + 1;
2493           int font1 = pos->font;
2494           int font2 = pos->font_alt;
2495
2496           size = (value < value_change ? size1 : size2);
2497           font = (value < value_change ? font1 : font2);
2498
2499 #if 0
2500           /* clear background if value just changed its size (dynamic digits) */
2501           if ((last_value < value_change) != (value < value_change))
2502           {
2503             int width1 = size1 * getFontWidth(font1);
2504             int width2 = size2 * getFontWidth(font2);
2505             int max_width = MAX(width1, width2);
2506             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2507
2508             pos->width = max_width;
2509
2510             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2511                                        max_width, max_height);
2512           }
2513 #endif
2514         }
2515       }
2516
2517 #if 1
2518       /* correct text size if "digits" is zero or less */
2519       if (size <= 0)
2520         size = strlen(int2str(value, size));
2521
2522       /* dynamically correct text alignment */
2523       pos->width = size * getFontWidth(font);
2524 #endif
2525
2526       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2527                   int2str(value, size), font, mask_mode);
2528     }
2529     else if (type == TYPE_ELEMENT)
2530     {
2531       int element, graphic;
2532       Bitmap *src_bitmap;
2533       int src_x, src_y;
2534       int width, height;
2535       int dst_x = PANEL_XPOS(pos);
2536       int dst_y = PANEL_YPOS(pos);
2537
2538 #if 1
2539       if (value != EL_UNDEFINED && value != EL_EMPTY)
2540       {
2541         element = value;
2542         graphic = el2panelimg(value);
2543
2544         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2545
2546 #if 1
2547         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2548           size = TILESIZE;
2549 #endif
2550
2551         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2552                               &src_x, &src_y);
2553
2554         width  = graphic_info[graphic].width  * size / TILESIZE;
2555         height = graphic_info[graphic].height * size / TILESIZE;
2556
2557         if (draw_masked)
2558         {
2559           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2560                         dst_x - src_x, dst_y - src_y);
2561           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2562                            dst_x, dst_y);
2563         }
2564         else
2565         {
2566           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2567                      dst_x, dst_y);
2568         }
2569       }
2570 #else
2571       if (value == EL_UNDEFINED || value == EL_EMPTY)
2572       {
2573         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2574         graphic = el2panelimg(element);
2575
2576         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2577         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2578         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2579       }
2580       else
2581       {
2582         element = value;
2583         graphic = el2panelimg(value);
2584
2585         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2586       }
2587
2588       width  = graphic_info[graphic].width  * size / TILESIZE;
2589       height = graphic_info[graphic].height * size / TILESIZE;
2590
2591       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2592 #endif
2593     }
2594     else if (type == TYPE_STRING)
2595     {
2596       boolean active = (value != 0);
2597       char *state_normal = "off";
2598       char *state_active = "on";
2599       char *state = (active ? state_active : state_normal);
2600       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2601                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2602                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2603                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2604
2605       if (nr == GAME_PANEL_GRAVITY_STATE)
2606       {
2607         int font1 = pos->font;          /* (used for normal state) */
2608         int font2 = pos->font_alt;      /* (used for active state) */
2609 #if 0
2610         int size1 = strlen(state_normal);
2611         int size2 = strlen(state_active);
2612         int width1 = size1 * getFontWidth(font1);
2613         int width2 = size2 * getFontWidth(font2);
2614         int max_width = MAX(width1, width2);
2615         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2616
2617         pos->width = max_width;
2618
2619         /* clear background for values that may have changed its size */
2620         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2621                                    max_width, max_height);
2622 #endif
2623
2624         font = (active ? font2 : font1);
2625       }
2626
2627       if (s != NULL)
2628       {
2629         char *s_cut;
2630
2631 #if 1
2632         if (size <= 0)
2633         {
2634           /* don't truncate output if "chars" is zero or less */
2635           size = strlen(s);
2636
2637           /* dynamically correct text alignment */
2638           pos->width = size * getFontWidth(font);
2639         }
2640 #endif
2641
2642         s_cut = getStringCopyN(s, size);
2643
2644         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2645                     s_cut, font, mask_mode);
2646
2647         free(s_cut);
2648       }
2649     }
2650
2651     redraw_mask |= REDRAW_DOOR_1;
2652   }
2653
2654   game_status = GAME_MODE_PLAYING;
2655 }
2656
2657 void DrawGameValue_Emeralds(int value)
2658 {
2659   struct TextPosInfo *pos = &game.panel.gems;
2660 #if 1
2661   int font_nr = pos->font;
2662 #else
2663   int font_nr = FONT_TEXT_2;
2664 #endif
2665   int font_width = getFontWidth(font_nr);
2666   int chars = pos->size;
2667
2668 #if 1
2669   return;       /* !!! USE NEW STUFF !!! */
2670 #endif
2671
2672   if (PANEL_DEACTIVATED(pos))
2673     return;
2674
2675   pos->width = chars * font_width;
2676
2677   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2678 }
2679
2680 void DrawGameValue_Dynamite(int value)
2681 {
2682   struct TextPosInfo *pos = &game.panel.inventory_count;
2683 #if 1
2684   int font_nr = pos->font;
2685 #else
2686   int font_nr = FONT_TEXT_2;
2687 #endif
2688   int font_width = getFontWidth(font_nr);
2689   int chars = pos->size;
2690
2691 #if 1
2692   return;       /* !!! USE NEW STUFF !!! */
2693 #endif
2694
2695   if (PANEL_DEACTIVATED(pos))
2696     return;
2697
2698   pos->width = chars * font_width;
2699
2700   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2701 }
2702
2703 void DrawGameValue_Score(int value)
2704 {
2705   struct TextPosInfo *pos = &game.panel.score;
2706 #if 1
2707   int font_nr = pos->font;
2708 #else
2709   int font_nr = FONT_TEXT_2;
2710 #endif
2711   int font_width = getFontWidth(font_nr);
2712   int chars = pos->size;
2713
2714 #if 1
2715   return;       /* !!! USE NEW STUFF !!! */
2716 #endif
2717
2718   if (PANEL_DEACTIVATED(pos))
2719     return;
2720
2721   pos->width = chars * font_width;
2722
2723   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2724 }
2725
2726 void DrawGameValue_Time(int value)
2727 {
2728   struct TextPosInfo *pos = &game.panel.time;
2729   static int last_value = -1;
2730   int chars1 = 3;
2731   int chars2 = 4;
2732   int chars = pos->size;
2733 #if 1
2734   int font1_nr = pos->font;
2735   int font2_nr = pos->font_alt;
2736 #else
2737   int font1_nr = FONT_TEXT_2;
2738   int font2_nr = FONT_TEXT_1;
2739 #endif
2740   int font_nr = font1_nr;
2741   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2742
2743 #if 1
2744   return;       /* !!! USE NEW STUFF !!! */
2745 #endif
2746
2747   if (PANEL_DEACTIVATED(pos))
2748     return;
2749
2750   if (use_dynamic_chars)                /* use dynamic number of chars */
2751   {
2752     chars   = (value < 1000 ? chars1   : chars2);
2753     font_nr = (value < 1000 ? font1_nr : font2_nr);
2754   }
2755
2756   /* clear background if value just changed its size (dynamic chars only) */
2757   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2758   {
2759     int width1 = chars1 * getFontWidth(font1_nr);
2760     int width2 = chars2 * getFontWidth(font2_nr);
2761     int max_width = MAX(width1, width2);
2762     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2763
2764     pos->width = max_width;
2765
2766     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2767                                max_width, max_height);
2768   }
2769
2770   pos->width = chars * getFontWidth(font_nr);
2771
2772   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2773
2774   last_value = value;
2775 }
2776
2777 void DrawGameValue_Level(int value)
2778 {
2779   struct TextPosInfo *pos = &game.panel.level_number;
2780   int chars1 = 2;
2781   int chars2 = 3;
2782   int chars = pos->size;
2783 #if 1
2784   int font1_nr = pos->font;
2785   int font2_nr = pos->font_alt;
2786 #else
2787   int font1_nr = FONT_TEXT_2;
2788   int font2_nr = FONT_TEXT_1;
2789 #endif
2790   int font_nr = font1_nr;
2791   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2792
2793 #if 1
2794   return;       /* !!! USE NEW STUFF !!! */
2795 #endif
2796
2797   if (PANEL_DEACTIVATED(pos))
2798     return;
2799
2800   if (use_dynamic_chars)                /* use dynamic number of chars */
2801   {
2802     chars   = (level_nr < 100 ? chars1   : chars2);
2803     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2804   }
2805
2806   pos->width = chars * getFontWidth(font_nr);
2807
2808   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2809 }
2810
2811 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2812 {
2813 #if 0
2814   struct TextPosInfo *pos = &game.panel.keys;
2815 #endif
2816 #if 0
2817   int base_key_graphic = EL_KEY_1;
2818 #endif
2819   int i;
2820
2821 #if 1
2822   return;       /* !!! USE NEW STUFF !!! */
2823 #endif
2824
2825 #if 0
2826   if (PANEL_DEACTIVATED(pos))
2827     return;
2828 #endif
2829
2830 #if 0
2831   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2832     base_key_graphic = EL_EM_KEY_1;
2833 #endif
2834
2835 #if 0
2836   pos->width = 4 * MINI_TILEX;
2837 #endif
2838
2839 #if 1
2840   for (i = 0; i < MAX_NUM_KEYS; i++)
2841 #else
2842   /* currently only 4 of 8 possible keys are displayed */
2843   for (i = 0; i < STD_NUM_KEYS; i++)
2844 #endif
2845   {
2846 #if 1
2847     struct TextPosInfo *pos = &game.panel.key[i];
2848 #endif
2849     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2850     int src_y = DOOR_GFX_PAGEY1 + 123;
2851 #if 1
2852     int dst_x = PANEL_XPOS(pos);
2853     int dst_y = PANEL_YPOS(pos);
2854 #else
2855     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2856     int dst_y = PANEL_YPOS(pos);
2857 #endif
2858
2859 #if 1
2860     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2861                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2862                    EL_KEY_1) + i;
2863     int graphic = el2edimg(element);
2864 #endif
2865
2866 #if 1
2867     if (PANEL_DEACTIVATED(pos))
2868       continue;
2869 #endif
2870
2871 #if 0
2872     /* masked blit with tiles from half-size scaled bitmap does not work yet
2873        (no mask bitmap created for these sizes after loading and scaling) --
2874        solution: load without creating mask, scale, then create final mask */
2875
2876     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2877                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2878
2879     if (key[i])
2880     {
2881 #if 0
2882       int graphic = el2edimg(base_key_graphic + i);
2883 #endif
2884       Bitmap *src_bitmap;
2885       int src_x, src_y;
2886
2887       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2888
2889       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2890                     dst_x - src_x, dst_y - src_y);
2891       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2892                        dst_x, dst_y);
2893     }
2894 #else
2895 #if 1
2896     if (key[i])
2897       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2898     else
2899       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2900                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2901 #else
2902     if (key[i])
2903       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2904     else
2905       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2906                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2907 #endif
2908 #endif
2909   }
2910 }
2911
2912 #else
2913
2914 void DrawGameValue_Emeralds(int value)
2915 {
2916   int font_nr = FONT_TEXT_2;
2917   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2918
2919   if (PANEL_DEACTIVATED(game.panel.gems))
2920     return;
2921
2922   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2923 }
2924
2925 void DrawGameValue_Dynamite(int value)
2926 {
2927   int font_nr = FONT_TEXT_2;
2928   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2929
2930   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2931     return;
2932
2933   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2934 }
2935
2936 void DrawGameValue_Score(int value)
2937 {
2938   int font_nr = FONT_TEXT_2;
2939   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2940
2941   if (PANEL_DEACTIVATED(game.panel.score))
2942     return;
2943
2944   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2945 }
2946
2947 void DrawGameValue_Time(int value)
2948 {
2949   int font1_nr = FONT_TEXT_2;
2950 #if 1
2951   int font2_nr = FONT_TEXT_1;
2952 #else
2953   int font2_nr = FONT_LEVEL_NUMBER;
2954 #endif
2955   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2956   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2957
2958   if (PANEL_DEACTIVATED(game.panel.time))
2959     return;
2960
2961   /* clear background if value just changed its size */
2962   if (value == 999 || value == 1000)
2963     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2964
2965   if (value < 1000)
2966     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2967   else
2968     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2969 }
2970
2971 void DrawGameValue_Level(int value)
2972 {
2973   int font1_nr = FONT_TEXT_2;
2974 #if 1
2975   int font2_nr = FONT_TEXT_1;
2976 #else
2977   int font2_nr = FONT_LEVEL_NUMBER;
2978 #endif
2979
2980   if (PANEL_DEACTIVATED(game.panel.level))
2981     return;
2982
2983   if (level_nr < 100)
2984     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2985   else
2986     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2987 }
2988
2989 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2990 {
2991   int base_key_graphic = EL_KEY_1;
2992   int i;
2993
2994   if (PANEL_DEACTIVATED(game.panel.keys))
2995     return;
2996
2997   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2998     base_key_graphic = EL_EM_KEY_1;
2999
3000   /* currently only 4 of 8 possible keys are displayed */
3001   for (i = 0; i < STD_NUM_KEYS; i++)
3002   {
3003     int x = XX_KEYS + i * MINI_TILEX;
3004     int y = YY_KEYS;
3005
3006     if (key[i])
3007       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3008     else
3009       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3010                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3011   }
3012 }
3013
3014 #endif
3015
3016 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3017                        int key_bits)
3018 {
3019   int key[MAX_NUM_KEYS];
3020   int i;
3021
3022   /* prevent EM engine from updating time/score values parallel to GameWon() */
3023   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3024       local_player->LevelSolved)
3025     return;
3026
3027   for (i = 0; i < MAX_NUM_KEYS; i++)
3028     key[i] = key_bits & (1 << i);
3029
3030   DrawGameValue_Level(level_nr);
3031
3032   DrawGameValue_Emeralds(emeralds);
3033   DrawGameValue_Dynamite(dynamite);
3034   DrawGameValue_Score(score);
3035   DrawGameValue_Time(time);
3036
3037   DrawGameValue_Keys(key);
3038 }
3039
3040 void UpdateGameDoorValues()
3041 {
3042   UpdateGameControlValues();
3043 }
3044
3045 void DrawGameDoorValues()
3046 {
3047   DisplayGameControlValues();
3048 }
3049
3050 void DrawGameDoorValues_OLD()
3051 {
3052   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3053   int dynamite_value = 0;
3054   int score_value = (local_player->LevelSolved ? local_player->score_final :
3055                      local_player->score);
3056   int gems_value = local_player->gems_still_needed;
3057   int key_bits = 0;
3058   int i, j;
3059
3060   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3061   {
3062     DrawGameDoorValues_EM();
3063
3064     return;
3065   }
3066
3067   if (game.centered_player_nr == -1)
3068   {
3069     for (i = 0; i < MAX_PLAYERS; i++)
3070     {
3071       for (j = 0; j < MAX_NUM_KEYS; j++)
3072         if (stored_player[i].key[j])
3073           key_bits |= (1 << j);
3074
3075       dynamite_value += stored_player[i].inventory_size;
3076     }
3077   }
3078   else
3079   {
3080     int player_nr = game.centered_player_nr;
3081
3082     for (i = 0; i < MAX_NUM_KEYS; i++)
3083       if (stored_player[player_nr].key[i])
3084         key_bits |= (1 << i);
3085
3086     dynamite_value = stored_player[player_nr].inventory_size;
3087   }
3088
3089   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3090                     key_bits);
3091 }
3092
3093
3094 /*
3095   =============================================================================
3096   InitGameEngine()
3097   -----------------------------------------------------------------------------
3098   initialize game engine due to level / tape version number
3099   =============================================================================
3100 */
3101
3102 static void InitGameEngine()
3103 {
3104   int i, j, k, l, x, y;
3105
3106   /* set game engine from tape file when re-playing, else from level file */
3107   game.engine_version = (tape.playing ? tape.engine_version :
3108                          level.game_version);
3109
3110   /* ---------------------------------------------------------------------- */
3111   /* set flags for bugs and changes according to active game engine version */
3112   /* ---------------------------------------------------------------------- */
3113
3114   /*
3115     Summary of bugfix/change:
3116     Fixed handling for custom elements that change when pushed by the player.
3117
3118     Fixed/changed in version:
3119     3.1.0
3120
3121     Description:
3122     Before 3.1.0, custom elements that "change when pushing" changed directly
3123     after the player started pushing them (until then handled in "DigField()").
3124     Since 3.1.0, these custom elements are not changed until the "pushing"
3125     move of the element is finished (now handled in "ContinueMoving()").
3126
3127     Affected levels/tapes:
3128     The first condition is generally needed for all levels/tapes before version
3129     3.1.0, which might use the old behaviour before it was changed; known tapes
3130     that are affected are some tapes from the level set "Walpurgis Gardens" by
3131     Jamie Cullen.
3132     The second condition is an exception from the above case and is needed for
3133     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3134     above (including some development versions of 3.1.0), but before it was
3135     known that this change would break tapes like the above and was fixed in
3136     3.1.1, so that the changed behaviour was active although the engine version
3137     while recording maybe was before 3.1.0. There is at least one tape that is
3138     affected by this exception, which is the tape for the one-level set "Bug
3139     Machine" by Juergen Bonhagen.
3140   */
3141
3142   game.use_change_when_pushing_bug =
3143     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3144      !(tape.playing &&
3145        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3146        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3147
3148   /*
3149     Summary of bugfix/change:
3150     Fixed handling for blocking the field the player leaves when moving.
3151
3152     Fixed/changed in version:
3153     3.1.1
3154
3155     Description:
3156     Before 3.1.1, when "block last field when moving" was enabled, the field
3157     the player is leaving when moving was blocked for the time of the move,
3158     and was directly unblocked afterwards. This resulted in the last field
3159     being blocked for exactly one less than the number of frames of one player
3160     move. Additionally, even when blocking was disabled, the last field was
3161     blocked for exactly one frame.
3162     Since 3.1.1, due to changes in player movement handling, the last field
3163     is not blocked at all when blocking is disabled. When blocking is enabled,
3164     the last field is blocked for exactly the number of frames of one player
3165     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3166     last field is blocked for exactly one more than the number of frames of
3167     one player move.
3168
3169     Affected levels/tapes:
3170     (!!! yet to be determined -- probably many !!!)
3171   */
3172
3173   game.use_block_last_field_bug =
3174     (game.engine_version < VERSION_IDENT(3,1,1,0));
3175
3176   /*
3177     Summary of bugfix/change:
3178     Changed behaviour of CE changes with multiple changes per single frame.
3179
3180     Fixed/changed in version:
3181     3.2.0-6
3182
3183     Description:
3184     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3185     This resulted in race conditions where CEs seem to behave strange in some
3186     situations (where triggered CE changes were just skipped because there was
3187     already a CE change on that tile in the playfield in that engine frame).
3188     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3189     (The number of changes per frame must be limited in any case, because else
3190     it is easily possible to define CE changes that would result in an infinite
3191     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3192     should be set large enough so that it would only be reached in cases where
3193     the corresponding CE change conditions run into a loop. Therefore, it seems
3194     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3195     maximal number of change pages for custom elements.)
3196
3197     Affected levels/tapes:
3198     Probably many.
3199   */
3200
3201 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3202   game.max_num_changes_per_frame = 1;
3203 #else
3204   game.max_num_changes_per_frame =
3205     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3206 #endif
3207
3208   /* ---------------------------------------------------------------------- */
3209
3210   /* default scan direction: scan playfield from top/left to bottom/right */
3211   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3212
3213   /* dynamically adjust element properties according to game engine version */
3214   InitElementPropertiesEngine(game.engine_version);
3215
3216 #if 0
3217   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3218   printf("          tape version == %06d [%s] [file: %06d]\n",
3219          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3220          tape.file_version);
3221   printf("       => game.engine_version == %06d\n", game.engine_version);
3222 #endif
3223
3224   /* ---------- initialize player's initial move delay --------------------- */
3225
3226   /* dynamically adjust player properties according to level information */
3227   for (i = 0; i < MAX_PLAYERS; i++)
3228     game.initial_move_delay_value[i] =
3229       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3230
3231   /* dynamically adjust player properties according to game engine version */
3232   for (i = 0; i < MAX_PLAYERS; i++)
3233     game.initial_move_delay[i] =
3234       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3235        game.initial_move_delay_value[i] : 0);
3236
3237   /* ---------- initialize player's initial push delay --------------------- */
3238
3239   /* dynamically adjust player properties according to game engine version */
3240   game.initial_push_delay_value =
3241     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3242
3243   /* ---------- initialize changing elements ------------------------------- */
3244
3245   /* initialize changing elements information */
3246   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3247   {
3248     struct ElementInfo *ei = &element_info[i];
3249
3250     /* this pointer might have been changed in the level editor */
3251     ei->change = &ei->change_page[0];
3252
3253     if (!IS_CUSTOM_ELEMENT(i))
3254     {
3255       ei->change->target_element = EL_EMPTY_SPACE;
3256       ei->change->delay_fixed = 0;
3257       ei->change->delay_random = 0;
3258       ei->change->delay_frames = 1;
3259     }
3260
3261     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3262     {
3263       ei->has_change_event[j] = FALSE;
3264
3265       ei->event_page_nr[j] = 0;
3266       ei->event_page[j] = &ei->change_page[0];
3267     }
3268   }
3269
3270   /* add changing elements from pre-defined list */
3271   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3272   {
3273     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3274     struct ElementInfo *ei = &element_info[ch_delay->element];
3275
3276     ei->change->target_element       = ch_delay->target_element;
3277     ei->change->delay_fixed          = ch_delay->change_delay;
3278
3279     ei->change->pre_change_function  = ch_delay->pre_change_function;
3280     ei->change->change_function      = ch_delay->change_function;
3281     ei->change->post_change_function = ch_delay->post_change_function;
3282
3283     ei->change->can_change = TRUE;
3284     ei->change->can_change_or_has_action = TRUE;
3285
3286     ei->has_change_event[CE_DELAY] = TRUE;
3287
3288     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3289     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3290   }
3291
3292   /* ---------- initialize internal run-time variables ------------- */
3293
3294   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3295   {
3296     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3297
3298     for (j = 0; j < ei->num_change_pages; j++)
3299     {
3300       ei->change_page[j].can_change_or_has_action =
3301         (ei->change_page[j].can_change |
3302          ei->change_page[j].has_action);
3303     }
3304   }
3305
3306   /* add change events from custom element configuration */
3307   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3308   {
3309     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3310
3311     for (j = 0; j < ei->num_change_pages; j++)
3312     {
3313       if (!ei->change_page[j].can_change_or_has_action)
3314         continue;
3315
3316       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3317       {
3318         /* only add event page for the first page found with this event */
3319         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3320         {
3321           ei->has_change_event[k] = TRUE;
3322
3323           ei->event_page_nr[k] = j;
3324           ei->event_page[k] = &ei->change_page[j];
3325         }
3326       }
3327     }
3328   }
3329
3330   /* ---------- initialize run-time trigger player and element ------------- */
3331
3332   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3333   {
3334     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3335
3336     for (j = 0; j < ei->num_change_pages; j++)
3337     {
3338       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3339       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3340       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3341       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3342       ei->change_page[j].actual_trigger_ce_value = 0;
3343       ei->change_page[j].actual_trigger_ce_score = 0;
3344     }
3345   }
3346
3347   /* ---------- initialize trigger events ---------------------------------- */
3348
3349   /* initialize trigger events information */
3350   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3351     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3352       trigger_events[i][j] = FALSE;
3353
3354   /* add trigger events from element change event properties */
3355   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3356   {
3357     struct ElementInfo *ei = &element_info[i];
3358
3359     for (j = 0; j < ei->num_change_pages; j++)
3360     {
3361       if (!ei->change_page[j].can_change_or_has_action)
3362         continue;
3363
3364       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3365       {
3366         int trigger_element = ei->change_page[j].trigger_element;
3367
3368         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3369         {
3370           if (ei->change_page[j].has_event[k])
3371           {
3372             if (IS_GROUP_ELEMENT(trigger_element))
3373             {
3374               struct ElementGroupInfo *group =
3375                 element_info[trigger_element].group;
3376
3377               for (l = 0; l < group->num_elements_resolved; l++)
3378                 trigger_events[group->element_resolved[l]][k] = TRUE;
3379             }
3380             else if (trigger_element == EL_ANY_ELEMENT)
3381               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3382                 trigger_events[l][k] = TRUE;
3383             else
3384               trigger_events[trigger_element][k] = TRUE;
3385           }
3386         }
3387       }
3388     }
3389   }
3390
3391   /* ---------- initialize push delay -------------------------------------- */
3392
3393   /* initialize push delay values to default */
3394   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3395   {
3396     if (!IS_CUSTOM_ELEMENT(i))
3397     {
3398       /* set default push delay values (corrected since version 3.0.7-1) */
3399       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3400       {
3401         element_info[i].push_delay_fixed = 2;
3402         element_info[i].push_delay_random = 8;
3403       }
3404       else
3405       {
3406         element_info[i].push_delay_fixed = 8;
3407         element_info[i].push_delay_random = 8;
3408       }
3409     }
3410   }
3411
3412   /* set push delay value for certain elements from pre-defined list */
3413   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3414   {
3415     int e = push_delay_list[i].element;
3416
3417     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3418     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3419   }
3420
3421   /* set push delay value for Supaplex elements for newer engine versions */
3422   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3423   {
3424     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3425     {
3426       if (IS_SP_ELEMENT(i))
3427       {
3428         /* set SP push delay to just enough to push under a falling zonk */
3429         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3430
3431         element_info[i].push_delay_fixed  = delay;
3432         element_info[i].push_delay_random = 0;
3433       }
3434     }
3435   }
3436
3437   /* ---------- initialize move stepsize ----------------------------------- */
3438
3439   /* initialize move stepsize values to default */
3440   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3441     if (!IS_CUSTOM_ELEMENT(i))
3442       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3443
3444   /* set move stepsize value for certain elements from pre-defined list */
3445   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3446   {
3447     int e = move_stepsize_list[i].element;
3448
3449     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3450   }
3451
3452   /* ---------- initialize collect score ----------------------------------- */
3453
3454   /* initialize collect score values for custom elements from initial value */
3455   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3456     if (IS_CUSTOM_ELEMENT(i))
3457       element_info[i].collect_score = element_info[i].collect_score_initial;
3458
3459   /* ---------- initialize collect count ----------------------------------- */
3460
3461   /* initialize collect count values for non-custom elements */
3462   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3463     if (!IS_CUSTOM_ELEMENT(i))
3464       element_info[i].collect_count_initial = 0;
3465
3466   /* add collect count values for all elements from pre-defined list */
3467   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3468     element_info[collect_count_list[i].element].collect_count_initial =
3469       collect_count_list[i].count;
3470
3471   /* ---------- initialize access direction -------------------------------- */
3472
3473   /* initialize access direction values to default (access from every side) */
3474   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3475     if (!IS_CUSTOM_ELEMENT(i))
3476       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3477
3478   /* set access direction value for certain elements from pre-defined list */
3479   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3480     element_info[access_direction_list[i].element].access_direction =
3481       access_direction_list[i].direction;
3482
3483   /* ---------- initialize explosion content ------------------------------- */
3484   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3485   {
3486     if (IS_CUSTOM_ELEMENT(i))
3487       continue;
3488
3489     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3490     {
3491       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3492
3493       element_info[i].content.e[x][y] =
3494         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3495          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3496          i == EL_PLAYER_3 ? EL_EMERALD :
3497          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3498          i == EL_MOLE ? EL_EMERALD_RED :
3499          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3500          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3501          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3502          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3503          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3504          i == EL_WALL_EMERALD ? EL_EMERALD :
3505          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3506          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3507          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3508          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3509          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3510          i == EL_WALL_PEARL ? EL_PEARL :
3511          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3512          EL_EMPTY);
3513     }
3514   }
3515
3516   /* ---------- initialize recursion detection ------------------------------ */
3517   recursion_loop_depth = 0;
3518   recursion_loop_detected = FALSE;
3519   recursion_loop_element = EL_UNDEFINED;
3520
3521   /* ---------- initialize graphics engine ---------------------------------- */
3522   game.scroll_delay_value =
3523     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3524      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3525   game.scroll_delay_value =
3526     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3527 }
3528
3529 int get_num_special_action(int element, int action_first, int action_last)
3530 {
3531   int num_special_action = 0;
3532   int i, j;
3533
3534   for (i = action_first; i <= action_last; i++)
3535   {
3536     boolean found = FALSE;
3537
3538     for (j = 0; j < NUM_DIRECTIONS; j++)
3539       if (el_act_dir2img(element, i, j) !=
3540           el_act_dir2img(element, ACTION_DEFAULT, j))
3541         found = TRUE;
3542
3543     if (found)
3544       num_special_action++;
3545     else
3546       break;
3547   }
3548
3549   return num_special_action;
3550 }
3551
3552
3553 /*
3554   =============================================================================
3555   InitGame()
3556   -----------------------------------------------------------------------------
3557   initialize and start new game
3558   =============================================================================
3559 */
3560
3561 void InitGame()
3562 {
3563   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3564   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3565   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3566 #if 0
3567   boolean do_fading = (game_status == GAME_MODE_MAIN);
3568 #endif
3569   int i, j, x, y;
3570
3571   game_status = GAME_MODE_PLAYING;
3572
3573   InitGameEngine();
3574   InitGameControlValues();
3575
3576   /* don't play tapes over network */
3577   network_playing = (options.network && !tape.playing);
3578
3579   for (i = 0; i < MAX_PLAYERS; i++)
3580   {
3581     struct PlayerInfo *player = &stored_player[i];
3582
3583     player->index_nr = i;
3584     player->index_bit = (1 << i);
3585     player->element_nr = EL_PLAYER_1 + i;
3586
3587     player->present = FALSE;
3588     player->active = FALSE;
3589     player->killed = FALSE;
3590
3591     player->action = 0;
3592     player->effective_action = 0;
3593     player->programmed_action = 0;
3594
3595     player->score = 0;
3596     player->score_final = 0;
3597
3598     player->gems_still_needed = level.gems_needed;
3599     player->sokobanfields_still_needed = 0;
3600     player->lights_still_needed = 0;
3601     player->friends_still_needed = 0;
3602
3603     for (j = 0; j < MAX_NUM_KEYS; j++)
3604       player->key[j] = FALSE;
3605
3606     player->num_white_keys = 0;
3607
3608     player->dynabomb_count = 0;
3609     player->dynabomb_size = 1;
3610     player->dynabombs_left = 0;
3611     player->dynabomb_xl = FALSE;
3612
3613     player->MovDir = MV_NONE;
3614     player->MovPos = 0;
3615     player->GfxPos = 0;
3616     player->GfxDir = MV_NONE;
3617     player->GfxAction = ACTION_DEFAULT;
3618     player->Frame = 0;
3619     player->StepFrame = 0;
3620
3621     player->use_murphy = FALSE;
3622     player->artwork_element =
3623       (level.use_artwork_element[i] ? level.artwork_element[i] :
3624        player->element_nr);
3625
3626     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3627     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3628
3629     player->gravity = level.initial_player_gravity[i];
3630
3631     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3632
3633     player->actual_frame_counter = 0;
3634
3635     player->step_counter = 0;
3636
3637     player->last_move_dir = MV_NONE;
3638
3639     player->is_active = FALSE;
3640
3641     player->is_waiting = FALSE;
3642     player->is_moving = FALSE;
3643     player->is_auto_moving = FALSE;
3644     player->is_digging = FALSE;
3645     player->is_snapping = FALSE;
3646     player->is_collecting = FALSE;
3647     player->is_pushing = FALSE;
3648     player->is_switching = FALSE;
3649     player->is_dropping = FALSE;
3650     player->is_dropping_pressed = FALSE;
3651
3652     player->is_bored = FALSE;
3653     player->is_sleeping = FALSE;
3654
3655     player->frame_counter_bored = -1;
3656     player->frame_counter_sleeping = -1;
3657
3658     player->anim_delay_counter = 0;
3659     player->post_delay_counter = 0;
3660
3661     player->dir_waiting = MV_NONE;
3662     player->action_waiting = ACTION_DEFAULT;
3663     player->last_action_waiting = ACTION_DEFAULT;
3664     player->special_action_bored = ACTION_DEFAULT;
3665     player->special_action_sleeping = ACTION_DEFAULT;
3666
3667     player->switch_x = -1;
3668     player->switch_y = -1;
3669
3670     player->drop_x = -1;
3671     player->drop_y = -1;
3672
3673     player->show_envelope = 0;
3674
3675     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3676
3677     player->push_delay       = -1;      /* initialized when pushing starts */
3678     player->push_delay_value = game.initial_push_delay_value;
3679
3680     player->drop_delay = 0;
3681     player->drop_pressed_delay = 0;
3682
3683     player->last_jx = -1;
3684     player->last_jy = -1;
3685     player->jx = -1;
3686     player->jy = -1;
3687
3688     player->shield_normal_time_left = 0;
3689     player->shield_deadly_time_left = 0;
3690
3691     player->inventory_infinite_element = EL_UNDEFINED;
3692     player->inventory_size = 0;
3693
3694     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3695     SnapField(player, 0, 0);
3696
3697     player->LevelSolved = FALSE;
3698     player->GameOver = FALSE;
3699
3700     player->LevelSolved_GameWon = FALSE;
3701     player->LevelSolved_GameEnd = FALSE;
3702     player->LevelSolved_PanelOff = FALSE;
3703     player->LevelSolved_SaveTape = FALSE;
3704     player->LevelSolved_SaveScore = FALSE;
3705     player->LevelSolved_CountingTime = 0;
3706     player->LevelSolved_CountingScore = 0;
3707   }
3708
3709   network_player_action_received = FALSE;
3710
3711 #if defined(NETWORK_AVALIABLE)
3712   /* initial null action */
3713   if (network_playing)
3714     SendToServer_MovePlayer(MV_NONE);
3715 #endif
3716
3717   ZX = ZY = -1;
3718   ExitX = ExitY = -1;
3719
3720   FrameCounter = 0;
3721   TimeFrames = 0;
3722   TimePlayed = 0;
3723   TimeLeft = level.time;
3724   TapeTime = 0;
3725
3726   ScreenMovDir = MV_NONE;
3727   ScreenMovPos = 0;
3728   ScreenGfxPos = 0;
3729
3730   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3731
3732   AllPlayersGone = FALSE;
3733
3734   game.yamyam_content_nr = 0;
3735   game.robot_wheel_active = FALSE;
3736   game.magic_wall_active = FALSE;
3737   game.magic_wall_time_left = 0;
3738   game.light_time_left = 0;
3739   game.timegate_time_left = 0;
3740   game.switchgate_pos = 0;
3741   game.wind_direction = level.wind_direction_initial;
3742
3743 #if !USE_PLAYER_GRAVITY
3744   game.gravity = FALSE;
3745   game.explosions_delayed = TRUE;
3746 #endif
3747
3748   game.lenses_time_left = 0;
3749   game.magnify_time_left = 0;
3750
3751   game.ball_state = level.ball_state_initial;
3752   game.ball_content_nr = 0;
3753
3754   game.envelope_active = FALSE;
3755
3756   /* set focus to local player for network games, else to all players */
3757   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3758   game.centered_player_nr_next = game.centered_player_nr;
3759   game.set_centered_player = FALSE;
3760
3761   if (network_playing && tape.recording)
3762   {
3763     /* store client dependent player focus when recording network games */
3764     tape.centered_player_nr_next = game.centered_player_nr_next;
3765     tape.set_centered_player = TRUE;
3766   }
3767
3768   for (i = 0; i < NUM_BELTS; i++)
3769   {
3770     game.belt_dir[i] = MV_NONE;
3771     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3772   }
3773
3774   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3775     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3776
3777   SCAN_PLAYFIELD(x, y)
3778   {
3779     Feld[x][y] = level.field[x][y];
3780     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3781     ChangeDelay[x][y] = 0;
3782     ChangePage[x][y] = -1;
3783 #if USE_NEW_CUSTOM_VALUE
3784     CustomValue[x][y] = 0;              /* initialized in InitField() */
3785 #endif
3786     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3787     AmoebaNr[x][y] = 0;
3788     WasJustMoving[x][y] = 0;
3789     WasJustFalling[x][y] = 0;
3790     CheckCollision[x][y] = 0;
3791     CheckImpact[x][y] = 0;
3792     Stop[x][y] = FALSE;
3793     Pushed[x][y] = FALSE;
3794
3795     ChangeCount[x][y] = 0;
3796     ChangeEvent[x][y] = -1;
3797
3798     ExplodePhase[x][y] = 0;
3799     ExplodeDelay[x][y] = 0;
3800     ExplodeField[x][y] = EX_TYPE_NONE;
3801
3802     RunnerVisit[x][y] = 0;
3803     PlayerVisit[x][y] = 0;
3804
3805     GfxFrame[x][y] = 0;
3806     GfxRandom[x][y] = INIT_GFX_RANDOM();
3807     GfxElement[x][y] = EL_UNDEFINED;
3808     GfxAction[x][y] = ACTION_DEFAULT;
3809     GfxDir[x][y] = MV_NONE;
3810   }
3811
3812   SCAN_PLAYFIELD(x, y)
3813   {
3814     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3815       emulate_bd = FALSE;
3816     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3817       emulate_sb = FALSE;
3818     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3819       emulate_sp = FALSE;
3820
3821     InitField(x, y, TRUE);
3822
3823     ResetGfxAnimation(x, y);
3824   }
3825
3826   InitBeltMovement();
3827
3828   for (i = 0; i < MAX_PLAYERS; i++)
3829   {
3830     struct PlayerInfo *player = &stored_player[i];
3831
3832     /* set number of special actions for bored and sleeping animation */
3833     player->num_special_action_bored =
3834       get_num_special_action(player->artwork_element,
3835                              ACTION_BORING_1, ACTION_BORING_LAST);
3836     player->num_special_action_sleeping =
3837       get_num_special_action(player->artwork_element,
3838                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3839   }
3840
3841   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3842                     emulate_sb ? EMU_SOKOBAN :
3843                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3844
3845 #if USE_NEW_ALL_SLIPPERY
3846   /* initialize type of slippery elements */
3847   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3848   {
3849     if (!IS_CUSTOM_ELEMENT(i))
3850     {
3851       /* default: elements slip down either to the left or right randomly */
3852       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3853
3854       /* SP style elements prefer to slip down on the left side */
3855       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3856         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3857
3858       /* BD style elements prefer to slip down on the left side */
3859       if (game.emulation == EMU_BOULDERDASH)
3860         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3861     }
3862   }
3863 #endif
3864
3865   /* initialize explosion and ignition delay */
3866   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3867   {
3868     if (!IS_CUSTOM_ELEMENT(i))
3869     {
3870       int num_phase = 8;
3871       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3872                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3873                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3874       int last_phase = (num_phase + 1) * delay;
3875       int half_phase = (num_phase / 2) * delay;
3876
3877       element_info[i].explosion_delay = last_phase - 1;
3878       element_info[i].ignition_delay = half_phase;
3879
3880       if (i == EL_BLACK_ORB)
3881         element_info[i].ignition_delay = 1;
3882     }
3883
3884 #if 0
3885     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3886       element_info[i].explosion_delay = 1;
3887
3888     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3889       element_info[i].ignition_delay = 1;
3890 #endif
3891   }
3892
3893   /* correct non-moving belts to start moving left */
3894   for (i = 0; i < NUM_BELTS; i++)
3895     if (game.belt_dir[i] == MV_NONE)
3896       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3897
3898   /* check if any connected player was not found in playfield */
3899   for (i = 0; i < MAX_PLAYERS; i++)
3900   {
3901     struct PlayerInfo *player = &stored_player[i];
3902
3903     if (player->connected && !player->present)
3904     {
3905       for (j = 0; j < MAX_PLAYERS; j++)
3906       {
3907         struct PlayerInfo *some_player = &stored_player[j];
3908         int jx = some_player->jx, jy = some_player->jy;
3909
3910         /* assign first free player found that is present in the playfield */
3911         if (some_player->present && !some_player->connected)
3912         {
3913           player->present = TRUE;
3914           player->active = TRUE;
3915
3916           some_player->present = FALSE;
3917           some_player->active = FALSE;
3918
3919           player->artwork_element = some_player->artwork_element;
3920
3921           player->block_last_field       = some_player->block_last_field;
3922           player->block_delay_adjustment = some_player->block_delay_adjustment;
3923
3924           StorePlayer[jx][jy] = player->element_nr;
3925           player->jx = player->last_jx = jx;
3926           player->jy = player->last_jy = jy;
3927
3928           break;
3929         }
3930       }
3931     }
3932   }
3933
3934   if (tape.playing)
3935   {
3936     /* when playing a tape, eliminate all players who do not participate */
3937
3938     for (i = 0; i < MAX_PLAYERS; i++)
3939     {
3940       if (stored_player[i].active && !tape.player_participates[i])
3941       {
3942         struct PlayerInfo *player = &stored_player[i];
3943         int jx = player->jx, jy = player->jy;
3944
3945         player->active = FALSE;
3946         StorePlayer[jx][jy] = 0;
3947         Feld[jx][jy] = EL_EMPTY;
3948       }
3949     }
3950   }
3951   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3952   {
3953     /* when in single player mode, eliminate all but the first active player */
3954
3955     for (i = 0; i < MAX_PLAYERS; i++)
3956     {
3957       if (stored_player[i].active)
3958       {
3959         for (j = i + 1; j < MAX_PLAYERS; j++)
3960         {
3961           if (stored_player[j].active)
3962           {
3963             struct PlayerInfo *player = &stored_player[j];
3964             int jx = player->jx, jy = player->jy;
3965
3966             player->active = FALSE;
3967             player->present = FALSE;
3968
3969             StorePlayer[jx][jy] = 0;
3970             Feld[jx][jy] = EL_EMPTY;
3971           }
3972         }
3973       }
3974     }
3975   }
3976
3977   /* when recording the game, store which players take part in the game */
3978   if (tape.recording)
3979   {
3980     for (i = 0; i < MAX_PLAYERS; i++)
3981       if (stored_player[i].active)
3982         tape.player_participates[i] = TRUE;
3983   }
3984
3985   if (options.debug)
3986   {
3987     for (i = 0; i < MAX_PLAYERS; i++)
3988     {
3989       struct PlayerInfo *player = &stored_player[i];
3990
3991       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3992              i+1,
3993              player->present,
3994              player->connected,
3995              player->active);
3996       if (local_player == player)
3997         printf("Player  %d is local player.\n", i+1);
3998     }
3999   }
4000
4001   if (BorderElement == EL_EMPTY)
4002   {
4003     SBX_Left = 0;
4004     SBX_Right = lev_fieldx - SCR_FIELDX;
4005     SBY_Upper = 0;
4006     SBY_Lower = lev_fieldy - SCR_FIELDY;
4007   }
4008   else
4009   {
4010     SBX_Left = -1;
4011     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4012     SBY_Upper = -1;
4013     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4014   }
4015
4016   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4017     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4018
4019   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4020     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4021
4022   /* if local player not found, look for custom element that might create
4023      the player (make some assumptions about the right custom element) */
4024   if (!local_player->present)
4025   {
4026     int start_x = 0, start_y = 0;
4027     int found_rating = 0;
4028     int found_element = EL_UNDEFINED;
4029     int player_nr = local_player->index_nr;
4030
4031     SCAN_PLAYFIELD(x, y)
4032     {
4033       int element = Feld[x][y];
4034       int content;
4035       int xx, yy;
4036       boolean is_player;
4037
4038       if (level.use_start_element[player_nr] &&
4039           level.start_element[player_nr] == element &&
4040           found_rating < 4)
4041       {
4042         start_x = x;
4043         start_y = y;
4044
4045         found_rating = 4;
4046         found_element = element;
4047       }
4048
4049       if (!IS_CUSTOM_ELEMENT(element))
4050         continue;
4051
4052       if (CAN_CHANGE(element))
4053       {
4054         for (i = 0; i < element_info[element].num_change_pages; i++)
4055         {
4056           /* check for player created from custom element as single target */
4057           content = element_info[element].change_page[i].target_element;
4058           is_player = ELEM_IS_PLAYER(content);
4059
4060           if (is_player && (found_rating < 3 ||
4061                             (found_rating == 3 && element < found_element)))
4062           {
4063             start_x = x;
4064             start_y = y;
4065
4066             found_rating = 3;
4067             found_element = element;
4068           }
4069         }
4070       }
4071
4072       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4073       {
4074         /* check for player created from custom element as explosion content */
4075         content = element_info[element].content.e[xx][yy];
4076         is_player = ELEM_IS_PLAYER(content);
4077
4078         if (is_player && (found_rating < 2 ||
4079                           (found_rating == 2 && element < found_element)))
4080         {
4081           start_x = x + xx - 1;
4082           start_y = y + yy - 1;
4083
4084           found_rating = 2;
4085           found_element = element;
4086         }
4087
4088         if (!CAN_CHANGE(element))
4089           continue;
4090
4091         for (i = 0; i < element_info[element].num_change_pages; i++)
4092         {
4093           /* check for player created from custom element as extended target */
4094           content =
4095             element_info[element].change_page[i].target_content.e[xx][yy];
4096
4097           is_player = ELEM_IS_PLAYER(content);
4098
4099           if (is_player && (found_rating < 1 ||
4100                             (found_rating == 1 && element < found_element)))
4101           {
4102             start_x = x + xx - 1;
4103             start_y = y + yy - 1;
4104
4105             found_rating = 1;
4106             found_element = element;
4107           }
4108         }
4109       }
4110     }
4111
4112     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4113                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4114                 start_x - MIDPOSX);
4115
4116     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4117                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4118                 start_y - MIDPOSY);
4119   }
4120   else
4121   {
4122     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4123                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4124                 local_player->jx - MIDPOSX);
4125
4126     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4127                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4128                 local_player->jy - MIDPOSY);
4129   }
4130
4131 #if 0
4132   /* do not use PLAYING mask for fading out from main screen */
4133   game_status = GAME_MODE_MAIN;
4134 #endif
4135
4136   StopAnimation();
4137
4138   if (!game.restart_level)
4139     CloseDoor(DOOR_CLOSE_1);
4140
4141 #if 1
4142   if (level_editor_test_game)
4143     FadeSkipNextFadeIn();
4144   else
4145     FadeSetEnterScreen();
4146 #else
4147   if (level_editor_test_game)
4148     fading = fading_none;
4149   else
4150     fading = menu.destination;
4151 #endif
4152
4153 #if 1
4154   FadeOut(REDRAW_FIELD);
4155 #else
4156   if (do_fading)
4157     FadeOut(REDRAW_FIELD);
4158 #endif
4159
4160 #if 0
4161   game_status = GAME_MODE_PLAYING;
4162 #endif
4163
4164   /* !!! FIX THIS (START) !!! */
4165   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4166   {
4167     InitGameEngine_EM();
4168
4169     /* blit playfield from scroll buffer to normal back buffer for fading in */
4170     BlitScreenToBitmap_EM(backbuffer);
4171   }
4172   else
4173   {
4174     DrawLevel();
4175     DrawAllPlayers();
4176
4177     /* after drawing the level, correct some elements */
4178     if (game.timegate_time_left == 0)
4179       CloseAllOpenTimegates();
4180
4181     /* blit playfield from scroll buffer to normal back buffer for fading in */
4182     if (setup.soft_scrolling)
4183       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4184
4185     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4186   }
4187   /* !!! FIX THIS (END) !!! */
4188
4189 #if 1
4190   FadeIn(REDRAW_FIELD);
4191 #else
4192   if (do_fading)
4193     FadeIn(REDRAW_FIELD);
4194
4195   BackToFront();
4196 #endif
4197
4198   if (!game.restart_level)
4199   {
4200     /* copy default game door content to main double buffer */
4201     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4202                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4203   }
4204
4205   SetPanelBackground();
4206   SetDrawBackgroundMask(REDRAW_DOOR_1);
4207
4208   UpdateGameDoorValues();
4209   DrawGameDoorValues();
4210
4211   if (!game.restart_level)
4212   {
4213     UnmapGameButtons();
4214     UnmapTapeButtons();
4215     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4216     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4217     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4218     MapGameButtons();
4219     MapTapeButtons();
4220
4221     /* copy actual game door content to door double buffer for OpenDoor() */
4222     BlitBitmap(drawto, bitmap_db_door,
4223                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4224
4225     OpenDoor(DOOR_OPEN_ALL);
4226
4227     PlaySound(SND_GAME_STARTING);
4228
4229     if (setup.sound_music)
4230       PlayLevelMusic();
4231
4232     KeyboardAutoRepeatOffUnlessAutoplay();
4233
4234     if (options.debug)
4235     {
4236       for (i = 0; i < MAX_PLAYERS; i++)
4237         printf("Player %d %sactive.\n",
4238                i + 1, (stored_player[i].active ? "" : "not "));
4239     }
4240   }
4241
4242 #if 1
4243   UnmapAllGadgets();
4244
4245   MapGameButtons();
4246   MapTapeButtons();
4247 #endif
4248
4249   game.restart_level = FALSE;
4250 }
4251
4252 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4253 {
4254   /* this is used for non-R'n'D game engines to update certain engine values */
4255
4256   /* needed to determine if sounds are played within the visible screen area */
4257   scroll_x = actual_scroll_x;
4258   scroll_y = actual_scroll_y;
4259 }
4260
4261 void InitMovDir(int x, int y)
4262 {
4263   int i, element = Feld[x][y];
4264   static int xy[4][2] =
4265   {
4266     {  0, +1 },
4267     { +1,  0 },
4268     {  0, -1 },
4269     { -1,  0 }
4270   };
4271   static int direction[3][4] =
4272   {
4273     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4274     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4275     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4276   };
4277
4278   switch (element)
4279   {
4280     case EL_BUG_RIGHT:
4281     case EL_BUG_UP:
4282     case EL_BUG_LEFT:
4283     case EL_BUG_DOWN:
4284       Feld[x][y] = EL_BUG;
4285       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4286       break;
4287
4288     case EL_SPACESHIP_RIGHT:
4289     case EL_SPACESHIP_UP:
4290     case EL_SPACESHIP_LEFT:
4291     case EL_SPACESHIP_DOWN:
4292       Feld[x][y] = EL_SPACESHIP;
4293       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4294       break;
4295
4296     case EL_BD_BUTTERFLY_RIGHT:
4297     case EL_BD_BUTTERFLY_UP:
4298     case EL_BD_BUTTERFLY_LEFT:
4299     case EL_BD_BUTTERFLY_DOWN:
4300       Feld[x][y] = EL_BD_BUTTERFLY;
4301       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4302       break;
4303
4304     case EL_BD_FIREFLY_RIGHT:
4305     case EL_BD_FIREFLY_UP:
4306     case EL_BD_FIREFLY_LEFT:
4307     case EL_BD_FIREFLY_DOWN:
4308       Feld[x][y] = EL_BD_FIREFLY;
4309       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4310       break;
4311
4312     case EL_PACMAN_RIGHT:
4313     case EL_PACMAN_UP:
4314     case EL_PACMAN_LEFT:
4315     case EL_PACMAN_DOWN:
4316       Feld[x][y] = EL_PACMAN;
4317       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4318       break;
4319
4320     case EL_YAMYAM_LEFT:
4321     case EL_YAMYAM_RIGHT:
4322     case EL_YAMYAM_UP:
4323     case EL_YAMYAM_DOWN:
4324       Feld[x][y] = EL_YAMYAM;
4325       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4326       break;
4327
4328     case EL_SP_SNIKSNAK:
4329       MovDir[x][y] = MV_UP;
4330       break;
4331
4332     case EL_SP_ELECTRON:
4333       MovDir[x][y] = MV_LEFT;
4334       break;
4335
4336     case EL_MOLE_LEFT:
4337     case EL_MOLE_RIGHT:
4338     case EL_MOLE_UP:
4339     case EL_MOLE_DOWN:
4340       Feld[x][y] = EL_MOLE;
4341       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4342       break;
4343
4344     default:
4345       if (IS_CUSTOM_ELEMENT(element))
4346       {
4347         struct ElementInfo *ei = &element_info[element];
4348         int move_direction_initial = ei->move_direction_initial;
4349         int move_pattern = ei->move_pattern;
4350
4351         if (move_direction_initial == MV_START_PREVIOUS)
4352         {
4353           if (MovDir[x][y] != MV_NONE)
4354             return;
4355
4356           move_direction_initial = MV_START_AUTOMATIC;
4357         }
4358
4359         if (move_direction_initial == MV_START_RANDOM)
4360           MovDir[x][y] = 1 << RND(4);
4361         else if (move_direction_initial & MV_ANY_DIRECTION)
4362           MovDir[x][y] = move_direction_initial;
4363         else if (move_pattern == MV_ALL_DIRECTIONS ||
4364                  move_pattern == MV_TURNING_LEFT ||
4365                  move_pattern == MV_TURNING_RIGHT ||
4366                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4367                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4368                  move_pattern == MV_TURNING_RANDOM)
4369           MovDir[x][y] = 1 << RND(4);
4370         else if (move_pattern == MV_HORIZONTAL)
4371           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4372         else if (move_pattern == MV_VERTICAL)
4373           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4374         else if (move_pattern & MV_ANY_DIRECTION)
4375           MovDir[x][y] = element_info[element].move_pattern;
4376         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4377                  move_pattern == MV_ALONG_RIGHT_SIDE)
4378         {
4379           /* use random direction as default start direction */
4380           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4381             MovDir[x][y] = 1 << RND(4);
4382
4383           for (i = 0; i < NUM_DIRECTIONS; i++)
4384           {
4385             int x1 = x + xy[i][0];
4386             int y1 = y + xy[i][1];
4387
4388             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4389             {
4390               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4391                 MovDir[x][y] = direction[0][i];
4392               else
4393                 MovDir[x][y] = direction[1][i];
4394
4395               break;
4396             }
4397           }
4398         }                
4399       }
4400       else
4401       {
4402         MovDir[x][y] = 1 << RND(4);
4403
4404         if (element != EL_BUG &&
4405             element != EL_SPACESHIP &&
4406             element != EL_BD_BUTTERFLY &&
4407             element != EL_BD_FIREFLY)
4408           break;
4409
4410         for (i = 0; i < NUM_DIRECTIONS; i++)
4411         {
4412           int x1 = x + xy[i][0];
4413           int y1 = y + xy[i][1];
4414
4415           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4416           {
4417             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4418             {
4419               MovDir[x][y] = direction[0][i];
4420               break;
4421             }
4422             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4423                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4424             {
4425               MovDir[x][y] = direction[1][i];
4426               break;
4427             }
4428           }
4429         }
4430       }
4431       break;
4432   }
4433
4434   GfxDir[x][y] = MovDir[x][y];
4435 }
4436
4437 void InitAmoebaNr(int x, int y)
4438 {
4439   int i;
4440   int group_nr = AmoebeNachbarNr(x, y);
4441
4442   if (group_nr == 0)
4443   {
4444     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4445     {
4446       if (AmoebaCnt[i] == 0)
4447       {
4448         group_nr = i;
4449         break;
4450       }
4451     }
4452   }
4453
4454   AmoebaNr[x][y] = group_nr;
4455   AmoebaCnt[group_nr]++;
4456   AmoebaCnt2[group_nr]++;
4457 }
4458
4459 static void PlayerWins(struct PlayerInfo *player)
4460 {
4461   player->LevelSolved = TRUE;
4462   player->GameOver = TRUE;
4463
4464   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4465                          level.native_em_level->lev->score : player->score);
4466
4467   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4468   player->LevelSolved_CountingScore = player->score_final;
4469 }
4470
4471 void GameWon()
4472 {
4473   static int time, time_final;
4474   static int score, score_final;
4475   static int game_over_delay_1 = 0;
4476   static int game_over_delay_2 = 0;
4477   int game_over_delay_value_1 = 50;
4478   int game_over_delay_value_2 = 50;
4479
4480   if (!local_player->LevelSolved_GameWon)
4481   {
4482     int i;
4483
4484     /* do not start end game actions before the player stops moving (to exit) */
4485     if (local_player->MovPos)
4486       return;
4487
4488     local_player->LevelSolved_GameWon = TRUE;
4489     local_player->LevelSolved_SaveTape = tape.recording;
4490     local_player->LevelSolved_SaveScore = !tape.playing;
4491
4492     if (tape.auto_play)         /* tape might already be stopped here */
4493       tape.auto_play_level_solved = TRUE;
4494
4495 #if 1
4496     TapeStop();
4497 #endif
4498
4499     game_over_delay_1 = game_over_delay_value_1;
4500     game_over_delay_2 = game_over_delay_value_2;
4501
4502     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4503     score = score_final = local_player->score_final;
4504
4505     if (TimeLeft > 0)
4506     {
4507       time_final = 0;
4508       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4509     }
4510     else if (level.time == 0 && TimePlayed < 999)
4511     {
4512       time_final = 999;
4513       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4514     }
4515
4516     local_player->score_final = score_final;
4517
4518     if (level_editor_test_game)
4519     {
4520       time = time_final;
4521       score = score_final;
4522
4523 #if 1
4524       local_player->LevelSolved_CountingTime = time;
4525       local_player->LevelSolved_CountingScore = score;
4526
4527       game_panel_controls[GAME_PANEL_TIME].value = time;
4528       game_panel_controls[GAME_PANEL_SCORE].value = score;
4529
4530       DisplayGameControlValues();
4531 #else
4532       DrawGameValue_Time(time);
4533       DrawGameValue_Score(score);
4534 #endif
4535     }
4536
4537     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4538     {
4539       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4540       {
4541         /* close exit door after last player */
4542         if ((AllPlayersGone &&
4543              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4544               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4545               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4546             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4547             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4548         {
4549           int element = Feld[ExitX][ExitY];
4550
4551 #if 0
4552           if (element == EL_EM_EXIT_OPEN ||
4553               element == EL_EM_STEEL_EXIT_OPEN)
4554           {
4555             Bang(ExitX, ExitY);
4556           }
4557           else
4558 #endif
4559           {
4560             Feld[ExitX][ExitY] =
4561               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4562                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4563                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4564                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4565                EL_EM_STEEL_EXIT_CLOSING);
4566
4567             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4568           }
4569         }
4570
4571         /* player disappears */
4572         DrawLevelField(ExitX, ExitY);
4573       }
4574
4575       for (i = 0; i < MAX_PLAYERS; i++)
4576       {
4577         struct PlayerInfo *player = &stored_player[i];
4578
4579         if (player->present)
4580         {
4581           RemovePlayer(player);
4582
4583           /* player disappears */
4584           DrawLevelField(player->jx, player->jy);
4585         }
4586       }
4587     }
4588
4589     PlaySound(SND_GAME_WINNING);
4590   }
4591
4592   if (game_over_delay_1 > 0)
4593   {
4594     game_over_delay_1--;
4595
4596     return;
4597   }
4598
4599   if (time != time_final)
4600   {
4601     int time_to_go = ABS(time_final - time);
4602     int time_count_dir = (time < time_final ? +1 : -1);
4603     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4604
4605     time  += time_count_steps * time_count_dir;
4606     score += time_count_steps * level.score[SC_TIME_BONUS];
4607
4608 #if 1
4609     local_player->LevelSolved_CountingTime = time;
4610     local_player->LevelSolved_CountingScore = score;
4611
4612     game_panel_controls[GAME_PANEL_TIME].value = time;
4613     game_panel_controls[GAME_PANEL_SCORE].value = score;
4614
4615     DisplayGameControlValues();
4616 #else
4617     DrawGameValue_Time(time);
4618     DrawGameValue_Score(score);
4619 #endif
4620
4621     if (time == time_final)
4622       StopSound(SND_GAME_LEVELTIME_BONUS);
4623     else if (setup.sound_loops)
4624       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4625     else
4626       PlaySound(SND_GAME_LEVELTIME_BONUS);
4627
4628     return;
4629   }
4630
4631   local_player->LevelSolved_PanelOff = TRUE;
4632
4633   if (game_over_delay_2 > 0)
4634   {
4635     game_over_delay_2--;
4636
4637     return;
4638   }
4639
4640 #if 1
4641   GameEnd();
4642 #endif
4643 }
4644
4645 void GameEnd()
4646 {
4647   int hi_pos;
4648   boolean raise_level = FALSE;
4649
4650   local_player->LevelSolved_GameEnd = TRUE;
4651
4652   CloseDoor(DOOR_CLOSE_1);
4653
4654   if (local_player->LevelSolved_SaveTape)
4655   {
4656 #if 0
4657     TapeStop();
4658 #endif
4659
4660 #if 1
4661     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4662 #else
4663     SaveTape(tape.level_nr);            /* ask to save tape */
4664 #endif
4665   }
4666
4667   if (level_editor_test_game)
4668   {
4669     game_status = GAME_MODE_MAIN;
4670
4671 #if 1
4672     DrawAndFadeInMainMenu(REDRAW_FIELD);
4673 #else
4674     DrawMainMenu();
4675 #endif
4676
4677     return;
4678   }
4679
4680   if (!local_player->LevelSolved_SaveScore)
4681   {
4682 #if 1
4683     FadeOut(REDRAW_FIELD);
4684 #endif
4685
4686     game_status = GAME_MODE_MAIN;
4687
4688     DrawAndFadeInMainMenu(REDRAW_FIELD);
4689
4690     return;
4691   }
4692
4693   if (level_nr == leveldir_current->handicap_level)
4694   {
4695     leveldir_current->handicap_level++;
4696     SaveLevelSetup_SeriesInfo();
4697   }
4698
4699   if (level_nr < leveldir_current->last_level)
4700     raise_level = TRUE;                 /* advance to next level */
4701
4702   if ((hi_pos = NewHiScore()) >= 0) 
4703   {
4704     game_status = GAME_MODE_SCORES;
4705
4706     DrawHallOfFame(hi_pos);
4707
4708     if (raise_level)
4709     {
4710       level_nr++;
4711       TapeErase();
4712     }
4713   }
4714   else
4715   {
4716 #if 1
4717     FadeOut(REDRAW_FIELD);
4718 #endif
4719
4720     game_status = GAME_MODE_MAIN;
4721
4722     if (raise_level)
4723     {
4724       level_nr++;
4725       TapeErase();
4726     }
4727
4728     DrawAndFadeInMainMenu(REDRAW_FIELD);
4729   }
4730 }
4731
4732 int NewHiScore()
4733 {
4734   int k, l;
4735   int position = -1;
4736
4737   LoadScore(level_nr);
4738
4739   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4740       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4741     return -1;
4742
4743   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4744   {
4745     if (local_player->score_final > highscore[k].Score)
4746     {
4747       /* player has made it to the hall of fame */
4748
4749       if (k < MAX_SCORE_ENTRIES - 1)
4750       {
4751         int m = MAX_SCORE_ENTRIES - 1;
4752
4753 #ifdef ONE_PER_NAME
4754         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4755           if (strEqual(setup.player_name, highscore[l].Name))
4756             m = l;
4757         if (m == k)     /* player's new highscore overwrites his old one */
4758           goto put_into_list;
4759 #endif
4760
4761         for (l = m; l > k; l--)
4762         {
4763           strcpy(highscore[l].Name, highscore[l - 1].Name);
4764           highscore[l].Score = highscore[l - 1].Score;
4765         }
4766       }
4767
4768 #ifdef ONE_PER_NAME
4769       put_into_list:
4770 #endif
4771       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4772       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4773       highscore[k].Score = local_player->score_final; 
4774       position = k;
4775       break;
4776     }
4777
4778 #ifdef ONE_PER_NAME
4779     else if (!strncmp(setup.player_name, highscore[k].Name,
4780                       MAX_PLAYER_NAME_LEN))
4781       break;    /* player already there with a higher score */
4782 #endif
4783
4784   }
4785
4786   if (position >= 0) 
4787     SaveScore(level_nr);
4788
4789   return position;
4790 }
4791
4792 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4793 {
4794   int element = Feld[x][y];
4795   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4796   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4797   int horiz_move = (dx != 0);
4798   int sign = (horiz_move ? dx : dy);
4799   int step = sign * element_info[element].move_stepsize;
4800
4801   /* special values for move stepsize for spring and things on conveyor belt */
4802   if (horiz_move)
4803   {
4804     if (CAN_FALL(element) &&
4805         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4806       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4807     else if (element == EL_SPRING)
4808       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4809   }
4810
4811   return step;
4812 }
4813
4814 inline static int getElementMoveStepsize(int x, int y)
4815 {
4816   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4817 }
4818
4819 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4820 {
4821   if (player->GfxAction != action || player->GfxDir != dir)
4822   {
4823 #if 0
4824     printf("Player frame reset! (%d => %d, %d => %d)\n",
4825            player->GfxAction, action, player->GfxDir, dir);
4826 #endif
4827
4828     player->GfxAction = action;
4829     player->GfxDir = dir;
4830     player->Frame = 0;
4831     player->StepFrame = 0;
4832   }
4833 }
4834
4835 #if USE_GFX_RESET_GFX_ANIMATION
4836 static void ResetGfxFrame(int x, int y, boolean redraw)
4837 {
4838   int element = Feld[x][y];
4839   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4840   int last_gfx_frame = GfxFrame[x][y];
4841
4842   if (graphic_info[graphic].anim_global_sync)
4843     GfxFrame[x][y] = FrameCounter;
4844   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4845     GfxFrame[x][y] = CustomValue[x][y];
4846   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4847     GfxFrame[x][y] = element_info[element].collect_score;
4848   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4849     GfxFrame[x][y] = ChangeDelay[x][y];
4850
4851   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4852     DrawLevelGraphicAnimation(x, y, graphic);
4853 }
4854 #endif
4855
4856 static void ResetGfxAnimation(int x, int y)
4857 {
4858   GfxAction[x][y] = ACTION_DEFAULT;
4859   GfxDir[x][y] = MovDir[x][y];
4860   GfxFrame[x][y] = 0;
4861
4862 #if USE_GFX_RESET_GFX_ANIMATION
4863   ResetGfxFrame(x, y, FALSE);
4864 #endif
4865 }
4866
4867 static void ResetRandomAnimationValue(int x, int y)
4868 {
4869   GfxRandom[x][y] = INIT_GFX_RANDOM();
4870 }
4871
4872 void InitMovingField(int x, int y, int direction)
4873 {
4874   int element = Feld[x][y];
4875   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4876   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4877   int newx = x + dx;
4878   int newy = y + dy;
4879   boolean is_moving_before, is_moving_after;
4880 #if 0
4881   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4882 #endif
4883
4884   /* check if element was/is moving or being moved before/after mode change */
4885 #if 1
4886 #if 1
4887   is_moving_before = (WasJustMoving[x][y] != 0);
4888 #else
4889   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4890   is_moving_before = WasJustMoving[x][y];
4891 #endif
4892 #else
4893   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4894 #endif
4895   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4896
4897   /* reset animation only for moving elements which change direction of moving
4898      or which just started or stopped moving
4899      (else CEs with property "can move" / "not moving" are reset each frame) */
4900 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4901 #if 1
4902   if (is_moving_before != is_moving_after ||
4903       direction != MovDir[x][y])
4904     ResetGfxAnimation(x, y);
4905 #else
4906   if ((is_moving_before || is_moving_after) && !continues_moving)
4907     ResetGfxAnimation(x, y);
4908 #endif
4909 #else
4910   if (!continues_moving)
4911     ResetGfxAnimation(x, y);
4912 #endif
4913
4914   MovDir[x][y] = direction;
4915   GfxDir[x][y] = direction;
4916
4917 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4918   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4919                      direction == MV_DOWN && CAN_FALL(element) ?
4920                      ACTION_FALLING : ACTION_MOVING);
4921 #else
4922   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4923                      ACTION_FALLING : ACTION_MOVING);
4924 #endif
4925
4926   /* this is needed for CEs with property "can move" / "not moving" */
4927
4928   if (is_moving_after)
4929   {
4930     if (Feld[newx][newy] == EL_EMPTY)
4931       Feld[newx][newy] = EL_BLOCKED;
4932
4933     MovDir[newx][newy] = MovDir[x][y];
4934
4935 #if USE_NEW_CUSTOM_VALUE
4936     CustomValue[newx][newy] = CustomValue[x][y];
4937 #endif
4938
4939     GfxFrame[newx][newy] = GfxFrame[x][y];
4940     GfxRandom[newx][newy] = GfxRandom[x][y];
4941     GfxAction[newx][newy] = GfxAction[x][y];
4942     GfxDir[newx][newy] = GfxDir[x][y];
4943   }
4944 }
4945
4946 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4947 {
4948   int direction = MovDir[x][y];
4949   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4950   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4951
4952   *goes_to_x = newx;
4953   *goes_to_y = newy;
4954 }
4955
4956 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4957 {
4958   int oldx = x, oldy = y;
4959   int direction = MovDir[x][y];
4960
4961   if (direction == MV_LEFT)
4962     oldx++;
4963   else if (direction == MV_RIGHT)
4964     oldx--;
4965   else if (direction == MV_UP)
4966     oldy++;
4967   else if (direction == MV_DOWN)
4968     oldy--;
4969
4970   *comes_from_x = oldx;
4971   *comes_from_y = oldy;
4972 }
4973
4974 int MovingOrBlocked2Element(int x, int y)
4975 {
4976   int element = Feld[x][y];
4977
4978   if (element == EL_BLOCKED)
4979   {
4980     int oldx, oldy;
4981
4982     Blocked2Moving(x, y, &oldx, &oldy);
4983     return Feld[oldx][oldy];
4984   }
4985   else
4986     return element;
4987 }
4988
4989 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4990 {
4991   /* like MovingOrBlocked2Element(), but if element is moving
4992      and (x,y) is the field the moving element is just leaving,
4993      return EL_BLOCKED instead of the element value */
4994   int element = Feld[x][y];
4995
4996   if (IS_MOVING(x, y))
4997   {
4998     if (element == EL_BLOCKED)
4999     {
5000       int oldx, oldy;
5001
5002       Blocked2Moving(x, y, &oldx, &oldy);
5003       return Feld[oldx][oldy];
5004     }
5005     else
5006       return EL_BLOCKED;
5007   }
5008   else
5009     return element;
5010 }
5011
5012 static void RemoveField(int x, int y)
5013 {
5014   Feld[x][y] = EL_EMPTY;
5015
5016   MovPos[x][y] = 0;
5017   MovDir[x][y] = 0;
5018   MovDelay[x][y] = 0;
5019
5020 #if USE_NEW_CUSTOM_VALUE
5021   CustomValue[x][y] = 0;
5022 #endif
5023
5024   AmoebaNr[x][y] = 0;
5025   ChangeDelay[x][y] = 0;
5026   ChangePage[x][y] = -1;
5027   Pushed[x][y] = FALSE;
5028
5029 #if 0
5030   ExplodeField[x][y] = EX_TYPE_NONE;
5031 #endif
5032
5033   GfxElement[x][y] = EL_UNDEFINED;
5034   GfxAction[x][y] = ACTION_DEFAULT;
5035   GfxDir[x][y] = MV_NONE;
5036 }
5037
5038 void RemoveMovingField(int x, int y)
5039 {
5040   int oldx = x, oldy = y, newx = x, newy = y;
5041   int element = Feld[x][y];
5042   int next_element = EL_UNDEFINED;
5043
5044   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5045     return;
5046
5047   if (IS_MOVING(x, y))
5048   {
5049     Moving2Blocked(x, y, &newx, &newy);
5050
5051     if (Feld[newx][newy] != EL_BLOCKED)
5052     {
5053       /* element is moving, but target field is not free (blocked), but
5054          already occupied by something different (example: acid pool);
5055          in this case, only remove the moving field, but not the target */
5056
5057       RemoveField(oldx, oldy);
5058
5059       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5060
5061       DrawLevelField(oldx, oldy);
5062
5063       return;
5064     }
5065   }
5066   else if (element == EL_BLOCKED)
5067   {
5068     Blocked2Moving(x, y, &oldx, &oldy);
5069     if (!IS_MOVING(oldx, oldy))
5070       return;
5071   }
5072
5073   if (element == EL_BLOCKED &&
5074       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5075        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5076        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5077        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5078        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5079        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5080     next_element = get_next_element(Feld[oldx][oldy]);
5081
5082   RemoveField(oldx, oldy);
5083   RemoveField(newx, newy);
5084
5085   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5086
5087   if (next_element != EL_UNDEFINED)
5088     Feld[oldx][oldy] = next_element;
5089
5090   DrawLevelField(oldx, oldy);
5091   DrawLevelField(newx, newy);
5092 }
5093
5094 void DrawDynamite(int x, int y)
5095 {
5096   int sx = SCREENX(x), sy = SCREENY(y);
5097   int graphic = el2img(Feld[x][y]);
5098   int frame;
5099
5100   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5101     return;
5102
5103   if (IS_WALKABLE_INSIDE(Back[x][y]))
5104     return;
5105
5106   if (Back[x][y])
5107     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5108   else if (Store[x][y])
5109     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5110
5111   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5112
5113   if (Back[x][y] || Store[x][y])
5114     DrawGraphicThruMask(sx, sy, graphic, frame);
5115   else
5116     DrawGraphic(sx, sy, graphic, frame);
5117 }
5118
5119 void CheckDynamite(int x, int y)
5120 {
5121   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5122   {
5123     MovDelay[x][y]--;
5124
5125     if (MovDelay[x][y] != 0)
5126     {
5127       DrawDynamite(x, y);
5128       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5129
5130       return;
5131     }
5132   }
5133
5134   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5135
5136   Bang(x, y);
5137 }
5138
5139 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5140 {
5141   boolean num_checked_players = 0;
5142   int i;
5143
5144   for (i = 0; i < MAX_PLAYERS; i++)
5145   {
5146     if (stored_player[i].active)
5147     {
5148       int sx = stored_player[i].jx;
5149       int sy = stored_player[i].jy;
5150
5151       if (num_checked_players == 0)
5152       {
5153         *sx1 = *sx2 = sx;
5154         *sy1 = *sy2 = sy;
5155       }
5156       else
5157       {
5158         *sx1 = MIN(*sx1, sx);
5159         *sy1 = MIN(*sy1, sy);
5160         *sx2 = MAX(*sx2, sx);
5161         *sy2 = MAX(*sy2, sy);
5162       }
5163
5164       num_checked_players++;
5165     }
5166   }
5167 }
5168
5169 static boolean checkIfAllPlayersFitToScreen_RND()
5170 {
5171   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5172
5173   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5174
5175   return (sx2 - sx1 < SCR_FIELDX &&
5176           sy2 - sy1 < SCR_FIELDY);
5177 }
5178
5179 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5180 {
5181   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5182
5183   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5184
5185   *sx = (sx1 + sx2) / 2;
5186   *sy = (sy1 + sy2) / 2;
5187 }
5188
5189 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5190                         boolean center_screen, boolean quick_relocation)
5191 {
5192   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5193   boolean no_delay = (tape.warp_forward);
5194   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5195   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5196
5197   if (quick_relocation)
5198   {
5199     int offset = game.scroll_delay_value;
5200
5201     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5202     {
5203       if (!level.shifted_relocation || center_screen)
5204       {
5205         /* quick relocation (without scrolling), with centering of screen */
5206
5207         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5208                     x > SBX_Right + MIDPOSX ? SBX_Right :
5209                     x - MIDPOSX);
5210
5211         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5212                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5213                     y - MIDPOSY);
5214       }
5215       else
5216       {
5217         /* quick relocation (without scrolling), but do not center screen */
5218
5219         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5220                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5221                                old_x - MIDPOSX);
5222
5223         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5224                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5225                                old_y - MIDPOSY);
5226
5227         int offset_x = x + (scroll_x - center_scroll_x);
5228         int offset_y = y + (scroll_y - center_scroll_y);
5229
5230         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5231                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5232                     offset_x - MIDPOSX);
5233
5234         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5235                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5236                     offset_y - MIDPOSY);
5237       }
5238     }
5239     else
5240     {
5241       /* quick relocation (without scrolling), inside visible screen area */
5242
5243       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5244           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5245         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5246
5247       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5248           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5249         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5250
5251       /* don't scroll over playfield boundaries */
5252       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5253         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5254
5255       /* don't scroll over playfield boundaries */
5256       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5257         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5258     }
5259
5260     RedrawPlayfield(TRUE, 0,0,0,0);
5261   }
5262   else
5263   {
5264 #if 1
5265     int scroll_xx, scroll_yy;
5266
5267     if (!level.shifted_relocation || center_screen)
5268     {
5269       /* visible relocation (with scrolling), with centering of screen */
5270
5271       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5272                    x > SBX_Right + MIDPOSX ? SBX_Right :
5273                    x - MIDPOSX);
5274
5275       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5276                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5277                    y - MIDPOSY);
5278     }
5279     else
5280     {
5281       /* visible relocation (with scrolling), but do not center screen */
5282
5283       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5284                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5285                              old_x - MIDPOSX);
5286
5287       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5288                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5289                              old_y - MIDPOSY);
5290
5291       int offset_x = x + (scroll_x - center_scroll_x);
5292       int offset_y = y + (scroll_y - center_scroll_y);
5293
5294       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5295                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5296                    offset_x - MIDPOSX);
5297
5298       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5299                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5300                    offset_y - MIDPOSY);
5301     }
5302
5303 #else
5304
5305     /* visible relocation (with scrolling), with centering of screen */
5306
5307     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5308                      x > SBX_Right + MIDPOSX ? SBX_Right :
5309                      x - MIDPOSX);
5310
5311     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5312                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5313                      y - MIDPOSY);
5314 #endif
5315
5316     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5317
5318     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5319     {
5320       int dx = 0, dy = 0;
5321       int fx = FX, fy = FY;
5322
5323       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5324       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5325
5326       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5327         break;
5328
5329       scroll_x -= dx;
5330       scroll_y -= dy;
5331
5332       fx += dx * TILEX / 2;
5333       fy += dy * TILEY / 2;
5334
5335       ScrollLevel(dx, dy);
5336       DrawAllPlayers();
5337
5338       /* scroll in two steps of half tile size to make things smoother */
5339       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5340       FlushDisplay();
5341       Delay(wait_delay_value);
5342
5343       /* scroll second step to align at full tile size */
5344       BackToFront();
5345       Delay(wait_delay_value);
5346     }
5347
5348     DrawAllPlayers();
5349     BackToFront();
5350     Delay(wait_delay_value);
5351   }
5352 }
5353
5354 void RelocatePlayer(int jx, int jy, int el_player_raw)
5355 {
5356   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5357   int player_nr = GET_PLAYER_NR(el_player);
5358   struct PlayerInfo *player = &stored_player[player_nr];
5359   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5360   boolean no_delay = (tape.warp_forward);
5361   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5362   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5363   int old_jx = player->jx;
5364   int old_jy = player->jy;
5365   int old_element = Feld[old_jx][old_jy];
5366   int element = Feld[jx][jy];
5367   boolean player_relocated = (old_jx != jx || old_jy != jy);
5368
5369   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5370   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5371   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5372   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5373   int leave_side_horiz = move_dir_horiz;
5374   int leave_side_vert  = move_dir_vert;
5375   int enter_side = enter_side_horiz | enter_side_vert;
5376   int leave_side = leave_side_horiz | leave_side_vert;
5377
5378   if (player->GameOver)         /* do not reanimate dead player */
5379     return;
5380
5381   if (!player_relocated)        /* no need to relocate the player */
5382     return;
5383
5384   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5385   {
5386     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5387     DrawLevelField(jx, jy);
5388   }
5389
5390   if (player->present)
5391   {
5392     while (player->MovPos)
5393     {
5394       ScrollPlayer(player, SCROLL_GO_ON);
5395       ScrollScreen(NULL, SCROLL_GO_ON);
5396
5397       AdvanceFrameAndPlayerCounters(player->index_nr);
5398
5399       DrawPlayer(player);
5400
5401       BackToFront();
5402       Delay(wait_delay_value);
5403     }
5404
5405     DrawPlayer(player);         /* needed here only to cleanup last field */
5406     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5407
5408     player->is_moving = FALSE;
5409   }
5410
5411   if (IS_CUSTOM_ELEMENT(old_element))
5412     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5413                                CE_LEFT_BY_PLAYER,
5414                                player->index_bit, leave_side);
5415
5416   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5417                                       CE_PLAYER_LEAVES_X,
5418                                       player->index_bit, leave_side);
5419
5420   Feld[jx][jy] = el_player;
5421   InitPlayerField(jx, jy, el_player, TRUE);
5422
5423   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5424   {
5425     Feld[jx][jy] = element;
5426     InitField(jx, jy, FALSE);
5427   }
5428
5429   /* only visually relocate centered player */
5430   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5431                      FALSE, level.instant_relocation);
5432
5433   TestIfPlayerTouchesBadThing(jx, jy);
5434   TestIfPlayerTouchesCustomElement(jx, jy);
5435
5436   if (IS_CUSTOM_ELEMENT(element))
5437     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5438                                player->index_bit, enter_side);
5439
5440   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5441                                       player->index_bit, enter_side);
5442 }
5443
5444 void Explode(int ex, int ey, int phase, int mode)
5445 {
5446   int x, y;
5447   int last_phase;
5448   int border_element;
5449
5450   /* !!! eliminate this variable !!! */
5451   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5452
5453   if (game.explosions_delayed)
5454   {
5455     ExplodeField[ex][ey] = mode;
5456     return;
5457   }
5458
5459   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5460   {
5461     int center_element = Feld[ex][ey];
5462     int artwork_element, explosion_element;     /* set these values later */
5463
5464 #if 0
5465     /* --- This is only really needed (and now handled) in "Impact()". --- */
5466     /* do not explode moving elements that left the explode field in time */
5467     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5468         center_element == EL_EMPTY &&
5469         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5470       return;
5471 #endif
5472
5473 #if 0
5474     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5475     if (mode == EX_TYPE_NORMAL ||
5476         mode == EX_TYPE_CENTER ||
5477         mode == EX_TYPE_CROSS)
5478       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5479 #endif
5480
5481     /* remove things displayed in background while burning dynamite */
5482     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5483       Back[ex][ey] = 0;
5484
5485     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5486     {
5487       /* put moving element to center field (and let it explode there) */
5488       center_element = MovingOrBlocked2Element(ex, ey);
5489       RemoveMovingField(ex, ey);
5490       Feld[ex][ey] = center_element;
5491     }
5492
5493     /* now "center_element" is finally determined -- set related values now */
5494     artwork_element = center_element;           /* for custom player artwork */
5495     explosion_element = center_element;         /* for custom player artwork */
5496
5497     if (IS_PLAYER(ex, ey))
5498     {
5499       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5500
5501       artwork_element = stored_player[player_nr].artwork_element;
5502
5503       if (level.use_explosion_element[player_nr])
5504       {
5505         explosion_element = level.explosion_element[player_nr];
5506         artwork_element = explosion_element;
5507       }
5508     }
5509
5510 #if 1
5511     if (mode == EX_TYPE_NORMAL ||
5512         mode == EX_TYPE_CENTER ||
5513         mode == EX_TYPE_CROSS)
5514       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5515 #endif
5516
5517     last_phase = element_info[explosion_element].explosion_delay + 1;
5518
5519     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5520     {
5521       int xx = x - ex + 1;
5522       int yy = y - ey + 1;
5523       int element;
5524
5525       if (!IN_LEV_FIELD(x, y) ||
5526           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5527           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5528         continue;
5529
5530       element = Feld[x][y];
5531
5532       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5533       {
5534         element = MovingOrBlocked2Element(x, y);
5535
5536         if (!IS_EXPLOSION_PROOF(element))
5537           RemoveMovingField(x, y);
5538       }
5539
5540       /* indestructible elements can only explode in center (but not flames) */
5541       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5542                                            mode == EX_TYPE_BORDER)) ||
5543           element == EL_FLAMES)
5544         continue;
5545
5546       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5547          behaviour, for example when touching a yamyam that explodes to rocks
5548          with active deadly shield, a rock is created under the player !!! */
5549       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5550 #if 0
5551       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5552           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5553            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5554 #else
5555       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5556 #endif
5557       {
5558         if (IS_ACTIVE_BOMB(element))
5559         {
5560           /* re-activate things under the bomb like gate or penguin */
5561           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5562           Back[x][y] = 0;
5563         }
5564
5565         continue;
5566       }
5567
5568       /* save walkable background elements while explosion on same tile */
5569       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5570           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5571         Back[x][y] = element;
5572
5573       /* ignite explodable elements reached by other explosion */
5574       if (element == EL_EXPLOSION)
5575         element = Store2[x][y];
5576
5577       if (AmoebaNr[x][y] &&
5578           (element == EL_AMOEBA_FULL ||
5579            element == EL_BD_AMOEBA ||
5580            element == EL_AMOEBA_GROWING))
5581       {
5582         AmoebaCnt[AmoebaNr[x][y]]--;
5583         AmoebaCnt2[AmoebaNr[x][y]]--;
5584       }
5585
5586       RemoveField(x, y);
5587
5588       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5589       {
5590         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5591
5592         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5593
5594         if (PLAYERINFO(ex, ey)->use_murphy)
5595           Store[x][y] = EL_EMPTY;
5596       }
5597
5598       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5599          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5600       else if (ELEM_IS_PLAYER(center_element))
5601         Store[x][y] = EL_EMPTY;
5602       else if (center_element == EL_YAMYAM)
5603         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5604       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5605         Store[x][y] = element_info[center_element].content.e[xx][yy];
5606 #if 1
5607       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5608          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5609          otherwise) -- FIX THIS !!! */
5610       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5611         Store[x][y] = element_info[element].content.e[1][1];
5612 #else
5613       else if (!CAN_EXPLODE(element))
5614         Store[x][y] = element_info[element].content.e[1][1];
5615 #endif
5616       else
5617         Store[x][y] = EL_EMPTY;
5618
5619       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5620           center_element == EL_AMOEBA_TO_DIAMOND)
5621         Store2[x][y] = element;
5622
5623       Feld[x][y] = EL_EXPLOSION;
5624       GfxElement[x][y] = artwork_element;
5625
5626       ExplodePhase[x][y] = 1;
5627       ExplodeDelay[x][y] = last_phase;
5628
5629       Stop[x][y] = TRUE;
5630     }
5631
5632     if (center_element == EL_YAMYAM)
5633       game.yamyam_content_nr =
5634         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5635
5636     return;
5637   }
5638
5639   if (Stop[ex][ey])
5640     return;
5641
5642   x = ex;
5643   y = ey;
5644
5645   if (phase == 1)
5646     GfxFrame[x][y] = 0;         /* restart explosion animation */
5647
5648   last_phase = ExplodeDelay[x][y];
5649
5650   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5651
5652 #ifdef DEBUG
5653
5654   /* activate this even in non-DEBUG version until cause for crash in
5655      getGraphicAnimationFrame() (see below) is found and eliminated */
5656
5657 #endif
5658 #if 1
5659
5660 #if 1
5661   /* this can happen if the player leaves an explosion just in time */
5662   if (GfxElement[x][y] == EL_UNDEFINED)
5663     GfxElement[x][y] = EL_EMPTY;
5664 #else
5665   if (GfxElement[x][y] == EL_UNDEFINED)
5666   {
5667     printf("\n\n");
5668     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5669     printf("Explode(): This should never happen!\n");
5670     printf("\n\n");
5671
5672     GfxElement[x][y] = EL_EMPTY;
5673   }
5674 #endif
5675
5676 #endif
5677
5678   border_element = Store2[x][y];
5679   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5680     border_element = StorePlayer[x][y];
5681
5682   if (phase == element_info[border_element].ignition_delay ||
5683       phase == last_phase)
5684   {
5685     boolean border_explosion = FALSE;
5686
5687     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5688         !PLAYER_EXPLOSION_PROTECTED(x, y))
5689     {
5690       KillPlayerUnlessExplosionProtected(x, y);
5691       border_explosion = TRUE;
5692     }
5693     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5694     {
5695       Feld[x][y] = Store2[x][y];
5696       Store2[x][y] = 0;
5697       Bang(x, y);
5698       border_explosion = TRUE;
5699     }
5700     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5701     {
5702       AmoebeUmwandeln(x, y);
5703       Store2[x][y] = 0;
5704       border_explosion = TRUE;
5705     }
5706
5707     /* if an element just explodes due to another explosion (chain-reaction),
5708        do not immediately end the new explosion when it was the last frame of
5709        the explosion (as it would be done in the following "if"-statement!) */
5710     if (border_explosion && phase == last_phase)
5711       return;
5712   }
5713
5714   if (phase == last_phase)
5715   {
5716     int element;
5717
5718     element = Feld[x][y] = Store[x][y];
5719     Store[x][y] = Store2[x][y] = 0;
5720     GfxElement[x][y] = EL_UNDEFINED;
5721
5722     /* player can escape from explosions and might therefore be still alive */
5723     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5724         element <= EL_PLAYER_IS_EXPLODING_4)
5725     {
5726       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5727       int explosion_element = EL_PLAYER_1 + player_nr;
5728       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5729       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5730
5731       if (level.use_explosion_element[player_nr])
5732         explosion_element = level.explosion_element[player_nr];
5733
5734       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5735                     element_info[explosion_element].content.e[xx][yy]);
5736     }
5737
5738     /* restore probably existing indestructible background element */
5739     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5740       element = Feld[x][y] = Back[x][y];
5741     Back[x][y] = 0;
5742
5743     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5744     GfxDir[x][y] = MV_NONE;
5745     ChangeDelay[x][y] = 0;
5746     ChangePage[x][y] = -1;
5747
5748 #if USE_NEW_CUSTOM_VALUE
5749     CustomValue[x][y] = 0;
5750 #endif
5751
5752     InitField_WithBug2(x, y, FALSE);
5753
5754     DrawLevelField(x, y);
5755
5756     TestIfElementTouchesCustomElement(x, y);
5757
5758     if (GFX_CRUMBLED(element))
5759       DrawLevelFieldCrumbledSandNeighbours(x, y);
5760
5761     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5762       StorePlayer[x][y] = 0;
5763
5764     if (ELEM_IS_PLAYER(element))
5765       RelocatePlayer(x, y, element);
5766   }
5767   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5768   {
5769     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5770     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5771
5772     if (phase == delay)
5773       DrawLevelFieldCrumbledSand(x, y);
5774
5775     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5776     {
5777       DrawLevelElement(x, y, Back[x][y]);
5778       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5779     }
5780     else if (IS_WALKABLE_UNDER(Back[x][y]))
5781     {
5782       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5783       DrawLevelElementThruMask(x, y, Back[x][y]);
5784     }
5785     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5786       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5787   }
5788 }
5789
5790 void DynaExplode(int ex, int ey)
5791 {
5792   int i, j;
5793   int dynabomb_element = Feld[ex][ey];
5794   int dynabomb_size = 1;
5795   boolean dynabomb_xl = FALSE;
5796   struct PlayerInfo *player;
5797   static int xy[4][2] =
5798   {
5799     { 0, -1 },
5800     { -1, 0 },
5801     { +1, 0 },
5802     { 0, +1 }
5803   };
5804
5805   if (IS_ACTIVE_BOMB(dynabomb_element))
5806   {
5807     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5808     dynabomb_size = player->dynabomb_size;
5809     dynabomb_xl = player->dynabomb_xl;
5810     player->dynabombs_left++;
5811   }
5812
5813   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5814
5815   for (i = 0; i < NUM_DIRECTIONS; i++)
5816   {
5817     for (j = 1; j <= dynabomb_size; j++)
5818     {
5819       int x = ex + j * xy[i][0];
5820       int y = ey + j * xy[i][1];
5821       int element;
5822
5823       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5824         break;
5825
5826       element = Feld[x][y];
5827
5828       /* do not restart explosions of fields with active bombs */
5829       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5830         continue;
5831
5832       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5833
5834       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5835           !IS_DIGGABLE(element) && !dynabomb_xl)
5836         break;
5837     }
5838   }
5839 }
5840
5841 void Bang(int x, int y)
5842 {
5843   int element = MovingOrBlocked2Element(x, y);
5844   int explosion_type = EX_TYPE_NORMAL;
5845
5846   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5847   {
5848     struct PlayerInfo *player = PLAYERINFO(x, y);
5849
5850     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5851                             player->element_nr);
5852
5853     if (level.use_explosion_element[player->index_nr])
5854     {
5855       int explosion_element = level.explosion_element[player->index_nr];
5856
5857       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5858         explosion_type = EX_TYPE_CROSS;
5859       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5860         explosion_type = EX_TYPE_CENTER;
5861     }
5862   }
5863
5864   switch (element)
5865   {
5866     case EL_BUG:
5867     case EL_SPACESHIP:
5868     case EL_BD_BUTTERFLY:
5869     case EL_BD_FIREFLY:
5870     case EL_YAMYAM:
5871     case EL_DARK_YAMYAM:
5872     case EL_ROBOT:
5873     case EL_PACMAN:
5874     case EL_MOLE:
5875       RaiseScoreElement(element);
5876       break;
5877
5878     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5879     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5880     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5881     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5882     case EL_DYNABOMB_INCREASE_NUMBER:
5883     case EL_DYNABOMB_INCREASE_SIZE:
5884     case EL_DYNABOMB_INCREASE_POWER:
5885       explosion_type = EX_TYPE_DYNA;
5886       break;
5887
5888     case EL_DC_LANDMINE:
5889 #if 0
5890     case EL_EM_EXIT_OPEN:
5891     case EL_EM_STEEL_EXIT_OPEN:
5892 #endif
5893       explosion_type = EX_TYPE_CENTER;
5894       break;
5895
5896     case EL_PENGUIN:
5897     case EL_LAMP:
5898     case EL_LAMP_ACTIVE:
5899     case EL_AMOEBA_TO_DIAMOND:
5900       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5901         explosion_type = EX_TYPE_CENTER;
5902       break;
5903
5904     default:
5905       if (element_info[element].explosion_type == EXPLODES_CROSS)
5906         explosion_type = EX_TYPE_CROSS;
5907       else if (element_info[element].explosion_type == EXPLODES_1X1)
5908         explosion_type = EX_TYPE_CENTER;
5909       break;
5910   }
5911
5912   if (explosion_type == EX_TYPE_DYNA)
5913     DynaExplode(x, y);
5914   else
5915     Explode(x, y, EX_PHASE_START, explosion_type);
5916
5917   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5918 }
5919
5920 void SplashAcid(int x, int y)
5921 {
5922   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5923       (!IN_LEV_FIELD(x - 1, y - 2) ||
5924        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5925     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5926
5927   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5928       (!IN_LEV_FIELD(x + 1, y - 2) ||
5929        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5930     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5931
5932   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5933 }
5934
5935 static void InitBeltMovement()
5936 {
5937   static int belt_base_element[4] =
5938   {
5939     EL_CONVEYOR_BELT_1_LEFT,
5940     EL_CONVEYOR_BELT_2_LEFT,
5941     EL_CONVEYOR_BELT_3_LEFT,
5942     EL_CONVEYOR_BELT_4_LEFT
5943   };
5944   static int belt_base_active_element[4] =
5945   {
5946     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5947     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5948     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5949     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5950   };
5951
5952   int x, y, i, j;
5953
5954   /* set frame order for belt animation graphic according to belt direction */
5955   for (i = 0; i < NUM_BELTS; i++)
5956   {
5957     int belt_nr = i;
5958
5959     for (j = 0; j < NUM_BELT_PARTS; j++)
5960     {
5961       int element = belt_base_active_element[belt_nr] + j;
5962       int graphic_1 = el2img(element);
5963       int graphic_2 = el2panelimg(element);
5964
5965       if (game.belt_dir[i] == MV_LEFT)
5966       {
5967         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5968         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5969       }
5970       else
5971       {
5972         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5973         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5974       }
5975     }
5976   }
5977
5978   SCAN_PLAYFIELD(x, y)
5979   {
5980     int element = Feld[x][y];
5981
5982     for (i = 0; i < NUM_BELTS; i++)
5983     {
5984       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5985       {
5986         int e_belt_nr = getBeltNrFromBeltElement(element);
5987         int belt_nr = i;
5988
5989         if (e_belt_nr == belt_nr)
5990         {
5991           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5992
5993           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5994         }
5995       }
5996     }
5997   }
5998 }
5999
6000 static void ToggleBeltSwitch(int x, int y)
6001 {
6002   static int belt_base_element[4] =
6003   {
6004     EL_CONVEYOR_BELT_1_LEFT,
6005     EL_CONVEYOR_BELT_2_LEFT,
6006     EL_CONVEYOR_BELT_3_LEFT,
6007     EL_CONVEYOR_BELT_4_LEFT
6008   };
6009   static int belt_base_active_element[4] =
6010   {
6011     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6012     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6013     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6014     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6015   };
6016   static int belt_base_switch_element[4] =
6017   {
6018     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6019     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6020     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6021     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6022   };
6023   static int belt_move_dir[4] =
6024   {
6025     MV_LEFT,
6026     MV_NONE,
6027     MV_RIGHT,
6028     MV_NONE,
6029   };
6030
6031   int element = Feld[x][y];
6032   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6033   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6034   int belt_dir = belt_move_dir[belt_dir_nr];
6035   int xx, yy, i;
6036
6037   if (!IS_BELT_SWITCH(element))
6038     return;
6039
6040   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6041   game.belt_dir[belt_nr] = belt_dir;
6042
6043   if (belt_dir_nr == 3)
6044     belt_dir_nr = 1;
6045
6046   /* set frame order for belt animation graphic according to belt direction */
6047   for (i = 0; i < NUM_BELT_PARTS; i++)
6048   {
6049     int element = belt_base_active_element[belt_nr] + i;
6050     int graphic_1 = el2img(element);
6051     int graphic_2 = el2panelimg(element);
6052
6053     if (belt_dir == MV_LEFT)
6054     {
6055       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6056       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6057     }
6058     else
6059     {
6060       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6061       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6062     }
6063   }
6064
6065   SCAN_PLAYFIELD(xx, yy)
6066   {
6067     int element = Feld[xx][yy];
6068
6069     if (IS_BELT_SWITCH(element))
6070     {
6071       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6072
6073       if (e_belt_nr == belt_nr)
6074       {
6075         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6076         DrawLevelField(xx, yy);
6077       }
6078     }
6079     else if (IS_BELT(element) && belt_dir != MV_NONE)
6080     {
6081       int e_belt_nr = getBeltNrFromBeltElement(element);
6082
6083       if (e_belt_nr == belt_nr)
6084       {
6085         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6086
6087         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6088         DrawLevelField(xx, yy);
6089       }
6090     }
6091     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6092     {
6093       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6094
6095       if (e_belt_nr == belt_nr)
6096       {
6097         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6098
6099         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6100         DrawLevelField(xx, yy);
6101       }
6102     }
6103   }
6104 }
6105
6106 static void ToggleSwitchgateSwitch(int x, int y)
6107 {
6108   int xx, yy;
6109
6110   game.switchgate_pos = !game.switchgate_pos;
6111
6112   SCAN_PLAYFIELD(xx, yy)
6113   {
6114     int element = Feld[xx][yy];
6115
6116 #if !USE_BOTH_SWITCHGATE_SWITCHES
6117     if (element == EL_SWITCHGATE_SWITCH_UP ||
6118         element == EL_SWITCHGATE_SWITCH_DOWN)
6119     {
6120       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6121       DrawLevelField(xx, yy);
6122     }
6123     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6124              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6125     {
6126       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6127       DrawLevelField(xx, yy);
6128     }
6129 #else
6130     if (element == EL_SWITCHGATE_SWITCH_UP)
6131     {
6132       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6133       DrawLevelField(xx, yy);
6134     }
6135     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6136     {
6137       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6138       DrawLevelField(xx, yy);
6139     }
6140     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6141     {
6142       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6143       DrawLevelField(xx, yy);
6144     }
6145     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6146     {
6147       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6148       DrawLevelField(xx, yy);
6149     }
6150 #endif
6151     else if (element == EL_SWITCHGATE_OPEN ||
6152              element == EL_SWITCHGATE_OPENING)
6153     {
6154       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6155
6156       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6157     }
6158     else if (element == EL_SWITCHGATE_CLOSED ||
6159              element == EL_SWITCHGATE_CLOSING)
6160     {
6161       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6162
6163       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6164     }
6165   }
6166 }
6167
6168 static int getInvisibleActiveFromInvisibleElement(int element)
6169 {
6170   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6171           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6172           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6173           element);
6174 }
6175
6176 static int getInvisibleFromInvisibleActiveElement(int element)
6177 {
6178   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6179           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6180           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6181           element);
6182 }
6183
6184 static void RedrawAllLightSwitchesAndInvisibleElements()
6185 {
6186   int x, y;
6187
6188   SCAN_PLAYFIELD(x, y)
6189   {
6190     int element = Feld[x][y];
6191
6192     if (element == EL_LIGHT_SWITCH &&
6193         game.light_time_left > 0)
6194     {
6195       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6196       DrawLevelField(x, y);
6197     }
6198     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6199              game.light_time_left == 0)
6200     {
6201       Feld[x][y] = EL_LIGHT_SWITCH;
6202       DrawLevelField(x, y);
6203     }
6204     else if (element == EL_EMC_DRIPPER &&
6205              game.light_time_left > 0)
6206     {
6207       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6208       DrawLevelField(x, y);
6209     }
6210     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6211              game.light_time_left == 0)
6212     {
6213       Feld[x][y] = EL_EMC_DRIPPER;
6214       DrawLevelField(x, y);
6215     }
6216     else if (element == EL_INVISIBLE_STEELWALL ||
6217              element == EL_INVISIBLE_WALL ||
6218              element == EL_INVISIBLE_SAND)
6219     {
6220       if (game.light_time_left > 0)
6221         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6222
6223       DrawLevelField(x, y);
6224
6225       /* uncrumble neighbour fields, if needed */
6226       if (element == EL_INVISIBLE_SAND)
6227         DrawLevelFieldCrumbledSandNeighbours(x, y);
6228     }
6229     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6230              element == EL_INVISIBLE_WALL_ACTIVE ||
6231              element == EL_INVISIBLE_SAND_ACTIVE)
6232     {
6233       if (game.light_time_left == 0)
6234         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6235
6236       DrawLevelField(x, y);
6237
6238       /* re-crumble neighbour fields, if needed */
6239       if (element == EL_INVISIBLE_SAND)
6240         DrawLevelFieldCrumbledSandNeighbours(x, y);
6241     }
6242   }
6243 }
6244
6245 static void RedrawAllInvisibleElementsForLenses()
6246 {
6247   int x, y;
6248
6249   SCAN_PLAYFIELD(x, y)
6250   {
6251     int element = Feld[x][y];
6252
6253     if (element == EL_EMC_DRIPPER &&
6254         game.lenses_time_left > 0)
6255     {
6256       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6257       DrawLevelField(x, y);
6258     }
6259     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6260              game.lenses_time_left == 0)
6261     {
6262       Feld[x][y] = EL_EMC_DRIPPER;
6263       DrawLevelField(x, y);
6264     }
6265     else if (element == EL_INVISIBLE_STEELWALL ||
6266              element == EL_INVISIBLE_WALL ||
6267              element == EL_INVISIBLE_SAND)
6268     {
6269       if (game.lenses_time_left > 0)
6270         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6271
6272       DrawLevelField(x, y);
6273
6274       /* uncrumble neighbour fields, if needed */
6275       if (element == EL_INVISIBLE_SAND)
6276         DrawLevelFieldCrumbledSandNeighbours(x, y);
6277     }
6278     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6279              element == EL_INVISIBLE_WALL_ACTIVE ||
6280              element == EL_INVISIBLE_SAND_ACTIVE)
6281     {
6282       if (game.lenses_time_left == 0)
6283         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6284
6285       DrawLevelField(x, y);
6286
6287       /* re-crumble neighbour fields, if needed */
6288       if (element == EL_INVISIBLE_SAND)
6289         DrawLevelFieldCrumbledSandNeighbours(x, y);
6290     }
6291   }
6292 }
6293
6294 static void RedrawAllInvisibleElementsForMagnifier()
6295 {
6296   int x, y;
6297
6298   SCAN_PLAYFIELD(x, y)
6299   {
6300     int element = Feld[x][y];
6301
6302     if (element == EL_EMC_FAKE_GRASS &&
6303         game.magnify_time_left > 0)
6304     {
6305       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6306       DrawLevelField(x, y);
6307     }
6308     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6309              game.magnify_time_left == 0)
6310     {
6311       Feld[x][y] = EL_EMC_FAKE_GRASS;
6312       DrawLevelField(x, y);
6313     }
6314     else if (IS_GATE_GRAY(element) &&
6315              game.magnify_time_left > 0)
6316     {
6317       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6318                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6319                     IS_EM_GATE_GRAY(element) ?
6320                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6321                     IS_EMC_GATE_GRAY(element) ?
6322                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6323                     element);
6324       DrawLevelField(x, y);
6325     }
6326     else if (IS_GATE_GRAY_ACTIVE(element) &&
6327              game.magnify_time_left == 0)
6328     {
6329       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6330                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6331                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6332                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6333                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6334                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6335                     element);
6336       DrawLevelField(x, y);
6337     }
6338   }
6339 }
6340
6341 static void ToggleLightSwitch(int x, int y)
6342 {
6343   int element = Feld[x][y];
6344
6345   game.light_time_left =
6346     (element == EL_LIGHT_SWITCH ?
6347      level.time_light * FRAMES_PER_SECOND : 0);
6348
6349   RedrawAllLightSwitchesAndInvisibleElements();
6350 }
6351
6352 static void ActivateTimegateSwitch(int x, int y)
6353 {
6354   int xx, yy;
6355
6356   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6357
6358   SCAN_PLAYFIELD(xx, yy)
6359   {
6360     int element = Feld[xx][yy];
6361
6362     if (element == EL_TIMEGATE_CLOSED ||
6363         element == EL_TIMEGATE_CLOSING)
6364     {
6365       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6366       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6367     }
6368
6369     /*
6370     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6371     {
6372       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6373       DrawLevelField(xx, yy);
6374     }
6375     */
6376
6377   }
6378
6379 #if 1
6380   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6381                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6382 #else
6383   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6384 #endif
6385 }
6386
6387 void Impact(int x, int y)
6388 {
6389   boolean last_line = (y == lev_fieldy - 1);
6390   boolean object_hit = FALSE;
6391   boolean impact = (last_line || object_hit);
6392   int element = Feld[x][y];
6393   int smashed = EL_STEELWALL;
6394
6395   if (!last_line)       /* check if element below was hit */
6396   {
6397     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6398       return;
6399
6400     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6401                                          MovDir[x][y + 1] != MV_DOWN ||
6402                                          MovPos[x][y + 1] <= TILEY / 2));
6403
6404     /* do not smash moving elements that left the smashed field in time */
6405     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6406         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6407       object_hit = FALSE;
6408
6409 #if USE_QUICKSAND_IMPACT_BUGFIX
6410     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6411     {
6412       RemoveMovingField(x, y + 1);
6413       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6414       Feld[x][y + 2] = EL_ROCK;
6415       DrawLevelField(x, y + 2);
6416
6417       object_hit = TRUE;
6418     }
6419
6420     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6421     {
6422       RemoveMovingField(x, y + 1);
6423       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6424       Feld[x][y + 2] = EL_ROCK;
6425       DrawLevelField(x, y + 2);
6426
6427       object_hit = TRUE;
6428     }
6429 #endif
6430
6431     if (object_hit)
6432       smashed = MovingOrBlocked2Element(x, y + 1);
6433
6434     impact = (last_line || object_hit);
6435   }
6436
6437   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6438   {
6439     SplashAcid(x, y + 1);
6440     return;
6441   }
6442
6443   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6444   /* only reset graphic animation if graphic really changes after impact */
6445   if (impact &&
6446       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6447   {
6448     ResetGfxAnimation(x, y);
6449     DrawLevelField(x, y);
6450   }
6451
6452   if (impact && CAN_EXPLODE_IMPACT(element))
6453   {
6454     Bang(x, y);
6455     return;
6456   }
6457   else if (impact && element == EL_PEARL &&
6458            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6459   {
6460     ResetGfxAnimation(x, y);
6461
6462     Feld[x][y] = EL_PEARL_BREAKING;
6463     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6464     return;
6465   }
6466   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6467   {
6468     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6469
6470     return;
6471   }
6472
6473   if (impact && element == EL_AMOEBA_DROP)
6474   {
6475     if (object_hit && IS_PLAYER(x, y + 1))
6476       KillPlayerUnlessEnemyProtected(x, y + 1);
6477     else if (object_hit && smashed == EL_PENGUIN)
6478       Bang(x, y + 1);
6479     else
6480     {
6481       Feld[x][y] = EL_AMOEBA_GROWING;
6482       Store[x][y] = EL_AMOEBA_WET;
6483
6484       ResetRandomAnimationValue(x, y);
6485     }
6486     return;
6487   }
6488
6489   if (object_hit)               /* check which object was hit */
6490   {
6491     if ((CAN_PASS_MAGIC_WALL(element) && 
6492          (smashed == EL_MAGIC_WALL ||
6493           smashed == EL_BD_MAGIC_WALL)) ||
6494         (CAN_PASS_DC_MAGIC_WALL(element) &&
6495          smashed == EL_DC_MAGIC_WALL))
6496     {
6497       int xx, yy;
6498       int activated_magic_wall =
6499         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6500          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6501          EL_DC_MAGIC_WALL_ACTIVE);
6502
6503       /* activate magic wall / mill */
6504       SCAN_PLAYFIELD(xx, yy)
6505       {
6506         if (Feld[xx][yy] == smashed)
6507           Feld[xx][yy] = activated_magic_wall;
6508       }
6509
6510       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6511       game.magic_wall_active = TRUE;
6512
6513       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6514                             SND_MAGIC_WALL_ACTIVATING :
6515                             smashed == EL_BD_MAGIC_WALL ?
6516                             SND_BD_MAGIC_WALL_ACTIVATING :
6517                             SND_DC_MAGIC_WALL_ACTIVATING));
6518     }
6519
6520     if (IS_PLAYER(x, y + 1))
6521     {
6522       if (CAN_SMASH_PLAYER(element))
6523       {
6524         KillPlayerUnlessEnemyProtected(x, y + 1);
6525         return;
6526       }
6527     }
6528     else if (smashed == EL_PENGUIN)
6529     {
6530       if (CAN_SMASH_PLAYER(element))
6531       {
6532         Bang(x, y + 1);
6533         return;
6534       }
6535     }
6536     else if (element == EL_BD_DIAMOND)
6537     {
6538       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6539       {
6540         Bang(x, y + 1);
6541         return;
6542       }
6543     }
6544     else if (((element == EL_SP_INFOTRON ||
6545                element == EL_SP_ZONK) &&
6546               (smashed == EL_SP_SNIKSNAK ||
6547                smashed == EL_SP_ELECTRON ||
6548                smashed == EL_SP_DISK_ORANGE)) ||
6549              (element == EL_SP_INFOTRON &&
6550               smashed == EL_SP_DISK_YELLOW))
6551     {
6552       Bang(x, y + 1);
6553       return;
6554     }
6555     else if (CAN_SMASH_EVERYTHING(element))
6556     {
6557       if (IS_CLASSIC_ENEMY(smashed) ||
6558           CAN_EXPLODE_SMASHED(smashed))
6559       {
6560         Bang(x, y + 1);
6561         return;
6562       }
6563       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6564       {
6565         if (smashed == EL_LAMP ||
6566             smashed == EL_LAMP_ACTIVE)
6567         {
6568           Bang(x, y + 1);
6569           return;
6570         }
6571         else if (smashed == EL_NUT)
6572         {
6573           Feld[x][y + 1] = EL_NUT_BREAKING;
6574           PlayLevelSound(x, y, SND_NUT_BREAKING);
6575           RaiseScoreElement(EL_NUT);
6576           return;
6577         }
6578         else if (smashed == EL_PEARL)
6579         {
6580           ResetGfxAnimation(x, y);
6581
6582           Feld[x][y + 1] = EL_PEARL_BREAKING;
6583           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6584           return;
6585         }
6586         else if (smashed == EL_DIAMOND)
6587         {
6588           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6589           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6590           return;
6591         }
6592         else if (IS_BELT_SWITCH(smashed))
6593         {
6594           ToggleBeltSwitch(x, y + 1);
6595         }
6596         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6597                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6598                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6599                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6600         {
6601           ToggleSwitchgateSwitch(x, y + 1);
6602         }
6603         else if (smashed == EL_LIGHT_SWITCH ||
6604                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6605         {
6606           ToggleLightSwitch(x, y + 1);
6607         }
6608         else
6609         {
6610 #if 0
6611           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6612 #endif
6613
6614           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6615
6616           CheckElementChangeBySide(x, y + 1, smashed, element,
6617                                    CE_SWITCHED, CH_SIDE_TOP);
6618           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6619                                             CH_SIDE_TOP);
6620         }
6621       }
6622       else
6623       {
6624         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6625       }
6626     }
6627   }
6628
6629   /* play sound of magic wall / mill */
6630   if (!last_line &&
6631       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6632        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6633        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6634   {
6635     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6636       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6637     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6638       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6639     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6640       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6641
6642     return;
6643   }
6644
6645   /* play sound of object that hits the ground */
6646   if (last_line || object_hit)
6647     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6648 }
6649
6650 inline static void TurnRoundExt(int x, int y)
6651 {
6652   static struct
6653   {
6654     int dx, dy;
6655   } move_xy[] =
6656   {
6657     {  0,  0 },
6658     { -1,  0 },
6659     { +1,  0 },
6660     {  0,  0 },
6661     {  0, -1 },
6662     {  0,  0 }, { 0, 0 }, { 0, 0 },
6663     {  0, +1 }
6664   };
6665   static struct
6666   {
6667     int left, right, back;
6668   } turn[] =
6669   {
6670     { 0,        0,              0        },
6671     { MV_DOWN,  MV_UP,          MV_RIGHT },
6672     { MV_UP,    MV_DOWN,        MV_LEFT  },
6673     { 0,        0,              0        },
6674     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6675     { 0,        0,              0        },
6676     { 0,        0,              0        },
6677     { 0,        0,              0        },
6678     { MV_RIGHT, MV_LEFT,        MV_UP    }
6679   };
6680
6681   int element = Feld[x][y];
6682   int move_pattern = element_info[element].move_pattern;
6683
6684   int old_move_dir = MovDir[x][y];
6685   int left_dir  = turn[old_move_dir].left;
6686   int right_dir = turn[old_move_dir].right;
6687   int back_dir  = turn[old_move_dir].back;
6688
6689   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6690   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6691   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6692   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6693
6694   int left_x  = x + left_dx,  left_y  = y + left_dy;
6695   int right_x = x + right_dx, right_y = y + right_dy;
6696   int move_x  = x + move_dx,  move_y  = y + move_dy;
6697
6698   int xx, yy;
6699
6700   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6701   {
6702     TestIfBadThingTouchesOtherBadThing(x, y);
6703
6704     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6705       MovDir[x][y] = right_dir;
6706     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6707       MovDir[x][y] = left_dir;
6708
6709     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6710       MovDelay[x][y] = 9;
6711     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6712       MovDelay[x][y] = 1;
6713   }
6714   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6715   {
6716     TestIfBadThingTouchesOtherBadThing(x, y);
6717
6718     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6719       MovDir[x][y] = left_dir;
6720     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6721       MovDir[x][y] = right_dir;
6722
6723     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6724       MovDelay[x][y] = 9;
6725     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6726       MovDelay[x][y] = 1;
6727   }
6728   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6729   {
6730     TestIfBadThingTouchesOtherBadThing(x, y);
6731
6732     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6733       MovDir[x][y] = left_dir;
6734     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6735       MovDir[x][y] = right_dir;
6736
6737     if (MovDir[x][y] != old_move_dir)
6738       MovDelay[x][y] = 9;
6739   }
6740   else if (element == EL_YAMYAM)
6741   {
6742     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6743     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6744
6745     if (can_turn_left && can_turn_right)
6746       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6747     else if (can_turn_left)
6748       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6749     else if (can_turn_right)
6750       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6751     else
6752       MovDir[x][y] = back_dir;
6753
6754     MovDelay[x][y] = 16 + 16 * RND(3);
6755   }
6756   else if (element == EL_DARK_YAMYAM)
6757   {
6758     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6759                                                          left_x, left_y);
6760     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6761                                                          right_x, right_y);
6762
6763     if (can_turn_left && can_turn_right)
6764       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6765     else if (can_turn_left)
6766       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6767     else if (can_turn_right)
6768       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6769     else
6770       MovDir[x][y] = back_dir;
6771
6772     MovDelay[x][y] = 16 + 16 * RND(3);
6773   }
6774   else if (element == EL_PACMAN)
6775   {
6776     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6777     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6778
6779     if (can_turn_left && can_turn_right)
6780       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6781     else if (can_turn_left)
6782       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6783     else if (can_turn_right)
6784       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6785     else
6786       MovDir[x][y] = back_dir;
6787
6788     MovDelay[x][y] = 6 + RND(40);
6789   }
6790   else if (element == EL_PIG)
6791   {
6792     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6793     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6794     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6795     boolean should_turn_left, should_turn_right, should_move_on;
6796     int rnd_value = 24;
6797     int rnd = RND(rnd_value);
6798
6799     should_turn_left = (can_turn_left &&
6800                         (!can_move_on ||
6801                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6802                                                    y + back_dy + left_dy)));
6803     should_turn_right = (can_turn_right &&
6804                          (!can_move_on ||
6805                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6806                                                     y + back_dy + right_dy)));
6807     should_move_on = (can_move_on &&
6808                       (!can_turn_left ||
6809                        !can_turn_right ||
6810                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6811                                                  y + move_dy + left_dy) ||
6812                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6813                                                  y + move_dy + right_dy)));
6814
6815     if (should_turn_left || should_turn_right || should_move_on)
6816     {
6817       if (should_turn_left && should_turn_right && should_move_on)
6818         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6819                         rnd < 2 * rnd_value / 3 ? right_dir :
6820                         old_move_dir);
6821       else if (should_turn_left && should_turn_right)
6822         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6823       else if (should_turn_left && should_move_on)
6824         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6825       else if (should_turn_right && should_move_on)
6826         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6827       else if (should_turn_left)
6828         MovDir[x][y] = left_dir;
6829       else if (should_turn_right)
6830         MovDir[x][y] = right_dir;
6831       else if (should_move_on)
6832         MovDir[x][y] = old_move_dir;
6833     }
6834     else if (can_move_on && rnd > rnd_value / 8)
6835       MovDir[x][y] = old_move_dir;
6836     else if (can_turn_left && can_turn_right)
6837       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6838     else if (can_turn_left && rnd > rnd_value / 8)
6839       MovDir[x][y] = left_dir;
6840     else if (can_turn_right && rnd > rnd_value/8)
6841       MovDir[x][y] = right_dir;
6842     else
6843       MovDir[x][y] = back_dir;
6844
6845     xx = x + move_xy[MovDir[x][y]].dx;
6846     yy = y + move_xy[MovDir[x][y]].dy;
6847
6848     if (!IN_LEV_FIELD(xx, yy) ||
6849         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6850       MovDir[x][y] = old_move_dir;
6851
6852     MovDelay[x][y] = 0;
6853   }
6854   else if (element == EL_DRAGON)
6855   {
6856     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6857     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6858     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6859     int rnd_value = 24;
6860     int rnd = RND(rnd_value);
6861
6862     if (can_move_on && rnd > rnd_value / 8)
6863       MovDir[x][y] = old_move_dir;
6864     else if (can_turn_left && can_turn_right)
6865       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6866     else if (can_turn_left && rnd > rnd_value / 8)
6867       MovDir[x][y] = left_dir;
6868     else if (can_turn_right && rnd > rnd_value / 8)
6869       MovDir[x][y] = right_dir;
6870     else
6871       MovDir[x][y] = back_dir;
6872
6873     xx = x + move_xy[MovDir[x][y]].dx;
6874     yy = y + move_xy[MovDir[x][y]].dy;
6875
6876     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6877       MovDir[x][y] = old_move_dir;
6878
6879     MovDelay[x][y] = 0;
6880   }
6881   else if (element == EL_MOLE)
6882   {
6883     boolean can_move_on =
6884       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6885                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6886                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6887     if (!can_move_on)
6888     {
6889       boolean can_turn_left =
6890         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6891                               IS_AMOEBOID(Feld[left_x][left_y])));
6892
6893       boolean can_turn_right =
6894         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6895                               IS_AMOEBOID(Feld[right_x][right_y])));
6896
6897       if (can_turn_left && can_turn_right)
6898         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6899       else if (can_turn_left)
6900         MovDir[x][y] = left_dir;
6901       else
6902         MovDir[x][y] = right_dir;
6903     }
6904
6905     if (MovDir[x][y] != old_move_dir)
6906       MovDelay[x][y] = 9;
6907   }
6908   else if (element == EL_BALLOON)
6909   {
6910     MovDir[x][y] = game.wind_direction;
6911     MovDelay[x][y] = 0;
6912   }
6913   else if (element == EL_SPRING)
6914   {
6915 #if USE_NEW_SPRING_BUMPER
6916     if (MovDir[x][y] & MV_HORIZONTAL)
6917     {
6918       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6919           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6920       {
6921         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6922         ResetGfxAnimation(move_x, move_y);
6923         DrawLevelField(move_x, move_y);
6924
6925         MovDir[x][y] = back_dir;
6926       }
6927       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6928                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6929         MovDir[x][y] = MV_NONE;
6930     }
6931 #else
6932     if (MovDir[x][y] & MV_HORIZONTAL &&
6933         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6934          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6935       MovDir[x][y] = MV_NONE;
6936 #endif
6937
6938     MovDelay[x][y] = 0;
6939   }
6940   else if (element == EL_ROBOT ||
6941            element == EL_SATELLITE ||
6942            element == EL_PENGUIN ||
6943            element == EL_EMC_ANDROID)
6944   {
6945     int attr_x = -1, attr_y = -1;
6946
6947     if (AllPlayersGone)
6948     {
6949       attr_x = ExitX;
6950       attr_y = ExitY;
6951     }
6952     else
6953     {
6954       int i;
6955
6956       for (i = 0; i < MAX_PLAYERS; i++)
6957       {
6958         struct PlayerInfo *player = &stored_player[i];
6959         int jx = player->jx, jy = player->jy;
6960
6961         if (!player->active)
6962           continue;
6963
6964         if (attr_x == -1 ||
6965             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6966         {
6967           attr_x = jx;
6968           attr_y = jy;
6969         }
6970       }
6971     }
6972
6973     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6974         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6975          game.engine_version < VERSION_IDENT(3,1,0,0)))
6976     {
6977       attr_x = ZX;
6978       attr_y = ZY;
6979     }
6980
6981     if (element == EL_PENGUIN)
6982     {
6983       int i;
6984       static int xy[4][2] =
6985       {
6986         { 0, -1 },
6987         { -1, 0 },
6988         { +1, 0 },
6989         { 0, +1 }
6990       };
6991
6992       for (i = 0; i < NUM_DIRECTIONS; i++)
6993       {
6994         int ex = x + xy[i][0];
6995         int ey = y + xy[i][1];
6996
6997         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6998                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6999                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7000                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7001         {
7002           attr_x = ex;
7003           attr_y = ey;
7004           break;
7005         }
7006       }
7007     }
7008
7009     MovDir[x][y] = MV_NONE;
7010     if (attr_x < x)
7011       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7012     else if (attr_x > x)
7013       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7014     if (attr_y < y)
7015       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7016     else if (attr_y > y)
7017       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7018
7019     if (element == EL_ROBOT)
7020     {
7021       int newx, newy;
7022
7023       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7024         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7025       Moving2Blocked(x, y, &newx, &newy);
7026
7027       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7028         MovDelay[x][y] = 8 + 8 * !RND(3);
7029       else
7030         MovDelay[x][y] = 16;
7031     }
7032     else if (element == EL_PENGUIN)
7033     {
7034       int newx, newy;
7035
7036       MovDelay[x][y] = 1;
7037
7038       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7039       {
7040         boolean first_horiz = RND(2);
7041         int new_move_dir = MovDir[x][y];
7042
7043         MovDir[x][y] =
7044           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7045         Moving2Blocked(x, y, &newx, &newy);
7046
7047         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7048           return;
7049
7050         MovDir[x][y] =
7051           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7052         Moving2Blocked(x, y, &newx, &newy);
7053
7054         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7055           return;
7056
7057         MovDir[x][y] = old_move_dir;
7058         return;
7059       }
7060     }
7061     else if (element == EL_SATELLITE)
7062     {
7063       int newx, newy;
7064
7065       MovDelay[x][y] = 1;
7066
7067       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7068       {
7069         boolean first_horiz = RND(2);
7070         int new_move_dir = MovDir[x][y];
7071
7072         MovDir[x][y] =
7073           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7074         Moving2Blocked(x, y, &newx, &newy);
7075
7076         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7077           return;
7078
7079         MovDir[x][y] =
7080           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7081         Moving2Blocked(x, y, &newx, &newy);
7082
7083         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7084           return;
7085
7086         MovDir[x][y] = old_move_dir;
7087         return;
7088       }
7089     }
7090     else if (element == EL_EMC_ANDROID)
7091     {
7092       static int check_pos[16] =
7093       {
7094         -1,             /*  0 => (invalid)          */
7095         7,              /*  1 => MV_LEFT            */
7096         3,              /*  2 => MV_RIGHT           */
7097         -1,             /*  3 => (invalid)          */
7098         1,              /*  4 =>            MV_UP   */
7099         0,              /*  5 => MV_LEFT  | MV_UP   */
7100         2,              /*  6 => MV_RIGHT | MV_UP   */
7101         -1,             /*  7 => (invalid)          */
7102         5,              /*  8 =>            MV_DOWN */
7103         6,              /*  9 => MV_LEFT  | MV_DOWN */
7104         4,              /* 10 => MV_RIGHT | MV_DOWN */
7105         -1,             /* 11 => (invalid)          */
7106         -1,             /* 12 => (invalid)          */
7107         -1,             /* 13 => (invalid)          */
7108         -1,             /* 14 => (invalid)          */
7109         -1,             /* 15 => (invalid)          */
7110       };
7111       static struct
7112       {
7113         int dx, dy;
7114         int dir;
7115       } check_xy[8] =
7116       {
7117         { -1, -1,       MV_LEFT  | MV_UP   },
7118         {  0, -1,                  MV_UP   },
7119         { +1, -1,       MV_RIGHT | MV_UP   },
7120         { +1,  0,       MV_RIGHT           },
7121         { +1, +1,       MV_RIGHT | MV_DOWN },
7122         {  0, +1,                  MV_DOWN },
7123         { -1, +1,       MV_LEFT  | MV_DOWN },
7124         { -1,  0,       MV_LEFT            },
7125       };
7126       int start_pos, check_order;
7127       boolean can_clone = FALSE;
7128       int i;
7129
7130       /* check if there is any free field around current position */
7131       for (i = 0; i < 8; i++)
7132       {
7133         int newx = x + check_xy[i].dx;
7134         int newy = y + check_xy[i].dy;
7135
7136         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7137         {
7138           can_clone = TRUE;
7139
7140           break;
7141         }
7142       }
7143
7144       if (can_clone)            /* randomly find an element to clone */
7145       {
7146         can_clone = FALSE;
7147
7148         start_pos = check_pos[RND(8)];
7149         check_order = (RND(2) ? -1 : +1);
7150
7151         for (i = 0; i < 8; i++)
7152         {
7153           int pos_raw = start_pos + i * check_order;
7154           int pos = (pos_raw + 8) % 8;
7155           int newx = x + check_xy[pos].dx;
7156           int newy = y + check_xy[pos].dy;
7157
7158           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7159           {
7160             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7161             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7162
7163             Store[x][y] = Feld[newx][newy];
7164
7165             can_clone = TRUE;
7166
7167             break;
7168           }
7169         }
7170       }
7171
7172       if (can_clone)            /* randomly find a direction to move */
7173       {
7174         can_clone = FALSE;
7175
7176         start_pos = check_pos[RND(8)];
7177         check_order = (RND(2) ? -1 : +1);
7178
7179         for (i = 0; i < 8; i++)
7180         {
7181           int pos_raw = start_pos + i * check_order;
7182           int pos = (pos_raw + 8) % 8;
7183           int newx = x + check_xy[pos].dx;
7184           int newy = y + check_xy[pos].dy;
7185           int new_move_dir = check_xy[pos].dir;
7186
7187           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7188           {
7189             MovDir[x][y] = new_move_dir;
7190             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7191
7192             can_clone = TRUE;
7193
7194             break;
7195           }
7196         }
7197       }
7198
7199       if (can_clone)            /* cloning and moving successful */
7200         return;
7201
7202       /* cannot clone -- try to move towards player */
7203
7204       start_pos = check_pos[MovDir[x][y] & 0x0f];
7205       check_order = (RND(2) ? -1 : +1);
7206
7207       for (i = 0; i < 3; i++)
7208       {
7209         /* first check start_pos, then previous/next or (next/previous) pos */
7210         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7211         int pos = (pos_raw + 8) % 8;
7212         int newx = x + check_xy[pos].dx;
7213         int newy = y + check_xy[pos].dy;
7214         int new_move_dir = check_xy[pos].dir;
7215
7216         if (IS_PLAYER(newx, newy))
7217           break;
7218
7219         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7220         {
7221           MovDir[x][y] = new_move_dir;
7222           MovDelay[x][y] = level.android_move_time * 8 + 1;
7223
7224           break;
7225         }
7226       }
7227     }
7228   }
7229   else if (move_pattern == MV_TURNING_LEFT ||
7230            move_pattern == MV_TURNING_RIGHT ||
7231            move_pattern == MV_TURNING_LEFT_RIGHT ||
7232            move_pattern == MV_TURNING_RIGHT_LEFT ||
7233            move_pattern == MV_TURNING_RANDOM ||
7234            move_pattern == MV_ALL_DIRECTIONS)
7235   {
7236     boolean can_turn_left =
7237       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7238     boolean can_turn_right =
7239       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7240
7241     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7242       return;
7243
7244     if (move_pattern == MV_TURNING_LEFT)
7245       MovDir[x][y] = left_dir;
7246     else if (move_pattern == MV_TURNING_RIGHT)
7247       MovDir[x][y] = right_dir;
7248     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7249       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7250     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7251       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7252     else if (move_pattern == MV_TURNING_RANDOM)
7253       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7254                       can_turn_right && !can_turn_left ? right_dir :
7255                       RND(2) ? left_dir : right_dir);
7256     else if (can_turn_left && can_turn_right)
7257       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7258     else if (can_turn_left)
7259       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7260     else if (can_turn_right)
7261       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7262     else
7263       MovDir[x][y] = back_dir;
7264
7265     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7266   }
7267   else if (move_pattern == MV_HORIZONTAL ||
7268            move_pattern == MV_VERTICAL)
7269   {
7270     if (move_pattern & old_move_dir)
7271       MovDir[x][y] = back_dir;
7272     else if (move_pattern == MV_HORIZONTAL)
7273       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7274     else if (move_pattern == MV_VERTICAL)
7275       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7276
7277     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7278   }
7279   else if (move_pattern & MV_ANY_DIRECTION)
7280   {
7281     MovDir[x][y] = move_pattern;
7282     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7283   }
7284   else if (move_pattern & MV_WIND_DIRECTION)
7285   {
7286     MovDir[x][y] = game.wind_direction;
7287     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7288   }
7289   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7290   {
7291     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7292       MovDir[x][y] = left_dir;
7293     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7294       MovDir[x][y] = right_dir;
7295
7296     if (MovDir[x][y] != old_move_dir)
7297       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7298   }
7299   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7300   {
7301     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7302       MovDir[x][y] = right_dir;
7303     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7304       MovDir[x][y] = left_dir;
7305
7306     if (MovDir[x][y] != old_move_dir)
7307       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7308   }
7309   else if (move_pattern == MV_TOWARDS_PLAYER ||
7310            move_pattern == MV_AWAY_FROM_PLAYER)
7311   {
7312     int attr_x = -1, attr_y = -1;
7313     int newx, newy;
7314     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7315
7316     if (AllPlayersGone)
7317     {
7318       attr_x = ExitX;
7319       attr_y = ExitY;
7320     }
7321     else
7322     {
7323       int i;
7324
7325       for (i = 0; i < MAX_PLAYERS; i++)
7326       {
7327         struct PlayerInfo *player = &stored_player[i];
7328         int jx = player->jx, jy = player->jy;
7329
7330         if (!player->active)
7331           continue;
7332
7333         if (attr_x == -1 ||
7334             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7335         {
7336           attr_x = jx;
7337           attr_y = jy;
7338         }
7339       }
7340     }
7341
7342     MovDir[x][y] = MV_NONE;
7343     if (attr_x < x)
7344       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7345     else if (attr_x > x)
7346       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7347     if (attr_y < y)
7348       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7349     else if (attr_y > y)
7350       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7351
7352     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7353
7354     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7355     {
7356       boolean first_horiz = RND(2);
7357       int new_move_dir = MovDir[x][y];
7358
7359       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7360       {
7361         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7362         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7363
7364         return;
7365       }
7366
7367       MovDir[x][y] =
7368         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7369       Moving2Blocked(x, y, &newx, &newy);
7370
7371       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7372         return;
7373
7374       MovDir[x][y] =
7375         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7376       Moving2Blocked(x, y, &newx, &newy);
7377
7378       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7379         return;
7380
7381       MovDir[x][y] = old_move_dir;
7382     }
7383   }
7384   else if (move_pattern == MV_WHEN_PUSHED ||
7385            move_pattern == MV_WHEN_DROPPED)
7386   {
7387     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7388       MovDir[x][y] = MV_NONE;
7389
7390     MovDelay[x][y] = 0;
7391   }
7392   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7393   {
7394     static int test_xy[7][2] =
7395     {
7396       { 0, -1 },
7397       { -1, 0 },
7398       { +1, 0 },
7399       { 0, +1 },
7400       { 0, -1 },
7401       { -1, 0 },
7402       { +1, 0 },
7403     };
7404     static int test_dir[7] =
7405     {
7406       MV_UP,
7407       MV_LEFT,
7408       MV_RIGHT,
7409       MV_DOWN,
7410       MV_UP,
7411       MV_LEFT,
7412       MV_RIGHT,
7413     };
7414     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7415     int move_preference = -1000000;     /* start with very low preference */
7416     int new_move_dir = MV_NONE;
7417     int start_test = RND(4);
7418     int i;
7419
7420     for (i = 0; i < NUM_DIRECTIONS; i++)
7421     {
7422       int move_dir = test_dir[start_test + i];
7423       int move_dir_preference;
7424
7425       xx = x + test_xy[start_test + i][0];
7426       yy = y + test_xy[start_test + i][1];
7427
7428       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7429           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7430       {
7431         new_move_dir = move_dir;
7432
7433         break;
7434       }
7435
7436       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7437         continue;
7438
7439       move_dir_preference = -1 * RunnerVisit[xx][yy];
7440       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7441         move_dir_preference = PlayerVisit[xx][yy];
7442
7443       if (move_dir_preference > move_preference)
7444       {
7445         /* prefer field that has not been visited for the longest time */
7446         move_preference = move_dir_preference;
7447         new_move_dir = move_dir;
7448       }
7449       else if (move_dir_preference == move_preference &&
7450                move_dir == old_move_dir)
7451       {
7452         /* prefer last direction when all directions are preferred equally */
7453         move_preference = move_dir_preference;
7454         new_move_dir = move_dir;
7455       }
7456     }
7457
7458     MovDir[x][y] = new_move_dir;
7459     if (old_move_dir != new_move_dir)
7460       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7461   }
7462 }
7463
7464 static void TurnRound(int x, int y)
7465 {
7466   int direction = MovDir[x][y];
7467
7468   TurnRoundExt(x, y);
7469
7470   GfxDir[x][y] = MovDir[x][y];
7471
7472   if (direction != MovDir[x][y])
7473     GfxFrame[x][y] = 0;
7474
7475   if (MovDelay[x][y])
7476     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7477
7478   ResetGfxFrame(x, y, FALSE);
7479 }
7480
7481 static boolean JustBeingPushed(int x, int y)
7482 {
7483   int i;
7484
7485   for (i = 0; i < MAX_PLAYERS; i++)
7486   {
7487     struct PlayerInfo *player = &stored_player[i];
7488
7489     if (player->active && player->is_pushing && player->MovPos)
7490     {
7491       int next_jx = player->jx + (player->jx - player->last_jx);
7492       int next_jy = player->jy + (player->jy - player->last_jy);
7493
7494       if (x == next_jx && y == next_jy)
7495         return TRUE;
7496     }
7497   }
7498
7499   return FALSE;
7500 }
7501
7502 void StartMoving(int x, int y)
7503 {
7504   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7505   int element = Feld[x][y];
7506
7507   if (Stop[x][y])
7508     return;
7509
7510   if (MovDelay[x][y] == 0)
7511     GfxAction[x][y] = ACTION_DEFAULT;
7512
7513   if (CAN_FALL(element) && y < lev_fieldy - 1)
7514   {
7515     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7516         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7517       if (JustBeingPushed(x, y))
7518         return;
7519
7520     if (element == EL_QUICKSAND_FULL)
7521     {
7522       if (IS_FREE(x, y + 1))
7523       {
7524         InitMovingField(x, y, MV_DOWN);
7525         started_moving = TRUE;
7526
7527         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7528 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7529         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7530           Store[x][y] = EL_ROCK;
7531 #else
7532         Store[x][y] = EL_ROCK;
7533 #endif
7534
7535         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7536       }
7537       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7538       {
7539         if (!MovDelay[x][y])
7540           MovDelay[x][y] = TILEY + 1;
7541
7542         if (MovDelay[x][y])
7543         {
7544           MovDelay[x][y]--;
7545           if (MovDelay[x][y])
7546             return;
7547         }
7548
7549         Feld[x][y] = EL_QUICKSAND_EMPTY;
7550         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7551         Store[x][y + 1] = Store[x][y];
7552         Store[x][y] = 0;
7553
7554         PlayLevelSoundAction(x, y, ACTION_FILLING);
7555       }
7556     }
7557     else if (element == EL_QUICKSAND_FAST_FULL)
7558     {
7559       if (IS_FREE(x, y + 1))
7560       {
7561         InitMovingField(x, y, MV_DOWN);
7562         started_moving = TRUE;
7563
7564         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7565 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7566         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7567           Store[x][y] = EL_ROCK;
7568 #else
7569         Store[x][y] = EL_ROCK;
7570 #endif
7571
7572         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7573       }
7574       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7575       {
7576         if (!MovDelay[x][y])
7577           MovDelay[x][y] = TILEY + 1;
7578
7579         if (MovDelay[x][y])
7580         {
7581           MovDelay[x][y]--;
7582           if (MovDelay[x][y])
7583             return;
7584         }
7585
7586         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7587         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7588         Store[x][y + 1] = Store[x][y];
7589         Store[x][y] = 0;
7590
7591         PlayLevelSoundAction(x, y, ACTION_FILLING);
7592       }
7593     }
7594     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7595              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7596     {
7597       InitMovingField(x, y, MV_DOWN);
7598       started_moving = TRUE;
7599
7600       Feld[x][y] = EL_QUICKSAND_FILLING;
7601       Store[x][y] = element;
7602
7603       PlayLevelSoundAction(x, y, ACTION_FILLING);
7604     }
7605     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7606              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7607     {
7608       InitMovingField(x, y, MV_DOWN);
7609       started_moving = TRUE;
7610
7611       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7612       Store[x][y] = element;
7613
7614       PlayLevelSoundAction(x, y, ACTION_FILLING);
7615     }
7616     else if (element == EL_MAGIC_WALL_FULL)
7617     {
7618       if (IS_FREE(x, y + 1))
7619       {
7620         InitMovingField(x, y, MV_DOWN);
7621         started_moving = TRUE;
7622
7623         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7624         Store[x][y] = EL_CHANGED(Store[x][y]);
7625       }
7626       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7627       {
7628         if (!MovDelay[x][y])
7629           MovDelay[x][y] = TILEY/4 + 1;
7630
7631         if (MovDelay[x][y])
7632         {
7633           MovDelay[x][y]--;
7634           if (MovDelay[x][y])
7635             return;
7636         }
7637
7638         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7639         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7640         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7641         Store[x][y] = 0;
7642       }
7643     }
7644     else if (element == EL_BD_MAGIC_WALL_FULL)
7645     {
7646       if (IS_FREE(x, y + 1))
7647       {
7648         InitMovingField(x, y, MV_DOWN);
7649         started_moving = TRUE;
7650
7651         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7652         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7653       }
7654       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7655       {
7656         if (!MovDelay[x][y])
7657           MovDelay[x][y] = TILEY/4 + 1;
7658
7659         if (MovDelay[x][y])
7660         {
7661           MovDelay[x][y]--;
7662           if (MovDelay[x][y])
7663             return;
7664         }
7665
7666         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7667         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7668         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7669         Store[x][y] = 0;
7670       }
7671     }
7672     else if (element == EL_DC_MAGIC_WALL_FULL)
7673     {
7674       if (IS_FREE(x, y + 1))
7675       {
7676         InitMovingField(x, y, MV_DOWN);
7677         started_moving = TRUE;
7678
7679         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7680         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7681       }
7682       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7683       {
7684         if (!MovDelay[x][y])
7685           MovDelay[x][y] = TILEY/4 + 1;
7686
7687         if (MovDelay[x][y])
7688         {
7689           MovDelay[x][y]--;
7690           if (MovDelay[x][y])
7691             return;
7692         }
7693
7694         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7695         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7696         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7697         Store[x][y] = 0;
7698       }
7699     }
7700     else if ((CAN_PASS_MAGIC_WALL(element) &&
7701               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7702                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7703              (CAN_PASS_DC_MAGIC_WALL(element) &&
7704               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7705
7706     {
7707       InitMovingField(x, y, MV_DOWN);
7708       started_moving = TRUE;
7709
7710       Feld[x][y] =
7711         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7712          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7713          EL_DC_MAGIC_WALL_FILLING);
7714       Store[x][y] = element;
7715     }
7716     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7717     {
7718       SplashAcid(x, y + 1);
7719
7720       InitMovingField(x, y, MV_DOWN);
7721       started_moving = TRUE;
7722
7723       Store[x][y] = EL_ACID;
7724     }
7725     else if (
7726 #if USE_FIX_IMPACT_COLLISION
7727              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7728               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7729 #else
7730              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7731               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7732 #endif
7733              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7734               CAN_FALL(element) && WasJustFalling[x][y] &&
7735               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7736
7737              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7738               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7739               (Feld[x][y + 1] == EL_BLOCKED)))
7740     {
7741       /* this is needed for a special case not covered by calling "Impact()"
7742          from "ContinueMoving()": if an element moves to a tile directly below
7743          another element which was just falling on that tile (which was empty
7744          in the previous frame), the falling element above would just stop
7745          instead of smashing the element below (in previous version, the above
7746          element was just checked for "moving" instead of "falling", resulting
7747          in incorrect smashes caused by horizontal movement of the above
7748          element; also, the case of the player being the element to smash was
7749          simply not covered here... :-/ ) */
7750
7751       CheckCollision[x][y] = 0;
7752       CheckImpact[x][y] = 0;
7753
7754       Impact(x, y);
7755     }
7756     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7757     {
7758       if (MovDir[x][y] == MV_NONE)
7759       {
7760         InitMovingField(x, y, MV_DOWN);
7761         started_moving = TRUE;
7762       }
7763     }
7764     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7765     {
7766       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7767         MovDir[x][y] = MV_DOWN;
7768
7769       InitMovingField(x, y, MV_DOWN);
7770       started_moving = TRUE;
7771     }
7772     else if (element == EL_AMOEBA_DROP)
7773     {
7774       Feld[x][y] = EL_AMOEBA_GROWING;
7775       Store[x][y] = EL_AMOEBA_WET;
7776     }
7777     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7778               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7779              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7780              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7781     {
7782       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7783                                 (IS_FREE(x - 1, y + 1) ||
7784                                  Feld[x - 1][y + 1] == EL_ACID));
7785       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7786                                 (IS_FREE(x + 1, y + 1) ||
7787                                  Feld[x + 1][y + 1] == EL_ACID));
7788       boolean can_fall_any  = (can_fall_left || can_fall_right);
7789       boolean can_fall_both = (can_fall_left && can_fall_right);
7790       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7791
7792 #if USE_NEW_ALL_SLIPPERY
7793       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7794       {
7795         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7796           can_fall_right = FALSE;
7797         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7798           can_fall_left = FALSE;
7799         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7800           can_fall_right = FALSE;
7801         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7802           can_fall_left = FALSE;
7803
7804         can_fall_any  = (can_fall_left || can_fall_right);
7805         can_fall_both = FALSE;
7806       }
7807 #else
7808       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7809       {
7810         if (slippery_type == SLIPPERY_ONLY_LEFT)
7811           can_fall_right = FALSE;
7812         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7813           can_fall_left = FALSE;
7814         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7815           can_fall_right = FALSE;
7816         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7817           can_fall_left = FALSE;
7818
7819         can_fall_any  = (can_fall_left || can_fall_right);
7820         can_fall_both = (can_fall_left && can_fall_right);
7821       }
7822 #endif
7823
7824 #if USE_NEW_ALL_SLIPPERY
7825 #else
7826 #if USE_NEW_SP_SLIPPERY
7827       /* !!! better use the same properties as for custom elements here !!! */
7828       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7829                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7830       {
7831         can_fall_right = FALSE;         /* slip down on left side */
7832         can_fall_both = FALSE;
7833       }
7834 #endif
7835 #endif
7836
7837 #if USE_NEW_ALL_SLIPPERY
7838       if (can_fall_both)
7839       {
7840         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7841           can_fall_right = FALSE;       /* slip down on left side */
7842         else
7843           can_fall_left = !(can_fall_right = RND(2));
7844
7845         can_fall_both = FALSE;
7846       }
7847 #else
7848       if (can_fall_both)
7849       {
7850         if (game.emulation == EMU_BOULDERDASH ||
7851             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7852           can_fall_right = FALSE;       /* slip down on left side */
7853         else
7854           can_fall_left = !(can_fall_right = RND(2));
7855
7856         can_fall_both = FALSE;
7857       }
7858 #endif
7859
7860       if (can_fall_any)
7861       {
7862         /* if not determined otherwise, prefer left side for slipping down */
7863         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7864         started_moving = TRUE;
7865       }
7866     }
7867 #if 0
7868     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7869 #else
7870     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7871 #endif
7872     {
7873       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7874       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7875       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7876       int belt_dir = game.belt_dir[belt_nr];
7877
7878       if ((belt_dir == MV_LEFT  && left_is_free) ||
7879           (belt_dir == MV_RIGHT && right_is_free))
7880       {
7881         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7882
7883         InitMovingField(x, y, belt_dir);
7884         started_moving = TRUE;
7885
7886         Pushed[x][y] = TRUE;
7887         Pushed[nextx][y] = TRUE;
7888
7889         GfxAction[x][y] = ACTION_DEFAULT;
7890       }
7891       else
7892       {
7893         MovDir[x][y] = 0;       /* if element was moving, stop it */
7894       }
7895     }
7896   }
7897
7898   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7899 #if 0
7900   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7901 #else
7902   if (CAN_MOVE(element) && !started_moving)
7903 #endif
7904   {
7905     int move_pattern = element_info[element].move_pattern;
7906     int newx, newy;
7907
7908 #if 0
7909 #if DEBUG
7910     if (MovDir[x][y] == MV_NONE)
7911     {
7912       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7913              x, y, element, element_info[element].token_name);
7914       printf("StartMoving(): This should never happen!\n");
7915     }
7916 #endif
7917 #endif
7918
7919     Moving2Blocked(x, y, &newx, &newy);
7920
7921     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7922       return;
7923
7924     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7925         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7926     {
7927       WasJustMoving[x][y] = 0;
7928       CheckCollision[x][y] = 0;
7929
7930       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7931
7932       if (Feld[x][y] != element)        /* element has changed */
7933         return;
7934     }
7935
7936     if (!MovDelay[x][y])        /* start new movement phase */
7937     {
7938       /* all objects that can change their move direction after each step
7939          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7940
7941       if (element != EL_YAMYAM &&
7942           element != EL_DARK_YAMYAM &&
7943           element != EL_PACMAN &&
7944           !(move_pattern & MV_ANY_DIRECTION) &&
7945           move_pattern != MV_TURNING_LEFT &&
7946           move_pattern != MV_TURNING_RIGHT &&
7947           move_pattern != MV_TURNING_LEFT_RIGHT &&
7948           move_pattern != MV_TURNING_RIGHT_LEFT &&
7949           move_pattern != MV_TURNING_RANDOM)
7950       {
7951         TurnRound(x, y);
7952
7953         if (MovDelay[x][y] && (element == EL_BUG ||
7954                                element == EL_SPACESHIP ||
7955                                element == EL_SP_SNIKSNAK ||
7956                                element == EL_SP_ELECTRON ||
7957                                element == EL_MOLE))
7958           DrawLevelField(x, y);
7959       }
7960     }
7961
7962     if (MovDelay[x][y])         /* wait some time before next movement */
7963     {
7964       MovDelay[x][y]--;
7965
7966       if (element == EL_ROBOT ||
7967           element == EL_YAMYAM ||
7968           element == EL_DARK_YAMYAM)
7969       {
7970         DrawLevelElementAnimationIfNeeded(x, y, element);
7971         PlayLevelSoundAction(x, y, ACTION_WAITING);
7972       }
7973       else if (element == EL_SP_ELECTRON)
7974         DrawLevelElementAnimationIfNeeded(x, y, element);
7975       else if (element == EL_DRAGON)
7976       {
7977         int i;
7978         int dir = MovDir[x][y];
7979         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7980         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7981         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7982                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7983                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7984                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7985         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7986
7987         GfxAction[x][y] = ACTION_ATTACKING;
7988
7989         if (IS_PLAYER(x, y))
7990           DrawPlayerField(x, y);
7991         else
7992           DrawLevelField(x, y);
7993
7994         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7995
7996         for (i = 1; i <= 3; i++)
7997         {
7998           int xx = x + i * dx;
7999           int yy = y + i * dy;
8000           int sx = SCREENX(xx);
8001           int sy = SCREENY(yy);
8002           int flame_graphic = graphic + (i - 1);
8003
8004           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8005             break;
8006
8007           if (MovDelay[x][y])
8008           {
8009             int flamed = MovingOrBlocked2Element(xx, yy);
8010
8011             /* !!! */
8012 #if 0
8013             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8014               Bang(xx, yy);
8015             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8016               RemoveMovingField(xx, yy);
8017             else
8018               RemoveField(xx, yy);
8019 #else
8020             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8021               Bang(xx, yy);
8022             else
8023               RemoveMovingField(xx, yy);
8024 #endif
8025
8026             ChangeDelay[xx][yy] = 0;
8027
8028             Feld[xx][yy] = EL_FLAMES;
8029
8030             if (IN_SCR_FIELD(sx, sy))
8031             {
8032               DrawLevelFieldCrumbledSand(xx, yy);
8033               DrawGraphic(sx, sy, flame_graphic, frame);
8034             }
8035           }
8036           else
8037           {
8038             if (Feld[xx][yy] == EL_FLAMES)
8039               Feld[xx][yy] = EL_EMPTY;
8040             DrawLevelField(xx, yy);
8041           }
8042         }
8043       }
8044
8045       if (MovDelay[x][y])       /* element still has to wait some time */
8046       {
8047         PlayLevelSoundAction(x, y, ACTION_WAITING);
8048
8049         return;
8050       }
8051     }
8052
8053     /* now make next step */
8054
8055     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8056
8057     if (DONT_COLLIDE_WITH(element) &&
8058         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8059         !PLAYER_ENEMY_PROTECTED(newx, newy))
8060     {
8061       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8062
8063       return;
8064     }
8065
8066     else if (CAN_MOVE_INTO_ACID(element) &&
8067              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8068              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8069              (MovDir[x][y] == MV_DOWN ||
8070               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8071     {
8072       SplashAcid(newx, newy);
8073       Store[x][y] = EL_ACID;
8074     }
8075     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8076     {
8077       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8078           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8079           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8080           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8081       {
8082         RemoveField(x, y);
8083         DrawLevelField(x, y);
8084
8085         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8086         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8087           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8088
8089         local_player->friends_still_needed--;
8090         if (!local_player->friends_still_needed &&
8091             !local_player->GameOver && AllPlayersGone)
8092           PlayerWins(local_player);
8093
8094         return;
8095       }
8096       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8097       {
8098         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8099           DrawLevelField(newx, newy);
8100         else
8101           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8102       }
8103       else if (!IS_FREE(newx, newy))
8104       {
8105         GfxAction[x][y] = ACTION_WAITING;
8106
8107         if (IS_PLAYER(x, y))
8108           DrawPlayerField(x, y);
8109         else
8110           DrawLevelField(x, y);
8111
8112         return;
8113       }
8114     }
8115     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8116     {
8117       if (IS_FOOD_PIG(Feld[newx][newy]))
8118       {
8119         if (IS_MOVING(newx, newy))
8120           RemoveMovingField(newx, newy);
8121         else
8122         {
8123           Feld[newx][newy] = EL_EMPTY;
8124           DrawLevelField(newx, newy);
8125         }
8126
8127         PlayLevelSound(x, y, SND_PIG_DIGGING);
8128       }
8129       else if (!IS_FREE(newx, newy))
8130       {
8131         if (IS_PLAYER(x, y))
8132           DrawPlayerField(x, y);
8133         else
8134           DrawLevelField(x, y);
8135
8136         return;
8137       }
8138     }
8139     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8140     {
8141       if (Store[x][y] != EL_EMPTY)
8142       {
8143         boolean can_clone = FALSE;
8144         int xx, yy;
8145
8146         /* check if element to clone is still there */
8147         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8148         {
8149           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8150           {
8151             can_clone = TRUE;
8152
8153             break;
8154           }
8155         }
8156
8157         /* cannot clone or target field not free anymore -- do not clone */
8158         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8159           Store[x][y] = EL_EMPTY;
8160       }
8161
8162       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8163       {
8164         if (IS_MV_DIAGONAL(MovDir[x][y]))
8165         {
8166           int diagonal_move_dir = MovDir[x][y];
8167           int stored = Store[x][y];
8168           int change_delay = 8;
8169           int graphic;
8170
8171           /* android is moving diagonally */
8172
8173           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8174
8175           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8176           GfxElement[x][y] = EL_EMC_ANDROID;
8177           GfxAction[x][y] = ACTION_SHRINKING;
8178           GfxDir[x][y] = diagonal_move_dir;
8179           ChangeDelay[x][y] = change_delay;
8180
8181           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8182                                    GfxDir[x][y]);
8183
8184           DrawLevelGraphicAnimation(x, y, graphic);
8185           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8186
8187           if (Feld[newx][newy] == EL_ACID)
8188           {
8189             SplashAcid(newx, newy);
8190
8191             return;
8192           }
8193
8194           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8195
8196           Store[newx][newy] = EL_EMC_ANDROID;
8197           GfxElement[newx][newy] = EL_EMC_ANDROID;
8198           GfxAction[newx][newy] = ACTION_GROWING;
8199           GfxDir[newx][newy] = diagonal_move_dir;
8200           ChangeDelay[newx][newy] = change_delay;
8201
8202           graphic = el_act_dir2img(GfxElement[newx][newy],
8203                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8204
8205           DrawLevelGraphicAnimation(newx, newy, graphic);
8206           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8207
8208           return;
8209         }
8210         else
8211         {
8212           Feld[newx][newy] = EL_EMPTY;
8213           DrawLevelField(newx, newy);
8214
8215           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8216         }
8217       }
8218       else if (!IS_FREE(newx, newy))
8219       {
8220 #if 0
8221         if (IS_PLAYER(x, y))
8222           DrawPlayerField(x, y);
8223         else
8224           DrawLevelField(x, y);
8225 #endif
8226
8227         return;
8228       }
8229     }
8230     else if (IS_CUSTOM_ELEMENT(element) &&
8231              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8232     {
8233       int new_element = Feld[newx][newy];
8234
8235       if (!IS_FREE(newx, newy))
8236       {
8237         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8238                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8239                       ACTION_BREAKING);
8240
8241         /* no element can dig solid indestructible elements */
8242         if (IS_INDESTRUCTIBLE(new_element) &&
8243             !IS_DIGGABLE(new_element) &&
8244             !IS_COLLECTIBLE(new_element))
8245           return;
8246
8247         if (AmoebaNr[newx][newy] &&
8248             (new_element == EL_AMOEBA_FULL ||
8249              new_element == EL_BD_AMOEBA ||
8250              new_element == EL_AMOEBA_GROWING))
8251         {
8252           AmoebaCnt[AmoebaNr[newx][newy]]--;
8253           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8254         }
8255
8256         if (IS_MOVING(newx, newy))
8257           RemoveMovingField(newx, newy);
8258         else
8259         {
8260           RemoveField(newx, newy);
8261           DrawLevelField(newx, newy);
8262         }
8263
8264         /* if digged element was about to explode, prevent the explosion */
8265         ExplodeField[newx][newy] = EX_TYPE_NONE;
8266
8267         PlayLevelSoundAction(x, y, action);
8268       }
8269
8270       Store[newx][newy] = EL_EMPTY;
8271 #if 1
8272       /* this makes it possible to leave the removed element again */
8273       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8274         Store[newx][newy] = new_element;
8275 #else
8276       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8277       {
8278         int move_leave_element = element_info[element].move_leave_element;
8279
8280         /* this makes it possible to leave the removed element again */
8281         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8282                              new_element : move_leave_element);
8283       }
8284 #endif
8285
8286       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8287       {
8288         RunnerVisit[x][y] = FrameCounter;
8289         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8290       }
8291     }
8292     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8293     {
8294       if (!IS_FREE(newx, newy))
8295       {
8296         if (IS_PLAYER(x, y))
8297           DrawPlayerField(x, y);
8298         else
8299           DrawLevelField(x, y);
8300
8301         return;
8302       }
8303       else
8304       {
8305         boolean wanna_flame = !RND(10);
8306         int dx = newx - x, dy = newy - y;
8307         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8308         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8309         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8310                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8311         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8312                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8313
8314         if ((wanna_flame ||
8315              IS_CLASSIC_ENEMY(element1) ||
8316              IS_CLASSIC_ENEMY(element2)) &&
8317             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8318             element1 != EL_FLAMES && element2 != EL_FLAMES)
8319         {
8320           ResetGfxAnimation(x, y);
8321           GfxAction[x][y] = ACTION_ATTACKING;
8322
8323           if (IS_PLAYER(x, y))
8324             DrawPlayerField(x, y);
8325           else
8326             DrawLevelField(x, y);
8327
8328           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8329
8330           MovDelay[x][y] = 50;
8331
8332           /* !!! */
8333 #if 0
8334           RemoveField(newx, newy);
8335 #endif
8336           Feld[newx][newy] = EL_FLAMES;
8337           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8338           {
8339 #if 0
8340             RemoveField(newx1, newy1);
8341 #endif
8342             Feld[newx1][newy1] = EL_FLAMES;
8343           }
8344           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8345           {
8346 #if 0
8347             RemoveField(newx2, newy2);
8348 #endif
8349             Feld[newx2][newy2] = EL_FLAMES;
8350           }
8351
8352           return;
8353         }
8354       }
8355     }
8356     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8357              Feld[newx][newy] == EL_DIAMOND)
8358     {
8359       if (IS_MOVING(newx, newy))
8360         RemoveMovingField(newx, newy);
8361       else
8362       {
8363         Feld[newx][newy] = EL_EMPTY;
8364         DrawLevelField(newx, newy);
8365       }
8366
8367       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8368     }
8369     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8370              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8371     {
8372       if (AmoebaNr[newx][newy])
8373       {
8374         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8375         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8376             Feld[newx][newy] == EL_BD_AMOEBA)
8377           AmoebaCnt[AmoebaNr[newx][newy]]--;
8378       }
8379
8380 #if 0
8381       /* !!! test !!! */
8382       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8383       {
8384         RemoveMovingField(newx, newy);
8385       }
8386 #else
8387       if (IS_MOVING(newx, newy))
8388       {
8389         RemoveMovingField(newx, newy);
8390       }
8391 #endif
8392       else
8393       {
8394         Feld[newx][newy] = EL_EMPTY;
8395         DrawLevelField(newx, newy);
8396       }
8397
8398       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8399     }
8400     else if ((element == EL_PACMAN || element == EL_MOLE)
8401              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8402     {
8403       if (AmoebaNr[newx][newy])
8404       {
8405         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8406         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8407             Feld[newx][newy] == EL_BD_AMOEBA)
8408           AmoebaCnt[AmoebaNr[newx][newy]]--;
8409       }
8410
8411       if (element == EL_MOLE)
8412       {
8413         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8414         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8415
8416         ResetGfxAnimation(x, y);
8417         GfxAction[x][y] = ACTION_DIGGING;
8418         DrawLevelField(x, y);
8419
8420         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8421
8422         return;                         /* wait for shrinking amoeba */
8423       }
8424       else      /* element == EL_PACMAN */
8425       {
8426         Feld[newx][newy] = EL_EMPTY;
8427         DrawLevelField(newx, newy);
8428         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8429       }
8430     }
8431     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8432              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8433               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8434     {
8435       /* wait for shrinking amoeba to completely disappear */
8436       return;
8437     }
8438     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8439     {
8440       /* object was running against a wall */
8441
8442       TurnRound(x, y);
8443
8444 #if 0
8445       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8446       if (move_pattern & MV_ANY_DIRECTION &&
8447           move_pattern == MovDir[x][y])
8448       {
8449         int blocking_element =
8450           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8451
8452         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8453                                  MovDir[x][y]);
8454
8455         element = Feld[x][y];   /* element might have changed */
8456       }
8457 #endif
8458
8459       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8460         DrawLevelElementAnimation(x, y, element);
8461
8462       if (DONT_TOUCH(element))
8463         TestIfBadThingTouchesPlayer(x, y);
8464
8465       return;
8466     }
8467
8468     InitMovingField(x, y, MovDir[x][y]);
8469
8470     PlayLevelSoundAction(x, y, ACTION_MOVING);
8471   }
8472
8473   if (MovDir[x][y])
8474     ContinueMoving(x, y);
8475 }
8476
8477 void ContinueMoving(int x, int y)
8478 {
8479   int element = Feld[x][y];
8480   struct ElementInfo *ei = &element_info[element];
8481   int direction = MovDir[x][y];
8482   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8483   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8484   int newx = x + dx, newy = y + dy;
8485   int stored = Store[x][y];
8486   int stored_new = Store[newx][newy];
8487   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8488   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8489   boolean last_line = (newy == lev_fieldy - 1);
8490
8491   MovPos[x][y] += getElementMoveStepsize(x, y);
8492
8493   if (pushed_by_player) /* special case: moving object pushed by player */
8494     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8495
8496   if (ABS(MovPos[x][y]) < TILEX)
8497   {
8498 #if 0
8499     int ee = Feld[x][y];
8500     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8501     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8502
8503     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8504            x, y, ABS(MovPos[x][y]),
8505            ee, gg, ff,
8506            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8507 #endif
8508
8509     DrawLevelField(x, y);
8510
8511     return;     /* element is still moving */
8512   }
8513
8514   /* element reached destination field */
8515
8516   Feld[x][y] = EL_EMPTY;
8517   Feld[newx][newy] = element;
8518   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8519
8520   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8521   {
8522     element = Feld[newx][newy] = EL_ACID;
8523   }
8524   else if (element == EL_MOLE)
8525   {
8526     Feld[x][y] = EL_SAND;
8527
8528     DrawLevelFieldCrumbledSandNeighbours(x, y);
8529   }
8530   else if (element == EL_QUICKSAND_FILLING)
8531   {
8532     element = Feld[newx][newy] = get_next_element(element);
8533     Store[newx][newy] = Store[x][y];
8534   }
8535   else if (element == EL_QUICKSAND_EMPTYING)
8536   {
8537     Feld[x][y] = get_next_element(element);
8538     element = Feld[newx][newy] = Store[x][y];
8539   }
8540   else if (element == EL_QUICKSAND_FAST_FILLING)
8541   {
8542     element = Feld[newx][newy] = get_next_element(element);
8543     Store[newx][newy] = Store[x][y];
8544   }
8545   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8546   {
8547     Feld[x][y] = get_next_element(element);
8548     element = Feld[newx][newy] = Store[x][y];
8549   }
8550   else if (element == EL_MAGIC_WALL_FILLING)
8551   {
8552     element = Feld[newx][newy] = get_next_element(element);
8553     if (!game.magic_wall_active)
8554       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8555     Store[newx][newy] = Store[x][y];
8556   }
8557   else if (element == EL_MAGIC_WALL_EMPTYING)
8558   {
8559     Feld[x][y] = get_next_element(element);
8560     if (!game.magic_wall_active)
8561       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8562     element = Feld[newx][newy] = Store[x][y];
8563
8564 #if USE_NEW_CUSTOM_VALUE
8565     InitField(newx, newy, FALSE);
8566 #endif
8567   }
8568   else if (element == EL_BD_MAGIC_WALL_FILLING)
8569   {
8570     element = Feld[newx][newy] = get_next_element(element);
8571     if (!game.magic_wall_active)
8572       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8573     Store[newx][newy] = Store[x][y];
8574   }
8575   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8576   {
8577     Feld[x][y] = get_next_element(element);
8578     if (!game.magic_wall_active)
8579       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8580     element = Feld[newx][newy] = Store[x][y];
8581
8582 #if USE_NEW_CUSTOM_VALUE
8583     InitField(newx, newy, FALSE);
8584 #endif
8585   }
8586   else if (element == EL_DC_MAGIC_WALL_FILLING)
8587   {
8588     element = Feld[newx][newy] = get_next_element(element);
8589     if (!game.magic_wall_active)
8590       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8591     Store[newx][newy] = Store[x][y];
8592   }
8593   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8594   {
8595     Feld[x][y] = get_next_element(element);
8596     if (!game.magic_wall_active)
8597       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8598     element = Feld[newx][newy] = Store[x][y];
8599
8600 #if USE_NEW_CUSTOM_VALUE
8601     InitField(newx, newy, FALSE);
8602 #endif
8603   }
8604   else if (element == EL_AMOEBA_DROPPING)
8605   {
8606     Feld[x][y] = get_next_element(element);
8607     element = Feld[newx][newy] = Store[x][y];
8608   }
8609   else if (element == EL_SOKOBAN_OBJECT)
8610   {
8611     if (Back[x][y])
8612       Feld[x][y] = Back[x][y];
8613
8614     if (Back[newx][newy])
8615       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8616
8617     Back[x][y] = Back[newx][newy] = 0;
8618   }
8619
8620   Store[x][y] = EL_EMPTY;
8621   MovPos[x][y] = 0;
8622   MovDir[x][y] = 0;
8623   MovDelay[x][y] = 0;
8624
8625   MovDelay[newx][newy] = 0;
8626
8627   if (CAN_CHANGE_OR_HAS_ACTION(element))
8628   {
8629     /* copy element change control values to new field */
8630     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8631     ChangePage[newx][newy]  = ChangePage[x][y];
8632     ChangeCount[newx][newy] = ChangeCount[x][y];
8633     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8634   }
8635
8636 #if USE_NEW_CUSTOM_VALUE
8637     CustomValue[newx][newy] = CustomValue[x][y];
8638 #endif
8639
8640   ChangeDelay[x][y] = 0;
8641   ChangePage[x][y] = -1;
8642   ChangeCount[x][y] = 0;
8643   ChangeEvent[x][y] = -1;
8644
8645 #if USE_NEW_CUSTOM_VALUE
8646   CustomValue[x][y] = 0;
8647 #endif
8648
8649   /* copy animation control values to new field */
8650   GfxFrame[newx][newy]  = GfxFrame[x][y];
8651   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8652   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8653   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8654
8655   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8656
8657   /* some elements can leave other elements behind after moving */
8658 #if 1
8659   if (ei->move_leave_element != EL_EMPTY &&
8660       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8661       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8662 #else
8663   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8664       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8665       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8666 #endif
8667   {
8668     int move_leave_element = ei->move_leave_element;
8669
8670 #if 1
8671 #if 1
8672     /* this makes it possible to leave the removed element again */
8673     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8674       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8675 #else
8676     /* this makes it possible to leave the removed element again */
8677     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8678       move_leave_element = stored;
8679 #endif
8680 #else
8681     /* this makes it possible to leave the removed element again */
8682     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8683         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8684       move_leave_element = stored;
8685 #endif
8686
8687     Feld[x][y] = move_leave_element;
8688
8689     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8690       MovDir[x][y] = direction;
8691
8692     InitField(x, y, FALSE);
8693
8694     if (GFX_CRUMBLED(Feld[x][y]))
8695       DrawLevelFieldCrumbledSandNeighbours(x, y);
8696
8697     if (ELEM_IS_PLAYER(move_leave_element))
8698       RelocatePlayer(x, y, move_leave_element);
8699   }
8700
8701   /* do this after checking for left-behind element */
8702   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8703
8704   if (!CAN_MOVE(element) ||
8705       (CAN_FALL(element) && direction == MV_DOWN &&
8706        (element == EL_SPRING ||
8707         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8708         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8709     GfxDir[x][y] = MovDir[newx][newy] = 0;
8710
8711   DrawLevelField(x, y);
8712   DrawLevelField(newx, newy);
8713
8714   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8715
8716   /* prevent pushed element from moving on in pushed direction */
8717   if (pushed_by_player && CAN_MOVE(element) &&
8718       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8719       !(element_info[element].move_pattern & direction))
8720     TurnRound(newx, newy);
8721
8722   /* prevent elements on conveyor belt from moving on in last direction */
8723   if (pushed_by_conveyor && CAN_FALL(element) &&
8724       direction & MV_HORIZONTAL)
8725     MovDir[newx][newy] = 0;
8726
8727   if (!pushed_by_player)
8728   {
8729     int nextx = newx + dx, nexty = newy + dy;
8730     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8731
8732     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8733
8734     if (CAN_FALL(element) && direction == MV_DOWN)
8735       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8736
8737     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8738       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8739
8740 #if USE_FIX_IMPACT_COLLISION
8741     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8742       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8743 #endif
8744   }
8745
8746   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8747   {
8748     TestIfBadThingTouchesPlayer(newx, newy);
8749     TestIfBadThingTouchesFriend(newx, newy);
8750
8751     if (!IS_CUSTOM_ELEMENT(element))
8752       TestIfBadThingTouchesOtherBadThing(newx, newy);
8753   }
8754   else if (element == EL_PENGUIN)
8755     TestIfFriendTouchesBadThing(newx, newy);
8756
8757   /* give the player one last chance (one more frame) to move away */
8758   if (CAN_FALL(element) && direction == MV_DOWN &&
8759       (last_line || (!IS_FREE(x, newy + 1) &&
8760                      (!IS_PLAYER(x, newy + 1) ||
8761                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8762     Impact(x, newy);
8763
8764   if (pushed_by_player && !game.use_change_when_pushing_bug)
8765   {
8766     int push_side = MV_DIR_OPPOSITE(direction);
8767     struct PlayerInfo *player = PLAYERINFO(x, y);
8768
8769     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8770                                player->index_bit, push_side);
8771     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8772                                         player->index_bit, push_side);
8773   }
8774
8775   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8776     MovDelay[newx][newy] = 1;
8777
8778   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8779
8780   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8781
8782 #if 0
8783   if (ChangePage[newx][newy] != -1)             /* delayed change */
8784   {
8785     int page = ChangePage[newx][newy];
8786     struct ElementChangeInfo *change = &ei->change_page[page];
8787
8788     ChangePage[newx][newy] = -1;
8789
8790     if (change->can_change)
8791     {
8792       if (ChangeElement(newx, newy, element, page))
8793       {
8794         if (change->post_change_function)
8795           change->post_change_function(newx, newy);
8796       }
8797     }
8798
8799     if (change->has_action)
8800       ExecuteCustomElementAction(newx, newy, element, page);
8801   }
8802 #endif
8803
8804   TestIfElementHitsCustomElement(newx, newy, direction);
8805   TestIfPlayerTouchesCustomElement(newx, newy);
8806   TestIfElementTouchesCustomElement(newx, newy);
8807
8808   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8809       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8810     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8811                              MV_DIR_OPPOSITE(direction));
8812 }
8813
8814 int AmoebeNachbarNr(int ax, int ay)
8815 {
8816   int i;
8817   int element = Feld[ax][ay];
8818   int group_nr = 0;
8819   static int xy[4][2] =
8820   {
8821     { 0, -1 },
8822     { -1, 0 },
8823     { +1, 0 },
8824     { 0, +1 }
8825   };
8826
8827   for (i = 0; i < NUM_DIRECTIONS; i++)
8828   {
8829     int x = ax + xy[i][0];
8830     int y = ay + xy[i][1];
8831
8832     if (!IN_LEV_FIELD(x, y))
8833       continue;
8834
8835     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8836       group_nr = AmoebaNr[x][y];
8837   }
8838
8839   return group_nr;
8840 }
8841
8842 void AmoebenVereinigen(int ax, int ay)
8843 {
8844   int i, x, y, xx, yy;
8845   int new_group_nr = AmoebaNr[ax][ay];
8846   static int xy[4][2] =
8847   {
8848     { 0, -1 },
8849     { -1, 0 },
8850     { +1, 0 },
8851     { 0, +1 }
8852   };
8853
8854   if (new_group_nr == 0)
8855     return;
8856
8857   for (i = 0; i < NUM_DIRECTIONS; i++)
8858   {
8859     x = ax + xy[i][0];
8860     y = ay + xy[i][1];
8861
8862     if (!IN_LEV_FIELD(x, y))
8863       continue;
8864
8865     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8866          Feld[x][y] == EL_BD_AMOEBA ||
8867          Feld[x][y] == EL_AMOEBA_DEAD) &&
8868         AmoebaNr[x][y] != new_group_nr)
8869     {
8870       int old_group_nr = AmoebaNr[x][y];
8871
8872       if (old_group_nr == 0)
8873         return;
8874
8875       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8876       AmoebaCnt[old_group_nr] = 0;
8877       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8878       AmoebaCnt2[old_group_nr] = 0;
8879
8880       SCAN_PLAYFIELD(xx, yy)
8881       {
8882         if (AmoebaNr[xx][yy] == old_group_nr)
8883           AmoebaNr[xx][yy] = new_group_nr;
8884       }
8885     }
8886   }
8887 }
8888
8889 void AmoebeUmwandeln(int ax, int ay)
8890 {
8891   int i, x, y;
8892
8893   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8894   {
8895     int group_nr = AmoebaNr[ax][ay];
8896
8897 #ifdef DEBUG
8898     if (group_nr == 0)
8899     {
8900       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8901       printf("AmoebeUmwandeln(): This should never happen!\n");
8902       return;
8903     }
8904 #endif
8905
8906     SCAN_PLAYFIELD(x, y)
8907     {
8908       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8909       {
8910         AmoebaNr[x][y] = 0;
8911         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8912       }
8913     }
8914
8915     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8916                             SND_AMOEBA_TURNING_TO_GEM :
8917                             SND_AMOEBA_TURNING_TO_ROCK));
8918     Bang(ax, ay);
8919   }
8920   else
8921   {
8922     static int xy[4][2] =
8923     {
8924       { 0, -1 },
8925       { -1, 0 },
8926       { +1, 0 },
8927       { 0, +1 }
8928     };
8929
8930     for (i = 0; i < NUM_DIRECTIONS; i++)
8931     {
8932       x = ax + xy[i][0];
8933       y = ay + xy[i][1];
8934
8935       if (!IN_LEV_FIELD(x, y))
8936         continue;
8937
8938       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8939       {
8940         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8941                               SND_AMOEBA_TURNING_TO_GEM :
8942                               SND_AMOEBA_TURNING_TO_ROCK));
8943         Bang(x, y);
8944       }
8945     }
8946   }
8947 }
8948
8949 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8950 {
8951   int x, y;
8952   int group_nr = AmoebaNr[ax][ay];
8953   boolean done = FALSE;
8954
8955 #ifdef DEBUG
8956   if (group_nr == 0)
8957   {
8958     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8959     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8960     return;
8961   }
8962 #endif
8963
8964   SCAN_PLAYFIELD(x, y)
8965   {
8966     if (AmoebaNr[x][y] == group_nr &&
8967         (Feld[x][y] == EL_AMOEBA_DEAD ||
8968          Feld[x][y] == EL_BD_AMOEBA ||
8969          Feld[x][y] == EL_AMOEBA_GROWING))
8970     {
8971       AmoebaNr[x][y] = 0;
8972       Feld[x][y] = new_element;
8973       InitField(x, y, FALSE);
8974       DrawLevelField(x, y);
8975       done = TRUE;
8976     }
8977   }
8978
8979   if (done)
8980     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8981                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8982                             SND_BD_AMOEBA_TURNING_TO_GEM));
8983 }
8984
8985 void AmoebeWaechst(int x, int y)
8986 {
8987   static unsigned long sound_delay = 0;
8988   static unsigned long sound_delay_value = 0;
8989
8990   if (!MovDelay[x][y])          /* start new growing cycle */
8991   {
8992     MovDelay[x][y] = 7;
8993
8994     if (DelayReached(&sound_delay, sound_delay_value))
8995     {
8996       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8997       sound_delay_value = 30;
8998     }
8999   }
9000
9001   if (MovDelay[x][y])           /* wait some time before growing bigger */
9002   {
9003     MovDelay[x][y]--;
9004     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9005     {
9006       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9007                                            6 - MovDelay[x][y]);
9008
9009       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9010     }
9011
9012     if (!MovDelay[x][y])
9013     {
9014       Feld[x][y] = Store[x][y];
9015       Store[x][y] = 0;
9016       DrawLevelField(x, y);
9017     }
9018   }
9019 }
9020
9021 void AmoebaDisappearing(int x, int y)
9022 {
9023   static unsigned long sound_delay = 0;
9024   static unsigned long sound_delay_value = 0;
9025
9026   if (!MovDelay[x][y])          /* start new shrinking cycle */
9027   {
9028     MovDelay[x][y] = 7;
9029
9030     if (DelayReached(&sound_delay, sound_delay_value))
9031       sound_delay_value = 30;
9032   }
9033
9034   if (MovDelay[x][y])           /* wait some time before shrinking */
9035   {
9036     MovDelay[x][y]--;
9037     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9038     {
9039       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9040                                            6 - MovDelay[x][y]);
9041
9042       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9043     }
9044
9045     if (!MovDelay[x][y])
9046     {
9047       Feld[x][y] = EL_EMPTY;
9048       DrawLevelField(x, y);
9049
9050       /* don't let mole enter this field in this cycle;
9051          (give priority to objects falling to this field from above) */
9052       Stop[x][y] = TRUE;
9053     }
9054   }
9055 }
9056
9057 void AmoebeAbleger(int ax, int ay)
9058 {
9059   int i;
9060   int element = Feld[ax][ay];
9061   int graphic = el2img(element);
9062   int newax = ax, neway = ay;
9063   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9064   static int xy[4][2] =
9065   {
9066     { 0, -1 },
9067     { -1, 0 },
9068     { +1, 0 },
9069     { 0, +1 }
9070   };
9071
9072   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9073   {
9074     Feld[ax][ay] = EL_AMOEBA_DEAD;
9075     DrawLevelField(ax, ay);
9076     return;
9077   }
9078
9079   if (IS_ANIMATED(graphic))
9080     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9081
9082   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9083     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9084
9085   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9086   {
9087     MovDelay[ax][ay]--;
9088     if (MovDelay[ax][ay])
9089       return;
9090   }
9091
9092   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9093   {
9094     int start = RND(4);
9095     int x = ax + xy[start][0];
9096     int y = ay + xy[start][1];
9097
9098     if (!IN_LEV_FIELD(x, y))
9099       return;
9100
9101     if (IS_FREE(x, y) ||
9102         CAN_GROW_INTO(Feld[x][y]) ||
9103         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9104         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9105     {
9106       newax = x;
9107       neway = y;
9108     }
9109
9110     if (newax == ax && neway == ay)
9111       return;
9112   }
9113   else                          /* normal or "filled" (BD style) amoeba */
9114   {
9115     int start = RND(4);
9116     boolean waiting_for_player = FALSE;
9117
9118     for (i = 0; i < NUM_DIRECTIONS; i++)
9119     {
9120       int j = (start + i) % 4;
9121       int x = ax + xy[j][0];
9122       int y = ay + xy[j][1];
9123
9124       if (!IN_LEV_FIELD(x, y))
9125         continue;
9126
9127       if (IS_FREE(x, y) ||
9128           CAN_GROW_INTO(Feld[x][y]) ||
9129           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9130           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9131       {
9132         newax = x;
9133         neway = y;
9134         break;
9135       }
9136       else if (IS_PLAYER(x, y))
9137         waiting_for_player = TRUE;
9138     }
9139
9140     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9141     {
9142       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9143       {
9144         Feld[ax][ay] = EL_AMOEBA_DEAD;
9145         DrawLevelField(ax, ay);
9146         AmoebaCnt[AmoebaNr[ax][ay]]--;
9147
9148         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9149         {
9150           if (element == EL_AMOEBA_FULL)
9151             AmoebeUmwandeln(ax, ay);
9152           else if (element == EL_BD_AMOEBA)
9153             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9154         }
9155       }
9156       return;
9157     }
9158     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9159     {
9160       /* amoeba gets larger by growing in some direction */
9161
9162       int new_group_nr = AmoebaNr[ax][ay];
9163
9164 #ifdef DEBUG
9165   if (new_group_nr == 0)
9166   {
9167     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9168     printf("AmoebeAbleger(): This should never happen!\n");
9169     return;
9170   }
9171 #endif
9172
9173       AmoebaNr[newax][neway] = new_group_nr;
9174       AmoebaCnt[new_group_nr]++;
9175       AmoebaCnt2[new_group_nr]++;
9176
9177       /* if amoeba touches other amoeba(s) after growing, unify them */
9178       AmoebenVereinigen(newax, neway);
9179
9180       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9181       {
9182         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9183         return;
9184       }
9185     }
9186   }
9187
9188   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9189       (neway == lev_fieldy - 1 && newax != ax))
9190   {
9191     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9192     Store[newax][neway] = element;
9193   }
9194   else if (neway == ay || element == EL_EMC_DRIPPER)
9195   {
9196     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9197
9198     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9199   }
9200   else
9201   {
9202     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9203     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9204     Store[ax][ay] = EL_AMOEBA_DROP;
9205     ContinueMoving(ax, ay);
9206     return;
9207   }
9208
9209   DrawLevelField(newax, neway);
9210 }
9211
9212 void Life(int ax, int ay)
9213 {
9214   int x1, y1, x2, y2;
9215   int life_time = 40;
9216   int element = Feld[ax][ay];
9217   int graphic = el2img(element);
9218   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9219                          level.biomaze);
9220   boolean changed = FALSE;
9221
9222   if (IS_ANIMATED(graphic))
9223     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9224
9225   if (Stop[ax][ay])
9226     return;
9227
9228   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9229     MovDelay[ax][ay] = life_time;
9230
9231   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9232   {
9233     MovDelay[ax][ay]--;
9234     if (MovDelay[ax][ay])
9235       return;
9236   }
9237
9238   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9239   {
9240     int xx = ax+x1, yy = ay+y1;
9241     int nachbarn = 0;
9242
9243     if (!IN_LEV_FIELD(xx, yy))
9244       continue;
9245
9246     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9247     {
9248       int x = xx+x2, y = yy+y2;
9249
9250       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9251         continue;
9252
9253       if (((Feld[x][y] == element ||
9254             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9255            !Stop[x][y]) ||
9256           (IS_FREE(x, y) && Stop[x][y]))
9257         nachbarn++;
9258     }
9259
9260     if (xx == ax && yy == ay)           /* field in the middle */
9261     {
9262       if (nachbarn < life_parameter[0] ||
9263           nachbarn > life_parameter[1])
9264       {
9265         Feld[xx][yy] = EL_EMPTY;
9266         if (!Stop[xx][yy])
9267           DrawLevelField(xx, yy);
9268         Stop[xx][yy] = TRUE;
9269         changed = TRUE;
9270       }
9271     }
9272     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9273     {                                   /* free border field */
9274       if (nachbarn >= life_parameter[2] &&
9275           nachbarn <= life_parameter[3])
9276       {
9277         Feld[xx][yy] = element;
9278         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9279         if (!Stop[xx][yy])
9280           DrawLevelField(xx, yy);
9281         Stop[xx][yy] = TRUE;
9282         changed = TRUE;
9283       }
9284     }
9285   }
9286
9287   if (changed)
9288     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9289                    SND_GAME_OF_LIFE_GROWING);
9290 }
9291
9292 static void InitRobotWheel(int x, int y)
9293 {
9294   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9295 }
9296
9297 static void RunRobotWheel(int x, int y)
9298 {
9299   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9300 }
9301
9302 static void StopRobotWheel(int x, int y)
9303 {
9304   if (ZX == x && ZY == y)
9305   {
9306     ZX = ZY = -1;
9307
9308     game.robot_wheel_active = FALSE;
9309   }
9310 }
9311
9312 static void InitTimegateWheel(int x, int y)
9313 {
9314   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9315 }
9316
9317 static void RunTimegateWheel(int x, int y)
9318 {
9319   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9320 }
9321
9322 static void InitMagicBallDelay(int x, int y)
9323 {
9324 #if 1
9325   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9326 #else
9327   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9328 #endif
9329 }
9330
9331 static void ActivateMagicBall(int bx, int by)
9332 {
9333   int x, y;
9334
9335   if (level.ball_random)
9336   {
9337     int pos_border = RND(8);    /* select one of the eight border elements */
9338     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9339     int xx = pos_content % 3;
9340     int yy = pos_content / 3;
9341
9342     x = bx - 1 + xx;
9343     y = by - 1 + yy;
9344
9345     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9346       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9347   }
9348   else
9349   {
9350     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9351     {
9352       int xx = x - bx + 1;
9353       int yy = y - by + 1;
9354
9355       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9356         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9357     }
9358   }
9359
9360   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9361 }
9362
9363 void CheckExit(int x, int y)
9364 {
9365   if (local_player->gems_still_needed > 0 ||
9366       local_player->sokobanfields_still_needed > 0 ||
9367       local_player->lights_still_needed > 0)
9368   {
9369     int element = Feld[x][y];
9370     int graphic = el2img(element);
9371
9372     if (IS_ANIMATED(graphic))
9373       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9374
9375     return;
9376   }
9377
9378   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9379     return;
9380
9381   Feld[x][y] = EL_EXIT_OPENING;
9382
9383   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9384 }
9385
9386 void CheckExitEM(int x, int y)
9387 {
9388   if (local_player->gems_still_needed > 0 ||
9389       local_player->sokobanfields_still_needed > 0 ||
9390       local_player->lights_still_needed > 0)
9391   {
9392     int element = Feld[x][y];
9393     int graphic = el2img(element);
9394
9395     if (IS_ANIMATED(graphic))
9396       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9397
9398     return;
9399   }
9400
9401   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9402     return;
9403
9404   Feld[x][y] = EL_EM_EXIT_OPENING;
9405
9406   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9407 }
9408
9409 void CheckExitSteel(int x, int y)
9410 {
9411   if (local_player->gems_still_needed > 0 ||
9412       local_player->sokobanfields_still_needed > 0 ||
9413       local_player->lights_still_needed > 0)
9414   {
9415     int element = Feld[x][y];
9416     int graphic = el2img(element);
9417
9418     if (IS_ANIMATED(graphic))
9419       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9420
9421     return;
9422   }
9423
9424   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9425     return;
9426
9427   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9428
9429   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9430 }
9431
9432 void CheckExitSteelEM(int x, int y)
9433 {
9434   if (local_player->gems_still_needed > 0 ||
9435       local_player->sokobanfields_still_needed > 0 ||
9436       local_player->lights_still_needed > 0)
9437   {
9438     int element = Feld[x][y];
9439     int graphic = el2img(element);
9440
9441     if (IS_ANIMATED(graphic))
9442       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9443
9444     return;
9445   }
9446
9447   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9448     return;
9449
9450   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9451
9452   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9453 }
9454
9455 void CheckExitSP(int x, int y)
9456 {
9457   if (local_player->gems_still_needed > 0)
9458   {
9459     int element = Feld[x][y];
9460     int graphic = el2img(element);
9461
9462     if (IS_ANIMATED(graphic))
9463       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9464
9465     return;
9466   }
9467
9468   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9469     return;
9470
9471   Feld[x][y] = EL_SP_EXIT_OPENING;
9472
9473   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9474 }
9475
9476 static void CloseAllOpenTimegates()
9477 {
9478   int x, y;
9479
9480   SCAN_PLAYFIELD(x, y)
9481   {
9482     int element = Feld[x][y];
9483
9484     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9485     {
9486       Feld[x][y] = EL_TIMEGATE_CLOSING;
9487
9488       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9489     }
9490   }
9491 }
9492
9493 void DrawTwinkleOnField(int x, int y)
9494 {
9495   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9496     return;
9497
9498   if (Feld[x][y] == EL_BD_DIAMOND)
9499     return;
9500
9501   if (MovDelay[x][y] == 0)      /* next animation frame */
9502     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9503
9504   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9505   {
9506     MovDelay[x][y]--;
9507
9508     DrawLevelElementAnimation(x, y, Feld[x][y]);
9509
9510     if (MovDelay[x][y] != 0)
9511     {
9512       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9513                                            10 - MovDelay[x][y]);
9514
9515       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9516     }
9517   }
9518 }
9519
9520 void MauerWaechst(int x, int y)
9521 {
9522   int delay = 6;
9523
9524   if (!MovDelay[x][y])          /* next animation frame */
9525     MovDelay[x][y] = 3 * delay;
9526
9527   if (MovDelay[x][y])           /* wait some time before next frame */
9528   {
9529     MovDelay[x][y]--;
9530
9531     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9532     {
9533       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9534       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9535
9536       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9537     }
9538
9539     if (!MovDelay[x][y])
9540     {
9541       if (MovDir[x][y] == MV_LEFT)
9542       {
9543         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9544           DrawLevelField(x - 1, y);
9545       }
9546       else if (MovDir[x][y] == MV_RIGHT)
9547       {
9548         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9549           DrawLevelField(x + 1, y);
9550       }
9551       else if (MovDir[x][y] == MV_UP)
9552       {
9553         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9554           DrawLevelField(x, y - 1);
9555       }
9556       else
9557       {
9558         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9559           DrawLevelField(x, y + 1);
9560       }
9561
9562       Feld[x][y] = Store[x][y];
9563       Store[x][y] = 0;
9564       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9565       DrawLevelField(x, y);
9566     }
9567   }
9568 }
9569
9570 void MauerAbleger(int ax, int ay)
9571 {
9572   int element = Feld[ax][ay];
9573   int graphic = el2img(element);
9574   boolean oben_frei = FALSE, unten_frei = FALSE;
9575   boolean links_frei = FALSE, rechts_frei = FALSE;
9576   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9577   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9578   boolean new_wall = FALSE;
9579
9580   if (IS_ANIMATED(graphic))
9581     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9582
9583   if (!MovDelay[ax][ay])        /* start building new wall */
9584     MovDelay[ax][ay] = 6;
9585
9586   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9587   {
9588     MovDelay[ax][ay]--;
9589     if (MovDelay[ax][ay])
9590       return;
9591   }
9592
9593   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9594     oben_frei = TRUE;
9595   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9596     unten_frei = TRUE;
9597   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9598     links_frei = TRUE;
9599   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9600     rechts_frei = TRUE;
9601
9602   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9603       element == EL_EXPANDABLE_WALL_ANY)
9604   {
9605     if (oben_frei)
9606     {
9607       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9608       Store[ax][ay-1] = element;
9609       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9610       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9611         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9612                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9613       new_wall = TRUE;
9614     }
9615     if (unten_frei)
9616     {
9617       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9618       Store[ax][ay+1] = element;
9619       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9620       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9621         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9622                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9623       new_wall = TRUE;
9624     }
9625   }
9626
9627   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9628       element == EL_EXPANDABLE_WALL_ANY ||
9629       element == EL_EXPANDABLE_WALL ||
9630       element == EL_BD_EXPANDABLE_WALL)
9631   {
9632     if (links_frei)
9633     {
9634       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9635       Store[ax-1][ay] = element;
9636       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9637       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9638         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9639                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9640       new_wall = TRUE;
9641     }
9642
9643     if (rechts_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_RIGHT;
9648       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9649         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9650                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9651       new_wall = TRUE;
9652     }
9653   }
9654
9655   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9656     DrawLevelField(ax, ay);
9657
9658   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9659     oben_massiv = TRUE;
9660   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9661     unten_massiv = TRUE;
9662   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9663     links_massiv = TRUE;
9664   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9665     rechts_massiv = TRUE;
9666
9667   if (((oben_massiv && unten_massiv) ||
9668        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9669        element == EL_EXPANDABLE_WALL) &&
9670       ((links_massiv && rechts_massiv) ||
9671        element == EL_EXPANDABLE_WALL_VERTICAL))
9672     Feld[ax][ay] = EL_WALL;
9673
9674   if (new_wall)
9675     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9676 }
9677
9678 void MauerAblegerStahl(int ax, int ay)
9679 {
9680   int element = Feld[ax][ay];
9681   int graphic = el2img(element);
9682   boolean oben_frei = FALSE, unten_frei = FALSE;
9683   boolean links_frei = FALSE, rechts_frei = FALSE;
9684   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9685   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9686   boolean new_wall = FALSE;
9687
9688   if (IS_ANIMATED(graphic))
9689     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9690
9691   if (!MovDelay[ax][ay])        /* start building new wall */
9692     MovDelay[ax][ay] = 6;
9693
9694   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9695   {
9696     MovDelay[ax][ay]--;
9697     if (MovDelay[ax][ay])
9698       return;
9699   }
9700
9701   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9702     oben_frei = TRUE;
9703   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9704     unten_frei = TRUE;
9705   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9706     links_frei = TRUE;
9707   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9708     rechts_frei = TRUE;
9709
9710   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9711       element == EL_EXPANDABLE_STEELWALL_ANY)
9712   {
9713     if (oben_frei)
9714     {
9715       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9716       Store[ax][ay-1] = element;
9717       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9718       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9719         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9720                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9721       new_wall = TRUE;
9722     }
9723     if (unten_frei)
9724     {
9725       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9726       Store[ax][ay+1] = element;
9727       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9728       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9729         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9730                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9731       new_wall = TRUE;
9732     }
9733   }
9734
9735   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9736       element == EL_EXPANDABLE_STEELWALL_ANY)
9737   {
9738     if (links_frei)
9739     {
9740       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9741       Store[ax-1][ay] = element;
9742       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9743       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9744         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9745                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9746       new_wall = TRUE;
9747     }
9748
9749     if (rechts_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_RIGHT;
9754       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9755         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9756                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9757       new_wall = TRUE;
9758     }
9759   }
9760
9761   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9762     oben_massiv = TRUE;
9763   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9764     unten_massiv = TRUE;
9765   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9766     links_massiv = TRUE;
9767   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9768     rechts_massiv = TRUE;
9769
9770   if (((oben_massiv && unten_massiv) ||
9771        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9772       ((links_massiv && rechts_massiv) ||
9773        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9774     Feld[ax][ay] = EL_WALL;
9775
9776   if (new_wall)
9777     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9778 }
9779
9780 void CheckForDragon(int x, int y)
9781 {
9782   int i, j;
9783   boolean dragon_found = FALSE;
9784   static int xy[4][2] =
9785   {
9786     { 0, -1 },
9787     { -1, 0 },
9788     { +1, 0 },
9789     { 0, +1 }
9790   };
9791
9792   for (i = 0; i < NUM_DIRECTIONS; i++)
9793   {
9794     for (j = 0; j < 4; j++)
9795     {
9796       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9797
9798       if (IN_LEV_FIELD(xx, yy) &&
9799           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9800       {
9801         if (Feld[xx][yy] == EL_DRAGON)
9802           dragon_found = TRUE;
9803       }
9804       else
9805         break;
9806     }
9807   }
9808
9809   if (!dragon_found)
9810   {
9811     for (i = 0; i < NUM_DIRECTIONS; i++)
9812     {
9813       for (j = 0; j < 3; j++)
9814       {
9815         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9816   
9817         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9818         {
9819           Feld[xx][yy] = EL_EMPTY;
9820           DrawLevelField(xx, yy);
9821         }
9822         else
9823           break;
9824       }
9825     }
9826   }
9827 }
9828
9829 static void InitBuggyBase(int x, int y)
9830 {
9831   int element = Feld[x][y];
9832   int activating_delay = FRAMES_PER_SECOND / 4;
9833
9834   ChangeDelay[x][y] =
9835     (element == EL_SP_BUGGY_BASE ?
9836      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9837      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9838      activating_delay :
9839      element == EL_SP_BUGGY_BASE_ACTIVE ?
9840      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9841 }
9842
9843 static void WarnBuggyBase(int x, int y)
9844 {
9845   int i;
9846   static int xy[4][2] =
9847   {
9848     { 0, -1 },
9849     { -1, 0 },
9850     { +1, 0 },
9851     { 0, +1 }
9852   };
9853
9854   for (i = 0; i < NUM_DIRECTIONS; i++)
9855   {
9856     int xx = x + xy[i][0];
9857     int yy = y + xy[i][1];
9858
9859     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9860     {
9861       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9862
9863       break;
9864     }
9865   }
9866 }
9867
9868 static void InitTrap(int x, int y)
9869 {
9870   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9871 }
9872
9873 static void ActivateTrap(int x, int y)
9874 {
9875   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9876 }
9877
9878 static void ChangeActiveTrap(int x, int y)
9879 {
9880   int graphic = IMG_TRAP_ACTIVE;
9881
9882   /* if new animation frame was drawn, correct crumbled sand border */
9883   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9884     DrawLevelFieldCrumbledSand(x, y);
9885 }
9886
9887 static int getSpecialActionElement(int element, int number, int base_element)
9888 {
9889   return (element != EL_EMPTY ? element :
9890           number != -1 ? base_element + number - 1 :
9891           EL_EMPTY);
9892 }
9893
9894 static int getModifiedActionNumber(int value_old, int operator, int operand,
9895                                    int value_min, int value_max)
9896 {
9897   int value_new = (operator == CA_MODE_SET      ? operand :
9898                    operator == CA_MODE_ADD      ? value_old + operand :
9899                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9900                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9901                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9902                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9903                    value_old);
9904
9905   return (value_new < value_min ? value_min :
9906           value_new > value_max ? value_max :
9907           value_new);
9908 }
9909
9910 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9911 {
9912   struct ElementInfo *ei = &element_info[element];
9913   struct ElementChangeInfo *change = &ei->change_page[page];
9914   int target_element = change->target_element;
9915   int action_type = change->action_type;
9916   int action_mode = change->action_mode;
9917   int action_arg = change->action_arg;
9918   int i;
9919
9920   if (!change->has_action)
9921     return;
9922
9923   /* ---------- determine action paramater values -------------------------- */
9924
9925   int level_time_value =
9926     (level.time > 0 ? TimeLeft :
9927      TimePlayed);
9928
9929   int action_arg_element =
9930     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9931      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9932      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9933      EL_EMPTY);
9934
9935   int action_arg_direction =
9936     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9937      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9938      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9939      change->actual_trigger_side :
9940      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9941      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9942      MV_NONE);
9943
9944   int action_arg_number_min =
9945     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9946      CA_ARG_MIN);
9947
9948   int action_arg_number_max =
9949     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9950      action_type == CA_SET_LEVEL_GEMS ? 999 :
9951      action_type == CA_SET_LEVEL_TIME ? 9999 :
9952      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9953      action_type == CA_SET_CE_VALUE ? 9999 :
9954      action_type == CA_SET_CE_SCORE ? 9999 :
9955      CA_ARG_MAX);
9956
9957   int action_arg_number_reset =
9958     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9959      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9960      action_type == CA_SET_LEVEL_TIME ? level.time :
9961      action_type == CA_SET_LEVEL_SCORE ? 0 :
9962 #if USE_NEW_CUSTOM_VALUE
9963      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9964 #else
9965      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9966 #endif
9967      action_type == CA_SET_CE_SCORE ? 0 :
9968      0);
9969
9970   int action_arg_number =
9971     (action_arg <= CA_ARG_MAX ? action_arg :
9972      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9973      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9974      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9975      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9976      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9977      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9978 #if USE_NEW_CUSTOM_VALUE
9979      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9980 #else
9981      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9982 #endif
9983      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9984      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9985      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9986      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9987      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9988      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9989      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9990      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9991      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9992      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9993      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9994      -1);
9995
9996   int action_arg_number_old =
9997     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9998      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9999      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10000      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10001      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10002      0);
10003
10004   int action_arg_number_new =
10005     getModifiedActionNumber(action_arg_number_old,
10006                             action_mode, action_arg_number,
10007                             action_arg_number_min, action_arg_number_max);
10008
10009 #if 1
10010   int trigger_player_bits = change->actual_trigger_player_bits;
10011 #else
10012   int trigger_player_bits =
10013     (change->actual_trigger_player >= EL_PLAYER_1 &&
10014      change->actual_trigger_player <= EL_PLAYER_4 ?
10015      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10016      PLAYER_BITS_ANY);
10017 #endif
10018
10019   int action_arg_player_bits =
10020     (action_arg >= CA_ARG_PLAYER_1 &&
10021      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10022      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10023      PLAYER_BITS_ANY);
10024
10025   /* ---------- execute action  -------------------------------------------- */
10026
10027   switch (action_type)
10028   {
10029     case CA_NO_ACTION:
10030     {
10031       return;
10032     }
10033
10034     /* ---------- level actions  ------------------------------------------- */
10035
10036     case CA_RESTART_LEVEL:
10037     {
10038       game.restart_level = TRUE;
10039
10040       break;
10041     }
10042
10043     case CA_SHOW_ENVELOPE:
10044     {
10045       int element = getSpecialActionElement(action_arg_element,
10046                                             action_arg_number, EL_ENVELOPE_1);
10047
10048       if (IS_ENVELOPE(element))
10049         local_player->show_envelope = element;
10050
10051       break;
10052     }
10053
10054     case CA_SET_LEVEL_TIME:
10055     {
10056       if (level.time > 0)       /* only modify limited time value */
10057       {
10058         TimeLeft = action_arg_number_new;
10059
10060 #if 1
10061         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10062
10063         DisplayGameControlValues();
10064 #else
10065         DrawGameValue_Time(TimeLeft);
10066 #endif
10067
10068         if (!TimeLeft && setup.time_limit)
10069           for (i = 0; i < MAX_PLAYERS; i++)
10070             KillPlayer(&stored_player[i]);
10071       }
10072
10073       break;
10074     }
10075
10076     case CA_SET_LEVEL_SCORE:
10077     {
10078       local_player->score = action_arg_number_new;
10079
10080 #if 1
10081       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10082
10083       DisplayGameControlValues();
10084 #else
10085       DrawGameValue_Score(local_player->score);
10086 #endif
10087
10088       break;
10089     }
10090
10091     case CA_SET_LEVEL_GEMS:
10092     {
10093       local_player->gems_still_needed = action_arg_number_new;
10094
10095 #if 1
10096       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10097
10098       DisplayGameControlValues();
10099 #else
10100       DrawGameValue_Emeralds(local_player->gems_still_needed);
10101 #endif
10102
10103       break;
10104     }
10105
10106 #if !USE_PLAYER_GRAVITY
10107     case CA_SET_LEVEL_GRAVITY:
10108     {
10109       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10110                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10111                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10112                       game.gravity);
10113       break;
10114     }
10115 #endif
10116
10117     case CA_SET_LEVEL_WIND:
10118     {
10119       game.wind_direction = action_arg_direction;
10120
10121       break;
10122     }
10123
10124     /* ---------- player actions  ------------------------------------------ */
10125
10126     case CA_MOVE_PLAYER:
10127     {
10128       /* automatically move to the next field in specified direction */
10129       for (i = 0; i < MAX_PLAYERS; i++)
10130         if (trigger_player_bits & (1 << i))
10131           stored_player[i].programmed_action = action_arg_direction;
10132
10133       break;
10134     }
10135
10136     case CA_EXIT_PLAYER:
10137     {
10138       for (i = 0; i < MAX_PLAYERS; i++)
10139         if (action_arg_player_bits & (1 << i))
10140           PlayerWins(&stored_player[i]);
10141
10142       break;
10143     }
10144
10145     case CA_KILL_PLAYER:
10146     {
10147       for (i = 0; i < MAX_PLAYERS; i++)
10148         if (action_arg_player_bits & (1 << i))
10149           KillPlayer(&stored_player[i]);
10150
10151       break;
10152     }
10153
10154     case CA_SET_PLAYER_KEYS:
10155     {
10156       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10157       int element = getSpecialActionElement(action_arg_element,
10158                                             action_arg_number, EL_KEY_1);
10159
10160       if (IS_KEY(element))
10161       {
10162         for (i = 0; i < MAX_PLAYERS; i++)
10163         {
10164           if (trigger_player_bits & (1 << i))
10165           {
10166             stored_player[i].key[KEY_NR(element)] = key_state;
10167
10168             DrawGameDoorValues();
10169           }
10170         }
10171       }
10172
10173       break;
10174     }
10175
10176     case CA_SET_PLAYER_SPEED:
10177     {
10178       for (i = 0; i < MAX_PLAYERS; i++)
10179       {
10180         if (trigger_player_bits & (1 << i))
10181         {
10182           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10183
10184           if (action_arg == CA_ARG_SPEED_FASTER &&
10185               stored_player[i].cannot_move)
10186           {
10187             action_arg_number = STEPSIZE_VERY_SLOW;
10188           }
10189           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10190                    action_arg == CA_ARG_SPEED_FASTER)
10191           {
10192             action_arg_number = 2;
10193             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10194                            CA_MODE_MULTIPLY);
10195           }
10196           else if (action_arg == CA_ARG_NUMBER_RESET)
10197           {
10198             action_arg_number = level.initial_player_stepsize[i];
10199           }
10200
10201           move_stepsize =
10202             getModifiedActionNumber(move_stepsize,
10203                                     action_mode,
10204                                     action_arg_number,
10205                                     action_arg_number_min,
10206                                     action_arg_number_max);
10207
10208           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10209         }
10210       }
10211
10212       break;
10213     }
10214
10215     case CA_SET_PLAYER_SHIELD:
10216     {
10217       for (i = 0; i < MAX_PLAYERS; i++)
10218       {
10219         if (trigger_player_bits & (1 << i))
10220         {
10221           if (action_arg == CA_ARG_SHIELD_OFF)
10222           {
10223             stored_player[i].shield_normal_time_left = 0;
10224             stored_player[i].shield_deadly_time_left = 0;
10225           }
10226           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10227           {
10228             stored_player[i].shield_normal_time_left = 999999;
10229           }
10230           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10231           {
10232             stored_player[i].shield_normal_time_left = 999999;
10233             stored_player[i].shield_deadly_time_left = 999999;
10234           }
10235         }
10236       }
10237
10238       break;
10239     }
10240
10241 #if USE_PLAYER_GRAVITY
10242     case CA_SET_PLAYER_GRAVITY:
10243     {
10244       for (i = 0; i < MAX_PLAYERS; i++)
10245       {
10246         if (trigger_player_bits & (1 << i))
10247         {
10248           stored_player[i].gravity =
10249             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10250              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10251              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10252              stored_player[i].gravity);
10253         }
10254       }
10255
10256       break;
10257     }
10258 #endif
10259
10260     case CA_SET_PLAYER_ARTWORK:
10261     {
10262       for (i = 0; i < MAX_PLAYERS; i++)
10263       {
10264         if (trigger_player_bits & (1 << i))
10265         {
10266           int artwork_element = action_arg_element;
10267
10268           if (action_arg == CA_ARG_ELEMENT_RESET)
10269             artwork_element =
10270               (level.use_artwork_element[i] ? level.artwork_element[i] :
10271                stored_player[i].element_nr);
10272
10273 #if USE_GFX_RESET_PLAYER_ARTWORK
10274           if (stored_player[i].artwork_element != artwork_element)
10275             stored_player[i].Frame = 0;
10276 #endif
10277
10278           stored_player[i].artwork_element = artwork_element;
10279
10280           SetPlayerWaiting(&stored_player[i], FALSE);
10281
10282           /* set number of special actions for bored and sleeping animation */
10283           stored_player[i].num_special_action_bored =
10284             get_num_special_action(artwork_element,
10285                                    ACTION_BORING_1, ACTION_BORING_LAST);
10286           stored_player[i].num_special_action_sleeping =
10287             get_num_special_action(artwork_element,
10288                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10289         }
10290       }
10291
10292       break;
10293     }
10294
10295     /* ---------- CE actions  ---------------------------------------------- */
10296
10297     case CA_SET_CE_VALUE:
10298     {
10299 #if USE_NEW_CUSTOM_VALUE
10300       int last_ce_value = CustomValue[x][y];
10301
10302       CustomValue[x][y] = action_arg_number_new;
10303
10304       if (CustomValue[x][y] != last_ce_value)
10305       {
10306         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10307         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10308
10309         if (CustomValue[x][y] == 0)
10310         {
10311           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10312           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10313         }
10314       }
10315 #endif
10316
10317       break;
10318     }
10319
10320     case CA_SET_CE_SCORE:
10321     {
10322 #if USE_NEW_CUSTOM_VALUE
10323       int last_ce_score = ei->collect_score;
10324
10325       ei->collect_score = action_arg_number_new;
10326
10327       if (ei->collect_score != last_ce_score)
10328       {
10329         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10330         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10331
10332         if (ei->collect_score == 0)
10333         {
10334           int xx, yy;
10335
10336           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10337           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10338
10339           /*
10340             This is a very special case that seems to be a mixture between
10341             CheckElementChange() and CheckTriggeredElementChange(): while
10342             the first one only affects single elements that are triggered
10343             directly, the second one affects multiple elements in the playfield
10344             that are triggered indirectly by another element. This is a third
10345             case: Changing the CE score always affects multiple identical CEs,
10346             so every affected CE must be checked, not only the single CE for
10347             which the CE score was changed in the first place (as every instance
10348             of that CE shares the same CE score, and therefore also can change)!
10349           */
10350           SCAN_PLAYFIELD(xx, yy)
10351           {
10352             if (Feld[xx][yy] == element)
10353               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10354                                  CE_SCORE_GETS_ZERO);
10355           }
10356         }
10357       }
10358 #endif
10359
10360       break;
10361     }
10362
10363     /* ---------- engine actions  ------------------------------------------ */
10364
10365     case CA_SET_ENGINE_SCAN_MODE:
10366     {
10367       InitPlayfieldScanMode(action_arg);
10368
10369       break;
10370     }
10371
10372     default:
10373       break;
10374   }
10375 }
10376
10377 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10378 {
10379   int old_element = Feld[x][y];
10380   int new_element = GetElementFromGroupElement(element);
10381   int previous_move_direction = MovDir[x][y];
10382 #if USE_NEW_CUSTOM_VALUE
10383   int last_ce_value = CustomValue[x][y];
10384 #endif
10385   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10386   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10387   boolean add_player_onto_element = (new_element_is_player &&
10388 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10389                                      /* this breaks SnakeBite when a snake is
10390                                         halfway through a door that closes */
10391                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10392                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10393 #endif
10394                                      IS_WALKABLE(old_element));
10395
10396 #if 0
10397   /* check if element under the player changes from accessible to unaccessible
10398      (needed for special case of dropping element which then changes) */
10399   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10400       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10401   {
10402     Bang(x, y);
10403
10404     return;
10405   }
10406 #endif
10407
10408   if (!add_player_onto_element)
10409   {
10410     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10411       RemoveMovingField(x, y);
10412     else
10413       RemoveField(x, y);
10414
10415     Feld[x][y] = new_element;
10416
10417 #if !USE_GFX_RESET_GFX_ANIMATION
10418     ResetGfxAnimation(x, y);
10419     ResetRandomAnimationValue(x, y);
10420 #endif
10421
10422     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10423       MovDir[x][y] = previous_move_direction;
10424
10425 #if USE_NEW_CUSTOM_VALUE
10426     if (element_info[new_element].use_last_ce_value)
10427       CustomValue[x][y] = last_ce_value;
10428 #endif
10429
10430     InitField_WithBug1(x, y, FALSE);
10431
10432     new_element = Feld[x][y];   /* element may have changed */
10433
10434 #if USE_GFX_RESET_GFX_ANIMATION
10435     ResetGfxAnimation(x, y);
10436     ResetRandomAnimationValue(x, y);
10437 #endif
10438
10439     DrawLevelField(x, y);
10440
10441     if (GFX_CRUMBLED(new_element))
10442       DrawLevelFieldCrumbledSandNeighbours(x, y);
10443   }
10444
10445 #if 1
10446   /* check if element under the player changes from accessible to unaccessible
10447      (needed for special case of dropping element which then changes) */
10448   /* (must be checked after creating new element for walkable group elements) */
10449 #if USE_FIX_KILLED_BY_NON_WALKABLE
10450   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10451       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10452   {
10453     Bang(x, y);
10454
10455     return;
10456   }
10457 #else
10458   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10459       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10460   {
10461     Bang(x, y);
10462
10463     return;
10464   }
10465 #endif
10466 #endif
10467
10468   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10469   if (new_element_is_player)
10470     RelocatePlayer(x, y, new_element);
10471
10472   if (is_change)
10473     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10474
10475   TestIfBadThingTouchesPlayer(x, y);
10476   TestIfPlayerTouchesCustomElement(x, y);
10477   TestIfElementTouchesCustomElement(x, y);
10478 }
10479
10480 static void CreateField(int x, int y, int element)
10481 {
10482   CreateFieldExt(x, y, element, FALSE);
10483 }
10484
10485 static void CreateElementFromChange(int x, int y, int element)
10486 {
10487   element = GET_VALID_RUNTIME_ELEMENT(element);
10488
10489 #if USE_STOP_CHANGED_ELEMENTS
10490   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10491   {
10492     int old_element = Feld[x][y];
10493
10494     /* prevent changed element from moving in same engine frame
10495        unless both old and new element can either fall or move */
10496     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10497         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10498       Stop[x][y] = TRUE;
10499   }
10500 #endif
10501
10502   CreateFieldExt(x, y, element, TRUE);
10503 }
10504
10505 static boolean ChangeElement(int x, int y, int element, int page)
10506 {
10507   struct ElementInfo *ei = &element_info[element];
10508   struct ElementChangeInfo *change = &ei->change_page[page];
10509   int ce_value = CustomValue[x][y];
10510   int ce_score = ei->collect_score;
10511   int target_element;
10512   int old_element = Feld[x][y];
10513
10514   /* always use default change event to prevent running into a loop */
10515   if (ChangeEvent[x][y] == -1)
10516     ChangeEvent[x][y] = CE_DELAY;
10517
10518   if (ChangeEvent[x][y] == CE_DELAY)
10519   {
10520     /* reset actual trigger element, trigger player and action element */
10521     change->actual_trigger_element = EL_EMPTY;
10522     change->actual_trigger_player = EL_PLAYER_1;
10523     change->actual_trigger_player_bits = CH_PLAYER_1;
10524     change->actual_trigger_side = CH_SIDE_NONE;
10525     change->actual_trigger_ce_value = 0;
10526     change->actual_trigger_ce_score = 0;
10527   }
10528
10529   /* do not change elements more than a specified maximum number of changes */
10530   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10531     return FALSE;
10532
10533   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10534
10535   if (change->explode)
10536   {
10537     Bang(x, y);
10538
10539     return TRUE;
10540   }
10541
10542   if (change->use_target_content)
10543   {
10544     boolean complete_replace = TRUE;
10545     boolean can_replace[3][3];
10546     int xx, yy;
10547
10548     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10549     {
10550       boolean is_empty;
10551       boolean is_walkable;
10552       boolean is_diggable;
10553       boolean is_collectible;
10554       boolean is_removable;
10555       boolean is_destructible;
10556       int ex = x + xx - 1;
10557       int ey = y + yy - 1;
10558       int content_element = change->target_content.e[xx][yy];
10559       int e;
10560
10561       can_replace[xx][yy] = TRUE;
10562
10563       if (ex == x && ey == y)   /* do not check changing element itself */
10564         continue;
10565
10566       if (content_element == EL_EMPTY_SPACE)
10567       {
10568         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10569
10570         continue;
10571       }
10572
10573       if (!IN_LEV_FIELD(ex, ey))
10574       {
10575         can_replace[xx][yy] = FALSE;
10576         complete_replace = FALSE;
10577
10578         continue;
10579       }
10580
10581       e = Feld[ex][ey];
10582
10583       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10584         e = MovingOrBlocked2Element(ex, ey);
10585
10586       is_empty = (IS_FREE(ex, ey) ||
10587                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10588
10589       is_walkable     = (is_empty || IS_WALKABLE(e));
10590       is_diggable     = (is_empty || IS_DIGGABLE(e));
10591       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10592       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10593       is_removable    = (is_diggable || is_collectible);
10594
10595       can_replace[xx][yy] =
10596         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10597           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10598           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10599           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10600           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10601           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10602          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10603
10604       if (!can_replace[xx][yy])
10605         complete_replace = FALSE;
10606     }
10607
10608     if (!change->only_if_complete || complete_replace)
10609     {
10610       boolean something_has_changed = FALSE;
10611
10612       if (change->only_if_complete && change->use_random_replace &&
10613           RND(100) < change->random_percentage)
10614         return FALSE;
10615
10616       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10617       {
10618         int ex = x + xx - 1;
10619         int ey = y + yy - 1;
10620         int content_element;
10621
10622         if (can_replace[xx][yy] && (!change->use_random_replace ||
10623                                     RND(100) < change->random_percentage))
10624         {
10625           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10626             RemoveMovingField(ex, ey);
10627
10628           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10629
10630           content_element = change->target_content.e[xx][yy];
10631           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10632                                               ce_value, ce_score);
10633
10634           CreateElementFromChange(ex, ey, target_element);
10635
10636           something_has_changed = TRUE;
10637
10638           /* for symmetry reasons, freeze newly created border elements */
10639           if (ex != x || ey != y)
10640             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10641         }
10642       }
10643
10644       if (something_has_changed)
10645       {
10646         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10647         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10648       }
10649     }
10650   }
10651   else
10652   {
10653     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10654                                         ce_value, ce_score);
10655
10656     if (element == EL_DIAGONAL_GROWING ||
10657         element == EL_DIAGONAL_SHRINKING)
10658     {
10659       target_element = Store[x][y];
10660
10661       Store[x][y] = EL_EMPTY;
10662     }
10663
10664     CreateElementFromChange(x, y, target_element);
10665
10666     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10667     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10668   }
10669
10670   /* this uses direct change before indirect change */
10671   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10672
10673   return TRUE;
10674 }
10675
10676 #if USE_NEW_DELAYED_ACTION
10677
10678 static void HandleElementChange(int x, int y, int page)
10679 {
10680   int element = MovingOrBlocked2Element(x, y);
10681   struct ElementInfo *ei = &element_info[element];
10682   struct ElementChangeInfo *change = &ei->change_page[page];
10683
10684 #ifdef DEBUG
10685   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10686       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10687   {
10688     printf("\n\n");
10689     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10690            x, y, element, element_info[element].token_name);
10691     printf("HandleElementChange(): This should never happen!\n");
10692     printf("\n\n");
10693   }
10694 #endif
10695
10696   /* this can happen with classic bombs on walkable, changing elements */
10697   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10698   {
10699 #if 0
10700     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10701       ChangeDelay[x][y] = 0;
10702 #endif
10703
10704     return;
10705   }
10706
10707   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10708   {
10709     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10710
10711     if (change->can_change)
10712     {
10713 #if 1
10714       /* !!! not clear why graphic animation should be reset at all here !!! */
10715       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10716 #if USE_GFX_RESET_WHEN_NOT_MOVING
10717       /* when a custom element is about to change (for example by change delay),
10718          do not reset graphic animation when the custom element is moving */
10719       if (!IS_MOVING(x, y))
10720 #endif
10721       {
10722         ResetGfxAnimation(x, y);
10723         ResetRandomAnimationValue(x, y);
10724       }
10725 #endif
10726
10727       if (change->pre_change_function)
10728         change->pre_change_function(x, y);
10729     }
10730   }
10731
10732   ChangeDelay[x][y]--;
10733
10734   if (ChangeDelay[x][y] != 0)           /* continue element change */
10735   {
10736     if (change->can_change)
10737     {
10738       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10739
10740       if (IS_ANIMATED(graphic))
10741         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10742
10743       if (change->change_function)
10744         change->change_function(x, y);
10745     }
10746   }
10747   else                                  /* finish element change */
10748   {
10749     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10750     {
10751       page = ChangePage[x][y];
10752       ChangePage[x][y] = -1;
10753
10754       change = &ei->change_page[page];
10755     }
10756
10757     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10758     {
10759       ChangeDelay[x][y] = 1;            /* try change after next move step */
10760       ChangePage[x][y] = page;          /* remember page to use for change */
10761
10762       return;
10763     }
10764
10765     if (change->can_change)
10766     {
10767       if (ChangeElement(x, y, element, page))
10768       {
10769         if (change->post_change_function)
10770           change->post_change_function(x, y);
10771       }
10772     }
10773
10774     if (change->has_action)
10775       ExecuteCustomElementAction(x, y, element, page);
10776   }
10777 }
10778
10779 #else
10780
10781 static void HandleElementChange(int x, int y, int page)
10782 {
10783   int element = MovingOrBlocked2Element(x, y);
10784   struct ElementInfo *ei = &element_info[element];
10785   struct ElementChangeInfo *change = &ei->change_page[page];
10786
10787 #ifdef DEBUG
10788   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10789   {
10790     printf("\n\n");
10791     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10792            x, y, element, element_info[element].token_name);
10793     printf("HandleElementChange(): This should never happen!\n");
10794     printf("\n\n");
10795   }
10796 #endif
10797
10798   /* this can happen with classic bombs on walkable, changing elements */
10799   if (!CAN_CHANGE(element))
10800   {
10801 #if 0
10802     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10803       ChangeDelay[x][y] = 0;
10804 #endif
10805
10806     return;
10807   }
10808
10809   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10810   {
10811     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10812
10813     ResetGfxAnimation(x, y);
10814     ResetRandomAnimationValue(x, y);
10815
10816     if (change->pre_change_function)
10817       change->pre_change_function(x, y);
10818   }
10819
10820   ChangeDelay[x][y]--;
10821
10822   if (ChangeDelay[x][y] != 0)           /* continue element change */
10823   {
10824     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10825
10826     if (IS_ANIMATED(graphic))
10827       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10828
10829     if (change->change_function)
10830       change->change_function(x, y);
10831   }
10832   else                                  /* finish element change */
10833   {
10834     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10835     {
10836       page = ChangePage[x][y];
10837       ChangePage[x][y] = -1;
10838
10839       change = &ei->change_page[page];
10840     }
10841
10842     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10843     {
10844       ChangeDelay[x][y] = 1;            /* try change after next move step */
10845       ChangePage[x][y] = page;          /* remember page to use for change */
10846
10847       return;
10848     }
10849
10850     if (ChangeElement(x, y, element, page))
10851     {
10852       if (change->post_change_function)
10853         change->post_change_function(x, y);
10854     }
10855   }
10856 }
10857
10858 #endif
10859
10860 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10861                                               int trigger_element,
10862                                               int trigger_event,
10863                                               int trigger_player,
10864                                               int trigger_side,
10865                                               int trigger_page)
10866 {
10867   boolean change_done_any = FALSE;
10868   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10869   int i;
10870
10871   if (!(trigger_events[trigger_element][trigger_event]))
10872     return FALSE;
10873
10874 #if 0
10875   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10876          trigger_event, recursion_loop_depth, recursion_loop_detected,
10877          recursion_loop_element, EL_NAME(recursion_loop_element));
10878 #endif
10879
10880   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10881
10882   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10883   {
10884     int element = EL_CUSTOM_START + i;
10885     boolean change_done = FALSE;
10886     int p;
10887
10888     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10889         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10890       continue;
10891
10892     for (p = 0; p < element_info[element].num_change_pages; p++)
10893     {
10894       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10895
10896       if (change->can_change_or_has_action &&
10897           change->has_event[trigger_event] &&
10898           change->trigger_side & trigger_side &&
10899           change->trigger_player & trigger_player &&
10900           change->trigger_page & trigger_page_bits &&
10901           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10902       {
10903         change->actual_trigger_element = trigger_element;
10904         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10905         change->actual_trigger_player_bits = trigger_player;
10906         change->actual_trigger_side = trigger_side;
10907         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10908         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10909
10910         if ((change->can_change && !change_done) || change->has_action)
10911         {
10912           int x, y;
10913
10914           SCAN_PLAYFIELD(x, y)
10915           {
10916             if (Feld[x][y] == element)
10917             {
10918               if (change->can_change && !change_done)
10919               {
10920                 ChangeDelay[x][y] = 1;
10921                 ChangeEvent[x][y] = trigger_event;
10922
10923                 HandleElementChange(x, y, p);
10924               }
10925 #if USE_NEW_DELAYED_ACTION
10926               else if (change->has_action)
10927               {
10928                 ExecuteCustomElementAction(x, y, element, p);
10929                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10930               }
10931 #else
10932               if (change->has_action)
10933               {
10934                 ExecuteCustomElementAction(x, y, element, p);
10935                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10936               }
10937 #endif
10938             }
10939           }
10940
10941           if (change->can_change)
10942           {
10943             change_done = TRUE;
10944             change_done_any = TRUE;
10945           }
10946         }
10947       }
10948     }
10949   }
10950
10951   RECURSION_LOOP_DETECTION_END();
10952
10953   return change_done_any;
10954 }
10955
10956 static boolean CheckElementChangeExt(int x, int y,
10957                                      int element,
10958                                      int trigger_element,
10959                                      int trigger_event,
10960                                      int trigger_player,
10961                                      int trigger_side)
10962 {
10963   boolean change_done = FALSE;
10964   int p;
10965
10966   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10967       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10968     return FALSE;
10969
10970   if (Feld[x][y] == EL_BLOCKED)
10971   {
10972     Blocked2Moving(x, y, &x, &y);
10973     element = Feld[x][y];
10974   }
10975
10976 #if 0
10977   /* check if element has already changed */
10978   if (Feld[x][y] != element)
10979     return FALSE;
10980 #else
10981   /* check if element has already changed or is about to change after moving */
10982   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10983        Feld[x][y] != element) ||
10984
10985       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10986        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10987         ChangePage[x][y] != -1)))
10988     return FALSE;
10989 #endif
10990
10991 #if 0
10992   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10993          trigger_event, recursion_loop_depth, recursion_loop_detected,
10994          recursion_loop_element, EL_NAME(recursion_loop_element));
10995 #endif
10996
10997   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10998
10999   for (p = 0; p < element_info[element].num_change_pages; p++)
11000   {
11001     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11002
11003     /* check trigger element for all events where the element that is checked
11004        for changing interacts with a directly adjacent element -- this is
11005        different to element changes that affect other elements to change on the
11006        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11007     boolean check_trigger_element =
11008       (trigger_event == CE_TOUCHING_X ||
11009        trigger_event == CE_HITTING_X ||
11010        trigger_event == CE_HIT_BY_X ||
11011 #if 1
11012        /* this one was forgotten until 3.2.3 */
11013        trigger_event == CE_DIGGING_X);
11014 #endif
11015
11016     if (change->can_change_or_has_action &&
11017         change->has_event[trigger_event] &&
11018         change->trigger_side & trigger_side &&
11019         change->trigger_player & trigger_player &&
11020         (!check_trigger_element ||
11021          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11022     {
11023       change->actual_trigger_element = trigger_element;
11024       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11025       change->actual_trigger_player_bits = trigger_player;
11026       change->actual_trigger_side = trigger_side;
11027       change->actual_trigger_ce_value = CustomValue[x][y];
11028       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11029
11030       /* special case: trigger element not at (x,y) position for some events */
11031       if (check_trigger_element)
11032       {
11033         static struct
11034         {
11035           int dx, dy;
11036         } move_xy[] =
11037           {
11038             {  0,  0 },
11039             { -1,  0 },
11040             { +1,  0 },
11041             {  0,  0 },
11042             {  0, -1 },
11043             {  0,  0 }, { 0, 0 }, { 0, 0 },
11044             {  0, +1 }
11045           };
11046
11047         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11048         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11049
11050         change->actual_trigger_ce_value = CustomValue[xx][yy];
11051         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11052       }
11053
11054       if (change->can_change && !change_done)
11055       {
11056         ChangeDelay[x][y] = 1;
11057         ChangeEvent[x][y] = trigger_event;
11058
11059         HandleElementChange(x, y, p);
11060
11061         change_done = TRUE;
11062       }
11063 #if USE_NEW_DELAYED_ACTION
11064       else if (change->has_action)
11065       {
11066         ExecuteCustomElementAction(x, y, element, p);
11067         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11068       }
11069 #else
11070       if (change->has_action)
11071       {
11072         ExecuteCustomElementAction(x, y, element, p);
11073         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11074       }
11075 #endif
11076     }
11077   }
11078
11079   RECURSION_LOOP_DETECTION_END();
11080
11081   return change_done;
11082 }
11083
11084 static void PlayPlayerSound(struct PlayerInfo *player)
11085 {
11086   int jx = player->jx, jy = player->jy;
11087   int sound_element = player->artwork_element;
11088   int last_action = player->last_action_waiting;
11089   int action = player->action_waiting;
11090
11091   if (player->is_waiting)
11092   {
11093     if (action != last_action)
11094       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11095     else
11096       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11097   }
11098   else
11099   {
11100     if (action != last_action)
11101       StopSound(element_info[sound_element].sound[last_action]);
11102
11103     if (last_action == ACTION_SLEEPING)
11104       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11105   }
11106 }
11107
11108 static void PlayAllPlayersSound()
11109 {
11110   int i;
11111
11112   for (i = 0; i < MAX_PLAYERS; i++)
11113     if (stored_player[i].active)
11114       PlayPlayerSound(&stored_player[i]);
11115 }
11116
11117 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11118 {
11119   boolean last_waiting = player->is_waiting;
11120   int move_dir = player->MovDir;
11121
11122   player->dir_waiting = move_dir;
11123   player->last_action_waiting = player->action_waiting;
11124
11125   if (is_waiting)
11126   {
11127     if (!last_waiting)          /* not waiting -> waiting */
11128     {
11129       player->is_waiting = TRUE;
11130
11131       player->frame_counter_bored =
11132         FrameCounter +
11133         game.player_boring_delay_fixed +
11134         GetSimpleRandom(game.player_boring_delay_random);
11135       player->frame_counter_sleeping =
11136         FrameCounter +
11137         game.player_sleeping_delay_fixed +
11138         GetSimpleRandom(game.player_sleeping_delay_random);
11139
11140       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11141     }
11142
11143     if (game.player_sleeping_delay_fixed +
11144         game.player_sleeping_delay_random > 0 &&
11145         player->anim_delay_counter == 0 &&
11146         player->post_delay_counter == 0 &&
11147         FrameCounter >= player->frame_counter_sleeping)
11148       player->is_sleeping = TRUE;
11149     else if (game.player_boring_delay_fixed +
11150              game.player_boring_delay_random > 0 &&
11151              FrameCounter >= player->frame_counter_bored)
11152       player->is_bored = TRUE;
11153
11154     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11155                               player->is_bored ? ACTION_BORING :
11156                               ACTION_WAITING);
11157
11158     if (player->is_sleeping && player->use_murphy)
11159     {
11160       /* special case for sleeping Murphy when leaning against non-free tile */
11161
11162       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11163           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11164            !IS_MOVING(player->jx - 1, player->jy)))
11165         move_dir = MV_LEFT;
11166       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11167                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11168                 !IS_MOVING(player->jx + 1, player->jy)))
11169         move_dir = MV_RIGHT;
11170       else
11171         player->is_sleeping = FALSE;
11172
11173       player->dir_waiting = move_dir;
11174     }
11175
11176     if (player->is_sleeping)
11177     {
11178       if (player->num_special_action_sleeping > 0)
11179       {
11180         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11181         {
11182           int last_special_action = player->special_action_sleeping;
11183           int num_special_action = player->num_special_action_sleeping;
11184           int special_action =
11185             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11186              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11187              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11188              last_special_action + 1 : ACTION_SLEEPING);
11189           int special_graphic =
11190             el_act_dir2img(player->artwork_element, special_action, move_dir);
11191
11192           player->anim_delay_counter =
11193             graphic_info[special_graphic].anim_delay_fixed +
11194             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11195           player->post_delay_counter =
11196             graphic_info[special_graphic].post_delay_fixed +
11197             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11198
11199           player->special_action_sleeping = special_action;
11200         }
11201
11202         if (player->anim_delay_counter > 0)
11203         {
11204           player->action_waiting = player->special_action_sleeping;
11205           player->anim_delay_counter--;
11206         }
11207         else if (player->post_delay_counter > 0)
11208         {
11209           player->post_delay_counter--;
11210         }
11211       }
11212     }
11213     else if (player->is_bored)
11214     {
11215       if (player->num_special_action_bored > 0)
11216       {
11217         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11218         {
11219           int special_action =
11220             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11221           int special_graphic =
11222             el_act_dir2img(player->artwork_element, special_action, move_dir);
11223
11224           player->anim_delay_counter =
11225             graphic_info[special_graphic].anim_delay_fixed +
11226             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11227           player->post_delay_counter =
11228             graphic_info[special_graphic].post_delay_fixed +
11229             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11230
11231           player->special_action_bored = special_action;
11232         }
11233
11234         if (player->anim_delay_counter > 0)
11235         {
11236           player->action_waiting = player->special_action_bored;
11237           player->anim_delay_counter--;
11238         }
11239         else if (player->post_delay_counter > 0)
11240         {
11241           player->post_delay_counter--;
11242         }
11243       }
11244     }
11245   }
11246   else if (last_waiting)        /* waiting -> not waiting */
11247   {
11248     player->is_waiting = FALSE;
11249     player->is_bored = FALSE;
11250     player->is_sleeping = FALSE;
11251
11252     player->frame_counter_bored = -1;
11253     player->frame_counter_sleeping = -1;
11254
11255     player->anim_delay_counter = 0;
11256     player->post_delay_counter = 0;
11257
11258     player->dir_waiting = player->MovDir;
11259     player->action_waiting = ACTION_DEFAULT;
11260
11261     player->special_action_bored = ACTION_DEFAULT;
11262     player->special_action_sleeping = ACTION_DEFAULT;
11263   }
11264 }
11265
11266 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11267 {
11268   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11269   int left      = player_action & JOY_LEFT;
11270   int right     = player_action & JOY_RIGHT;
11271   int up        = player_action & JOY_UP;
11272   int down      = player_action & JOY_DOWN;
11273   int button1   = player_action & JOY_BUTTON_1;
11274   int button2   = player_action & JOY_BUTTON_2;
11275   int dx        = (left ? -1 : right ? 1 : 0);
11276   int dy        = (up   ? -1 : down  ? 1 : 0);
11277
11278   if (!player->active || tape.pausing)
11279     return 0;
11280
11281   if (player_action)
11282   {
11283     if (button1)
11284       snapped = SnapField(player, dx, dy);
11285     else
11286     {
11287       if (button2)
11288         dropped = DropElement(player);
11289
11290       moved = MovePlayer(player, dx, dy);
11291     }
11292
11293     if (tape.single_step && tape.recording && !tape.pausing)
11294     {
11295       if (button1 || (dropped && !moved))
11296       {
11297         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11298         SnapField(player, 0, 0);                /* stop snapping */
11299       }
11300     }
11301
11302     SetPlayerWaiting(player, FALSE);
11303
11304     return player_action;
11305   }
11306   else
11307   {
11308     /* no actions for this player (no input at player's configured device) */
11309
11310     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11311     SnapField(player, 0, 0);
11312     CheckGravityMovementWhenNotMoving(player);
11313
11314     if (player->MovPos == 0)
11315       SetPlayerWaiting(player, TRUE);
11316
11317     if (player->MovPos == 0)    /* needed for tape.playing */
11318       player->is_moving = FALSE;
11319
11320     player->is_dropping = FALSE;
11321     player->is_dropping_pressed = FALSE;
11322     player->drop_pressed_delay = 0;
11323
11324     return 0;
11325   }
11326 }
11327
11328 static void CheckLevelTime()
11329 {
11330   int i;
11331
11332   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11333   {
11334     if (level.native_em_level->lev->home == 0)  /* all players at home */
11335     {
11336       PlayerWins(local_player);
11337
11338       AllPlayersGone = TRUE;
11339
11340       level.native_em_level->lev->home = -1;
11341     }
11342
11343     if (level.native_em_level->ply[0]->alive == 0 &&
11344         level.native_em_level->ply[1]->alive == 0 &&
11345         level.native_em_level->ply[2]->alive == 0 &&
11346         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11347       AllPlayersGone = TRUE;
11348   }
11349
11350   if (TimeFrames >= FRAMES_PER_SECOND)
11351   {
11352     TimeFrames = 0;
11353     TapeTime++;
11354
11355     for (i = 0; i < MAX_PLAYERS; i++)
11356     {
11357       struct PlayerInfo *player = &stored_player[i];
11358
11359       if (SHIELD_ON(player))
11360       {
11361         player->shield_normal_time_left--;
11362
11363         if (player->shield_deadly_time_left > 0)
11364           player->shield_deadly_time_left--;
11365       }
11366     }
11367
11368     if (!local_player->LevelSolved && !level.use_step_counter)
11369     {
11370       TimePlayed++;
11371
11372       if (TimeLeft > 0)
11373       {
11374         TimeLeft--;
11375
11376         if (TimeLeft <= 10 && setup.time_limit)
11377           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11378
11379 #if 1
11380         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11381
11382         DisplayGameControlValues();
11383 #else
11384         DrawGameValue_Time(TimeLeft);
11385 #endif
11386
11387         if (!TimeLeft && setup.time_limit)
11388         {
11389           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11390             level.native_em_level->lev->killed_out_of_time = TRUE;
11391           else
11392             for (i = 0; i < MAX_PLAYERS; i++)
11393               KillPlayer(&stored_player[i]);
11394         }
11395       }
11396 #if 1
11397       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11398       {
11399         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11400
11401         DisplayGameControlValues();
11402       }
11403 #else
11404       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11405         DrawGameValue_Time(TimePlayed);
11406 #endif
11407
11408       level.native_em_level->lev->time =
11409         (level.time == 0 ? TimePlayed : TimeLeft);
11410     }
11411
11412     if (tape.recording || tape.playing)
11413       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11414   }
11415
11416   UpdateGameDoorValues();
11417   DrawGameDoorValues();
11418 }
11419
11420 void AdvanceFrameAndPlayerCounters(int player_nr)
11421 {
11422   int i;
11423
11424   /* advance frame counters (global frame counter and time frame counter) */
11425   FrameCounter++;
11426   TimeFrames++;
11427
11428   /* advance player counters (counters for move delay, move animation etc.) */
11429   for (i = 0; i < MAX_PLAYERS; i++)
11430   {
11431     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11432     int move_delay_value = stored_player[i].move_delay_value;
11433     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11434
11435     if (!advance_player_counters)       /* not all players may be affected */
11436       continue;
11437
11438 #if USE_NEW_PLAYER_ANIM
11439     if (move_frames == 0)       /* less than one move per game frame */
11440     {
11441       int stepsize = TILEX / move_delay_value;
11442       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11443       int count = (stored_player[i].is_moving ?
11444                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11445
11446       if (count % delay == 0)
11447         move_frames = 1;
11448     }
11449 #endif
11450
11451     stored_player[i].Frame += move_frames;
11452
11453     if (stored_player[i].MovPos != 0)
11454       stored_player[i].StepFrame += move_frames;
11455
11456     if (stored_player[i].move_delay > 0)
11457       stored_player[i].move_delay--;
11458
11459     /* due to bugs in previous versions, counter must count up, not down */
11460     if (stored_player[i].push_delay != -1)
11461       stored_player[i].push_delay++;
11462
11463     if (stored_player[i].drop_delay > 0)
11464       stored_player[i].drop_delay--;
11465
11466     if (stored_player[i].is_dropping_pressed)
11467       stored_player[i].drop_pressed_delay++;
11468   }
11469 }
11470
11471 void StartGameActions(boolean init_network_game, boolean record_tape,
11472                       long random_seed)
11473 {
11474   unsigned long new_random_seed = InitRND(random_seed);
11475
11476   if (record_tape)
11477     TapeStartRecording(new_random_seed);
11478
11479 #if defined(NETWORK_AVALIABLE)
11480   if (init_network_game)
11481   {
11482     SendToServer_StartPlaying();
11483
11484     return;
11485   }
11486 #endif
11487
11488   InitGame();
11489 }
11490
11491 void GameActions()
11492 {
11493   static unsigned long game_frame_delay = 0;
11494   unsigned long game_frame_delay_value;
11495   byte *recorded_player_action;
11496   byte summarized_player_action = 0;
11497   byte tape_action[MAX_PLAYERS];
11498   int i;
11499
11500   /* detect endless loops, caused by custom element programming */
11501   if (recursion_loop_detected && recursion_loop_depth == 0)
11502   {
11503     char *message = getStringCat3("Internal Error ! Element ",
11504                                   EL_NAME(recursion_loop_element),
11505                                   " caused endless loop ! Quit the game ?");
11506
11507     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11508           EL_NAME(recursion_loop_element));
11509
11510     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11511
11512     recursion_loop_detected = FALSE;    /* if game should be continued */
11513
11514     free(message);
11515
11516     return;
11517   }
11518
11519   if (game.restart_level)
11520     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11521
11522   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11523   {
11524     if (level.native_em_level->lev->home == 0)  /* all players at home */
11525     {
11526       PlayerWins(local_player);
11527
11528       AllPlayersGone = TRUE;
11529
11530       level.native_em_level->lev->home = -1;
11531     }
11532
11533     if (level.native_em_level->ply[0]->alive == 0 &&
11534         level.native_em_level->ply[1]->alive == 0 &&
11535         level.native_em_level->ply[2]->alive == 0 &&
11536         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11537       AllPlayersGone = TRUE;
11538   }
11539
11540   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11541     GameWon();
11542
11543   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11544     TapeStop();
11545
11546   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11547     return;
11548
11549   game_frame_delay_value =
11550     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11551
11552   if (tape.playing && tape.warp_forward && !tape.pausing)
11553     game_frame_delay_value = 0;
11554
11555   /* ---------- main game synchronization point ---------- */
11556
11557   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11558
11559   if (network_playing && !network_player_action_received)
11560   {
11561     /* try to get network player actions in time */
11562
11563 #if defined(NETWORK_AVALIABLE)
11564     /* last chance to get network player actions without main loop delay */
11565     HandleNetworking();
11566 #endif
11567
11568     /* game was quit by network peer */
11569     if (game_status != GAME_MODE_PLAYING)
11570       return;
11571
11572     if (!network_player_action_received)
11573       return;           /* failed to get network player actions in time */
11574
11575     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11576   }
11577
11578   if (tape.pausing)
11579     return;
11580
11581   /* at this point we know that we really continue executing the game */
11582
11583   network_player_action_received = FALSE;
11584
11585   /* when playing tape, read previously recorded player input from tape data */
11586   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11587
11588 #if 1
11589   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11590   if (tape.pausing)
11591     return;
11592 #endif
11593
11594   if (tape.set_centered_player)
11595   {
11596     game.centered_player_nr_next = tape.centered_player_nr_next;
11597     game.set_centered_player = TRUE;
11598   }
11599
11600   for (i = 0; i < MAX_PLAYERS; i++)
11601   {
11602     summarized_player_action |= stored_player[i].action;
11603
11604     if (!network_playing)
11605       stored_player[i].effective_action = stored_player[i].action;
11606   }
11607
11608 #if defined(NETWORK_AVALIABLE)
11609   if (network_playing)
11610     SendToServer_MovePlayer(summarized_player_action);
11611 #endif
11612
11613   if (!options.network && !setup.team_mode)
11614     local_player->effective_action = summarized_player_action;
11615
11616   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11617   {
11618     for (i = 0; i < MAX_PLAYERS; i++)
11619       stored_player[i].effective_action =
11620         (i == game.centered_player_nr ? summarized_player_action : 0);
11621   }
11622
11623   if (recorded_player_action != NULL)
11624     for (i = 0; i < MAX_PLAYERS; i++)
11625       stored_player[i].effective_action = recorded_player_action[i];
11626
11627   for (i = 0; i < MAX_PLAYERS; i++)
11628   {
11629     tape_action[i] = stored_player[i].effective_action;
11630
11631     /* (this can only happen in the R'n'D game engine) */
11632     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11633       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11634   }
11635
11636   /* only record actions from input devices, but not programmed actions */
11637   if (tape.recording)
11638     TapeRecordAction(tape_action);
11639
11640   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11641   {
11642     GameActions_EM_Main();
11643   }
11644   else
11645   {
11646     GameActions_RND();
11647   }
11648 }
11649
11650 void GameActions_EM_Main()
11651 {
11652   byte effective_action[MAX_PLAYERS];
11653   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11654   int i;
11655
11656   for (i = 0; i < MAX_PLAYERS; i++)
11657     effective_action[i] = stored_player[i].effective_action;
11658
11659   GameActions_EM(effective_action, warp_mode);
11660
11661   CheckLevelTime();
11662
11663   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11664 }
11665
11666 void GameActions_RND()
11667 {
11668   int magic_wall_x = 0, magic_wall_y = 0;
11669   int i, x, y, element, graphic;
11670
11671   InitPlayfieldScanModeVars();
11672
11673 #if USE_ONE_MORE_CHANGE_PER_FRAME
11674   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11675   {
11676     SCAN_PLAYFIELD(x, y)
11677     {
11678       ChangeCount[x][y] = 0;
11679       ChangeEvent[x][y] = -1;
11680     }
11681   }
11682 #endif
11683
11684   if (game.set_centered_player)
11685   {
11686     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11687
11688     /* switching to "all players" only possible if all players fit to screen */
11689     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11690     {
11691       game.centered_player_nr_next = game.centered_player_nr;
11692       game.set_centered_player = FALSE;
11693     }
11694
11695     /* do not switch focus to non-existing (or non-active) player */
11696     if (game.centered_player_nr_next >= 0 &&
11697         !stored_player[game.centered_player_nr_next].active)
11698     {
11699       game.centered_player_nr_next = game.centered_player_nr;
11700       game.set_centered_player = FALSE;
11701     }
11702   }
11703
11704   if (game.set_centered_player &&
11705       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11706   {
11707     int sx, sy;
11708
11709     if (game.centered_player_nr_next == -1)
11710     {
11711       setScreenCenteredToAllPlayers(&sx, &sy);
11712     }
11713     else
11714     {
11715       sx = stored_player[game.centered_player_nr_next].jx;
11716       sy = stored_player[game.centered_player_nr_next].jy;
11717     }
11718
11719     game.centered_player_nr = game.centered_player_nr_next;
11720     game.set_centered_player = FALSE;
11721
11722     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11723     DrawGameDoorValues();
11724   }
11725
11726   for (i = 0; i < MAX_PLAYERS; i++)
11727   {
11728     int actual_player_action = stored_player[i].effective_action;
11729
11730 #if 1
11731     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11732        - rnd_equinox_tetrachloride 048
11733        - rnd_equinox_tetrachloride_ii 096
11734        - rnd_emanuel_schmieg 002
11735        - doctor_sloan_ww 001, 020
11736     */
11737     if (stored_player[i].MovPos == 0)
11738       CheckGravityMovement(&stored_player[i]);
11739 #endif
11740
11741     /* overwrite programmed action with tape action */
11742     if (stored_player[i].programmed_action)
11743       actual_player_action = stored_player[i].programmed_action;
11744
11745     PlayerActions(&stored_player[i], actual_player_action);
11746
11747     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11748   }
11749
11750   ScrollScreen(NULL, SCROLL_GO_ON);
11751
11752   /* for backwards compatibility, the following code emulates a fixed bug that
11753      occured when pushing elements (causing elements that just made their last
11754      pushing step to already (if possible) make their first falling step in the
11755      same game frame, which is bad); this code is also needed to use the famous
11756      "spring push bug" which is used in older levels and might be wanted to be
11757      used also in newer levels, but in this case the buggy pushing code is only
11758      affecting the "spring" element and no other elements */
11759
11760   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11761   {
11762     for (i = 0; i < MAX_PLAYERS; i++)
11763     {
11764       struct PlayerInfo *player = &stored_player[i];
11765       int x = player->jx;
11766       int y = player->jy;
11767
11768       if (player->active && player->is_pushing && player->is_moving &&
11769           IS_MOVING(x, y) &&
11770           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11771            Feld[x][y] == EL_SPRING))
11772       {
11773         ContinueMoving(x, y);
11774
11775         /* continue moving after pushing (this is actually a bug) */
11776         if (!IS_MOVING(x, y))
11777           Stop[x][y] = FALSE;
11778       }
11779     }
11780   }
11781
11782 #if 0
11783   debug_print_timestamp(0, "start main loop profiling");
11784 #endif
11785
11786   SCAN_PLAYFIELD(x, y)
11787   {
11788     ChangeCount[x][y] = 0;
11789     ChangeEvent[x][y] = -1;
11790
11791     /* this must be handled before main playfield loop */
11792     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11793     {
11794       MovDelay[x][y]--;
11795       if (MovDelay[x][y] <= 0)
11796         RemoveField(x, y);
11797     }
11798
11799 #if USE_NEW_SNAP_DELAY
11800     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11801     {
11802       MovDelay[x][y]--;
11803       if (MovDelay[x][y] <= 0)
11804       {
11805         RemoveField(x, y);
11806         DrawLevelField(x, y);
11807
11808         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11809       }
11810     }
11811 #endif
11812
11813 #if DEBUG
11814     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11815     {
11816       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11817       printf("GameActions(): This should never happen!\n");
11818
11819       ChangePage[x][y] = -1;
11820     }
11821 #endif
11822
11823     Stop[x][y] = FALSE;
11824     if (WasJustMoving[x][y] > 0)
11825       WasJustMoving[x][y]--;
11826     if (WasJustFalling[x][y] > 0)
11827       WasJustFalling[x][y]--;
11828     if (CheckCollision[x][y] > 0)
11829       CheckCollision[x][y]--;
11830     if (CheckImpact[x][y] > 0)
11831       CheckImpact[x][y]--;
11832
11833     GfxFrame[x][y]++;
11834
11835     /* reset finished pushing action (not done in ContinueMoving() to allow
11836        continuous pushing animation for elements with zero push delay) */
11837     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11838     {
11839       ResetGfxAnimation(x, y);
11840       DrawLevelField(x, y);
11841     }
11842
11843 #if DEBUG
11844     if (IS_BLOCKED(x, y))
11845     {
11846       int oldx, oldy;
11847
11848       Blocked2Moving(x, y, &oldx, &oldy);
11849       if (!IS_MOVING(oldx, oldy))
11850       {
11851         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11852         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11853         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11854         printf("GameActions(): This should never happen!\n");
11855       }
11856     }
11857 #endif
11858   }
11859
11860 #if 0
11861   debug_print_timestamp(0, "- time for pre-main loop:");
11862 #endif
11863
11864 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11865   SCAN_PLAYFIELD(x, y)
11866   {
11867     element = Feld[x][y];
11868     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11869
11870 #if 1
11871     {
11872 #if 1
11873       int element2 = element;
11874       int graphic2 = graphic;
11875 #else
11876       int element2 = Feld[x][y];
11877       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11878 #endif
11879       int last_gfx_frame = GfxFrame[x][y];
11880
11881       if (graphic_info[graphic2].anim_global_sync)
11882         GfxFrame[x][y] = FrameCounter;
11883       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11884         GfxFrame[x][y] = CustomValue[x][y];
11885       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11886         GfxFrame[x][y] = element_info[element2].collect_score;
11887       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11888         GfxFrame[x][y] = ChangeDelay[x][y];
11889
11890       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11891         DrawLevelGraphicAnimation(x, y, graphic2);
11892     }
11893 #else
11894     ResetGfxFrame(x, y, TRUE);
11895 #endif
11896
11897 #if 1
11898     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11899         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11900       ResetRandomAnimationValue(x, y);
11901 #endif
11902
11903 #if 1
11904     SetRandomAnimationValue(x, y);
11905 #endif
11906
11907 #if 1
11908     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11909 #endif
11910   }
11911 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11912
11913 #if 0
11914   debug_print_timestamp(0, "- time for TEST loop:     -->");
11915 #endif
11916
11917   SCAN_PLAYFIELD(x, y)
11918   {
11919     element = Feld[x][y];
11920     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11921
11922     ResetGfxFrame(x, y, TRUE);
11923
11924     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11925         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11926       ResetRandomAnimationValue(x, y);
11927
11928     SetRandomAnimationValue(x, y);
11929
11930     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11931
11932     if (IS_INACTIVE(element))
11933     {
11934       if (IS_ANIMATED(graphic))
11935         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11936
11937       continue;
11938     }
11939
11940     /* this may take place after moving, so 'element' may have changed */
11941     if (IS_CHANGING(x, y) &&
11942         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11943     {
11944       int page = element_info[element].event_page_nr[CE_DELAY];
11945
11946 #if 1
11947       HandleElementChange(x, y, page);
11948 #else
11949       if (CAN_CHANGE(element))
11950         HandleElementChange(x, y, page);
11951
11952       if (HAS_ACTION(element))
11953         ExecuteCustomElementAction(x, y, element, page);
11954 #endif
11955
11956       element = Feld[x][y];
11957       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11958     }
11959
11960 #if 0   // ---------------------------------------------------------------------
11961
11962     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11963     {
11964       StartMoving(x, y);
11965
11966       element = Feld[x][y];
11967       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11968
11969       if (IS_ANIMATED(graphic) &&
11970           !IS_MOVING(x, y) &&
11971           !Stop[x][y])
11972         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11973
11974       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11975         DrawTwinkleOnField(x, y);
11976     }
11977     else if (IS_MOVING(x, y))
11978       ContinueMoving(x, y);
11979     else
11980     {
11981       switch (element)
11982       {
11983         case EL_ACID:
11984         case EL_EXIT_OPEN:
11985         case EL_EM_EXIT_OPEN:
11986         case EL_SP_EXIT_OPEN:
11987         case EL_STEEL_EXIT_OPEN:
11988         case EL_EM_STEEL_EXIT_OPEN:
11989         case EL_SP_TERMINAL:
11990         case EL_SP_TERMINAL_ACTIVE:
11991         case EL_EXTRA_TIME:
11992         case EL_SHIELD_NORMAL:
11993         case EL_SHIELD_DEADLY:
11994           if (IS_ANIMATED(graphic))
11995             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11996           break;
11997
11998         case EL_DYNAMITE_ACTIVE:
11999         case EL_EM_DYNAMITE_ACTIVE:
12000         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12001         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12002         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12003         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12004         case EL_SP_DISK_RED_ACTIVE:
12005           CheckDynamite(x, y);
12006           break;
12007
12008         case EL_AMOEBA_GROWING:
12009           AmoebeWaechst(x, y);
12010           break;
12011
12012         case EL_AMOEBA_SHRINKING:
12013           AmoebaDisappearing(x, y);
12014           break;
12015
12016 #if !USE_NEW_AMOEBA_CODE
12017         case EL_AMOEBA_WET:
12018         case EL_AMOEBA_DRY:
12019         case EL_AMOEBA_FULL:
12020         case EL_BD_AMOEBA:
12021         case EL_EMC_DRIPPER:
12022           AmoebeAbleger(x, y);
12023           break;
12024 #endif
12025
12026         case EL_GAME_OF_LIFE:
12027         case EL_BIOMAZE:
12028           Life(x, y);
12029           break;
12030
12031         case EL_EXIT_CLOSED:
12032           CheckExit(x, y);
12033           break;
12034
12035         case EL_EM_EXIT_CLOSED:
12036           CheckExitEM(x, y);
12037           break;
12038
12039         case EL_STEEL_EXIT_CLOSED:
12040           CheckExitSteel(x, y);
12041           break;
12042
12043         case EL_EM_STEEL_EXIT_CLOSED:
12044           CheckExitSteelEM(x, y);
12045           break;
12046
12047         case EL_SP_EXIT_CLOSED:
12048           CheckExitSP(x, y);
12049           break;
12050
12051         case EL_EXPANDABLE_WALL_GROWING:
12052         case EL_EXPANDABLE_STEELWALL_GROWING:
12053           MauerWaechst(x, y);
12054           break;
12055
12056         case EL_EXPANDABLE_WALL:
12057         case EL_EXPANDABLE_WALL_HORIZONTAL:
12058         case EL_EXPANDABLE_WALL_VERTICAL:
12059         case EL_EXPANDABLE_WALL_ANY:
12060         case EL_BD_EXPANDABLE_WALL:
12061           MauerAbleger(x, y);
12062           break;
12063
12064         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12065         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12066         case EL_EXPANDABLE_STEELWALL_ANY:
12067           MauerAblegerStahl(x, y);
12068           break;
12069
12070         case EL_FLAMES:
12071           CheckForDragon(x, y);
12072           break;
12073
12074         case EL_EXPLOSION:
12075           break;
12076
12077         case EL_ELEMENT_SNAPPING:
12078         case EL_DIAGONAL_SHRINKING:
12079         case EL_DIAGONAL_GROWING:
12080         {
12081           graphic =
12082             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12083
12084           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12085           break;
12086         }
12087
12088         default:
12089           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12090             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12091           break;
12092       }
12093     }
12094
12095 #else   // ---------------------------------------------------------------------
12096
12097     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12098     {
12099       StartMoving(x, y);
12100
12101       element = Feld[x][y];
12102       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12103
12104       if (IS_ANIMATED(graphic) &&
12105           !IS_MOVING(x, y) &&
12106           !Stop[x][y])
12107         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12108
12109       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12110         DrawTwinkleOnField(x, y);
12111     }
12112     else if ((element == EL_ACID ||
12113               element == EL_EXIT_OPEN ||
12114               element == EL_EM_EXIT_OPEN ||
12115               element == EL_SP_EXIT_OPEN ||
12116               element == EL_STEEL_EXIT_OPEN ||
12117               element == EL_EM_STEEL_EXIT_OPEN ||
12118               element == EL_SP_TERMINAL ||
12119               element == EL_SP_TERMINAL_ACTIVE ||
12120               element == EL_EXTRA_TIME ||
12121               element == EL_SHIELD_NORMAL ||
12122               element == EL_SHIELD_DEADLY) &&
12123              IS_ANIMATED(graphic))
12124       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12125     else if (IS_MOVING(x, y))
12126       ContinueMoving(x, y);
12127     else if (IS_ACTIVE_BOMB(element))
12128       CheckDynamite(x, y);
12129     else if (element == EL_AMOEBA_GROWING)
12130       AmoebeWaechst(x, y);
12131     else if (element == EL_AMOEBA_SHRINKING)
12132       AmoebaDisappearing(x, y);
12133
12134 #if !USE_NEW_AMOEBA_CODE
12135     else if (IS_AMOEBALIVE(element))
12136       AmoebeAbleger(x, y);
12137 #endif
12138
12139     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12140       Life(x, y);
12141     else if (element == EL_EXIT_CLOSED)
12142       CheckExit(x, y);
12143     else if (element == EL_EM_EXIT_CLOSED)
12144       CheckExitEM(x, y);
12145     else if (element == EL_STEEL_EXIT_CLOSED)
12146       CheckExitSteel(x, y);
12147     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12148       CheckExitSteelEM(x, y);
12149     else if (element == EL_SP_EXIT_CLOSED)
12150       CheckExitSP(x, y);
12151     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12152              element == EL_EXPANDABLE_STEELWALL_GROWING)
12153       MauerWaechst(x, y);
12154     else if (element == EL_EXPANDABLE_WALL ||
12155              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12156              element == EL_EXPANDABLE_WALL_VERTICAL ||
12157              element == EL_EXPANDABLE_WALL_ANY ||
12158              element == EL_BD_EXPANDABLE_WALL)
12159       MauerAbleger(x, y);
12160     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12161              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12162              element == EL_EXPANDABLE_STEELWALL_ANY)
12163       MauerAblegerStahl(x, y);
12164     else if (element == EL_FLAMES)
12165       CheckForDragon(x, y);
12166     else if (element == EL_EXPLOSION)
12167       ; /* drawing of correct explosion animation is handled separately */
12168     else if (element == EL_ELEMENT_SNAPPING ||
12169              element == EL_DIAGONAL_SHRINKING ||
12170              element == EL_DIAGONAL_GROWING)
12171     {
12172       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12173
12174       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12175     }
12176     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12177       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12178
12179 #endif  // ---------------------------------------------------------------------
12180
12181     if (IS_BELT_ACTIVE(element))
12182       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12183
12184     if (game.magic_wall_active)
12185     {
12186       int jx = local_player->jx, jy = local_player->jy;
12187
12188       /* play the element sound at the position nearest to the player */
12189       if ((element == EL_MAGIC_WALL_FULL ||
12190            element == EL_MAGIC_WALL_ACTIVE ||
12191            element == EL_MAGIC_WALL_EMPTYING ||
12192            element == EL_BD_MAGIC_WALL_FULL ||
12193            element == EL_BD_MAGIC_WALL_ACTIVE ||
12194            element == EL_BD_MAGIC_WALL_EMPTYING ||
12195            element == EL_DC_MAGIC_WALL_FULL ||
12196            element == EL_DC_MAGIC_WALL_ACTIVE ||
12197            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12198           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12199       {
12200         magic_wall_x = x;
12201         magic_wall_y = y;
12202       }
12203     }
12204   }
12205
12206 #if 0
12207   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12208 #endif
12209
12210 #if USE_NEW_AMOEBA_CODE
12211   /* new experimental amoeba growth stuff */
12212   if (!(FrameCounter % 8))
12213   {
12214     static unsigned long random = 1684108901;
12215
12216     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12217     {
12218       x = RND(lev_fieldx);
12219       y = RND(lev_fieldy);
12220       element = Feld[x][y];
12221
12222       if (!IS_PLAYER(x,y) &&
12223           (element == EL_EMPTY ||
12224            CAN_GROW_INTO(element) ||
12225            element == EL_QUICKSAND_EMPTY ||
12226            element == EL_QUICKSAND_FAST_EMPTY ||
12227            element == EL_ACID_SPLASH_LEFT ||
12228            element == EL_ACID_SPLASH_RIGHT))
12229       {
12230         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12231             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12232             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12233             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12234           Feld[x][y] = EL_AMOEBA_DROP;
12235       }
12236
12237       random = random * 129 + 1;
12238     }
12239   }
12240 #endif
12241
12242 #if 0
12243   if (game.explosions_delayed)
12244 #endif
12245   {
12246     game.explosions_delayed = FALSE;
12247
12248     SCAN_PLAYFIELD(x, y)
12249     {
12250       element = Feld[x][y];
12251
12252       if (ExplodeField[x][y])
12253         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12254       else if (element == EL_EXPLOSION)
12255         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12256
12257       ExplodeField[x][y] = EX_TYPE_NONE;
12258     }
12259
12260     game.explosions_delayed = TRUE;
12261   }
12262
12263   if (game.magic_wall_active)
12264   {
12265     if (!(game.magic_wall_time_left % 4))
12266     {
12267       int element = Feld[magic_wall_x][magic_wall_y];
12268
12269       if (element == EL_BD_MAGIC_WALL_FULL ||
12270           element == EL_BD_MAGIC_WALL_ACTIVE ||
12271           element == EL_BD_MAGIC_WALL_EMPTYING)
12272         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12273       else if (element == EL_DC_MAGIC_WALL_FULL ||
12274                element == EL_DC_MAGIC_WALL_ACTIVE ||
12275                element == EL_DC_MAGIC_WALL_EMPTYING)
12276         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12277       else
12278         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12279     }
12280
12281     if (game.magic_wall_time_left > 0)
12282     {
12283       game.magic_wall_time_left--;
12284
12285       if (!game.magic_wall_time_left)
12286       {
12287         SCAN_PLAYFIELD(x, y)
12288         {
12289           element = Feld[x][y];
12290
12291           if (element == EL_MAGIC_WALL_ACTIVE ||
12292               element == EL_MAGIC_WALL_FULL)
12293           {
12294             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12295             DrawLevelField(x, y);
12296           }
12297           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12298                    element == EL_BD_MAGIC_WALL_FULL)
12299           {
12300             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12301             DrawLevelField(x, y);
12302           }
12303           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12304                    element == EL_DC_MAGIC_WALL_FULL)
12305           {
12306             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12307             DrawLevelField(x, y);
12308           }
12309         }
12310
12311         game.magic_wall_active = FALSE;
12312       }
12313     }
12314   }
12315
12316   if (game.light_time_left > 0)
12317   {
12318     game.light_time_left--;
12319
12320     if (game.light_time_left == 0)
12321       RedrawAllLightSwitchesAndInvisibleElements();
12322   }
12323
12324   if (game.timegate_time_left > 0)
12325   {
12326     game.timegate_time_left--;
12327
12328     if (game.timegate_time_left == 0)
12329       CloseAllOpenTimegates();
12330   }
12331
12332   if (game.lenses_time_left > 0)
12333   {
12334     game.lenses_time_left--;
12335
12336     if (game.lenses_time_left == 0)
12337       RedrawAllInvisibleElementsForLenses();
12338   }
12339
12340   if (game.magnify_time_left > 0)
12341   {
12342     game.magnify_time_left--;
12343
12344     if (game.magnify_time_left == 0)
12345       RedrawAllInvisibleElementsForMagnifier();
12346   }
12347
12348   for (i = 0; i < MAX_PLAYERS; i++)
12349   {
12350     struct PlayerInfo *player = &stored_player[i];
12351
12352     if (SHIELD_ON(player))
12353     {
12354       if (player->shield_deadly_time_left)
12355         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12356       else if (player->shield_normal_time_left)
12357         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12358     }
12359   }
12360
12361   CheckLevelTime();
12362
12363   DrawAllPlayers();
12364   PlayAllPlayersSound();
12365
12366   if (options.debug)                    /* calculate frames per second */
12367   {
12368     static unsigned long fps_counter = 0;
12369     static int fps_frames = 0;
12370     unsigned long fps_delay_ms = Counter() - fps_counter;
12371
12372     fps_frames++;
12373
12374     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12375     {
12376       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12377
12378       fps_frames = 0;
12379       fps_counter = Counter();
12380     }
12381
12382     redraw_mask |= REDRAW_FPS;
12383   }
12384
12385   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12386
12387   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12388   {
12389     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12390
12391     local_player->show_envelope = 0;
12392   }
12393
12394 #if 0
12395   debug_print_timestamp(0, "stop main loop profiling ");
12396   printf("----------------------------------------------------------\n");
12397 #endif
12398
12399   /* use random number generator in every frame to make it less predictable */
12400   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12401     RND(1);
12402 }
12403
12404 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12405 {
12406   int min_x = x, min_y = y, max_x = x, max_y = y;
12407   int i;
12408
12409   for (i = 0; i < MAX_PLAYERS; i++)
12410   {
12411     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12412
12413     if (!stored_player[i].active || &stored_player[i] == player)
12414       continue;
12415
12416     min_x = MIN(min_x, jx);
12417     min_y = MIN(min_y, jy);
12418     max_x = MAX(max_x, jx);
12419     max_y = MAX(max_y, jy);
12420   }
12421
12422   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12423 }
12424
12425 static boolean AllPlayersInVisibleScreen()
12426 {
12427   int i;
12428
12429   for (i = 0; i < MAX_PLAYERS; i++)
12430   {
12431     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12432
12433     if (!stored_player[i].active)
12434       continue;
12435
12436     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12437       return FALSE;
12438   }
12439
12440   return TRUE;
12441 }
12442
12443 void ScrollLevel(int dx, int dy)
12444 {
12445 #if 1
12446   static Bitmap *bitmap_db_field2 = NULL;
12447   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12448   int x, y;
12449 #else
12450   int i, x, y;
12451 #endif
12452
12453 #if 0
12454   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12455   /* only horizontal XOR vertical scroll direction allowed */
12456   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12457     return;
12458 #endif
12459
12460 #if 1
12461   if (bitmap_db_field2 == NULL)
12462     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12463
12464   /* needed when blitting directly to same bitmap -- should not be needed with
12465      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12466   BlitBitmap(drawto_field, bitmap_db_field2,
12467              FX + TILEX * (dx == -1) - softscroll_offset,
12468              FY + TILEY * (dy == -1) - softscroll_offset,
12469              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12470              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12471              FX + TILEX * (dx == 1) - softscroll_offset,
12472              FY + TILEY * (dy == 1) - softscroll_offset);
12473   BlitBitmap(bitmap_db_field2, drawto_field,
12474              FX + TILEX * (dx == 1) - softscroll_offset,
12475              FY + TILEY * (dy == 1) - softscroll_offset,
12476              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12477              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12478              FX + TILEX * (dx == 1) - softscroll_offset,
12479              FY + TILEY * (dy == 1) - softscroll_offset);
12480
12481 #else
12482
12483 #if 1
12484   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12485   int xsize = (BX2 - BX1 + 1);
12486   int ysize = (BY2 - BY1 + 1);
12487   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12488   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12489   int step  = (start < end ? +1 : -1);
12490
12491   for (i = start; i != end; i += step)
12492   {
12493     BlitBitmap(drawto_field, drawto_field,
12494                FX + TILEX * (dx != 0 ? i + step : 0),
12495                FY + TILEY * (dy != 0 ? i + step : 0),
12496                TILEX * (dx != 0 ? 1 : xsize),
12497                TILEY * (dy != 0 ? 1 : ysize),
12498                FX + TILEX * (dx != 0 ? i : 0),
12499                FY + TILEY * (dy != 0 ? i : 0));
12500   }
12501
12502 #else
12503
12504   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12505
12506   BlitBitmap(drawto_field, drawto_field,
12507              FX + TILEX * (dx == -1) - softscroll_offset,
12508              FY + TILEY * (dy == -1) - softscroll_offset,
12509              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12510              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12511              FX + TILEX * (dx == 1) - softscroll_offset,
12512              FY + TILEY * (dy == 1) - softscroll_offset);
12513 #endif
12514 #endif
12515
12516   if (dx != 0)
12517   {
12518     x = (dx == 1 ? BX1 : BX2);
12519     for (y = BY1; y <= BY2; y++)
12520       DrawScreenField(x, y);
12521   }
12522
12523   if (dy != 0)
12524   {
12525     y = (dy == 1 ? BY1 : BY2);
12526     for (x = BX1; x <= BX2; x++)
12527       DrawScreenField(x, y);
12528   }
12529
12530   redraw_mask |= REDRAW_FIELD;
12531 }
12532
12533 static boolean canFallDown(struct PlayerInfo *player)
12534 {
12535   int jx = player->jx, jy = player->jy;
12536
12537   return (IN_LEV_FIELD(jx, jy + 1) &&
12538           (IS_FREE(jx, jy + 1) ||
12539            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12540           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12541           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12542 }
12543
12544 static boolean canPassField(int x, int y, int move_dir)
12545 {
12546   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12547   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12548   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12549   int nextx = x + dx;
12550   int nexty = y + dy;
12551   int element = Feld[x][y];
12552
12553   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12554           !CAN_MOVE(element) &&
12555           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12556           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12557           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12558 }
12559
12560 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12561 {
12562   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12563   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12564   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12565   int newx = x + dx;
12566   int newy = y + dy;
12567
12568   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12569           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12570           (IS_DIGGABLE(Feld[newx][newy]) ||
12571            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12572            canPassField(newx, newy, move_dir)));
12573 }
12574
12575 static void CheckGravityMovement(struct PlayerInfo *player)
12576 {
12577 #if USE_PLAYER_GRAVITY
12578   if (player->gravity && !player->programmed_action)
12579 #else
12580   if (game.gravity && !player->programmed_action)
12581 #endif
12582   {
12583     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12584     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12585     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12586     int jx = player->jx, jy = player->jy;
12587     boolean player_is_moving_to_valid_field =
12588       (!player_is_snapping &&
12589        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12590         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12591     boolean player_can_fall_down = canFallDown(player);
12592
12593     if (player_can_fall_down &&
12594         !player_is_moving_to_valid_field)
12595       player->programmed_action = MV_DOWN;
12596   }
12597 }
12598
12599 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12600 {
12601   return CheckGravityMovement(player);
12602
12603 #if USE_PLAYER_GRAVITY
12604   if (player->gravity && !player->programmed_action)
12605 #else
12606   if (game.gravity && !player->programmed_action)
12607 #endif
12608   {
12609     int jx = player->jx, jy = player->jy;
12610     boolean field_under_player_is_free =
12611       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12612     boolean player_is_standing_on_valid_field =
12613       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12614        (IS_WALKABLE(Feld[jx][jy]) &&
12615         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12616
12617     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12618       player->programmed_action = MV_DOWN;
12619   }
12620 }
12621
12622 /*
12623   MovePlayerOneStep()
12624   -----------------------------------------------------------------------------
12625   dx, dy:               direction (non-diagonal) to try to move the player to
12626   real_dx, real_dy:     direction as read from input device (can be diagonal)
12627 */
12628
12629 boolean MovePlayerOneStep(struct PlayerInfo *player,
12630                           int dx, int dy, int real_dx, int real_dy)
12631 {
12632   int jx = player->jx, jy = player->jy;
12633   int new_jx = jx + dx, new_jy = jy + dy;
12634 #if !USE_FIXED_DONT_RUN_INTO
12635   int element;
12636 #endif
12637   int can_move;
12638   boolean player_can_move = !player->cannot_move;
12639
12640   if (!player->active || (!dx && !dy))
12641     return MP_NO_ACTION;
12642
12643   player->MovDir = (dx < 0 ? MV_LEFT :
12644                     dx > 0 ? MV_RIGHT :
12645                     dy < 0 ? MV_UP :
12646                     dy > 0 ? MV_DOWN :  MV_NONE);
12647
12648   if (!IN_LEV_FIELD(new_jx, new_jy))
12649     return MP_NO_ACTION;
12650
12651   if (!player_can_move)
12652   {
12653     if (player->MovPos == 0)
12654     {
12655       player->is_moving = FALSE;
12656       player->is_digging = FALSE;
12657       player->is_collecting = FALSE;
12658       player->is_snapping = FALSE;
12659       player->is_pushing = FALSE;
12660     }
12661   }
12662
12663 #if 1
12664   if (!options.network && game.centered_player_nr == -1 &&
12665       !AllPlayersInSight(player, new_jx, new_jy))
12666     return MP_NO_ACTION;
12667 #else
12668   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12669     return MP_NO_ACTION;
12670 #endif
12671
12672 #if !USE_FIXED_DONT_RUN_INTO
12673   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12674
12675   /* (moved to DigField()) */
12676   if (player_can_move && DONT_RUN_INTO(element))
12677   {
12678     if (element == EL_ACID && dx == 0 && dy == 1)
12679     {
12680       SplashAcid(new_jx, new_jy);
12681       Feld[jx][jy] = EL_PLAYER_1;
12682       InitMovingField(jx, jy, MV_DOWN);
12683       Store[jx][jy] = EL_ACID;
12684       ContinueMoving(jx, jy);
12685       BuryPlayer(player);
12686     }
12687     else
12688       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12689
12690     return MP_MOVING;
12691   }
12692 #endif
12693
12694   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12695   if (can_move != MP_MOVING)
12696     return can_move;
12697
12698   /* check if DigField() has caused relocation of the player */
12699   if (player->jx != jx || player->jy != jy)
12700     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12701
12702   StorePlayer[jx][jy] = 0;
12703   player->last_jx = jx;
12704   player->last_jy = jy;
12705   player->jx = new_jx;
12706   player->jy = new_jy;
12707   StorePlayer[new_jx][new_jy] = player->element_nr;
12708
12709   if (player->move_delay_value_next != -1)
12710   {
12711     player->move_delay_value = player->move_delay_value_next;
12712     player->move_delay_value_next = -1;
12713   }
12714
12715   player->MovPos =
12716     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12717
12718   player->step_counter++;
12719
12720   PlayerVisit[jx][jy] = FrameCounter;
12721
12722 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12723   player->is_moving = TRUE;
12724 #endif
12725
12726 #if 1
12727   /* should better be called in MovePlayer(), but this breaks some tapes */
12728   ScrollPlayer(player, SCROLL_INIT);
12729 #endif
12730
12731   return MP_MOVING;
12732 }
12733
12734 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12735 {
12736   int jx = player->jx, jy = player->jy;
12737   int old_jx = jx, old_jy = jy;
12738   int moved = MP_NO_ACTION;
12739
12740   if (!player->active)
12741     return FALSE;
12742
12743   if (!dx && !dy)
12744   {
12745     if (player->MovPos == 0)
12746     {
12747       player->is_moving = FALSE;
12748       player->is_digging = FALSE;
12749       player->is_collecting = FALSE;
12750       player->is_snapping = FALSE;
12751       player->is_pushing = FALSE;
12752     }
12753
12754     return FALSE;
12755   }
12756
12757   if (player->move_delay > 0)
12758     return FALSE;
12759
12760   player->move_delay = -1;              /* set to "uninitialized" value */
12761
12762   /* store if player is automatically moved to next field */
12763   player->is_auto_moving = (player->programmed_action != MV_NONE);
12764
12765   /* remove the last programmed player action */
12766   player->programmed_action = 0;
12767
12768   if (player->MovPos)
12769   {
12770     /* should only happen if pre-1.2 tape recordings are played */
12771     /* this is only for backward compatibility */
12772
12773     int original_move_delay_value = player->move_delay_value;
12774
12775 #if DEBUG
12776     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12777            tape.counter);
12778 #endif
12779
12780     /* scroll remaining steps with finest movement resolution */
12781     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12782
12783     while (player->MovPos)
12784     {
12785       ScrollPlayer(player, SCROLL_GO_ON);
12786       ScrollScreen(NULL, SCROLL_GO_ON);
12787
12788       AdvanceFrameAndPlayerCounters(player->index_nr);
12789
12790       DrawAllPlayers();
12791       BackToFront();
12792     }
12793
12794     player->move_delay_value = original_move_delay_value;
12795   }
12796
12797   player->is_active = FALSE;
12798
12799   if (player->last_move_dir & MV_HORIZONTAL)
12800   {
12801     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12802       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12803   }
12804   else
12805   {
12806     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12807       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12808   }
12809
12810 #if USE_FIXED_BORDER_RUNNING_GFX
12811   if (!moved && !player->is_active)
12812   {
12813     player->is_moving = FALSE;
12814     player->is_digging = FALSE;
12815     player->is_collecting = FALSE;
12816     player->is_snapping = FALSE;
12817     player->is_pushing = FALSE;
12818   }
12819 #endif
12820
12821   jx = player->jx;
12822   jy = player->jy;
12823
12824 #if 1
12825   if (moved & MP_MOVING && !ScreenMovPos &&
12826       (player->index_nr == game.centered_player_nr ||
12827        game.centered_player_nr == -1))
12828 #else
12829   if (moved & MP_MOVING && !ScreenMovPos &&
12830       (player == local_player || !options.network))
12831 #endif
12832   {
12833     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12834     int offset = game.scroll_delay_value;
12835
12836     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12837     {
12838       /* actual player has left the screen -- scroll in that direction */
12839       if (jx != old_jx)         /* player has moved horizontally */
12840         scroll_x += (jx - old_jx);
12841       else                      /* player has moved vertically */
12842         scroll_y += (jy - old_jy);
12843     }
12844     else
12845     {
12846       if (jx != old_jx)         /* player has moved horizontally */
12847       {
12848         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12849             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12850           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12851
12852         /* don't scroll over playfield boundaries */
12853         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12854           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12855
12856         /* don't scroll more than one field at a time */
12857         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12858
12859         /* don't scroll against the player's moving direction */
12860         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12861             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12862           scroll_x = old_scroll_x;
12863       }
12864       else                      /* player has moved vertically */
12865       {
12866         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12867             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12868           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12869
12870         /* don't scroll over playfield boundaries */
12871         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12872           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12873
12874         /* don't scroll more than one field at a time */
12875         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12876
12877         /* don't scroll against the player's moving direction */
12878         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12879             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12880           scroll_y = old_scroll_y;
12881       }
12882     }
12883
12884     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12885     {
12886 #if 1
12887       if (!options.network && game.centered_player_nr == -1 &&
12888           !AllPlayersInVisibleScreen())
12889       {
12890         scroll_x = old_scroll_x;
12891         scroll_y = old_scroll_y;
12892       }
12893       else
12894 #else
12895       if (!options.network && !AllPlayersInVisibleScreen())
12896       {
12897         scroll_x = old_scroll_x;
12898         scroll_y = old_scroll_y;
12899       }
12900       else
12901 #endif
12902       {
12903         ScrollScreen(player, SCROLL_INIT);
12904         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12905       }
12906     }
12907   }
12908
12909   player->StepFrame = 0;
12910
12911   if (moved & MP_MOVING)
12912   {
12913     if (old_jx != jx && old_jy == jy)
12914       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12915     else if (old_jx == jx && old_jy != jy)
12916       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12917
12918     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12919
12920     player->last_move_dir = player->MovDir;
12921     player->is_moving = TRUE;
12922     player->is_snapping = FALSE;
12923     player->is_switching = FALSE;
12924     player->is_dropping = FALSE;
12925     player->is_dropping_pressed = FALSE;
12926     player->drop_pressed_delay = 0;
12927
12928 #if 0
12929     /* should better be called here than above, but this breaks some tapes */
12930     ScrollPlayer(player, SCROLL_INIT);
12931 #endif
12932   }
12933   else
12934   {
12935     CheckGravityMovementWhenNotMoving(player);
12936
12937     player->is_moving = FALSE;
12938
12939     /* at this point, the player is allowed to move, but cannot move right now
12940        (e.g. because of something blocking the way) -- ensure that the player
12941        is also allowed to move in the next frame (in old versions before 3.1.1,
12942        the player was forced to wait again for eight frames before next try) */
12943
12944     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12945       player->move_delay = 0;   /* allow direct movement in the next frame */
12946   }
12947
12948   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12949     player->move_delay = player->move_delay_value;
12950
12951   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12952   {
12953     TestIfPlayerTouchesBadThing(jx, jy);
12954     TestIfPlayerTouchesCustomElement(jx, jy);
12955   }
12956
12957   if (!player->active)
12958     RemovePlayer(player);
12959
12960   return moved;
12961 }
12962
12963 void ScrollPlayer(struct PlayerInfo *player, int mode)
12964 {
12965   int jx = player->jx, jy = player->jy;
12966   int last_jx = player->last_jx, last_jy = player->last_jy;
12967   int move_stepsize = TILEX / player->move_delay_value;
12968
12969 #if USE_NEW_PLAYER_SPEED
12970   if (!player->active)
12971     return;
12972
12973   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12974     return;
12975 #else
12976   if (!player->active || player->MovPos == 0)
12977     return;
12978 #endif
12979
12980   if (mode == SCROLL_INIT)
12981   {
12982     player->actual_frame_counter = FrameCounter;
12983     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12984
12985     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12986         Feld[last_jx][last_jy] == EL_EMPTY)
12987     {
12988       int last_field_block_delay = 0;   /* start with no blocking at all */
12989       int block_delay_adjustment = player->block_delay_adjustment;
12990
12991       /* if player blocks last field, add delay for exactly one move */
12992       if (player->block_last_field)
12993       {
12994         last_field_block_delay += player->move_delay_value;
12995
12996         /* when blocking enabled, prevent moving up despite gravity */
12997 #if USE_PLAYER_GRAVITY
12998         if (player->gravity && player->MovDir == MV_UP)
12999           block_delay_adjustment = -1;
13000 #else
13001         if (game.gravity && player->MovDir == MV_UP)
13002           block_delay_adjustment = -1;
13003 #endif
13004       }
13005
13006       /* add block delay adjustment (also possible when not blocking) */
13007       last_field_block_delay += block_delay_adjustment;
13008
13009       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13010       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13011     }
13012
13013 #if USE_NEW_PLAYER_SPEED
13014     if (player->MovPos != 0)    /* player has not yet reached destination */
13015       return;
13016 #else
13017     return;
13018 #endif
13019   }
13020   else if (!FrameReached(&player->actual_frame_counter, 1))
13021     return;
13022
13023 #if USE_NEW_PLAYER_SPEED
13024   if (player->MovPos != 0)
13025   {
13026     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13027     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13028
13029     /* before DrawPlayer() to draw correct player graphic for this case */
13030     if (player->MovPos == 0)
13031       CheckGravityMovement(player);
13032   }
13033 #else
13034   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13035   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13036
13037   /* before DrawPlayer() to draw correct player graphic for this case */
13038   if (player->MovPos == 0)
13039     CheckGravityMovement(player);
13040 #endif
13041
13042   if (player->MovPos == 0)      /* player reached destination field */
13043   {
13044     if (player->move_delay_reset_counter > 0)
13045     {
13046       player->move_delay_reset_counter--;
13047
13048       if (player->move_delay_reset_counter == 0)
13049       {
13050         /* continue with normal speed after quickly moving through gate */
13051         HALVE_PLAYER_SPEED(player);
13052
13053         /* be able to make the next move without delay */
13054         player->move_delay = 0;
13055       }
13056     }
13057
13058     player->last_jx = jx;
13059     player->last_jy = jy;
13060
13061     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13062         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13063         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13064         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13065         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13066         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13067     {
13068       DrawPlayer(player);       /* needed here only to cleanup last field */
13069       RemovePlayer(player);
13070
13071       if (local_player->friends_still_needed == 0 ||
13072           IS_SP_ELEMENT(Feld[jx][jy]))
13073         PlayerWins(player);
13074     }
13075
13076     /* this breaks one level: "machine", level 000 */
13077     {
13078       int move_direction = player->MovDir;
13079       int enter_side = MV_DIR_OPPOSITE(move_direction);
13080       int leave_side = move_direction;
13081       int old_jx = last_jx;
13082       int old_jy = last_jy;
13083       int old_element = Feld[old_jx][old_jy];
13084       int new_element = Feld[jx][jy];
13085
13086       if (IS_CUSTOM_ELEMENT(old_element))
13087         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13088                                    CE_LEFT_BY_PLAYER,
13089                                    player->index_bit, leave_side);
13090
13091       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13092                                           CE_PLAYER_LEAVES_X,
13093                                           player->index_bit, leave_side);
13094
13095       if (IS_CUSTOM_ELEMENT(new_element))
13096         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13097                                    player->index_bit, enter_side);
13098
13099       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13100                                           CE_PLAYER_ENTERS_X,
13101                                           player->index_bit, enter_side);
13102
13103       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13104                                         CE_MOVE_OF_X, move_direction);
13105     }
13106
13107     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13108     {
13109       TestIfPlayerTouchesBadThing(jx, jy);
13110       TestIfPlayerTouchesCustomElement(jx, jy);
13111
13112       /* needed because pushed element has not yet reached its destination,
13113          so it would trigger a change event at its previous field location */
13114       if (!player->is_pushing)
13115         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13116
13117       if (!player->active)
13118         RemovePlayer(player);
13119     }
13120
13121     if (!local_player->LevelSolved && level.use_step_counter)
13122     {
13123       int i;
13124
13125       TimePlayed++;
13126
13127       if (TimeLeft > 0)
13128       {
13129         TimeLeft--;
13130
13131         if (TimeLeft <= 10 && setup.time_limit)
13132           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13133
13134 #if 1
13135         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13136
13137         DisplayGameControlValues();
13138 #else
13139         DrawGameValue_Time(TimeLeft);
13140 #endif
13141
13142         if (!TimeLeft && setup.time_limit)
13143           for (i = 0; i < MAX_PLAYERS; i++)
13144             KillPlayer(&stored_player[i]);
13145       }
13146 #if 1
13147       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13148       {
13149         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13150
13151         DisplayGameControlValues();
13152       }
13153 #else
13154       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13155         DrawGameValue_Time(TimePlayed);
13156 #endif
13157     }
13158
13159     if (tape.single_step && tape.recording && !tape.pausing &&
13160         !player->programmed_action)
13161       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13162   }
13163 }
13164
13165 void ScrollScreen(struct PlayerInfo *player, int mode)
13166 {
13167   static unsigned long screen_frame_counter = 0;
13168
13169   if (mode == SCROLL_INIT)
13170   {
13171     /* set scrolling step size according to actual player's moving speed */
13172     ScrollStepSize = TILEX / player->move_delay_value;
13173
13174     screen_frame_counter = FrameCounter;
13175     ScreenMovDir = player->MovDir;
13176     ScreenMovPos = player->MovPos;
13177     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13178     return;
13179   }
13180   else if (!FrameReached(&screen_frame_counter, 1))
13181     return;
13182
13183   if (ScreenMovPos)
13184   {
13185     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13186     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13187     redraw_mask |= REDRAW_FIELD;
13188   }
13189   else
13190     ScreenMovDir = MV_NONE;
13191 }
13192
13193 void TestIfPlayerTouchesCustomElement(int x, int y)
13194 {
13195   static int xy[4][2] =
13196   {
13197     { 0, -1 },
13198     { -1, 0 },
13199     { +1, 0 },
13200     { 0, +1 }
13201   };
13202   static int trigger_sides[4][2] =
13203   {
13204     /* center side       border side */
13205     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13206     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13207     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13208     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13209   };
13210   static int touch_dir[4] =
13211   {
13212     MV_LEFT | MV_RIGHT,
13213     MV_UP   | MV_DOWN,
13214     MV_UP   | MV_DOWN,
13215     MV_LEFT | MV_RIGHT
13216   };
13217   int center_element = Feld[x][y];      /* should always be non-moving! */
13218   int i;
13219
13220   for (i = 0; i < NUM_DIRECTIONS; i++)
13221   {
13222     int xx = x + xy[i][0];
13223     int yy = y + xy[i][1];
13224     int center_side = trigger_sides[i][0];
13225     int border_side = trigger_sides[i][1];
13226     int border_element;
13227
13228     if (!IN_LEV_FIELD(xx, yy))
13229       continue;
13230
13231     if (IS_PLAYER(x, y))
13232     {
13233       struct PlayerInfo *player = PLAYERINFO(x, y);
13234
13235       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13236         border_element = Feld[xx][yy];          /* may be moving! */
13237       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13238         border_element = Feld[xx][yy];
13239       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13240         border_element = MovingOrBlocked2Element(xx, yy);
13241       else
13242         continue;               /* center and border element do not touch */
13243
13244       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13245                                  player->index_bit, border_side);
13246       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13247                                           CE_PLAYER_TOUCHES_X,
13248                                           player->index_bit, border_side);
13249     }
13250     else if (IS_PLAYER(xx, yy))
13251     {
13252       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13253
13254       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13255       {
13256         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13257           continue;             /* center and border element do not touch */
13258       }
13259
13260       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13261                                  player->index_bit, center_side);
13262       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13263                                           CE_PLAYER_TOUCHES_X,
13264                                           player->index_bit, center_side);
13265       break;
13266     }
13267   }
13268 }
13269
13270 #if USE_ELEMENT_TOUCHING_BUGFIX
13271
13272 void TestIfElementTouchesCustomElement(int x, int y)
13273 {
13274   static int xy[4][2] =
13275   {
13276     { 0, -1 },
13277     { -1, 0 },
13278     { +1, 0 },
13279     { 0, +1 }
13280   };
13281   static int trigger_sides[4][2] =
13282   {
13283     /* center side      border side */
13284     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13285     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13286     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13287     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13288   };
13289   static int touch_dir[4] =
13290   {
13291     MV_LEFT | MV_RIGHT,
13292     MV_UP   | MV_DOWN,
13293     MV_UP   | MV_DOWN,
13294     MV_LEFT | MV_RIGHT
13295   };
13296   boolean change_center_element = FALSE;
13297   int center_element = Feld[x][y];      /* should always be non-moving! */
13298   int border_element_old[NUM_DIRECTIONS];
13299   int i;
13300
13301   for (i = 0; i < NUM_DIRECTIONS; i++)
13302   {
13303     int xx = x + xy[i][0];
13304     int yy = y + xy[i][1];
13305     int border_element;
13306
13307     border_element_old[i] = -1;
13308
13309     if (!IN_LEV_FIELD(xx, yy))
13310       continue;
13311
13312     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13313       border_element = Feld[xx][yy];    /* may be moving! */
13314     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13315       border_element = Feld[xx][yy];
13316     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13317       border_element = MovingOrBlocked2Element(xx, yy);
13318     else
13319       continue;                 /* center and border element do not touch */
13320
13321     border_element_old[i] = border_element;
13322   }
13323
13324   for (i = 0; i < NUM_DIRECTIONS; i++)
13325   {
13326     int xx = x + xy[i][0];
13327     int yy = y + xy[i][1];
13328     int center_side = trigger_sides[i][0];
13329     int border_element = border_element_old[i];
13330
13331     if (border_element == -1)
13332       continue;
13333
13334     /* check for change of border element */
13335     CheckElementChangeBySide(xx, yy, border_element, center_element,
13336                              CE_TOUCHING_X, center_side);
13337   }
13338
13339   for (i = 0; i < NUM_DIRECTIONS; i++)
13340   {
13341     int border_side = trigger_sides[i][1];
13342     int border_element = border_element_old[i];
13343
13344     if (border_element == -1)
13345       continue;
13346
13347     /* check for change of center element (but change it only once) */
13348     if (!change_center_element)
13349       change_center_element =
13350         CheckElementChangeBySide(x, y, center_element, border_element,
13351                                  CE_TOUCHING_X, border_side);
13352   }
13353 }
13354
13355 #else
13356
13357 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13358 {
13359   static int xy[4][2] =
13360   {
13361     { 0, -1 },
13362     { -1, 0 },
13363     { +1, 0 },
13364     { 0, +1 }
13365   };
13366   static int trigger_sides[4][2] =
13367   {
13368     /* center side      border side */
13369     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13370     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13371     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13372     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13373   };
13374   static int touch_dir[4] =
13375   {
13376     MV_LEFT | MV_RIGHT,
13377     MV_UP   | MV_DOWN,
13378     MV_UP   | MV_DOWN,
13379     MV_LEFT | MV_RIGHT
13380   };
13381   boolean change_center_element = FALSE;
13382   int center_element = Feld[x][y];      /* should always be non-moving! */
13383   int i;
13384
13385   for (i = 0; i < NUM_DIRECTIONS; i++)
13386   {
13387     int xx = x + xy[i][0];
13388     int yy = y + xy[i][1];
13389     int center_side = trigger_sides[i][0];
13390     int border_side = trigger_sides[i][1];
13391     int border_element;
13392
13393     if (!IN_LEV_FIELD(xx, yy))
13394       continue;
13395
13396     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13397       border_element = Feld[xx][yy];    /* may be moving! */
13398     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13399       border_element = Feld[xx][yy];
13400     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13401       border_element = MovingOrBlocked2Element(xx, yy);
13402     else
13403       continue;                 /* center and border element do not touch */
13404
13405     /* check for change of center element (but change it only once) */
13406     if (!change_center_element)
13407       change_center_element =
13408         CheckElementChangeBySide(x, y, center_element, border_element,
13409                                  CE_TOUCHING_X, border_side);
13410
13411     /* check for change of border element */
13412     CheckElementChangeBySide(xx, yy, border_element, center_element,
13413                              CE_TOUCHING_X, center_side);
13414   }
13415 }
13416
13417 #endif
13418
13419 void TestIfElementHitsCustomElement(int x, int y, int direction)
13420 {
13421   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13422   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13423   int hitx = x + dx, hity = y + dy;
13424   int hitting_element = Feld[x][y];
13425   int touched_element;
13426
13427   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13428     return;
13429
13430   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13431                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13432
13433   if (IN_LEV_FIELD(hitx, hity))
13434   {
13435     int opposite_direction = MV_DIR_OPPOSITE(direction);
13436     int hitting_side = direction;
13437     int touched_side = opposite_direction;
13438     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13439                           MovDir[hitx][hity] != direction ||
13440                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13441
13442     object_hit = TRUE;
13443
13444     if (object_hit)
13445     {
13446       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13447                                CE_HITTING_X, touched_side);
13448
13449       CheckElementChangeBySide(hitx, hity, touched_element,
13450                                hitting_element, CE_HIT_BY_X, hitting_side);
13451
13452       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13453                                CE_HIT_BY_SOMETHING, opposite_direction);
13454     }
13455   }
13456
13457   /* "hitting something" is also true when hitting the playfield border */
13458   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13459                            CE_HITTING_SOMETHING, direction);
13460 }
13461
13462 #if 0
13463 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13464 {
13465   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13466   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13467   int hitx = x + dx, hity = y + dy;
13468   int hitting_element = Feld[x][y];
13469   int touched_element;
13470 #if 0
13471   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13472                         !IS_FREE(hitx, hity) &&
13473                         (!IS_MOVING(hitx, hity) ||
13474                          MovDir[hitx][hity] != direction ||
13475                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13476 #endif
13477
13478   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13479     return;
13480
13481 #if 0
13482   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13483     return;
13484 #endif
13485
13486   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13487                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13488
13489   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13490                            EP_CAN_SMASH_EVERYTHING, direction);
13491
13492   if (IN_LEV_FIELD(hitx, hity))
13493   {
13494     int opposite_direction = MV_DIR_OPPOSITE(direction);
13495     int hitting_side = direction;
13496     int touched_side = opposite_direction;
13497 #if 0
13498     int touched_element = MovingOrBlocked2Element(hitx, hity);
13499 #endif
13500 #if 1
13501     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13502                           MovDir[hitx][hity] != direction ||
13503                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13504
13505     object_hit = TRUE;
13506 #endif
13507
13508     if (object_hit)
13509     {
13510       int i;
13511
13512       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13513                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13514
13515       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13516                                CE_OTHER_IS_SMASHING, touched_side);
13517
13518       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13519                                CE_OTHER_GETS_SMASHED, hitting_side);
13520     }
13521   }
13522 }
13523 #endif
13524
13525 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13526 {
13527   int i, kill_x = -1, kill_y = -1;
13528
13529   int bad_element = -1;
13530   static int test_xy[4][2] =
13531   {
13532     { 0, -1 },
13533     { -1, 0 },
13534     { +1, 0 },
13535     { 0, +1 }
13536   };
13537   static int test_dir[4] =
13538   {
13539     MV_UP,
13540     MV_LEFT,
13541     MV_RIGHT,
13542     MV_DOWN
13543   };
13544
13545   for (i = 0; i < NUM_DIRECTIONS; i++)
13546   {
13547     int test_x, test_y, test_move_dir, test_element;
13548
13549     test_x = good_x + test_xy[i][0];
13550     test_y = good_y + test_xy[i][1];
13551
13552     if (!IN_LEV_FIELD(test_x, test_y))
13553       continue;
13554
13555     test_move_dir =
13556       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13557
13558     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13559
13560     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13561        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13562     */
13563     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13564         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13565     {
13566       kill_x = test_x;
13567       kill_y = test_y;
13568       bad_element = test_element;
13569
13570       break;
13571     }
13572   }
13573
13574   if (kill_x != -1 || kill_y != -1)
13575   {
13576     if (IS_PLAYER(good_x, good_y))
13577     {
13578       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13579
13580       if (player->shield_deadly_time_left > 0 &&
13581           !IS_INDESTRUCTIBLE(bad_element))
13582         Bang(kill_x, kill_y);
13583       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13584         KillPlayer(player);
13585     }
13586     else
13587       Bang(good_x, good_y);
13588   }
13589 }
13590
13591 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13592 {
13593   int i, kill_x = -1, kill_y = -1;
13594   int bad_element = Feld[bad_x][bad_y];
13595   static int test_xy[4][2] =
13596   {
13597     { 0, -1 },
13598     { -1, 0 },
13599     { +1, 0 },
13600     { 0, +1 }
13601   };
13602   static int touch_dir[4] =
13603   {
13604     MV_LEFT | MV_RIGHT,
13605     MV_UP   | MV_DOWN,
13606     MV_UP   | MV_DOWN,
13607     MV_LEFT | MV_RIGHT
13608   };
13609   static int test_dir[4] =
13610   {
13611     MV_UP,
13612     MV_LEFT,
13613     MV_RIGHT,
13614     MV_DOWN
13615   };
13616
13617   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13618     return;
13619
13620   for (i = 0; i < NUM_DIRECTIONS; i++)
13621   {
13622     int test_x, test_y, test_move_dir, test_element;
13623
13624     test_x = bad_x + test_xy[i][0];
13625     test_y = bad_y + test_xy[i][1];
13626     if (!IN_LEV_FIELD(test_x, test_y))
13627       continue;
13628
13629     test_move_dir =
13630       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13631
13632     test_element = Feld[test_x][test_y];
13633
13634     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13635        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13636     */
13637     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13638         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13639     {
13640       /* good thing is player or penguin that does not move away */
13641       if (IS_PLAYER(test_x, test_y))
13642       {
13643         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13644
13645         if (bad_element == EL_ROBOT && player->is_moving)
13646           continue;     /* robot does not kill player if he is moving */
13647
13648         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13649         {
13650           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13651             continue;           /* center and border element do not touch */
13652         }
13653
13654         kill_x = test_x;
13655         kill_y = test_y;
13656         break;
13657       }
13658       else if (test_element == EL_PENGUIN)
13659       {
13660         kill_x = test_x;
13661         kill_y = test_y;
13662         break;
13663       }
13664     }
13665   }
13666
13667   if (kill_x != -1 || kill_y != -1)
13668   {
13669     if (IS_PLAYER(kill_x, kill_y))
13670     {
13671       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13672
13673       if (player->shield_deadly_time_left > 0 &&
13674           !IS_INDESTRUCTIBLE(bad_element))
13675         Bang(bad_x, bad_y);
13676       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13677         KillPlayer(player);
13678     }
13679     else
13680       Bang(kill_x, kill_y);
13681   }
13682 }
13683
13684 void TestIfPlayerTouchesBadThing(int x, int y)
13685 {
13686   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13687 }
13688
13689 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13690 {
13691   TestIfGoodThingHitsBadThing(x, y, move_dir);
13692 }
13693
13694 void TestIfBadThingTouchesPlayer(int x, int y)
13695 {
13696   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13697 }
13698
13699 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13700 {
13701   TestIfBadThingHitsGoodThing(x, y, move_dir);
13702 }
13703
13704 void TestIfFriendTouchesBadThing(int x, int y)
13705 {
13706   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13707 }
13708
13709 void TestIfBadThingTouchesFriend(int x, int y)
13710 {
13711   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13712 }
13713
13714 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13715 {
13716   int i, kill_x = bad_x, kill_y = bad_y;
13717   static int xy[4][2] =
13718   {
13719     { 0, -1 },
13720     { -1, 0 },
13721     { +1, 0 },
13722     { 0, +1 }
13723   };
13724
13725   for (i = 0; i < NUM_DIRECTIONS; i++)
13726   {
13727     int x, y, element;
13728
13729     x = bad_x + xy[i][0];
13730     y = bad_y + xy[i][1];
13731     if (!IN_LEV_FIELD(x, y))
13732       continue;
13733
13734     element = Feld[x][y];
13735     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13736         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13737     {
13738       kill_x = x;
13739       kill_y = y;
13740       break;
13741     }
13742   }
13743
13744   if (kill_x != bad_x || kill_y != bad_y)
13745     Bang(bad_x, bad_y);
13746 }
13747
13748 void KillPlayer(struct PlayerInfo *player)
13749 {
13750   int jx = player->jx, jy = player->jy;
13751
13752   if (!player->active)
13753     return;
13754
13755   /* the following code was introduced to prevent an infinite loop when calling
13756      -> Bang()
13757      -> CheckTriggeredElementChangeExt()
13758      -> ExecuteCustomElementAction()
13759      -> KillPlayer()
13760      -> (infinitely repeating the above sequence of function calls)
13761      which occurs when killing the player while having a CE with the setting
13762      "kill player X when explosion of <player X>"; the solution using a new
13763      field "player->killed" was chosen for backwards compatibility, although
13764      clever use of the fields "player->active" etc. would probably also work */
13765 #if 1
13766   if (player->killed)
13767     return;
13768 #endif
13769
13770   player->killed = TRUE;
13771
13772   /* remove accessible field at the player's position */
13773   Feld[jx][jy] = EL_EMPTY;
13774
13775   /* deactivate shield (else Bang()/Explode() would not work right) */
13776   player->shield_normal_time_left = 0;
13777   player->shield_deadly_time_left = 0;
13778
13779   Bang(jx, jy);
13780   BuryPlayer(player);
13781 }
13782
13783 static void KillPlayerUnlessEnemyProtected(int x, int y)
13784 {
13785   if (!PLAYER_ENEMY_PROTECTED(x, y))
13786     KillPlayer(PLAYERINFO(x, y));
13787 }
13788
13789 static void KillPlayerUnlessExplosionProtected(int x, int y)
13790 {
13791   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13792     KillPlayer(PLAYERINFO(x, y));
13793 }
13794
13795 void BuryPlayer(struct PlayerInfo *player)
13796 {
13797   int jx = player->jx, jy = player->jy;
13798
13799   if (!player->active)
13800     return;
13801
13802   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13803   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13804
13805   player->GameOver = TRUE;
13806   RemovePlayer(player);
13807 }
13808
13809 void RemovePlayer(struct PlayerInfo *player)
13810 {
13811   int jx = player->jx, jy = player->jy;
13812   int i, found = FALSE;
13813
13814   player->present = FALSE;
13815   player->active = FALSE;
13816
13817   if (!ExplodeField[jx][jy])
13818     StorePlayer[jx][jy] = 0;
13819
13820   if (player->is_moving)
13821     DrawLevelField(player->last_jx, player->last_jy);
13822
13823   for (i = 0; i < MAX_PLAYERS; i++)
13824     if (stored_player[i].active)
13825       found = TRUE;
13826
13827   if (!found)
13828     AllPlayersGone = TRUE;
13829
13830   ExitX = ZX = jx;
13831   ExitY = ZY = jy;
13832 }
13833
13834 #if USE_NEW_SNAP_DELAY
13835 static void setFieldForSnapping(int x, int y, int element, int direction)
13836 {
13837   struct ElementInfo *ei = &element_info[element];
13838   int direction_bit = MV_DIR_TO_BIT(direction);
13839   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13840   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13841                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13842
13843   Feld[x][y] = EL_ELEMENT_SNAPPING;
13844   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13845
13846   ResetGfxAnimation(x, y);
13847
13848   GfxElement[x][y] = element;
13849   GfxAction[x][y] = action;
13850   GfxDir[x][y] = direction;
13851   GfxFrame[x][y] = -1;
13852 }
13853 #endif
13854
13855 /*
13856   =============================================================================
13857   checkDiagonalPushing()
13858   -----------------------------------------------------------------------------
13859   check if diagonal input device direction results in pushing of object
13860   (by checking if the alternative direction is walkable, diggable, ...)
13861   =============================================================================
13862 */
13863
13864 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13865                                     int x, int y, int real_dx, int real_dy)
13866 {
13867   int jx, jy, dx, dy, xx, yy;
13868
13869   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13870     return TRUE;
13871
13872   /* diagonal direction: check alternative direction */
13873   jx = player->jx;
13874   jy = player->jy;
13875   dx = x - jx;
13876   dy = y - jy;
13877   xx = jx + (dx == 0 ? real_dx : 0);
13878   yy = jy + (dy == 0 ? real_dy : 0);
13879
13880   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13881 }
13882
13883 /*
13884   =============================================================================
13885   DigField()
13886   -----------------------------------------------------------------------------
13887   x, y:                 field next to player (non-diagonal) to try to dig to
13888   real_dx, real_dy:     direction as read from input device (can be diagonal)
13889   =============================================================================
13890 */
13891
13892 int DigField(struct PlayerInfo *player,
13893              int oldx, int oldy, int x, int y,
13894              int real_dx, int real_dy, int mode)
13895 {
13896   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13897   boolean player_was_pushing = player->is_pushing;
13898   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13899   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13900   int jx = oldx, jy = oldy;
13901   int dx = x - jx, dy = y - jy;
13902   int nextx = x + dx, nexty = y + dy;
13903   int move_direction = (dx == -1 ? MV_LEFT  :
13904                         dx == +1 ? MV_RIGHT :
13905                         dy == -1 ? MV_UP    :
13906                         dy == +1 ? MV_DOWN  : MV_NONE);
13907   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13908   int dig_side = MV_DIR_OPPOSITE(move_direction);
13909   int old_element = Feld[jx][jy];
13910 #if USE_FIXED_DONT_RUN_INTO
13911   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13912 #else
13913   int element;
13914 #endif
13915   int collect_count;
13916
13917   if (is_player)                /* function can also be called by EL_PENGUIN */
13918   {
13919     if (player->MovPos == 0)
13920     {
13921       player->is_digging = FALSE;
13922       player->is_collecting = FALSE;
13923     }
13924
13925     if (player->MovPos == 0)    /* last pushing move finished */
13926       player->is_pushing = FALSE;
13927
13928     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13929     {
13930       player->is_switching = FALSE;
13931       player->push_delay = -1;
13932
13933       return MP_NO_ACTION;
13934     }
13935   }
13936
13937 #if !USE_FIXED_DONT_RUN_INTO
13938   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13939     return MP_NO_ACTION;
13940 #endif
13941
13942   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13943     old_element = Back[jx][jy];
13944
13945   /* in case of element dropped at player position, check background */
13946   else if (Back[jx][jy] != EL_EMPTY &&
13947            game.engine_version >= VERSION_IDENT(2,2,0,0))
13948     old_element = Back[jx][jy];
13949
13950   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13951     return MP_NO_ACTION;        /* field has no opening in this direction */
13952
13953   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13954     return MP_NO_ACTION;        /* field has no opening in this direction */
13955
13956 #if USE_FIXED_DONT_RUN_INTO
13957   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13958   {
13959     SplashAcid(x, y);
13960
13961     Feld[jx][jy] = player->artwork_element;
13962     InitMovingField(jx, jy, MV_DOWN);
13963     Store[jx][jy] = EL_ACID;
13964     ContinueMoving(jx, jy);
13965     BuryPlayer(player);
13966
13967     return MP_DONT_RUN_INTO;
13968   }
13969 #endif
13970
13971 #if USE_FIXED_DONT_RUN_INTO
13972   if (player_can_move && DONT_RUN_INTO(element))
13973   {
13974     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13975
13976     return MP_DONT_RUN_INTO;
13977   }
13978 #endif
13979
13980 #if USE_FIXED_DONT_RUN_INTO
13981   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13982     return MP_NO_ACTION;
13983 #endif
13984
13985 #if !USE_FIXED_DONT_RUN_INTO
13986   element = Feld[x][y];
13987 #endif
13988
13989   collect_count = element_info[element].collect_count_initial;
13990
13991   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13992     return MP_NO_ACTION;
13993
13994   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13995     player_can_move = player_can_move_or_snap;
13996
13997   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13998       game.engine_version >= VERSION_IDENT(2,2,0,0))
13999   {
14000     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14001                                player->index_bit, dig_side);
14002     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14003                                         player->index_bit, dig_side);
14004
14005     if (element == EL_DC_LANDMINE)
14006       Bang(x, y);
14007
14008     if (Feld[x][y] != element)          /* field changed by snapping */
14009       return MP_ACTION;
14010
14011     return MP_NO_ACTION;
14012   }
14013
14014 #if USE_PLAYER_GRAVITY
14015   if (player->gravity && is_player && !player->is_auto_moving &&
14016       canFallDown(player) && move_direction != MV_DOWN &&
14017       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14018     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14019 #else
14020   if (game.gravity && is_player && !player->is_auto_moving &&
14021       canFallDown(player) && move_direction != MV_DOWN &&
14022       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14023     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14024 #endif
14025
14026   if (player_can_move &&
14027       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14028   {
14029     int sound_element = SND_ELEMENT(element);
14030     int sound_action = ACTION_WALKING;
14031
14032     if (IS_RND_GATE(element))
14033     {
14034       if (!player->key[RND_GATE_NR(element)])
14035         return MP_NO_ACTION;
14036     }
14037     else if (IS_RND_GATE_GRAY(element))
14038     {
14039       if (!player->key[RND_GATE_GRAY_NR(element)])
14040         return MP_NO_ACTION;
14041     }
14042     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14043     {
14044       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14045         return MP_NO_ACTION;
14046     }
14047     else if (element == EL_EXIT_OPEN ||
14048              element == EL_EM_EXIT_OPEN ||
14049              element == EL_STEEL_EXIT_OPEN ||
14050              element == EL_EM_STEEL_EXIT_OPEN ||
14051              element == EL_SP_EXIT_OPEN ||
14052              element == EL_SP_EXIT_OPENING)
14053     {
14054       sound_action = ACTION_PASSING;    /* player is passing exit */
14055     }
14056     else if (element == EL_EMPTY)
14057     {
14058       sound_action = ACTION_MOVING;             /* nothing to walk on */
14059     }
14060
14061     /* play sound from background or player, whatever is available */
14062     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14063       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14064     else
14065       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14066   }
14067   else if (player_can_move &&
14068            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14069   {
14070     if (!ACCESS_FROM(element, opposite_direction))
14071       return MP_NO_ACTION;      /* field not accessible from this direction */
14072
14073     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14074       return MP_NO_ACTION;
14075
14076     if (IS_EM_GATE(element))
14077     {
14078       if (!player->key[EM_GATE_NR(element)])
14079         return MP_NO_ACTION;
14080     }
14081     else if (IS_EM_GATE_GRAY(element))
14082     {
14083       if (!player->key[EM_GATE_GRAY_NR(element)])
14084         return MP_NO_ACTION;
14085     }
14086     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14087     {
14088       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14089         return MP_NO_ACTION;
14090     }
14091     else if (IS_EMC_GATE(element))
14092     {
14093       if (!player->key[EMC_GATE_NR(element)])
14094         return MP_NO_ACTION;
14095     }
14096     else if (IS_EMC_GATE_GRAY(element))
14097     {
14098       if (!player->key[EMC_GATE_GRAY_NR(element)])
14099         return MP_NO_ACTION;
14100     }
14101     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14102     {
14103       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14104         return MP_NO_ACTION;
14105     }
14106     else if (element == EL_DC_GATE_WHITE ||
14107              element == EL_DC_GATE_WHITE_GRAY ||
14108              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14109     {
14110       if (player->num_white_keys == 0)
14111         return MP_NO_ACTION;
14112
14113       player->num_white_keys--;
14114     }
14115     else if (IS_SP_PORT(element))
14116     {
14117       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14118           element == EL_SP_GRAVITY_PORT_RIGHT ||
14119           element == EL_SP_GRAVITY_PORT_UP ||
14120           element == EL_SP_GRAVITY_PORT_DOWN)
14121 #if USE_PLAYER_GRAVITY
14122         player->gravity = !player->gravity;
14123 #else
14124         game.gravity = !game.gravity;
14125 #endif
14126       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14127                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14128                element == EL_SP_GRAVITY_ON_PORT_UP ||
14129                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14130 #if USE_PLAYER_GRAVITY
14131         player->gravity = TRUE;
14132 #else
14133         game.gravity = TRUE;
14134 #endif
14135       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14136                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14137                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14138                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14139 #if USE_PLAYER_GRAVITY
14140         player->gravity = FALSE;
14141 #else
14142         game.gravity = FALSE;
14143 #endif
14144     }
14145
14146     /* automatically move to the next field with double speed */
14147     player->programmed_action = move_direction;
14148
14149     if (player->move_delay_reset_counter == 0)
14150     {
14151       player->move_delay_reset_counter = 2;     /* two double speed steps */
14152
14153       DOUBLE_PLAYER_SPEED(player);
14154     }
14155
14156     PlayLevelSoundAction(x, y, ACTION_PASSING);
14157   }
14158   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14159   {
14160     RemoveField(x, y);
14161
14162     if (mode != DF_SNAP)
14163     {
14164       GfxElement[x][y] = GFX_ELEMENT(element);
14165       player->is_digging = TRUE;
14166     }
14167
14168     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14169
14170     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14171                                         player->index_bit, dig_side);
14172
14173     if (mode == DF_SNAP)
14174     {
14175 #if USE_NEW_SNAP_DELAY
14176       if (level.block_snap_field)
14177         setFieldForSnapping(x, y, element, move_direction);
14178       else
14179         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14180 #else
14181       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14182 #endif
14183
14184       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14185                                           player->index_bit, dig_side);
14186     }
14187   }
14188   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14189   {
14190     RemoveField(x, y);
14191
14192     if (is_player && mode != DF_SNAP)
14193     {
14194       GfxElement[x][y] = element;
14195       player->is_collecting = TRUE;
14196     }
14197
14198     if (element == EL_SPEED_PILL)
14199     {
14200       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14201     }
14202     else if (element == EL_EXTRA_TIME && level.time > 0)
14203     {
14204       TimeLeft += level.extra_time;
14205
14206 #if 1
14207       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14208
14209       DisplayGameControlValues();
14210 #else
14211       DrawGameValue_Time(TimeLeft);
14212 #endif
14213     }
14214     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14215     {
14216       player->shield_normal_time_left += level.shield_normal_time;
14217       if (element == EL_SHIELD_DEADLY)
14218         player->shield_deadly_time_left += level.shield_deadly_time;
14219     }
14220     else if (element == EL_DYNAMITE ||
14221              element == EL_EM_DYNAMITE ||
14222              element == EL_SP_DISK_RED)
14223     {
14224       if (player->inventory_size < MAX_INVENTORY_SIZE)
14225         player->inventory_element[player->inventory_size++] = element;
14226
14227       DrawGameDoorValues();
14228     }
14229     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14230     {
14231       player->dynabomb_count++;
14232       player->dynabombs_left++;
14233     }
14234     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14235     {
14236       player->dynabomb_size++;
14237     }
14238     else if (element == EL_DYNABOMB_INCREASE_POWER)
14239     {
14240       player->dynabomb_xl = TRUE;
14241     }
14242     else if (IS_KEY(element))
14243     {
14244       player->key[KEY_NR(element)] = TRUE;
14245
14246       DrawGameDoorValues();
14247     }
14248     else if (element == EL_DC_KEY_WHITE)
14249     {
14250       player->num_white_keys++;
14251
14252       /* display white keys? */
14253       /* DrawGameDoorValues(); */
14254     }
14255     else if (IS_ENVELOPE(element))
14256     {
14257       player->show_envelope = element;
14258     }
14259     else if (element == EL_EMC_LENSES)
14260     {
14261       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14262
14263       RedrawAllInvisibleElementsForLenses();
14264     }
14265     else if (element == EL_EMC_MAGNIFIER)
14266     {
14267       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14268
14269       RedrawAllInvisibleElementsForMagnifier();
14270     }
14271     else if (IS_DROPPABLE(element) ||
14272              IS_THROWABLE(element))     /* can be collected and dropped */
14273     {
14274       int i;
14275
14276       if (collect_count == 0)
14277         player->inventory_infinite_element = element;
14278       else
14279         for (i = 0; i < collect_count; i++)
14280           if (player->inventory_size < MAX_INVENTORY_SIZE)
14281             player->inventory_element[player->inventory_size++] = element;
14282
14283       DrawGameDoorValues();
14284     }
14285     else if (collect_count > 0)
14286     {
14287       local_player->gems_still_needed -= collect_count;
14288       if (local_player->gems_still_needed < 0)
14289         local_player->gems_still_needed = 0;
14290
14291 #if 1
14292       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14293
14294       DisplayGameControlValues();
14295 #else
14296       DrawGameValue_Emeralds(local_player->gems_still_needed);
14297 #endif
14298     }
14299
14300     RaiseScoreElement(element);
14301     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14302
14303     if (is_player)
14304       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14305                                           player->index_bit, dig_side);
14306
14307     if (mode == DF_SNAP)
14308     {
14309 #if USE_NEW_SNAP_DELAY
14310       if (level.block_snap_field)
14311         setFieldForSnapping(x, y, element, move_direction);
14312       else
14313         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14314 #else
14315       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14316 #endif
14317
14318       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14319                                           player->index_bit, dig_side);
14320     }
14321   }
14322   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14323   {
14324     if (mode == DF_SNAP && element != EL_BD_ROCK)
14325       return MP_NO_ACTION;
14326
14327     if (CAN_FALL(element) && dy)
14328       return MP_NO_ACTION;
14329
14330     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14331         !(element == EL_SPRING && level.use_spring_bug))
14332       return MP_NO_ACTION;
14333
14334     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14335         ((move_direction & MV_VERTICAL &&
14336           ((element_info[element].move_pattern & MV_LEFT &&
14337             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14338            (element_info[element].move_pattern & MV_RIGHT &&
14339             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14340          (move_direction & MV_HORIZONTAL &&
14341           ((element_info[element].move_pattern & MV_UP &&
14342             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14343            (element_info[element].move_pattern & MV_DOWN &&
14344             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14345       return MP_NO_ACTION;
14346
14347     /* do not push elements already moving away faster than player */
14348     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14349         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14350       return MP_NO_ACTION;
14351
14352     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14353     {
14354       if (player->push_delay_value == -1 || !player_was_pushing)
14355         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14356     }
14357     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14358     {
14359       if (player->push_delay_value == -1)
14360         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14361     }
14362     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14363     {
14364       if (!player->is_pushing)
14365         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14366     }
14367
14368     player->is_pushing = TRUE;
14369     player->is_active = TRUE;
14370
14371     if (!(IN_LEV_FIELD(nextx, nexty) &&
14372           (IS_FREE(nextx, nexty) ||
14373            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14374             IS_SB_ELEMENT(element)))))
14375       return MP_NO_ACTION;
14376
14377     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14378       return MP_NO_ACTION;
14379
14380     if (player->push_delay == -1)       /* new pushing; restart delay */
14381       player->push_delay = 0;
14382
14383     if (player->push_delay < player->push_delay_value &&
14384         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14385         element != EL_SPRING && element != EL_BALLOON)
14386     {
14387       /* make sure that there is no move delay before next try to push */
14388       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14389         player->move_delay = 0;
14390
14391       return MP_NO_ACTION;
14392     }
14393
14394     if (IS_SB_ELEMENT(element))
14395     {
14396       if (element == EL_SOKOBAN_FIELD_FULL)
14397       {
14398         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14399         local_player->sokobanfields_still_needed++;
14400       }
14401
14402       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14403       {
14404         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14405         local_player->sokobanfields_still_needed--;
14406       }
14407
14408       Feld[x][y] = EL_SOKOBAN_OBJECT;
14409
14410       if (Back[x][y] == Back[nextx][nexty])
14411         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14412       else if (Back[x][y] != 0)
14413         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14414                                     ACTION_EMPTYING);
14415       else
14416         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14417                                     ACTION_FILLING);
14418
14419       if (local_player->sokobanfields_still_needed == 0 &&
14420           game.emulation == EMU_SOKOBAN)
14421       {
14422         PlayerWins(player);
14423
14424         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14425       }
14426     }
14427     else
14428       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14429
14430     InitMovingField(x, y, move_direction);
14431     GfxAction[x][y] = ACTION_PUSHING;
14432
14433     if (mode == DF_SNAP)
14434       ContinueMoving(x, y);
14435     else
14436       MovPos[x][y] = (dx != 0 ? dx : dy);
14437
14438     Pushed[x][y] = TRUE;
14439     Pushed[nextx][nexty] = TRUE;
14440
14441     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14442       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14443     else
14444       player->push_delay_value = -1;    /* get new value later */
14445
14446     /* check for element change _after_ element has been pushed */
14447     if (game.use_change_when_pushing_bug)
14448     {
14449       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14450                                  player->index_bit, dig_side);
14451       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14452                                           player->index_bit, dig_side);
14453     }
14454   }
14455   else if (IS_SWITCHABLE(element))
14456   {
14457     if (PLAYER_SWITCHING(player, x, y))
14458     {
14459       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14460                                           player->index_bit, dig_side);
14461
14462       return MP_ACTION;
14463     }
14464
14465     player->is_switching = TRUE;
14466     player->switch_x = x;
14467     player->switch_y = y;
14468
14469     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14470
14471     if (element == EL_ROBOT_WHEEL)
14472     {
14473       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14474       ZX = x;
14475       ZY = y;
14476
14477       game.robot_wheel_active = TRUE;
14478
14479       DrawLevelField(x, y);
14480     }
14481     else if (element == EL_SP_TERMINAL)
14482     {
14483       int xx, yy;
14484
14485       SCAN_PLAYFIELD(xx, yy)
14486       {
14487         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14488           Bang(xx, yy);
14489         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14490           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14491       }
14492     }
14493     else if (IS_BELT_SWITCH(element))
14494     {
14495       ToggleBeltSwitch(x, y);
14496     }
14497     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14498              element == EL_SWITCHGATE_SWITCH_DOWN ||
14499              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14500              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14501     {
14502       ToggleSwitchgateSwitch(x, y);
14503     }
14504     else if (element == EL_LIGHT_SWITCH ||
14505              element == EL_LIGHT_SWITCH_ACTIVE)
14506     {
14507       ToggleLightSwitch(x, y);
14508     }
14509     else if (element == EL_TIMEGATE_SWITCH ||
14510              element == EL_DC_TIMEGATE_SWITCH)
14511     {
14512       ActivateTimegateSwitch(x, y);
14513     }
14514     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14515              element == EL_BALLOON_SWITCH_RIGHT ||
14516              element == EL_BALLOON_SWITCH_UP    ||
14517              element == EL_BALLOON_SWITCH_DOWN  ||
14518              element == EL_BALLOON_SWITCH_NONE  ||
14519              element == EL_BALLOON_SWITCH_ANY)
14520     {
14521       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14522                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14523                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14524                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14525                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14526                              move_direction);
14527     }
14528     else if (element == EL_LAMP)
14529     {
14530       Feld[x][y] = EL_LAMP_ACTIVE;
14531       local_player->lights_still_needed--;
14532
14533       ResetGfxAnimation(x, y);
14534       DrawLevelField(x, y);
14535     }
14536     else if (element == EL_TIME_ORB_FULL)
14537     {
14538       Feld[x][y] = EL_TIME_ORB_EMPTY;
14539
14540       if (level.time > 0 || level.use_time_orb_bug)
14541       {
14542         TimeLeft += level.time_orb_time;
14543
14544 #if 1
14545         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14546
14547         DisplayGameControlValues();
14548 #else
14549         DrawGameValue_Time(TimeLeft);
14550 #endif
14551       }
14552
14553       ResetGfxAnimation(x, y);
14554       DrawLevelField(x, y);
14555     }
14556     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14557              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14558     {
14559       int xx, yy;
14560
14561       game.ball_state = !game.ball_state;
14562
14563       SCAN_PLAYFIELD(xx, yy)
14564       {
14565         int e = Feld[xx][yy];
14566
14567         if (game.ball_state)
14568         {
14569           if (e == EL_EMC_MAGIC_BALL)
14570             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14571           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14572             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14573         }
14574         else
14575         {
14576           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14577             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14578           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14579             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14580         }
14581       }
14582     }
14583
14584     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14585                                         player->index_bit, dig_side);
14586
14587     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14588                                         player->index_bit, dig_side);
14589
14590     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14591                                         player->index_bit, dig_side);
14592
14593     return MP_ACTION;
14594   }
14595   else
14596   {
14597     if (!PLAYER_SWITCHING(player, x, y))
14598     {
14599       player->is_switching = TRUE;
14600       player->switch_x = x;
14601       player->switch_y = y;
14602
14603       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14604                                  player->index_bit, dig_side);
14605       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14606                                           player->index_bit, dig_side);
14607
14608       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14609                                  player->index_bit, dig_side);
14610       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14611                                           player->index_bit, dig_side);
14612     }
14613
14614     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14615                                player->index_bit, dig_side);
14616     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14617                                         player->index_bit, dig_side);
14618
14619     return MP_NO_ACTION;
14620   }
14621
14622   player->push_delay = -1;
14623
14624   if (is_player)                /* function can also be called by EL_PENGUIN */
14625   {
14626     if (Feld[x][y] != element)          /* really digged/collected something */
14627     {
14628       player->is_collecting = !player->is_digging;
14629       player->is_active = TRUE;
14630     }
14631   }
14632
14633   return MP_MOVING;
14634 }
14635
14636 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14637 {
14638   int jx = player->jx, jy = player->jy;
14639   int x = jx + dx, y = jy + dy;
14640   int snap_direction = (dx == -1 ? MV_LEFT  :
14641                         dx == +1 ? MV_RIGHT :
14642                         dy == -1 ? MV_UP    :
14643                         dy == +1 ? MV_DOWN  : MV_NONE);
14644   boolean can_continue_snapping = (level.continuous_snapping &&
14645                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14646
14647   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14648     return FALSE;
14649
14650   if (!player->active || !IN_LEV_FIELD(x, y))
14651     return FALSE;
14652
14653   if (dx && dy)
14654     return FALSE;
14655
14656   if (!dx && !dy)
14657   {
14658     if (player->MovPos == 0)
14659       player->is_pushing = FALSE;
14660
14661     player->is_snapping = FALSE;
14662
14663     if (player->MovPos == 0)
14664     {
14665       player->is_moving = FALSE;
14666       player->is_digging = FALSE;
14667       player->is_collecting = FALSE;
14668     }
14669
14670     return FALSE;
14671   }
14672
14673 #if USE_NEW_CONTINUOUS_SNAPPING
14674   /* prevent snapping with already pressed snap key when not allowed */
14675   if (player->is_snapping && !can_continue_snapping)
14676     return FALSE;
14677 #else
14678   if (player->is_snapping)
14679     return FALSE;
14680 #endif
14681
14682   player->MovDir = snap_direction;
14683
14684   if (player->MovPos == 0)
14685   {
14686     player->is_moving = FALSE;
14687     player->is_digging = FALSE;
14688     player->is_collecting = FALSE;
14689   }
14690
14691   player->is_dropping = FALSE;
14692   player->is_dropping_pressed = FALSE;
14693   player->drop_pressed_delay = 0;
14694
14695   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14696     return FALSE;
14697
14698   player->is_snapping = TRUE;
14699   player->is_active = TRUE;
14700
14701   if (player->MovPos == 0)
14702   {
14703     player->is_moving = FALSE;
14704     player->is_digging = FALSE;
14705     player->is_collecting = FALSE;
14706   }
14707
14708   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14709     DrawLevelField(player->last_jx, player->last_jy);
14710
14711   DrawLevelField(x, y);
14712
14713   return TRUE;
14714 }
14715
14716 boolean DropElement(struct PlayerInfo *player)
14717 {
14718   int old_element, new_element;
14719   int dropx = player->jx, dropy = player->jy;
14720   int drop_direction = player->MovDir;
14721   int drop_side = drop_direction;
14722 #if 1
14723   int drop_element = get_next_dropped_element(player);
14724 #else
14725   int drop_element = (player->inventory_size > 0 ?
14726                       player->inventory_element[player->inventory_size - 1] :
14727                       player->inventory_infinite_element != EL_UNDEFINED ?
14728                       player->inventory_infinite_element :
14729                       player->dynabombs_left > 0 ?
14730                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14731                       EL_UNDEFINED);
14732 #endif
14733
14734   player->is_dropping_pressed = TRUE;
14735
14736   /* do not drop an element on top of another element; when holding drop key
14737      pressed without moving, dropped element must move away before the next
14738      element can be dropped (this is especially important if the next element
14739      is dynamite, which can be placed on background for historical reasons) */
14740   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14741     return MP_ACTION;
14742
14743   if (IS_THROWABLE(drop_element))
14744   {
14745     dropx += GET_DX_FROM_DIR(drop_direction);
14746     dropy += GET_DY_FROM_DIR(drop_direction);
14747
14748     if (!IN_LEV_FIELD(dropx, dropy))
14749       return FALSE;
14750   }
14751
14752   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14753   new_element = drop_element;           /* default: no change when dropping */
14754
14755   /* check if player is active, not moving and ready to drop */
14756   if (!player->active || player->MovPos || player->drop_delay > 0)
14757     return FALSE;
14758
14759   /* check if player has anything that can be dropped */
14760   if (new_element == EL_UNDEFINED)
14761     return FALSE;
14762
14763   /* check if drop key was pressed long enough for EM style dynamite */
14764   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14765     return FALSE;
14766
14767   /* check if anything can be dropped at the current position */
14768   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14769     return FALSE;
14770
14771   /* collected custom elements can only be dropped on empty fields */
14772   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14773     return FALSE;
14774
14775   if (old_element != EL_EMPTY)
14776     Back[dropx][dropy] = old_element;   /* store old element on this field */
14777
14778   ResetGfxAnimation(dropx, dropy);
14779   ResetRandomAnimationValue(dropx, dropy);
14780
14781   if (player->inventory_size > 0 ||
14782       player->inventory_infinite_element != EL_UNDEFINED)
14783   {
14784     if (player->inventory_size > 0)
14785     {
14786       player->inventory_size--;
14787
14788       DrawGameDoorValues();
14789
14790       if (new_element == EL_DYNAMITE)
14791         new_element = EL_DYNAMITE_ACTIVE;
14792       else if (new_element == EL_EM_DYNAMITE)
14793         new_element = EL_EM_DYNAMITE_ACTIVE;
14794       else if (new_element == EL_SP_DISK_RED)
14795         new_element = EL_SP_DISK_RED_ACTIVE;
14796     }
14797
14798     Feld[dropx][dropy] = new_element;
14799
14800     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14801       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14802                           el2img(Feld[dropx][dropy]), 0);
14803
14804     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14805
14806     /* needed if previous element just changed to "empty" in the last frame */
14807     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14808
14809     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14810                                player->index_bit, drop_side);
14811     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14812                                         CE_PLAYER_DROPS_X,
14813                                         player->index_bit, drop_side);
14814
14815     TestIfElementTouchesCustomElement(dropx, dropy);
14816   }
14817   else          /* player is dropping a dyna bomb */
14818   {
14819     player->dynabombs_left--;
14820
14821     Feld[dropx][dropy] = new_element;
14822
14823     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14824       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14825                           el2img(Feld[dropx][dropy]), 0);
14826
14827     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14828   }
14829
14830   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14831     InitField_WithBug1(dropx, dropy, FALSE);
14832
14833   new_element = Feld[dropx][dropy];     /* element might have changed */
14834
14835   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14836       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14837   {
14838     int move_direction, nextx, nexty;
14839
14840     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14841       MovDir[dropx][dropy] = drop_direction;
14842
14843     move_direction = MovDir[dropx][dropy];
14844     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14845     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14846
14847     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14848
14849 #if USE_FIX_IMPACT_COLLISION
14850     /* do not cause impact style collision by dropping elements that can fall */
14851     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14852 #else
14853     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14854 #endif
14855   }
14856
14857   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14858   player->is_dropping = TRUE;
14859
14860   player->drop_pressed_delay = 0;
14861   player->is_dropping_pressed = FALSE;
14862
14863   player->drop_x = dropx;
14864   player->drop_y = dropy;
14865
14866   return TRUE;
14867 }
14868
14869 /* ------------------------------------------------------------------------- */
14870 /* game sound playing functions                                              */
14871 /* ------------------------------------------------------------------------- */
14872
14873 static int *loop_sound_frame = NULL;
14874 static int *loop_sound_volume = NULL;
14875
14876 void InitPlayLevelSound()
14877 {
14878   int num_sounds = getSoundListSize();
14879
14880   checked_free(loop_sound_frame);
14881   checked_free(loop_sound_volume);
14882
14883   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14884   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14885 }
14886
14887 static void PlayLevelSound(int x, int y, int nr)
14888 {
14889   int sx = SCREENX(x), sy = SCREENY(y);
14890   int volume, stereo_position;
14891   int max_distance = 8;
14892   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14893
14894   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14895       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14896     return;
14897
14898   if (!IN_LEV_FIELD(x, y) ||
14899       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14900       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14901     return;
14902
14903   volume = SOUND_MAX_VOLUME;
14904
14905   if (!IN_SCR_FIELD(sx, sy))
14906   {
14907     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14908     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14909
14910     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14911   }
14912
14913   stereo_position = (SOUND_MAX_LEFT +
14914                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14915                      (SCR_FIELDX + 2 * max_distance));
14916
14917   if (IS_LOOP_SOUND(nr))
14918   {
14919     /* This assures that quieter loop sounds do not overwrite louder ones,
14920        while restarting sound volume comparison with each new game frame. */
14921
14922     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14923       return;
14924
14925     loop_sound_volume[nr] = volume;
14926     loop_sound_frame[nr] = FrameCounter;
14927   }
14928
14929   PlaySoundExt(nr, volume, stereo_position, type);
14930 }
14931
14932 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14933 {
14934   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14935                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14936                  y < LEVELY(BY1) ? LEVELY(BY1) :
14937                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14938                  sound_action);
14939 }
14940
14941 static void PlayLevelSoundAction(int x, int y, int action)
14942 {
14943   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14944 }
14945
14946 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14947 {
14948   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14949
14950   if (sound_effect != SND_UNDEFINED)
14951     PlayLevelSound(x, y, sound_effect);
14952 }
14953
14954 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14955                                               int action)
14956 {
14957   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14958
14959   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14960     PlayLevelSound(x, y, sound_effect);
14961 }
14962
14963 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14964 {
14965   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14966
14967   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14968     PlayLevelSound(x, y, sound_effect);
14969 }
14970
14971 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14972 {
14973   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14974
14975   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14976     StopSound(sound_effect);
14977 }
14978
14979 static void PlayLevelMusic()
14980 {
14981   if (levelset.music[level_nr] != MUS_UNDEFINED)
14982     PlayMusic(levelset.music[level_nr]);        /* from config file */
14983   else
14984     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14985 }
14986
14987 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14988 {
14989   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14990   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14991   int x = xx - 1 - offset;
14992   int y = yy - 1 - offset;
14993
14994   switch (sample)
14995   {
14996     case SAMPLE_blank:
14997       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14998       break;
14999
15000     case SAMPLE_roll:
15001       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15002       break;
15003
15004     case SAMPLE_stone:
15005       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15006       break;
15007
15008     case SAMPLE_nut:
15009       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15010       break;
15011
15012     case SAMPLE_crack:
15013       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15014       break;
15015
15016     case SAMPLE_bug:
15017       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15018       break;
15019
15020     case SAMPLE_tank:
15021       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15022       break;
15023
15024     case SAMPLE_android_clone:
15025       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15026       break;
15027
15028     case SAMPLE_android_move:
15029       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15030       break;
15031
15032     case SAMPLE_spring:
15033       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15034       break;
15035
15036     case SAMPLE_slurp:
15037       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15038       break;
15039
15040     case SAMPLE_eater:
15041       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15042       break;
15043
15044     case SAMPLE_eater_eat:
15045       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15046       break;
15047
15048     case SAMPLE_alien:
15049       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15050       break;
15051
15052     case SAMPLE_collect:
15053       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15054       break;
15055
15056     case SAMPLE_diamond:
15057       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15058       break;
15059
15060     case SAMPLE_squash:
15061       /* !!! CHECK THIS !!! */
15062 #if 1
15063       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15064 #else
15065       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15066 #endif
15067       break;
15068
15069     case SAMPLE_wonderfall:
15070       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15071       break;
15072
15073     case SAMPLE_drip:
15074       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15075       break;
15076
15077     case SAMPLE_push:
15078       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15079       break;
15080
15081     case SAMPLE_dirt:
15082       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15083       break;
15084
15085     case SAMPLE_acid:
15086       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15087       break;
15088
15089     case SAMPLE_ball:
15090       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15091       break;
15092
15093     case SAMPLE_grow:
15094       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15095       break;
15096
15097     case SAMPLE_wonder:
15098       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15099       break;
15100
15101     case SAMPLE_door:
15102       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15103       break;
15104
15105     case SAMPLE_exit_open:
15106       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15107       break;
15108
15109     case SAMPLE_exit_leave:
15110       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15111       break;
15112
15113     case SAMPLE_dynamite:
15114       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15115       break;
15116
15117     case SAMPLE_tick:
15118       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15119       break;
15120
15121     case SAMPLE_press:
15122       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15123       break;
15124
15125     case SAMPLE_wheel:
15126       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15127       break;
15128
15129     case SAMPLE_boom:
15130       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15131       break;
15132
15133     case SAMPLE_die:
15134       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15135       break;
15136
15137     case SAMPLE_time:
15138       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15139       break;
15140
15141     default:
15142       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15143       break;
15144   }
15145 }
15146
15147 #if 0
15148 void ChangeTime(int value)
15149 {
15150   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15151
15152   *time += value;
15153
15154   /* EMC game engine uses value from time counter of RND game engine */
15155   level.native_em_level->lev->time = *time;
15156
15157   DrawGameValue_Time(*time);
15158 }
15159
15160 void RaiseScore(int value)
15161 {
15162   /* EMC game engine and RND game engine have separate score counters */
15163   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15164                 &level.native_em_level->lev->score : &local_player->score);
15165
15166   *score += value;
15167
15168   DrawGameValue_Score(*score);
15169 }
15170 #endif
15171
15172 void RaiseScore(int value)
15173 {
15174   local_player->score += value;
15175
15176 #if 1
15177   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15178
15179   DisplayGameControlValues();
15180 #else
15181   DrawGameValue_Score(local_player->score);
15182 #endif
15183 }
15184
15185 void RaiseScoreElement(int element)
15186 {
15187   switch (element)
15188   {
15189     case EL_EMERALD:
15190     case EL_BD_DIAMOND:
15191     case EL_EMERALD_YELLOW:
15192     case EL_EMERALD_RED:
15193     case EL_EMERALD_PURPLE:
15194     case EL_SP_INFOTRON:
15195       RaiseScore(level.score[SC_EMERALD]);
15196       break;
15197     case EL_DIAMOND:
15198       RaiseScore(level.score[SC_DIAMOND]);
15199       break;
15200     case EL_CRYSTAL:
15201       RaiseScore(level.score[SC_CRYSTAL]);
15202       break;
15203     case EL_PEARL:
15204       RaiseScore(level.score[SC_PEARL]);
15205       break;
15206     case EL_BUG:
15207     case EL_BD_BUTTERFLY:
15208     case EL_SP_ELECTRON:
15209       RaiseScore(level.score[SC_BUG]);
15210       break;
15211     case EL_SPACESHIP:
15212     case EL_BD_FIREFLY:
15213     case EL_SP_SNIKSNAK:
15214       RaiseScore(level.score[SC_SPACESHIP]);
15215       break;
15216     case EL_YAMYAM:
15217     case EL_DARK_YAMYAM:
15218       RaiseScore(level.score[SC_YAMYAM]);
15219       break;
15220     case EL_ROBOT:
15221       RaiseScore(level.score[SC_ROBOT]);
15222       break;
15223     case EL_PACMAN:
15224       RaiseScore(level.score[SC_PACMAN]);
15225       break;
15226     case EL_NUT:
15227       RaiseScore(level.score[SC_NUT]);
15228       break;
15229     case EL_DYNAMITE:
15230     case EL_EM_DYNAMITE:
15231     case EL_SP_DISK_RED:
15232     case EL_DYNABOMB_INCREASE_NUMBER:
15233     case EL_DYNABOMB_INCREASE_SIZE:
15234     case EL_DYNABOMB_INCREASE_POWER:
15235       RaiseScore(level.score[SC_DYNAMITE]);
15236       break;
15237     case EL_SHIELD_NORMAL:
15238     case EL_SHIELD_DEADLY:
15239       RaiseScore(level.score[SC_SHIELD]);
15240       break;
15241     case EL_EXTRA_TIME:
15242       RaiseScore(level.extra_time_score);
15243       break;
15244     case EL_KEY_1:
15245     case EL_KEY_2:
15246     case EL_KEY_3:
15247     case EL_KEY_4:
15248     case EL_EM_KEY_1:
15249     case EL_EM_KEY_2:
15250     case EL_EM_KEY_3:
15251     case EL_EM_KEY_4:
15252     case EL_EMC_KEY_5:
15253     case EL_EMC_KEY_6:
15254     case EL_EMC_KEY_7:
15255     case EL_EMC_KEY_8:
15256     case EL_DC_KEY_WHITE:
15257       RaiseScore(level.score[SC_KEY]);
15258       break;
15259     default:
15260       RaiseScore(element_info[element].collect_score);
15261       break;
15262   }
15263 }
15264
15265 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15266 {
15267   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15268   {
15269 #if defined(NETWORK_AVALIABLE)
15270     if (options.network)
15271       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15272     else
15273 #endif
15274     {
15275       if (quick_quit)
15276       {
15277 #if 1
15278
15279 #if 1
15280         FadeSkipNextFadeIn();
15281 #else
15282         fading = fading_none;
15283 #endif
15284
15285 #else
15286         OpenDoor(DOOR_CLOSE_1);
15287 #endif
15288
15289         game_status = GAME_MODE_MAIN;
15290
15291 #if 1
15292         DrawAndFadeInMainMenu(REDRAW_FIELD);
15293 #else
15294         DrawMainMenu();
15295 #endif
15296       }
15297       else
15298       {
15299 #if 0
15300         FadeOut(REDRAW_FIELD);
15301 #endif
15302
15303         game_status = GAME_MODE_MAIN;
15304
15305         DrawAndFadeInMainMenu(REDRAW_FIELD);
15306       }
15307     }
15308   }
15309   else          /* continue playing the game */
15310   {
15311     if (tape.playing && tape.deactivate_display)
15312       TapeDeactivateDisplayOff(TRUE);
15313
15314     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15315
15316     if (tape.playing && tape.deactivate_display)
15317       TapeDeactivateDisplayOn();
15318   }
15319 }
15320
15321 void RequestQuitGame(boolean ask_if_really_quit)
15322 {
15323   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15324   boolean skip_request = AllPlayersGone || quick_quit;
15325
15326   RequestQuitGameExt(skip_request, quick_quit,
15327                      "Do you really want to quit the game ?");
15328 }
15329
15330
15331 /* ------------------------------------------------------------------------- */
15332 /* random generator functions                                                */
15333 /* ------------------------------------------------------------------------- */
15334
15335 unsigned int InitEngineRandom_RND(long seed)
15336 {
15337   game.num_random_calls = 0;
15338
15339 #if 0
15340   unsigned int rnd_seed = InitEngineRandom(seed);
15341
15342   printf("::: START RND: %d\n", rnd_seed);
15343
15344   return rnd_seed;
15345 #else
15346
15347   return InitEngineRandom(seed);
15348
15349 #endif
15350
15351 }
15352
15353 unsigned int RND(int max)
15354 {
15355   if (max > 0)
15356   {
15357     game.num_random_calls++;
15358
15359     return GetEngineRandom(max);
15360   }
15361
15362   return 0;
15363 }
15364
15365
15366 /* ------------------------------------------------------------------------- */
15367 /* game engine snapshot handling functions                                   */
15368 /* ------------------------------------------------------------------------- */
15369
15370 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15371
15372 struct EngineSnapshotInfo
15373 {
15374   /* runtime values for custom element collect score */
15375   int collect_score[NUM_CUSTOM_ELEMENTS];
15376
15377   /* runtime values for group element choice position */
15378   int choice_pos[NUM_GROUP_ELEMENTS];
15379
15380   /* runtime values for belt position animations */
15381   int belt_graphic[4 * NUM_BELT_PARTS];
15382   int belt_anim_mode[4 * NUM_BELT_PARTS];
15383 };
15384
15385 struct EngineSnapshotNodeInfo
15386 {
15387   void *buffer_orig;
15388   void *buffer_copy;
15389   int size;
15390 };
15391
15392 static struct EngineSnapshotInfo engine_snapshot_rnd;
15393 static ListNode *engine_snapshot_list = NULL;
15394 static char *snapshot_level_identifier = NULL;
15395 static int snapshot_level_nr = -1;
15396
15397 void FreeEngineSnapshot()
15398 {
15399   while (engine_snapshot_list != NULL)
15400     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15401                        checked_free);
15402
15403   setString(&snapshot_level_identifier, NULL);
15404   snapshot_level_nr = -1;
15405 }
15406
15407 static void SaveEngineSnapshotValues_RND()
15408 {
15409   static int belt_base_active_element[4] =
15410   {
15411     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15412     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15413     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15414     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15415   };
15416   int i, j;
15417
15418   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15419   {
15420     int element = EL_CUSTOM_START + i;
15421
15422     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15423   }
15424
15425   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15426   {
15427     int element = EL_GROUP_START + i;
15428
15429     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15430   }
15431
15432   for (i = 0; i < 4; i++)
15433   {
15434     for (j = 0; j < NUM_BELT_PARTS; j++)
15435     {
15436       int element = belt_base_active_element[i] + j;
15437       int graphic = el2img(element);
15438       int anim_mode = graphic_info[graphic].anim_mode;
15439
15440       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15441       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15442     }
15443   }
15444 }
15445
15446 static void LoadEngineSnapshotValues_RND()
15447 {
15448   unsigned long num_random_calls = game.num_random_calls;
15449   int i, j;
15450
15451   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15452   {
15453     int element = EL_CUSTOM_START + i;
15454
15455     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15456   }
15457
15458   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15459   {
15460     int element = EL_GROUP_START + i;
15461
15462     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15463   }
15464
15465   for (i = 0; i < 4; i++)
15466   {
15467     for (j = 0; j < NUM_BELT_PARTS; j++)
15468     {
15469       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15470       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15471
15472       graphic_info[graphic].anim_mode = anim_mode;
15473     }
15474   }
15475
15476   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15477   {
15478     InitRND(tape.random_seed);
15479     for (i = 0; i < num_random_calls; i++)
15480       RND(1);
15481   }
15482
15483   if (game.num_random_calls != num_random_calls)
15484   {
15485     Error(ERR_INFO, "number of random calls out of sync");
15486     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15487     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15488     Error(ERR_EXIT, "this should not happen -- please debug");
15489   }
15490 }
15491
15492 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15493 {
15494   struct EngineSnapshotNodeInfo *bi =
15495     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15496
15497   bi->buffer_orig = buffer;
15498   bi->buffer_copy = checked_malloc(size);
15499   bi->size = size;
15500
15501   memcpy(bi->buffer_copy, buffer, size);
15502
15503   addNodeToList(&engine_snapshot_list, NULL, bi);
15504 }
15505
15506 void SaveEngineSnapshot()
15507 {
15508   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15509
15510   if (level_editor_test_game)   /* do not save snapshots from editor */
15511     return;
15512
15513   /* copy some special values to a structure better suited for the snapshot */
15514
15515   SaveEngineSnapshotValues_RND();
15516   SaveEngineSnapshotValues_EM();
15517
15518   /* save values stored in special snapshot structure */
15519
15520   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15521   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15522
15523   /* save further RND engine values */
15524
15525   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15526   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15527   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15528
15529   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15530   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15531   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15532   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15533
15534   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15535   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15536   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15537   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15538   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15539
15540   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15541   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15542   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15543
15544   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15545
15546   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15547
15548   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15549   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15550
15551   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15553   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15554   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15555   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15556   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15558   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15559   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15560   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15561   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15562   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15563   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15565   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15566   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15567   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15569
15570   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15572
15573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15576
15577   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15579
15580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15585
15586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15588
15589   /* save level identification information */
15590
15591   setString(&snapshot_level_identifier, leveldir_current->identifier);
15592   snapshot_level_nr = level_nr;
15593
15594 #if 0
15595   ListNode *node = engine_snapshot_list;
15596   int num_bytes = 0;
15597
15598   while (node != NULL)
15599   {
15600     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15601
15602     node = node->next;
15603   }
15604
15605   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15606 #endif
15607 }
15608
15609 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15610 {
15611   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15612 }
15613
15614 void LoadEngineSnapshot()
15615 {
15616   ListNode *node = engine_snapshot_list;
15617
15618   if (engine_snapshot_list == NULL)
15619     return;
15620
15621   while (node != NULL)
15622   {
15623     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15624
15625     node = node->next;
15626   }
15627
15628   /* restore special values from snapshot structure */
15629
15630   LoadEngineSnapshotValues_RND();
15631   LoadEngineSnapshotValues_EM();
15632 }
15633
15634 boolean CheckEngineSnapshot()
15635 {
15636   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15637           snapshot_level_nr == level_nr);
15638 }
15639
15640
15641 /* ---------- new game button stuff ---------------------------------------- */
15642
15643 /* graphic position values for game buttons */
15644 #define GAME_BUTTON_XSIZE       30
15645 #define GAME_BUTTON_YSIZE       30
15646 #define GAME_BUTTON_XPOS        5
15647 #define GAME_BUTTON_YPOS        215
15648 #define SOUND_BUTTON_XPOS       5
15649 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15650
15651 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15652 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15653 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15654 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15655 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15656 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15657
15658 static struct
15659 {
15660   int *x, *y;
15661   int gd_x, gd_y;
15662   int gadget_id;
15663   char *infotext;
15664 } gamebutton_info[NUM_GAME_BUTTONS] =
15665 {
15666 #if 1
15667   {
15668     &game.button.stop.x,        &game.button.stop.y,
15669     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15670     GAME_CTRL_ID_STOP,
15671     "stop game"
15672   },
15673   {
15674     &game.button.pause.x,       &game.button.pause.y,
15675     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15676     GAME_CTRL_ID_PAUSE,
15677     "pause game"
15678   },
15679   {
15680     &game.button.play.x,        &game.button.play.y,
15681     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15682     GAME_CTRL_ID_PLAY,
15683     "play game"
15684   },
15685   {
15686     &game.button.sound_music.x, &game.button.sound_music.y,
15687     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15688     SOUND_CTRL_ID_MUSIC,
15689     "background music on/off"
15690   },
15691   {
15692     &game.button.sound_loops.x, &game.button.sound_loops.y,
15693     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15694     SOUND_CTRL_ID_LOOPS,
15695     "sound loops on/off"
15696   },
15697   {
15698     &game.button.sound_simple.x,&game.button.sound_simple.y,
15699     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15700     SOUND_CTRL_ID_SIMPLE,
15701     "normal sounds on/off"
15702   }
15703 #else
15704   {
15705     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15706     GAME_CTRL_ID_STOP,
15707     "stop game"
15708   },
15709   {
15710     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15711     GAME_CTRL_ID_PAUSE,
15712     "pause game"
15713   },
15714   {
15715     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15716     GAME_CTRL_ID_PLAY,
15717     "play game"
15718   },
15719   {
15720     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15721     SOUND_CTRL_ID_MUSIC,
15722     "background music on/off"
15723   },
15724   {
15725     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15726     SOUND_CTRL_ID_LOOPS,
15727     "sound loops on/off"
15728   },
15729   {
15730     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15731     SOUND_CTRL_ID_SIMPLE,
15732     "normal sounds on/off"
15733   }
15734 #endif
15735 };
15736
15737 void CreateGameButtons()
15738 {
15739   int i;
15740
15741   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15742   {
15743     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15744     struct GadgetInfo *gi;
15745     int button_type;
15746     boolean checked;
15747     unsigned long event_mask;
15748     int x, y;
15749     int gd_xoffset, gd_yoffset;
15750     int gd_x1, gd_x2, gd_y1, gd_y2;
15751     int id = i;
15752
15753     x = DX + *gamebutton_info[i].x;
15754     y = DY + *gamebutton_info[i].y;
15755     gd_xoffset = gamebutton_info[i].gd_x;
15756     gd_yoffset = gamebutton_info[i].gd_y;
15757     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15758     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15759
15760     if (id == GAME_CTRL_ID_STOP ||
15761         id == GAME_CTRL_ID_PAUSE ||
15762         id == GAME_CTRL_ID_PLAY)
15763     {
15764       button_type = GD_TYPE_NORMAL_BUTTON;
15765       checked = FALSE;
15766       event_mask = GD_EVENT_RELEASED;
15767       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15768       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15769     }
15770     else
15771     {
15772       button_type = GD_TYPE_CHECK_BUTTON;
15773       checked =
15774         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15775          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15776          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15777       event_mask = GD_EVENT_PRESSED;
15778       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15779       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15780     }
15781
15782     gi = CreateGadget(GDI_CUSTOM_ID, id,
15783                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15784 #if 1
15785                       GDI_X, x,
15786                       GDI_Y, y,
15787 #else
15788                       GDI_X, DX + gd_xoffset,
15789                       GDI_Y, DY + gd_yoffset,
15790 #endif
15791                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15792                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15793                       GDI_TYPE, button_type,
15794                       GDI_STATE, GD_BUTTON_UNPRESSED,
15795                       GDI_CHECKED, checked,
15796                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15797                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15798                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15799                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15800                       GDI_DIRECT_DRAW, FALSE,
15801                       GDI_EVENT_MASK, event_mask,
15802                       GDI_CALLBACK_ACTION, HandleGameButtons,
15803                       GDI_END);
15804
15805     if (gi == NULL)
15806       Error(ERR_EXIT, "cannot create gadget");
15807
15808     game_gadget[id] = gi;
15809   }
15810 }
15811
15812 void FreeGameButtons()
15813 {
15814   int i;
15815
15816   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15817     FreeGadget(game_gadget[i]);
15818 }
15819
15820 static void MapGameButtons()
15821 {
15822   int i;
15823
15824   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15825     MapGadget(game_gadget[i]);
15826 }
15827
15828 void UnmapGameButtons()
15829 {
15830   int i;
15831
15832   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15833     UnmapGadget(game_gadget[i]);
15834 }
15835
15836 void RedrawGameButtons()
15837 {
15838   int i;
15839
15840   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15841     RedrawGadget(game_gadget[i]);
15842 }
15843
15844 static void HandleGameButtons(struct GadgetInfo *gi)
15845 {
15846   int id = gi->custom_id;
15847
15848   if (game_status != GAME_MODE_PLAYING)
15849     return;
15850
15851   switch (id)
15852   {
15853     case GAME_CTRL_ID_STOP:
15854       if (tape.playing)
15855         TapeStop();
15856       else
15857         RequestQuitGame(TRUE);
15858       break;
15859
15860     case GAME_CTRL_ID_PAUSE:
15861       if (options.network)
15862       {
15863 #if defined(NETWORK_AVALIABLE)
15864         if (tape.pausing)
15865           SendToServer_ContinuePlaying();
15866         else
15867           SendToServer_PausePlaying();
15868 #endif
15869       }
15870       else
15871         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15872       break;
15873
15874     case GAME_CTRL_ID_PLAY:
15875       if (tape.pausing)
15876       {
15877 #if defined(NETWORK_AVALIABLE)
15878         if (options.network)
15879           SendToServer_ContinuePlaying();
15880         else
15881 #endif
15882         {
15883           tape.pausing = FALSE;
15884           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15885         }
15886       }
15887       break;
15888
15889     case SOUND_CTRL_ID_MUSIC:
15890       if (setup.sound_music)
15891       { 
15892         setup.sound_music = FALSE;
15893         FadeMusic();
15894       }
15895       else if (audio.music_available)
15896       { 
15897         setup.sound = setup.sound_music = TRUE;
15898
15899         SetAudioMode(setup.sound);
15900
15901         PlayLevelMusic();
15902       }
15903       break;
15904
15905     case SOUND_CTRL_ID_LOOPS:
15906       if (setup.sound_loops)
15907         setup.sound_loops = FALSE;
15908       else if (audio.loops_available)
15909       {
15910         setup.sound = setup.sound_loops = TRUE;
15911         SetAudioMode(setup.sound);
15912       }
15913       break;
15914
15915     case SOUND_CTRL_ID_SIMPLE:
15916       if (setup.sound_simple)
15917         setup.sound_simple = FALSE;
15918       else if (audio.sound_available)
15919       {
15920         setup.sound = setup.sound_simple = TRUE;
15921         SetAudioMode(setup.sound);
15922       }
15923       break;
15924
15925     default:
15926       break;
15927   }
15928 }