rnd-20070913-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_PANEL_LEVEL_NUMBER                 0
135 #define GAME_PANEL_GEMS                         1
136 #define GAME_PANEL_INVENTORY_COUNT              2
137 #define GAME_PANEL_INVENTORY_FIRST_1            3
138 #define GAME_PANEL_INVENTORY_FIRST_2            4
139 #define GAME_PANEL_INVENTORY_FIRST_3            5
140 #define GAME_PANEL_INVENTORY_FIRST_4            6
141 #define GAME_PANEL_INVENTORY_FIRST_5            7
142 #define GAME_PANEL_INVENTORY_FIRST_6            8
143 #define GAME_PANEL_INVENTORY_FIRST_7            9
144 #define GAME_PANEL_INVENTORY_FIRST_8            10
145 #define GAME_PANEL_INVENTORY_LAST_1             11
146 #define GAME_PANEL_INVENTORY_LAST_2             12
147 #define GAME_PANEL_INVENTORY_LAST_3             13
148 #define GAME_PANEL_INVENTORY_LAST_4             14
149 #define GAME_PANEL_INVENTORY_LAST_5             15
150 #define GAME_PANEL_INVENTORY_LAST_6             16
151 #define GAME_PANEL_INVENTORY_LAST_7             17
152 #define GAME_PANEL_INVENTORY_LAST_8             18
153 #define GAME_PANEL_KEY_1                        19
154 #define GAME_PANEL_KEY_2                        20
155 #define GAME_PANEL_KEY_3                        21
156 #define GAME_PANEL_KEY_4                        22
157 #define GAME_PANEL_KEY_5                        23
158 #define GAME_PANEL_KEY_6                        24
159 #define GAME_PANEL_KEY_7                        25
160 #define GAME_PANEL_KEY_8                        26
161 #define GAME_PANEL_KEY_WHITE                    27
162 #define GAME_PANEL_KEY_WHITE_COUNT              28
163 #define GAME_PANEL_SCORE                        29
164 #define GAME_PANEL_TIME                         30
165 #define GAME_PANEL_TIME_HH                      31
166 #define GAME_PANEL_TIME_MM                      32
167 #define GAME_PANEL_TIME_SS                      33
168 #define GAME_PANEL_SHIELD_NORMAL                34
169 #define GAME_PANEL_SHIELD_NORMAL_TIME           35
170 #define GAME_PANEL_SHIELD_DEADLY                36
171 #define GAME_PANEL_SHIELD_DEADLY_TIME           37
172 #define GAME_PANEL_EXIT                         38
173 #define GAME_PANEL_EMC_MAGIC_BALL               39
174 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        40
175 #define GAME_PANEL_LIGHT_SWITCH                 41
176 #define GAME_PANEL_LIGHT_SWITCH_TIME            42
177 #define GAME_PANEL_TIMEGATE_SWITCH              43
178 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         44
179 #define GAME_PANEL_SWITCHGATE_SWITCH            45
180 #define GAME_PANEL_EMC_LENSES                   46
181 #define GAME_PANEL_EMC_LENSES_TIME              47
182 #define GAME_PANEL_EMC_MAGNIFIER                48
183 #define GAME_PANEL_EMC_MAGNIFIER_TIME           49
184 #define GAME_PANEL_BALLOON_SWITCH               50
185 #define GAME_PANEL_DYNABOMB_NUMBER              51
186 #define GAME_PANEL_DYNABOMB_SIZE                52
187 #define GAME_PANEL_DYNABOMB_POWER               53
188 #define GAME_PANEL_PENGUINS                     54
189 #define GAME_PANEL_SOKOBAN_OBJECTS              55
190 #define GAME_PANEL_SOKOBAN_FIELDS               56
191 #define GAME_PANEL_ROBOT_WHEEL                  57
192 #define GAME_PANEL_CONVEYOR_BELT_1              58
193 #define GAME_PANEL_CONVEYOR_BELT_2              59
194 #define GAME_PANEL_CONVEYOR_BELT_3              60
195 #define GAME_PANEL_CONVEYOR_BELT_4              61
196 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       62
197 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       63
198 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       64
199 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       65
200 #define GAME_PANEL_MAGIC_WALL                   66
201 #define GAME_PANEL_MAGIC_WALL_TIME              67
202 #define GAME_PANEL_GRAVITY_STATE                68
203 #define GAME_PANEL_GRAPHIC_1                    69
204 #define GAME_PANEL_GRAPHIC_2                    70
205 #define GAME_PANEL_GRAPHIC_3                    71
206 #define GAME_PANEL_GRAPHIC_4                    72
207 #define GAME_PANEL_GRAPHIC_5                    73
208 #define GAME_PANEL_GRAPHIC_6                    74
209 #define GAME_PANEL_GRAPHIC_7                    75
210 #define GAME_PANEL_GRAPHIC_8                    76
211 #define GAME_PANEL_ELEMENT_1                    77
212 #define GAME_PANEL_ELEMENT_2                    78
213 #define GAME_PANEL_ELEMENT_3                    79
214 #define GAME_PANEL_ELEMENT_4                    80
215 #define GAME_PANEL_ELEMENT_5                    81
216 #define GAME_PANEL_ELEMENT_6                    82
217 #define GAME_PANEL_ELEMENT_7                    83
218 #define GAME_PANEL_ELEMENT_8                    84
219 #define GAME_PANEL_ELEMENT_COUNT_1              85
220 #define GAME_PANEL_ELEMENT_COUNT_2              86
221 #define GAME_PANEL_ELEMENT_COUNT_3              87
222 #define GAME_PANEL_ELEMENT_COUNT_4              88
223 #define GAME_PANEL_ELEMENT_COUNT_5              89
224 #define GAME_PANEL_ELEMENT_COUNT_6              90
225 #define GAME_PANEL_ELEMENT_COUNT_7              91
226 #define GAME_PANEL_ELEMENT_COUNT_8              92
227 #define GAME_PANEL_CE_SCORE_1                   93
228 #define GAME_PANEL_CE_SCORE_2                   94
229 #define GAME_PANEL_CE_SCORE_3                   95
230 #define GAME_PANEL_CE_SCORE_4                   96
231 #define GAME_PANEL_CE_SCORE_5                   97
232 #define GAME_PANEL_CE_SCORE_6                   98
233 #define GAME_PANEL_CE_SCORE_7                   99
234 #define GAME_PANEL_CE_SCORE_8                   100
235 #define GAME_PANEL_CE_SCORE_1_ELEMENT           101
236 #define GAME_PANEL_CE_SCORE_2_ELEMENT           102
237 #define GAME_PANEL_CE_SCORE_3_ELEMENT           103
238 #define GAME_PANEL_CE_SCORE_4_ELEMENT           104
239 #define GAME_PANEL_CE_SCORE_5_ELEMENT           105
240 #define GAME_PANEL_CE_SCORE_6_ELEMENT           106
241 #define GAME_PANEL_CE_SCORE_7_ELEMENT           107
242 #define GAME_PANEL_CE_SCORE_8_ELEMENT           108
243 #define GAME_PANEL_PLAYER_NAME                  109
244 #define GAME_PANEL_LEVEL_NAME                   110
245 #define GAME_PANEL_LEVEL_AUTHOR                 111
246
247 #define NUM_GAME_PANEL_CONTROLS                 112
248
249 struct GamePanelOrderInfo
250 {
251   int nr;
252   int sort_priority;
253 };
254
255 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
256
257 struct GamePanelControlInfo
258 {
259   int nr;
260
261   struct TextPosInfo *pos;
262   int type;
263
264   int value, last_value;
265   int frame, last_frame;
266   int gfx_frame;
267   int gfx_random;
268 };
269
270 static struct GamePanelControlInfo game_panel_controls[] =
271 {
272   {
273     GAME_PANEL_LEVEL_NUMBER,
274     &game.panel.level_number,
275     TYPE_INTEGER,
276   },
277   {
278     GAME_PANEL_GEMS,
279     &game.panel.gems,
280     TYPE_INTEGER,
281   },
282   {
283     GAME_PANEL_INVENTORY_COUNT,
284     &game.panel.inventory_count,
285     TYPE_INTEGER,
286   },
287   {
288     GAME_PANEL_INVENTORY_FIRST_1,
289     &game.panel.inventory_first[0],
290     TYPE_ELEMENT,
291   },
292   {
293     GAME_PANEL_INVENTORY_FIRST_2,
294     &game.panel.inventory_first[1],
295     TYPE_ELEMENT,
296   },
297   {
298     GAME_PANEL_INVENTORY_FIRST_3,
299     &game.panel.inventory_first[2],
300     TYPE_ELEMENT,
301   },
302   {
303     GAME_PANEL_INVENTORY_FIRST_4,
304     &game.panel.inventory_first[3],
305     TYPE_ELEMENT,
306   },
307   {
308     GAME_PANEL_INVENTORY_FIRST_5,
309     &game.panel.inventory_first[4],
310     TYPE_ELEMENT,
311   },
312   {
313     GAME_PANEL_INVENTORY_FIRST_6,
314     &game.panel.inventory_first[5],
315     TYPE_ELEMENT,
316   },
317   {
318     GAME_PANEL_INVENTORY_FIRST_7,
319     &game.panel.inventory_first[6],
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_PANEL_INVENTORY_FIRST_8,
324     &game.panel.inventory_first[7],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_PANEL_INVENTORY_LAST_1,
329     &game.panel.inventory_last[0],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_PANEL_INVENTORY_LAST_2,
334     &game.panel.inventory_last[1],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_PANEL_INVENTORY_LAST_3,
339     &game.panel.inventory_last[2],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_PANEL_INVENTORY_LAST_4,
344     &game.panel.inventory_last[3],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_PANEL_INVENTORY_LAST_5,
349     &game.panel.inventory_last[4],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_PANEL_INVENTORY_LAST_6,
354     &game.panel.inventory_last[5],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_PANEL_INVENTORY_LAST_7,
359     &game.panel.inventory_last[6],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_PANEL_INVENTORY_LAST_8,
364     &game.panel.inventory_last[7],
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_PANEL_KEY_1,
369     &game.panel.key[0],
370     TYPE_ELEMENT,
371   },
372   {
373     GAME_PANEL_KEY_2,
374     &game.panel.key[1],
375     TYPE_ELEMENT,
376   },
377   {
378     GAME_PANEL_KEY_3,
379     &game.panel.key[2],
380     TYPE_ELEMENT,
381   },
382   {
383     GAME_PANEL_KEY_4,
384     &game.panel.key[3],
385     TYPE_ELEMENT,
386   },
387   {
388     GAME_PANEL_KEY_5,
389     &game.panel.key[4],
390     TYPE_ELEMENT,
391   },
392   {
393     GAME_PANEL_KEY_6,
394     &game.panel.key[5],
395     TYPE_ELEMENT,
396   },
397   {
398     GAME_PANEL_KEY_7,
399     &game.panel.key[6],
400     TYPE_ELEMENT,
401   },
402   {
403     GAME_PANEL_KEY_8,
404     &game.panel.key[7],
405     TYPE_ELEMENT,
406   },
407   {
408     GAME_PANEL_KEY_WHITE,
409     &game.panel.key_white,
410     TYPE_ELEMENT,
411   },
412   {
413     GAME_PANEL_KEY_WHITE_COUNT,
414     &game.panel.key_white_count,
415     TYPE_INTEGER,
416   },
417   {
418     GAME_PANEL_SCORE,
419     &game.panel.score,
420     TYPE_INTEGER,
421   },
422   {
423     GAME_PANEL_TIME,
424     &game.panel.time,
425     TYPE_INTEGER,
426   },
427   {
428     GAME_PANEL_TIME_HH,
429     &game.panel.time_hh,
430     TYPE_INTEGER,
431   },
432   {
433     GAME_PANEL_TIME_MM,
434     &game.panel.time_mm,
435     TYPE_INTEGER,
436   },
437   {
438     GAME_PANEL_TIME_SS,
439     &game.panel.time_ss,
440     TYPE_INTEGER,
441   },
442   {
443     GAME_PANEL_SHIELD_NORMAL,
444     &game.panel.shield_normal,
445     TYPE_ELEMENT,
446   },
447   {
448     GAME_PANEL_SHIELD_NORMAL_TIME,
449     &game.panel.shield_normal_time,
450     TYPE_INTEGER,
451   },
452   {
453     GAME_PANEL_SHIELD_DEADLY,
454     &game.panel.shield_deadly,
455     TYPE_ELEMENT,
456   },
457   {
458     GAME_PANEL_SHIELD_DEADLY_TIME,
459     &game.panel.shield_deadly_time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_PANEL_EXIT,
464     &game.panel.exit,
465     TYPE_ELEMENT,
466   },
467   {
468     GAME_PANEL_EMC_MAGIC_BALL,
469     &game.panel.emc_magic_ball,
470     TYPE_ELEMENT,
471   },
472   {
473     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
474     &game.panel.emc_magic_ball_switch,
475     TYPE_ELEMENT,
476   },
477   {
478     GAME_PANEL_LIGHT_SWITCH,
479     &game.panel.light_switch,
480     TYPE_ELEMENT,
481   },
482   {
483     GAME_PANEL_LIGHT_SWITCH_TIME,
484     &game.panel.light_switch_time,
485     TYPE_INTEGER,
486   },
487   {
488     GAME_PANEL_TIMEGATE_SWITCH,
489     &game.panel.timegate_switch,
490     TYPE_ELEMENT,
491   },
492   {
493     GAME_PANEL_TIMEGATE_SWITCH_TIME,
494     &game.panel.timegate_switch_time,
495     TYPE_INTEGER,
496   },
497   {
498     GAME_PANEL_SWITCHGATE_SWITCH,
499     &game.panel.switchgate_switch,
500     TYPE_ELEMENT,
501   },
502   {
503     GAME_PANEL_EMC_LENSES,
504     &game.panel.emc_lenses,
505     TYPE_ELEMENT,
506   },
507   {
508     GAME_PANEL_EMC_LENSES_TIME,
509     &game.panel.emc_lenses_time,
510     TYPE_INTEGER,
511   },
512   {
513     GAME_PANEL_EMC_MAGNIFIER,
514     &game.panel.emc_magnifier,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_PANEL_EMC_MAGNIFIER_TIME,
519     &game.panel.emc_magnifier_time,
520     TYPE_INTEGER,
521   },
522   {
523     GAME_PANEL_BALLOON_SWITCH,
524     &game.panel.balloon_switch,
525     TYPE_ELEMENT,
526   },
527   {
528     GAME_PANEL_DYNABOMB_NUMBER,
529     &game.panel.dynabomb_number,
530     TYPE_INTEGER,
531   },
532   {
533     GAME_PANEL_DYNABOMB_SIZE,
534     &game.panel.dynabomb_size,
535     TYPE_INTEGER,
536   },
537   {
538     GAME_PANEL_DYNABOMB_POWER,
539     &game.panel.dynabomb_power,
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_PANEL_PENGUINS,
544     &game.panel.penguins,
545     TYPE_INTEGER,
546   },
547   {
548     GAME_PANEL_SOKOBAN_OBJECTS,
549     &game.panel.sokoban_objects,
550     TYPE_INTEGER,
551   },
552   {
553     GAME_PANEL_SOKOBAN_FIELDS,
554     &game.panel.sokoban_fields,
555     TYPE_INTEGER,
556   },
557   {
558     GAME_PANEL_ROBOT_WHEEL,
559     &game.panel.robot_wheel,
560     TYPE_ELEMENT,
561   },
562   {
563     GAME_PANEL_CONVEYOR_BELT_1,
564     &game.panel.conveyor_belt[0],
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_PANEL_CONVEYOR_BELT_2,
569     &game.panel.conveyor_belt[1],
570     TYPE_ELEMENT,
571   },
572   {
573     GAME_PANEL_CONVEYOR_BELT_3,
574     &game.panel.conveyor_belt[2],
575     TYPE_ELEMENT,
576   },
577   {
578     GAME_PANEL_CONVEYOR_BELT_4,
579     &game.panel.conveyor_belt[3],
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
584     &game.panel.conveyor_belt_switch[0],
585     TYPE_ELEMENT,
586   },
587   {
588     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
589     &game.panel.conveyor_belt_switch[1],
590     TYPE_ELEMENT,
591   },
592   {
593     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
594     &game.panel.conveyor_belt_switch[2],
595     TYPE_ELEMENT,
596   },
597   {
598     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
599     &game.panel.conveyor_belt_switch[3],
600     TYPE_ELEMENT,
601   },
602   {
603     GAME_PANEL_MAGIC_WALL,
604     &game.panel.magic_wall,
605     TYPE_ELEMENT,
606   },
607   {
608     GAME_PANEL_MAGIC_WALL_TIME,
609     &game.panel.magic_wall_time,
610     TYPE_INTEGER,
611   },
612   {
613     GAME_PANEL_GRAVITY_STATE,
614     &game.panel.gravity_state,
615     TYPE_STRING,
616   },
617   {
618     GAME_PANEL_GRAPHIC_1,
619     &game.panel.graphic[0],
620     TYPE_ELEMENT,
621   },
622   {
623     GAME_PANEL_GRAPHIC_2,
624     &game.panel.graphic[1],
625     TYPE_ELEMENT,
626   },
627   {
628     GAME_PANEL_GRAPHIC_3,
629     &game.panel.graphic[2],
630     TYPE_ELEMENT,
631   },
632   {
633     GAME_PANEL_GRAPHIC_4,
634     &game.panel.graphic[3],
635     TYPE_ELEMENT,
636   },
637   {
638     GAME_PANEL_GRAPHIC_5,
639     &game.panel.graphic[4],
640     TYPE_ELEMENT,
641   },
642   {
643     GAME_PANEL_GRAPHIC_6,
644     &game.panel.graphic[5],
645     TYPE_ELEMENT,
646   },
647   {
648     GAME_PANEL_GRAPHIC_7,
649     &game.panel.graphic[6],
650     TYPE_ELEMENT,
651   },
652   {
653     GAME_PANEL_GRAPHIC_8,
654     &game.panel.graphic[7],
655     TYPE_ELEMENT,
656   },
657   {
658     GAME_PANEL_ELEMENT_1,
659     &game.panel.element[0],
660     TYPE_ELEMENT,
661   },
662   {
663     GAME_PANEL_ELEMENT_2,
664     &game.panel.element[1],
665     TYPE_ELEMENT,
666   },
667   {
668     GAME_PANEL_ELEMENT_3,
669     &game.panel.element[2],
670     TYPE_ELEMENT,
671   },
672   {
673     GAME_PANEL_ELEMENT_4,
674     &game.panel.element[3],
675     TYPE_ELEMENT,
676   },
677   {
678     GAME_PANEL_ELEMENT_5,
679     &game.panel.element[4],
680     TYPE_ELEMENT,
681   },
682   {
683     GAME_PANEL_ELEMENT_6,
684     &game.panel.element[5],
685     TYPE_ELEMENT,
686   },
687   {
688     GAME_PANEL_ELEMENT_7,
689     &game.panel.element[6],
690     TYPE_ELEMENT,
691   },
692   {
693     GAME_PANEL_ELEMENT_8,
694     &game.panel.element[7],
695     TYPE_ELEMENT,
696   },
697   {
698     GAME_PANEL_ELEMENT_COUNT_1,
699     &game.panel.element_count[0],
700     TYPE_INTEGER,
701   },
702   {
703     GAME_PANEL_ELEMENT_COUNT_2,
704     &game.panel.element_count[1],
705     TYPE_INTEGER,
706   },
707   {
708     GAME_PANEL_ELEMENT_COUNT_3,
709     &game.panel.element_count[2],
710     TYPE_INTEGER,
711   },
712   {
713     GAME_PANEL_ELEMENT_COUNT_4,
714     &game.panel.element_count[3],
715     TYPE_INTEGER,
716   },
717   {
718     GAME_PANEL_ELEMENT_COUNT_5,
719     &game.panel.element_count[4],
720     TYPE_INTEGER,
721   },
722   {
723     GAME_PANEL_ELEMENT_COUNT_6,
724     &game.panel.element_count[5],
725     TYPE_INTEGER,
726   },
727   {
728     GAME_PANEL_ELEMENT_COUNT_7,
729     &game.panel.element_count[6],
730     TYPE_INTEGER,
731   },
732   {
733     GAME_PANEL_ELEMENT_COUNT_8,
734     &game.panel.element_count[7],
735     TYPE_INTEGER,
736   },
737   {
738     GAME_PANEL_CE_SCORE_1,
739     &game.panel.ce_score[0],
740     TYPE_INTEGER,
741   },
742   {
743     GAME_PANEL_CE_SCORE_2,
744     &game.panel.ce_score[1],
745     TYPE_INTEGER,
746   },
747   {
748     GAME_PANEL_CE_SCORE_3,
749     &game.panel.ce_score[2],
750     TYPE_INTEGER,
751   },
752   {
753     GAME_PANEL_CE_SCORE_4,
754     &game.panel.ce_score[3],
755     TYPE_INTEGER,
756   },
757   {
758     GAME_PANEL_CE_SCORE_5,
759     &game.panel.ce_score[4],
760     TYPE_INTEGER,
761   },
762   {
763     GAME_PANEL_CE_SCORE_6,
764     &game.panel.ce_score[5],
765     TYPE_INTEGER,
766   },
767   {
768     GAME_PANEL_CE_SCORE_7,
769     &game.panel.ce_score[6],
770     TYPE_INTEGER,
771   },
772   {
773     GAME_PANEL_CE_SCORE_8,
774     &game.panel.ce_score[7],
775     TYPE_INTEGER,
776   },
777   {
778     GAME_PANEL_CE_SCORE_1_ELEMENT,
779     &game.panel.ce_score_element[0],
780     TYPE_ELEMENT,
781   },
782   {
783     GAME_PANEL_CE_SCORE_2_ELEMENT,
784     &game.panel.ce_score_element[1],
785     TYPE_ELEMENT,
786   },
787   {
788     GAME_PANEL_CE_SCORE_3_ELEMENT,
789     &game.panel.ce_score_element[2],
790     TYPE_ELEMENT,
791   },
792   {
793     GAME_PANEL_CE_SCORE_4_ELEMENT,
794     &game.panel.ce_score_element[3],
795     TYPE_ELEMENT,
796   },
797   {
798     GAME_PANEL_CE_SCORE_5_ELEMENT,
799     &game.panel.ce_score_element[4],
800     TYPE_ELEMENT,
801   },
802   {
803     GAME_PANEL_CE_SCORE_6_ELEMENT,
804     &game.panel.ce_score_element[5],
805     TYPE_ELEMENT,
806   },
807   {
808     GAME_PANEL_CE_SCORE_7_ELEMENT,
809     &game.panel.ce_score_element[6],
810     TYPE_ELEMENT,
811   },
812   {
813     GAME_PANEL_CE_SCORE_8_ELEMENT,
814     &game.panel.ce_score_element[7],
815     TYPE_ELEMENT,
816   },
817   {
818     GAME_PANEL_PLAYER_NAME,
819     &game.panel.player_name,
820     TYPE_STRING,
821   },
822   {
823     GAME_PANEL_LEVEL_NAME,
824     &game.panel.level_name,
825     TYPE_STRING,
826   },
827   {
828     GAME_PANEL_LEVEL_AUTHOR,
829     &game.panel.level_author,
830     TYPE_STRING,
831   },
832
833   {
834     -1,
835     NULL,
836     -1,
837   }
838 };
839 #endif
840
841
842 /* values for delayed check of falling and moving elements and for collision */
843 #define CHECK_DELAY_MOVING      3
844 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
845 #define CHECK_DELAY_COLLISION   2
846 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
847
848 /* values for initial player move delay (initial delay counter value) */
849 #define INITIAL_MOVE_DELAY_OFF  -1
850 #define INITIAL_MOVE_DELAY_ON   0
851
852 /* values for player movement speed (which is in fact a delay value) */
853 #define MOVE_DELAY_MIN_SPEED    32
854 #define MOVE_DELAY_NORMAL_SPEED 8
855 #define MOVE_DELAY_HIGH_SPEED   4
856 #define MOVE_DELAY_MAX_SPEED    1
857
858 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
859 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
860
861 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
862 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
863
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
883                                  RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
886                                  RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
888                                  RND((c)->delay_random))
889
890
891 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
892          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
895         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
896          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
897          (be) + (e) - EL_SELF)
898
899 #define GET_PLAYER_FROM_BITS(p)                                         \
900         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
903         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
904          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
905          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
906          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
907          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
908          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
909          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
910          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
911          (e))
912
913 #define CAN_GROW_INTO(e)                                                \
914         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
917                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
918                                         (condition)))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (CAN_MOVE_INTO_ACID(e) &&       \
923                                          Feld[x][y] == EL_ACID) ||      \
924                                         (condition)))
925
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
927                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
928                                         (CAN_MOVE_INTO_ACID(e) &&       \
929                                          Feld[x][y] == EL_ACID) ||      \
930                                         (condition)))
931
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
933                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
934                                         (condition) ||                  \
935                                         (CAN_MOVE_INTO_ACID(e) &&       \
936                                          Feld[x][y] == EL_ACID) ||      \
937                                         (DONT_COLLIDE_WITH(e) &&        \
938                                          IS_PLAYER(x, y) &&             \
939                                          !PLAYER_ENEMY_PROTECTED(x, y))))
940
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
942         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
945         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
948         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949
950 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
951         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
955         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
958         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
961         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
964         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965
966 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
972                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974                                                  IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
976         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
985         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
986                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987
988 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
989
990 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
991                 (!IS_PLAYER(x, y) &&                                    \
992                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
995         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999
1000 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004
1005 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP               0
1007 #define GAME_CTRL_ID_PAUSE              1
1008 #define GAME_CTRL_ID_PLAY               2
1009 #define SOUND_CTRL_ID_MUSIC             3
1010 #define SOUND_CTRL_ID_LOOPS             4
1011 #define SOUND_CTRL_ID_SIMPLE            5
1012
1013 #define NUM_GAME_BUTTONS                6
1014
1015
1016 /* forward declaration for internal use */
1017
1018 static void CreateField(int, int, int);
1019
1020 static void ResetGfxAnimation(int, int);
1021
1022 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1023 static void AdvanceFrameAndPlayerCounters(int);
1024
1025 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1026 static boolean MovePlayer(struct PlayerInfo *, int, int);
1027 static void ScrollPlayer(struct PlayerInfo *, int);
1028 static void ScrollScreen(struct PlayerInfo *, int);
1029
1030 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1031
1032 static void InitBeltMovement(void);
1033 static void CloseAllOpenTimegates(void);
1034 static void CheckGravityMovement(struct PlayerInfo *);
1035 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1036 static void KillPlayerUnlessEnemyProtected(int, int);
1037 static void KillPlayerUnlessExplosionProtected(int, int);
1038
1039 static void TestIfPlayerTouchesCustomElement(int, int);
1040 static void TestIfElementTouchesCustomElement(int, int);
1041 static void TestIfElementHitsCustomElement(int, int, int);
1042 #if 0
1043 static void TestIfElementSmashesCustomElement(int, int, int);
1044 #endif
1045
1046 static void HandleElementChange(int, int, int);
1047 static void ExecuteCustomElementAction(int, int, int, int);
1048 static boolean ChangeElement(int, int, int, int);
1049
1050 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1051 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1052         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1053 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1054         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1055 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1056         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1057 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1058         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1059
1060 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1061 #define CheckElementChange(x, y, e, te, ev)                             \
1062         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1063 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1064         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1065 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1066         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1067
1068 static void PlayLevelSound(int, int, int);
1069 static void PlayLevelSoundNearest(int, int, int);
1070 static void PlayLevelSoundAction(int, int, int);
1071 static void PlayLevelSoundElementAction(int, int, int, int);
1072 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1073 static void PlayLevelSoundActionIfLoop(int, int, int);
1074 static void StopLevelSoundActionIfLoop(int, int, int);
1075 static void PlayLevelMusic();
1076
1077 static void MapGameButtons();
1078 static void HandleGameButtons(struct GadgetInfo *);
1079
1080 int AmoebeNachbarNr(int, int);
1081 void AmoebeUmwandeln(int, int);
1082 void ContinueMoving(int, int);
1083 void Bang(int, int);
1084 void InitMovDir(int, int);
1085 void InitAmoebaNr(int, int);
1086 int NewHiScore(void);
1087
1088 void TestIfGoodThingHitsBadThing(int, int, int);
1089 void TestIfBadThingHitsGoodThing(int, int, int);
1090 void TestIfPlayerTouchesBadThing(int, int);
1091 void TestIfPlayerRunsIntoBadThing(int, int, int);
1092 void TestIfBadThingTouchesPlayer(int, int);
1093 void TestIfBadThingRunsIntoPlayer(int, int, int);
1094 void TestIfFriendTouchesBadThing(int, int);
1095 void TestIfBadThingTouchesFriend(int, int);
1096 void TestIfBadThingTouchesOtherBadThing(int, int);
1097
1098 void KillPlayer(struct PlayerInfo *);
1099 void BuryPlayer(struct PlayerInfo *);
1100 void RemovePlayer(struct PlayerInfo *);
1101
1102 boolean SnapField(struct PlayerInfo *, int, int);
1103 boolean DropElement(struct PlayerInfo *);
1104
1105 static int getInvisibleActiveFromInvisibleElement(int);
1106 static int getInvisibleFromInvisibleActiveElement(int);
1107
1108 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1109
1110 /* for detection of endless loops, caused by custom element programming */
1111 /* (using maximal playfield width x 10 is just a rough approximation) */
1112 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1113
1114 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1115 {                                                                       \
1116   if (recursion_loop_detected)                                          \
1117     return (rc);                                                        \
1118                                                                         \
1119   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1120   {                                                                     \
1121     recursion_loop_detected = TRUE;                                     \
1122     recursion_loop_element = (e);                                       \
1123   }                                                                     \
1124                                                                         \
1125   recursion_loop_depth++;                                               \
1126 }
1127
1128 #define RECURSION_LOOP_DETECTION_END()                                  \
1129 {                                                                       \
1130   recursion_loop_depth--;                                               \
1131 }
1132
1133 static int recursion_loop_depth;
1134 static boolean recursion_loop_detected;
1135 static boolean recursion_loop_element;
1136
1137
1138 /* ------------------------------------------------------------------------- */
1139 /* definition of elements that automatically change to other elements after  */
1140 /* a specified time, eventually calling a function when changing             */
1141 /* ------------------------------------------------------------------------- */
1142
1143 /* forward declaration for changer functions */
1144 static void InitBuggyBase(int, int);
1145 static void WarnBuggyBase(int, int);
1146
1147 static void InitTrap(int, int);
1148 static void ActivateTrap(int, int);
1149 static void ChangeActiveTrap(int, int);
1150
1151 static void InitRobotWheel(int, int);
1152 static void RunRobotWheel(int, int);
1153 static void StopRobotWheel(int, int);
1154
1155 static void InitTimegateWheel(int, int);
1156 static void RunTimegateWheel(int, int);
1157
1158 static void InitMagicBallDelay(int, int);
1159 static void ActivateMagicBall(int, int);
1160
1161 struct ChangingElementInfo
1162 {
1163   int element;
1164   int target_element;
1165   int change_delay;
1166   void (*pre_change_function)(int x, int y);
1167   void (*change_function)(int x, int y);
1168   void (*post_change_function)(int x, int y);
1169 };
1170
1171 static struct ChangingElementInfo change_delay_list[] =
1172 {
1173   {
1174     EL_NUT_BREAKING,
1175     EL_EMERALD,
1176     6,
1177     NULL,
1178     NULL,
1179     NULL
1180   },
1181   {
1182     EL_PEARL_BREAKING,
1183     EL_EMPTY,
1184     8,
1185     NULL,
1186     NULL,
1187     NULL
1188   },
1189   {
1190     EL_EXIT_OPENING,
1191     EL_EXIT_OPEN,
1192     29,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_EXIT_CLOSING,
1199     EL_EXIT_CLOSED,
1200     29,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_STEEL_EXIT_OPENING,
1207     EL_STEEL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_STEEL_EXIT_CLOSING,
1215     EL_STEEL_EXIT_CLOSED,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_EM_EXIT_OPENING,
1223     EL_EM_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_EM_EXIT_CLOSING,
1231 #if 1
1232     EL_EMPTY,
1233 #else
1234     EL_EM_EXIT_CLOSED,
1235 #endif
1236     29,
1237     NULL,
1238     NULL,
1239     NULL
1240   },
1241   {
1242     EL_EM_STEEL_EXIT_OPENING,
1243     EL_EM_STEEL_EXIT_OPEN,
1244     29,
1245     NULL,
1246     NULL,
1247     NULL
1248   },
1249   {
1250     EL_EM_STEEL_EXIT_CLOSING,
1251 #if 1
1252     EL_STEELWALL,
1253 #else
1254     EL_EM_STEEL_EXIT_CLOSED,
1255 #endif
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_SP_EXIT_OPENING,
1263     EL_SP_EXIT_OPEN,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269   {
1270     EL_SP_EXIT_CLOSING,
1271     EL_SP_EXIT_CLOSED,
1272     29,
1273     NULL,
1274     NULL,
1275     NULL
1276   },
1277   {
1278     EL_SWITCHGATE_OPENING,
1279     EL_SWITCHGATE_OPEN,
1280     29,
1281     NULL,
1282     NULL,
1283     NULL
1284   },
1285   {
1286     EL_SWITCHGATE_CLOSING,
1287     EL_SWITCHGATE_CLOSED,
1288     29,
1289     NULL,
1290     NULL,
1291     NULL
1292   },
1293   {
1294     EL_TIMEGATE_OPENING,
1295     EL_TIMEGATE_OPEN,
1296     29,
1297     NULL,
1298     NULL,
1299     NULL
1300   },
1301   {
1302     EL_TIMEGATE_CLOSING,
1303     EL_TIMEGATE_CLOSED,
1304     29,
1305     NULL,
1306     NULL,
1307     NULL
1308   },
1309
1310   {
1311     EL_ACID_SPLASH_LEFT,
1312     EL_EMPTY,
1313     8,
1314     NULL,
1315     NULL,
1316     NULL
1317   },
1318   {
1319     EL_ACID_SPLASH_RIGHT,
1320     EL_EMPTY,
1321     8,
1322     NULL,
1323     NULL,
1324     NULL
1325   },
1326   {
1327     EL_SP_BUGGY_BASE,
1328     EL_SP_BUGGY_BASE_ACTIVATING,
1329     0,
1330     InitBuggyBase,
1331     NULL,
1332     NULL
1333   },
1334   {
1335     EL_SP_BUGGY_BASE_ACTIVATING,
1336     EL_SP_BUGGY_BASE_ACTIVE,
1337     0,
1338     InitBuggyBase,
1339     NULL,
1340     NULL
1341   },
1342   {
1343     EL_SP_BUGGY_BASE_ACTIVE,
1344     EL_SP_BUGGY_BASE,
1345     0,
1346     InitBuggyBase,
1347     WarnBuggyBase,
1348     NULL
1349   },
1350   {
1351     EL_TRAP,
1352     EL_TRAP_ACTIVE,
1353     0,
1354     InitTrap,
1355     NULL,
1356     ActivateTrap
1357   },
1358   {
1359     EL_TRAP_ACTIVE,
1360     EL_TRAP,
1361     31,
1362     NULL,
1363     ChangeActiveTrap,
1364     NULL
1365   },
1366   {
1367     EL_ROBOT_WHEEL_ACTIVE,
1368     EL_ROBOT_WHEEL,
1369     0,
1370     InitRobotWheel,
1371     RunRobotWheel,
1372     StopRobotWheel
1373   },
1374   {
1375     EL_TIMEGATE_SWITCH_ACTIVE,
1376     EL_TIMEGATE_SWITCH,
1377     0,
1378     InitTimegateWheel,
1379     RunTimegateWheel,
1380     NULL
1381   },
1382   {
1383     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1384     EL_DC_TIMEGATE_SWITCH,
1385     0,
1386     InitTimegateWheel,
1387     RunTimegateWheel,
1388     NULL
1389   },
1390   {
1391     EL_EMC_MAGIC_BALL_ACTIVE,
1392     EL_EMC_MAGIC_BALL_ACTIVE,
1393     0,
1394     InitMagicBallDelay,
1395     NULL,
1396     ActivateMagicBall
1397   },
1398   {
1399     EL_EMC_SPRING_BUMPER_ACTIVE,
1400     EL_EMC_SPRING_BUMPER,
1401     8,
1402     NULL,
1403     NULL,
1404     NULL
1405   },
1406   {
1407     EL_DIAGONAL_SHRINKING,
1408     EL_UNDEFINED,
1409     0,
1410     NULL,
1411     NULL,
1412     NULL
1413   },
1414   {
1415     EL_DIAGONAL_GROWING,
1416     EL_UNDEFINED,
1417     0,
1418     NULL,
1419     NULL,
1420     NULL,
1421   },
1422
1423   {
1424     EL_UNDEFINED,
1425     EL_UNDEFINED,
1426     -1,
1427     NULL,
1428     NULL,
1429     NULL
1430   }
1431 };
1432
1433 struct
1434 {
1435   int element;
1436   int push_delay_fixed, push_delay_random;
1437 }
1438 push_delay_list[] =
1439 {
1440   { EL_SPRING,                  0, 0 },
1441   { EL_BALLOON,                 0, 0 },
1442
1443   { EL_SOKOBAN_OBJECT,          2, 0 },
1444   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1445   { EL_SATELLITE,               2, 0 },
1446   { EL_SP_DISK_YELLOW,          2, 0 },
1447
1448   { EL_UNDEFINED,               0, 0 },
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int move_stepsize;
1455 }
1456 move_stepsize_list[] =
1457 {
1458   { EL_AMOEBA_DROP,             2 },
1459   { EL_AMOEBA_DROPPING,         2 },
1460   { EL_QUICKSAND_FILLING,       1 },
1461   { EL_QUICKSAND_EMPTYING,      1 },
1462   { EL_QUICKSAND_FAST_FILLING,  2 },
1463   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1464   { EL_MAGIC_WALL_FILLING,      2 },
1465   { EL_MAGIC_WALL_EMPTYING,     2 },
1466   { EL_BD_MAGIC_WALL_FILLING,   2 },
1467   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1468   { EL_DC_MAGIC_WALL_FILLING,   2 },
1469   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1470
1471   { EL_UNDEFINED,               0 },
1472 };
1473
1474 struct
1475 {
1476   int element;
1477   int count;
1478 }
1479 collect_count_list[] =
1480 {
1481   { EL_EMERALD,                 1 },
1482   { EL_BD_DIAMOND,              1 },
1483   { EL_EMERALD_YELLOW,          1 },
1484   { EL_EMERALD_RED,             1 },
1485   { EL_EMERALD_PURPLE,          1 },
1486   { EL_DIAMOND,                 3 },
1487   { EL_SP_INFOTRON,             1 },
1488   { EL_PEARL,                   5 },
1489   { EL_CRYSTAL,                 8 },
1490
1491   { EL_UNDEFINED,               0 },
1492 };
1493
1494 struct
1495 {
1496   int element;
1497   int direction;
1498 }
1499 access_direction_list[] =
1500 {
1501   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1502   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1503   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1504   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1505   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1506   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1507   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1508   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1509   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1510   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1511   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1512
1513   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1514   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1515   { EL_SP_PORT_UP,                                                   MV_DOWN },
1516   { EL_SP_PORT_DOWN,                                         MV_UP           },
1517   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1518   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1519   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1521   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1522   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1523   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1524   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1525   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1526   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1527   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1528   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1529   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1530   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1531   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1532
1533   { EL_UNDEFINED,                       MV_NONE                              }
1534 };
1535
1536 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1537
1538 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1539 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1540 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1541                                  IS_JUST_CHANGING(x, y))
1542
1543 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1544
1545 /* static variables for playfield scan mode (scanning forward or backward) */
1546 static int playfield_scan_start_x = 0;
1547 static int playfield_scan_start_y = 0;
1548 static int playfield_scan_delta_x = 1;
1549 static int playfield_scan_delta_y = 1;
1550
1551 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1552                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1553                                      (y) += playfield_scan_delta_y)     \
1554                                 for ((x) = playfield_scan_start_x;      \
1555                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1556                                      (x) += playfield_scan_delta_x)
1557
1558 #ifdef DEBUG
1559 void DEBUG_SetMaximumDynamite()
1560 {
1561   int i;
1562
1563   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1564     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1565       local_player->inventory_element[local_player->inventory_size++] =
1566         EL_DYNAMITE;
1567 }
1568 #endif
1569
1570 static void InitPlayfieldScanModeVars()
1571 {
1572   if (game.use_reverse_scan_direction)
1573   {
1574     playfield_scan_start_x = lev_fieldx - 1;
1575     playfield_scan_start_y = lev_fieldy - 1;
1576
1577     playfield_scan_delta_x = -1;
1578     playfield_scan_delta_y = -1;
1579   }
1580   else
1581   {
1582     playfield_scan_start_x = 0;
1583     playfield_scan_start_y = 0;
1584
1585     playfield_scan_delta_x = 1;
1586     playfield_scan_delta_y = 1;
1587   }
1588 }
1589
1590 static void InitPlayfieldScanMode(int mode)
1591 {
1592   game.use_reverse_scan_direction =
1593     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1594
1595   InitPlayfieldScanModeVars();
1596 }
1597
1598 static int get_move_delay_from_stepsize(int move_stepsize)
1599 {
1600   move_stepsize =
1601     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1602
1603   /* make sure that stepsize value is always a power of 2 */
1604   move_stepsize = (1 << log_2(move_stepsize));
1605
1606   return TILEX / move_stepsize;
1607 }
1608
1609 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1610                                boolean init_game)
1611 {
1612   int player_nr = player->index_nr;
1613   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1614   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1615
1616   /* do no immediately change move delay -- the player might just be moving */
1617   player->move_delay_value_next = move_delay;
1618
1619   /* information if player can move must be set separately */
1620   player->cannot_move = cannot_move;
1621
1622   if (init_game)
1623   {
1624     player->move_delay       = game.initial_move_delay[player_nr];
1625     player->move_delay_value = game.initial_move_delay_value[player_nr];
1626
1627     player->move_delay_value_next = -1;
1628
1629     player->move_delay_reset_counter = 0;
1630   }
1631 }
1632
1633 void GetPlayerConfig()
1634 {
1635   GameFrameDelay = setup.game_frame_delay;
1636
1637   if (!audio.sound_available)
1638     setup.sound_simple = FALSE;
1639
1640   if (!audio.loops_available)
1641     setup.sound_loops = FALSE;
1642
1643   if (!audio.music_available)
1644     setup.sound_music = FALSE;
1645
1646   if (!video.fullscreen_available)
1647     setup.fullscreen = FALSE;
1648
1649   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1650
1651   SetAudioMode(setup.sound);
1652   InitJoysticks();
1653 }
1654
1655 int GetElementFromGroupElement(int element)
1656 {
1657   if (IS_GROUP_ELEMENT(element))
1658   {
1659     struct ElementGroupInfo *group = element_info[element].group;
1660     int last_anim_random_frame = gfx.anim_random_frame;
1661     int element_pos;
1662
1663     if (group->choice_mode == ANIM_RANDOM)
1664       gfx.anim_random_frame = RND(group->num_elements_resolved);
1665
1666     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1667                                     group->choice_mode, 0,
1668                                     group->choice_pos);
1669
1670     if (group->choice_mode == ANIM_RANDOM)
1671       gfx.anim_random_frame = last_anim_random_frame;
1672
1673     group->choice_pos++;
1674
1675     element = group->element_resolved[element_pos];
1676   }
1677
1678   return element;
1679 }
1680
1681 static void InitPlayerField(int x, int y, int element, boolean init_game)
1682 {
1683   if (element == EL_SP_MURPHY)
1684   {
1685     if (init_game)
1686     {
1687       if (stored_player[0].present)
1688       {
1689         Feld[x][y] = EL_SP_MURPHY_CLONE;
1690
1691         return;
1692       }
1693       else
1694       {
1695         stored_player[0].use_murphy = TRUE;
1696
1697         if (!level.use_artwork_element[0])
1698           stored_player[0].artwork_element = EL_SP_MURPHY;
1699       }
1700
1701       Feld[x][y] = EL_PLAYER_1;
1702     }
1703   }
1704
1705   if (init_game)
1706   {
1707     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1708     int jx = player->jx, jy = player->jy;
1709
1710     player->present = TRUE;
1711
1712     player->block_last_field = (element == EL_SP_MURPHY ?
1713                                 level.sp_block_last_field :
1714                                 level.block_last_field);
1715
1716     /* ---------- initialize player's last field block delay --------------- */
1717
1718     /* always start with reliable default value (no adjustment needed) */
1719     player->block_delay_adjustment = 0;
1720
1721     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1722     if (player->block_last_field && element == EL_SP_MURPHY)
1723       player->block_delay_adjustment = 1;
1724
1725     /* special case 2: in game engines before 3.1.1, blocking was different */
1726     if (game.use_block_last_field_bug)
1727       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1728
1729     if (!options.network || player->connected)
1730     {
1731       player->active = TRUE;
1732
1733       /* remove potentially duplicate players */
1734       if (StorePlayer[jx][jy] == Feld[x][y])
1735         StorePlayer[jx][jy] = 0;
1736
1737       StorePlayer[x][y] = Feld[x][y];
1738
1739       if (options.debug)
1740       {
1741         printf("Player %d activated.\n", player->element_nr);
1742         printf("[Local player is %d and currently %s.]\n",
1743                local_player->element_nr,
1744                local_player->active ? "active" : "not active");
1745       }
1746     }
1747
1748     Feld[x][y] = EL_EMPTY;
1749
1750     player->jx = player->last_jx = x;
1751     player->jy = player->last_jy = y;
1752   }
1753 }
1754
1755 static void InitField(int x, int y, boolean init_game)
1756 {
1757   int element = Feld[x][y];
1758
1759   switch (element)
1760   {
1761     case EL_SP_MURPHY:
1762     case EL_PLAYER_1:
1763     case EL_PLAYER_2:
1764     case EL_PLAYER_3:
1765     case EL_PLAYER_4:
1766       InitPlayerField(x, y, element, init_game);
1767       break;
1768
1769     case EL_SOKOBAN_FIELD_PLAYER:
1770       element = Feld[x][y] = EL_PLAYER_1;
1771       InitField(x, y, init_game);
1772
1773       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1774       InitField(x, y, init_game);
1775       break;
1776
1777     case EL_SOKOBAN_FIELD_EMPTY:
1778       local_player->sokobanfields_still_needed++;
1779       break;
1780
1781     case EL_STONEBLOCK:
1782       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1783         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1784       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1785         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1786       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1787         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1788       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1789         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1790       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1791         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1792       break;
1793
1794     case EL_BUG:
1795     case EL_BUG_RIGHT:
1796     case EL_BUG_UP:
1797     case EL_BUG_LEFT:
1798     case EL_BUG_DOWN:
1799     case EL_SPACESHIP:
1800     case EL_SPACESHIP_RIGHT:
1801     case EL_SPACESHIP_UP:
1802     case EL_SPACESHIP_LEFT:
1803     case EL_SPACESHIP_DOWN:
1804     case EL_BD_BUTTERFLY:
1805     case EL_BD_BUTTERFLY_RIGHT:
1806     case EL_BD_BUTTERFLY_UP:
1807     case EL_BD_BUTTERFLY_LEFT:
1808     case EL_BD_BUTTERFLY_DOWN:
1809     case EL_BD_FIREFLY:
1810     case EL_BD_FIREFLY_RIGHT:
1811     case EL_BD_FIREFLY_UP:
1812     case EL_BD_FIREFLY_LEFT:
1813     case EL_BD_FIREFLY_DOWN:
1814     case EL_PACMAN_RIGHT:
1815     case EL_PACMAN_UP:
1816     case EL_PACMAN_LEFT:
1817     case EL_PACMAN_DOWN:
1818     case EL_YAMYAM:
1819     case EL_YAMYAM_LEFT:
1820     case EL_YAMYAM_RIGHT:
1821     case EL_YAMYAM_UP:
1822     case EL_YAMYAM_DOWN:
1823     case EL_DARK_YAMYAM:
1824     case EL_ROBOT:
1825     case EL_PACMAN:
1826     case EL_SP_SNIKSNAK:
1827     case EL_SP_ELECTRON:
1828     case EL_MOLE:
1829     case EL_MOLE_LEFT:
1830     case EL_MOLE_RIGHT:
1831     case EL_MOLE_UP:
1832     case EL_MOLE_DOWN:
1833       InitMovDir(x, y);
1834       break;
1835
1836     case EL_AMOEBA_FULL:
1837     case EL_BD_AMOEBA:
1838       InitAmoebaNr(x, y);
1839       break;
1840
1841     case EL_AMOEBA_DROP:
1842       if (y == lev_fieldy - 1)
1843       {
1844         Feld[x][y] = EL_AMOEBA_GROWING;
1845         Store[x][y] = EL_AMOEBA_WET;
1846       }
1847       break;
1848
1849     case EL_DYNAMITE_ACTIVE:
1850     case EL_SP_DISK_RED_ACTIVE:
1851     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1852     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1853     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1854     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1855       MovDelay[x][y] = 96;
1856       break;
1857
1858     case EL_EM_DYNAMITE_ACTIVE:
1859       MovDelay[x][y] = 32;
1860       break;
1861
1862     case EL_LAMP:
1863       local_player->lights_still_needed++;
1864       break;
1865
1866     case EL_PENGUIN:
1867       local_player->friends_still_needed++;
1868       break;
1869
1870     case EL_PIG:
1871     case EL_DRAGON:
1872       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1873       break;
1874
1875     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1876     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1877     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1878     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1879     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1880     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1881     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1882     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1883     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1884     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1885     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1886     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1887       if (init_game)
1888       {
1889         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1890         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1891         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1892
1893         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1894         {
1895           game.belt_dir[belt_nr] = belt_dir;
1896           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1897         }
1898         else    /* more than one switch -- set it like the first switch */
1899         {
1900           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1901         }
1902       }
1903       break;
1904
1905 #if !USE_BOTH_SWITCHGATE_SWITCHES
1906     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1907       if (init_game)
1908         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1909       break;
1910
1911     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1912       if (init_game)
1913         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1914       break;
1915 #endif
1916
1917     case EL_LIGHT_SWITCH_ACTIVE:
1918       if (init_game)
1919         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1920       break;
1921
1922     case EL_INVISIBLE_STEELWALL:
1923     case EL_INVISIBLE_WALL:
1924     case EL_INVISIBLE_SAND:
1925       if (game.light_time_left > 0 ||
1926           game.lenses_time_left > 0)
1927         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1928       break;
1929
1930     case EL_EMC_MAGIC_BALL:
1931       if (game.ball_state)
1932         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL_SWITCH:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1938       break;
1939
1940     default:
1941       if (IS_CUSTOM_ELEMENT(element))
1942       {
1943         if (CAN_MOVE(element))
1944           InitMovDir(x, y);
1945
1946 #if USE_NEW_CUSTOM_VALUE
1947         if (!element_info[element].use_last_ce_value || init_game)
1948           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1949 #endif
1950       }
1951       else if (IS_GROUP_ELEMENT(element))
1952       {
1953         Feld[x][y] = GetElementFromGroupElement(element);
1954
1955         InitField(x, y, init_game);
1956       }
1957
1958       break;
1959   }
1960
1961   if (!init_game)
1962     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1963 }
1964
1965 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1966 {
1967   InitField(x, y, init_game);
1968
1969   /* not needed to call InitMovDir() -- already done by InitField()! */
1970   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971       CAN_MOVE(Feld[x][y]))
1972     InitMovDir(x, y);
1973 }
1974
1975 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1976 {
1977   int old_element = Feld[x][y];
1978
1979   InitField(x, y, init_game);
1980
1981   /* not needed to call InitMovDir() -- already done by InitField()! */
1982   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1983       CAN_MOVE(old_element) &&
1984       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1985     InitMovDir(x, y);
1986
1987   /* this case is in fact a combination of not less than three bugs:
1988      first, it calls InitMovDir() for elements that can move, although this is
1989      already done by InitField(); then, it checks the element that was at this
1990      field _before_ the call to InitField() (which can change it); lastly, it
1991      was not called for "mole with direction" elements, which were treated as
1992      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1993   */
1994 }
1995
1996 #if 1
1997
1998 static int get_key_element_from_nr(int key_nr)
1999 {
2000   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2001                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2002                           EL_EM_KEY_1 : EL_KEY_1);
2003
2004   return key_base_element + key_nr;
2005 }
2006
2007 static int get_next_dropped_element(struct PlayerInfo *player)
2008 {
2009   return (player->inventory_size > 0 ?
2010           player->inventory_element[player->inventory_size - 1] :
2011           player->inventory_infinite_element != EL_UNDEFINED ?
2012           player->inventory_infinite_element :
2013           player->dynabombs_left > 0 ?
2014           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2015           EL_UNDEFINED);
2016 }
2017
2018 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2019 {
2020   /* pos >= 0: get element from bottom of the stack;
2021      pos <  0: get element from top of the stack */
2022
2023   if (pos < 0)
2024   {
2025     int min_inventory_size = -pos;
2026     int inventory_pos = player->inventory_size - min_inventory_size;
2027     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2028
2029     return (player->inventory_size >= min_inventory_size ?
2030             player->inventory_element[inventory_pos] :
2031             player->inventory_infinite_element != EL_UNDEFINED ?
2032             player->inventory_infinite_element :
2033             player->dynabombs_left >= min_dynabombs_left ?
2034             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2035             EL_UNDEFINED);
2036   }
2037   else
2038   {
2039     int min_dynabombs_left = pos + 1;
2040     int min_inventory_size = pos + 1 - player->dynabombs_left;
2041     int inventory_pos = pos - player->dynabombs_left;
2042
2043     return (player->inventory_infinite_element != EL_UNDEFINED ?
2044             player->inventory_infinite_element :
2045             player->dynabombs_left >= min_dynabombs_left ?
2046             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2047             player->inventory_size >= min_inventory_size ?
2048             player->inventory_element[inventory_pos] :
2049             EL_UNDEFINED);
2050   }
2051 }
2052
2053 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2054 {
2055   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2056   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2057   int compare_result;
2058
2059   if (gpo1->sort_priority != gpo2->sort_priority)
2060     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2061   else
2062     compare_result = gpo1->nr - gpo2->nr;
2063
2064   return compare_result;
2065 }
2066
2067 void InitGameControlValues()
2068 {
2069   int i;
2070
2071   for (i = 0; game_panel_controls[i].nr != -1; i++)
2072   {
2073     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2074     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2075     struct TextPosInfo *pos = gpc->pos;
2076     int nr = gpc->nr;
2077     int type = gpc->type;
2078
2079     if (nr != i)
2080     {
2081       Error(ERR_INFO, "'game_panel_controls' structure corrupted");
2082       Error(ERR_EXIT, "this should not happen -- please debug");
2083     }
2084
2085     /* force update of game controls after initialization */
2086     gpc->value = gpc->last_value = -1;
2087     gpc->frame = gpc->last_frame = -1;
2088     gpc->gfx_frame = -1;
2089
2090     /* determine panel value width for later calculation of alignment */
2091     if (type == TYPE_INTEGER || type == TYPE_STRING)
2092     {
2093       pos->width = pos->size * getFontWidth(pos->font);
2094       pos->height = getFontHeight(pos->font);
2095     }
2096     else if (type == TYPE_ELEMENT)
2097     {
2098       pos->width = pos->size;
2099       pos->height = pos->size;
2100     }
2101
2102     /* fill structure for game panel draw order */
2103     gpo->nr = gpc->nr;
2104     gpo->sort_priority = pos->sort_priority;
2105   }
2106
2107   /* sort game panel controls according to sort_priority and control number */
2108   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2109         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2110 }
2111
2112 void UpdatePlayfieldElementCount()
2113 {
2114   boolean use_element_count = FALSE;
2115   int i, j, x, y;
2116
2117   /* first check if it is needed at all to calculate playfield element count */
2118   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2119     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2120       use_element_count = TRUE;
2121
2122   if (!use_element_count)
2123     return;
2124
2125   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2126     element_info[i].element_count = 0;
2127
2128   SCAN_PLAYFIELD(x, y)
2129   {
2130     element_info[Feld[x][y]].element_count++;
2131   }
2132
2133   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2134     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2135       if (IS_IN_GROUP(j, i))
2136         element_info[EL_GROUP_START + i].element_count +=
2137           element_info[j].element_count;
2138 }
2139
2140 void UpdateGameControlValues()
2141 {
2142   int i, k;
2143   int time = (local_player->LevelSolved ?
2144               local_player->LevelSolved_CountingTime :
2145               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2146               level.native_em_level->lev->time :
2147               level.time == 0 ? TimePlayed : TimeLeft);
2148   int score = (local_player->LevelSolved ?
2149                local_player->LevelSolved_CountingScore :
2150                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2151                level.native_em_level->lev->score :
2152                local_player->score);
2153   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2154               level.native_em_level->lev->required :
2155               local_player->gems_still_needed);
2156   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2157                      level.native_em_level->lev->required > 0 :
2158                      local_player->gems_still_needed > 0 ||
2159                      local_player->sokobanfields_still_needed > 0 ||
2160                      local_player->lights_still_needed > 0);
2161
2162   UpdatePlayfieldElementCount();
2163
2164   /* update game panel control values */
2165
2166   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2167   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2168
2169   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2170   for (i = 0; i < MAX_NUM_KEYS; i++)
2171     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2172   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2173   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2174
2175   if (game.centered_player_nr == -1)
2176   {
2177     for (i = 0; i < MAX_PLAYERS; i++)
2178     {
2179       for (k = 0; k < MAX_NUM_KEYS; k++)
2180       {
2181         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2182         {
2183           if (level.native_em_level->ply[i]->keys & (1 << k))
2184             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2185               get_key_element_from_nr(k);
2186         }
2187         else if (stored_player[i].key[k])
2188           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2189             get_key_element_from_nr(k);
2190       }
2191
2192       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2193         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2194           level.native_em_level->ply[i]->dynamite;
2195       else
2196         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2197           stored_player[i].inventory_size;
2198
2199       if (stored_player[i].num_white_keys > 0)
2200         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2201           EL_DC_KEY_WHITE;
2202
2203       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2204         stored_player[i].num_white_keys;
2205     }
2206   }
2207   else
2208   {
2209     int player_nr = game.centered_player_nr;
2210
2211     for (k = 0; k < MAX_NUM_KEYS; k++)
2212     {
2213       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2214       {
2215         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2216           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217             get_key_element_from_nr(k);
2218       }
2219       else if (stored_player[player_nr].key[k])
2220         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221           get_key_element_from_nr(k);
2222     }
2223
2224     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2226         level.native_em_level->ply[player_nr]->dynamite;
2227     else
2228       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229         stored_player[player_nr].inventory_size;
2230
2231     if (stored_player[player_nr].num_white_keys > 0)
2232       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2233
2234     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2235       stored_player[player_nr].num_white_keys;
2236   }
2237
2238   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2239   {
2240     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2241       get_inventory_element_from_pos(local_player, i);
2242     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2243       get_inventory_element_from_pos(local_player, -i - 1);
2244   }
2245
2246   game_panel_controls[GAME_PANEL_SCORE].value = score;
2247
2248   game_panel_controls[GAME_PANEL_TIME].value = time;
2249
2250   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2251   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2252   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2253
2254   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2255     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2256      EL_EMPTY);
2257   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2258     local_player->shield_normal_time_left;
2259   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2260     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2261      EL_EMPTY);
2262   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2263     local_player->shield_deadly_time_left;
2264
2265   game_panel_controls[GAME_PANEL_EXIT].value =
2266     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2267
2268   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2269     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2270   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2271     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2272      EL_EMC_MAGIC_BALL_SWITCH);
2273
2274   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2275     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2276   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2277     game.light_time_left;
2278
2279   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2280     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2281   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2282     game.timegate_time_left;
2283
2284   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2285     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2286
2287   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2288     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2289   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2290     game.lenses_time_left;
2291
2292   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2293     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2294   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2295     game.magnify_time_left;
2296
2297   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2298     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2299      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2300      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2301      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2302      EL_BALLOON_SWITCH_NONE);
2303
2304   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2305     local_player->dynabomb_count;
2306   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2307     local_player->dynabomb_size;
2308   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2309     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2310
2311   game_panel_controls[GAME_PANEL_PENGUINS].value =
2312     local_player->friends_still_needed;
2313
2314   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2315     local_player->sokobanfields_still_needed;
2316   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2317     local_player->sokobanfields_still_needed;
2318
2319   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2320     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2321
2322   for (i = 0; i < NUM_BELTS; i++)
2323   {
2324     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2325       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2326        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2327     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2328       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2329   }
2330
2331   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2332     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2333   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2334     game.magic_wall_time_left;
2335
2336 #if USE_PLAYER_GRAVITY
2337   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2338     local_player->gravity;
2339 #else
2340   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2341 #endif
2342
2343   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2344     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2345
2346   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2347     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2348       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2349        game.panel.element[i].id : EL_UNDEFINED);
2350
2351   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2352     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2353       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2354        element_info[game.panel.element_count[i].id].element_count : 0);
2355
2356   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2357     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2358       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2359        element_info[game.panel.ce_score[i].id].collect_score : 0);
2360
2361   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2362     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2363       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2364        element_info[game.panel.ce_score_element[i].id].collect_score :
2365        EL_UNDEFINED);
2366
2367   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2368   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2369   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2370
2371   /* update game panel control frames */
2372
2373   for (i = 0; game_panel_controls[i].nr != -1; i++)
2374   {
2375     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2376
2377     if (gpc->type == TYPE_ELEMENT)
2378     {
2379       int last_anim_random_frame = gfx.anim_random_frame;
2380       int element = gpc->value;
2381       int graphic = el2panelimg(element);
2382
2383       if (gpc->value != gpc->last_value)
2384       {
2385         gpc->gfx_frame = 0;
2386         gpc->gfx_random = INIT_GFX_RANDOM();
2387       }
2388       else
2389       {
2390         gpc->gfx_frame++;
2391
2392         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2393             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2394           gpc->gfx_random = INIT_GFX_RANDOM();
2395       }
2396
2397       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2398         gfx.anim_random_frame = gpc->gfx_random;
2399
2400       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2401         gpc->gfx_frame = element_info[element].collect_score;
2402
2403       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2404                                             gpc->gfx_frame);
2405
2406       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2407         gfx.anim_random_frame = last_anim_random_frame;
2408     }
2409   }
2410 }
2411
2412 void DisplayGameControlValues()
2413 {
2414   boolean redraw_panel = FALSE;
2415   int i;
2416
2417   for (i = 0; game_panel_controls[i].nr != -1; i++)
2418   {
2419     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2420
2421     if (PANEL_DEACTIVATED(gpc->pos))
2422       continue;
2423
2424     if (gpc->value == gpc->last_value &&
2425         gpc->frame == gpc->last_frame)
2426       continue;
2427
2428     redraw_panel = TRUE;
2429   }
2430
2431   if (!redraw_panel)
2432     return;
2433
2434   /* copy default game door content to main double buffer */
2435   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2436              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2437
2438   /* redraw game control buttons */
2439 #if 1
2440   RedrawGameButtons();
2441 #else
2442   UnmapGameButtons();
2443   MapGameButtons();
2444 #endif
2445
2446   game_status = GAME_MODE_PSEUDO_PANEL;
2447
2448 #if 1
2449   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2450 #else
2451   for (i = 0; game_panel_controls[i].nr != -1; i++)
2452 #endif
2453   {
2454 #if 1
2455     int nr = game_panel_order[i].nr;
2456     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2457 #else
2458     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2459     int nr = gpc->nr;
2460 #endif
2461     struct TextPosInfo *pos = gpc->pos;
2462     int type = gpc->type;
2463     int value = gpc->value;
2464     int frame = gpc->frame;
2465 #if 0
2466     int last_value = gpc->last_value;
2467     int last_frame = gpc->last_frame;
2468 #endif
2469     int size = pos->size;
2470     int font = pos->font;
2471     boolean draw_masked = pos->draw_masked;
2472     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2473
2474     if (PANEL_DEACTIVATED(pos))
2475       continue;
2476
2477 #if 0
2478     if (value == last_value && frame == last_frame)
2479       continue;
2480 #endif
2481
2482     gpc->last_value = value;
2483     gpc->last_frame = frame;
2484
2485 #if 0
2486     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2487 #endif
2488
2489     if (type == TYPE_INTEGER)
2490     {
2491       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2492           nr == GAME_PANEL_TIME)
2493       {
2494         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2495
2496         if (use_dynamic_size)           /* use dynamic number of digits */
2497         {
2498           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2499           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2500           int size2 = size1 + 1;
2501           int font1 = pos->font;
2502           int font2 = pos->font_alt;
2503
2504           size = (value < value_change ? size1 : size2);
2505           font = (value < value_change ? font1 : font2);
2506
2507 #if 0
2508           /* clear background if value just changed its size (dynamic digits) */
2509           if ((last_value < value_change) != (value < value_change))
2510           {
2511             int width1 = size1 * getFontWidth(font1);
2512             int width2 = size2 * getFontWidth(font2);
2513             int max_width = MAX(width1, width2);
2514             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2515
2516             pos->width = max_width;
2517
2518             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2519                                        max_width, max_height);
2520           }
2521 #endif
2522         }
2523       }
2524
2525 #if 1
2526       /* correct text size if "digits" is zero or less */
2527       if (size <= 0)
2528         size = strlen(int2str(value, size));
2529
2530       /* dynamically correct text alignment */
2531       pos->width = size * getFontWidth(font);
2532 #endif
2533
2534       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2535                   int2str(value, size), font, mask_mode);
2536     }
2537     else if (type == TYPE_ELEMENT)
2538     {
2539       int element, graphic;
2540       Bitmap *src_bitmap;
2541       int src_x, src_y;
2542       int width, height;
2543       int dst_x = PANEL_XPOS(pos);
2544       int dst_y = PANEL_YPOS(pos);
2545
2546 #if 1
2547       if (value != EL_UNDEFINED && value != EL_EMPTY)
2548       {
2549         element = value;
2550         graphic = el2panelimg(value);
2551
2552         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2553
2554 #if 1
2555         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2556           size = TILESIZE;
2557 #endif
2558
2559         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2560                               &src_x, &src_y);
2561
2562         width  = graphic_info[graphic].width  * size / TILESIZE;
2563         height = graphic_info[graphic].height * size / TILESIZE;
2564
2565         if (draw_masked)
2566         {
2567           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2568                         dst_x - src_x, dst_y - src_y);
2569           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2570                            dst_x, dst_y);
2571         }
2572         else
2573         {
2574           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2575                      dst_x, dst_y);
2576         }
2577       }
2578 #else
2579       if (value == EL_UNDEFINED || value == EL_EMPTY)
2580       {
2581         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2582         graphic = el2panelimg(element);
2583
2584         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2585         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2586         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2587       }
2588       else
2589       {
2590         element = value;
2591         graphic = el2panelimg(value);
2592
2593         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2594       }
2595
2596       width  = graphic_info[graphic].width  * size / TILESIZE;
2597       height = graphic_info[graphic].height * size / TILESIZE;
2598
2599       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2600 #endif
2601     }
2602     else if (type == TYPE_STRING)
2603     {
2604       boolean active = (value != 0);
2605       char *state_normal = "off";
2606       char *state_active = "on";
2607       char *state = (active ? state_active : state_normal);
2608       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2609                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2610                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2611                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2612
2613       if (nr == GAME_PANEL_GRAVITY_STATE)
2614       {
2615         int font1 = pos->font;          /* (used for normal state) */
2616         int font2 = pos->font_alt;      /* (used for active state) */
2617 #if 0
2618         int size1 = strlen(state_normal);
2619         int size2 = strlen(state_active);
2620         int width1 = size1 * getFontWidth(font1);
2621         int width2 = size2 * getFontWidth(font2);
2622         int max_width = MAX(width1, width2);
2623         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2624
2625         pos->width = max_width;
2626
2627         /* clear background for values that may have changed its size */
2628         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2629                                    max_width, max_height);
2630 #endif
2631
2632         font = (active ? font2 : font1);
2633       }
2634
2635       if (s != NULL)
2636       {
2637         char *s_cut;
2638
2639 #if 1
2640         if (size <= 0)
2641         {
2642           /* don't truncate output if "chars" is zero or less */
2643           size = strlen(s);
2644
2645           /* dynamically correct text alignment */
2646           pos->width = size * getFontWidth(font);
2647         }
2648 #endif
2649
2650         s_cut = getStringCopyN(s, size);
2651
2652         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2653                     s_cut, font, mask_mode);
2654
2655         free(s_cut);
2656       }
2657     }
2658
2659     redraw_mask |= REDRAW_DOOR_1;
2660   }
2661
2662   game_status = GAME_MODE_PLAYING;
2663 }
2664
2665 void UpdateAndDisplayGameControlValues()
2666 {
2667   if (tape.warp_forward)
2668     return;
2669
2670   UpdateGameControlValues();
2671   DisplayGameControlValues();
2672 }
2673
2674 void DrawGameValue_Emeralds(int value)
2675 {
2676   struct TextPosInfo *pos = &game.panel.gems;
2677 #if 1
2678   int font_nr = pos->font;
2679 #else
2680   int font_nr = FONT_TEXT_2;
2681 #endif
2682   int font_width = getFontWidth(font_nr);
2683   int chars = pos->size;
2684
2685 #if 1
2686   return;       /* !!! USE NEW STUFF !!! */
2687 #endif
2688
2689   if (PANEL_DEACTIVATED(pos))
2690     return;
2691
2692   pos->width = chars * font_width;
2693
2694   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2695 }
2696
2697 void DrawGameValue_Dynamite(int value)
2698 {
2699   struct TextPosInfo *pos = &game.panel.inventory_count;
2700 #if 1
2701   int font_nr = pos->font;
2702 #else
2703   int font_nr = FONT_TEXT_2;
2704 #endif
2705   int font_width = getFontWidth(font_nr);
2706   int chars = pos->size;
2707
2708 #if 1
2709   return;       /* !!! USE NEW STUFF !!! */
2710 #endif
2711
2712   if (PANEL_DEACTIVATED(pos))
2713     return;
2714
2715   pos->width = chars * font_width;
2716
2717   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2718 }
2719
2720 void DrawGameValue_Score(int value)
2721 {
2722   struct TextPosInfo *pos = &game.panel.score;
2723 #if 1
2724   int font_nr = pos->font;
2725 #else
2726   int font_nr = FONT_TEXT_2;
2727 #endif
2728   int font_width = getFontWidth(font_nr);
2729   int chars = pos->size;
2730
2731 #if 1
2732   return;       /* !!! USE NEW STUFF !!! */
2733 #endif
2734
2735   if (PANEL_DEACTIVATED(pos))
2736     return;
2737
2738   pos->width = chars * font_width;
2739
2740   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2741 }
2742
2743 void DrawGameValue_Time(int value)
2744 {
2745   struct TextPosInfo *pos = &game.panel.time;
2746   static int last_value = -1;
2747   int chars1 = 3;
2748   int chars2 = 4;
2749   int chars = pos->size;
2750 #if 1
2751   int font1_nr = pos->font;
2752   int font2_nr = pos->font_alt;
2753 #else
2754   int font1_nr = FONT_TEXT_2;
2755   int font2_nr = FONT_TEXT_1;
2756 #endif
2757   int font_nr = font1_nr;
2758   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2759
2760 #if 1
2761   return;       /* !!! USE NEW STUFF !!! */
2762 #endif
2763
2764   if (PANEL_DEACTIVATED(pos))
2765     return;
2766
2767   if (use_dynamic_chars)                /* use dynamic number of chars */
2768   {
2769     chars   = (value < 1000 ? chars1   : chars2);
2770     font_nr = (value < 1000 ? font1_nr : font2_nr);
2771   }
2772
2773   /* clear background if value just changed its size (dynamic chars only) */
2774   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2775   {
2776     int width1 = chars1 * getFontWidth(font1_nr);
2777     int width2 = chars2 * getFontWidth(font2_nr);
2778     int max_width = MAX(width1, width2);
2779     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2780
2781     pos->width = max_width;
2782
2783     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2784                                max_width, max_height);
2785   }
2786
2787   pos->width = chars * getFontWidth(font_nr);
2788
2789   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2790
2791   last_value = value;
2792 }
2793
2794 void DrawGameValue_Level(int value)
2795 {
2796   struct TextPosInfo *pos = &game.panel.level_number;
2797   int chars1 = 2;
2798   int chars2 = 3;
2799   int chars = pos->size;
2800 #if 1
2801   int font1_nr = pos->font;
2802   int font2_nr = pos->font_alt;
2803 #else
2804   int font1_nr = FONT_TEXT_2;
2805   int font2_nr = FONT_TEXT_1;
2806 #endif
2807   int font_nr = font1_nr;
2808   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2809
2810 #if 1
2811   return;       /* !!! USE NEW STUFF !!! */
2812 #endif
2813
2814   if (PANEL_DEACTIVATED(pos))
2815     return;
2816
2817   if (use_dynamic_chars)                /* use dynamic number of chars */
2818   {
2819     chars   = (level_nr < 100 ? chars1   : chars2);
2820     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2821   }
2822
2823   pos->width = chars * getFontWidth(font_nr);
2824
2825   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2826 }
2827
2828 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2829 {
2830 #if 0
2831   struct TextPosInfo *pos = &game.panel.keys;
2832 #endif
2833 #if 0
2834   int base_key_graphic = EL_KEY_1;
2835 #endif
2836   int i;
2837
2838 #if 1
2839   return;       /* !!! USE NEW STUFF !!! */
2840 #endif
2841
2842 #if 0
2843   if (PANEL_DEACTIVATED(pos))
2844     return;
2845 #endif
2846
2847 #if 0
2848   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2849     base_key_graphic = EL_EM_KEY_1;
2850 #endif
2851
2852 #if 0
2853   pos->width = 4 * MINI_TILEX;
2854 #endif
2855
2856 #if 1
2857   for (i = 0; i < MAX_NUM_KEYS; i++)
2858 #else
2859   /* currently only 4 of 8 possible keys are displayed */
2860   for (i = 0; i < STD_NUM_KEYS; i++)
2861 #endif
2862   {
2863 #if 1
2864     struct TextPosInfo *pos = &game.panel.key[i];
2865 #endif
2866     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2867     int src_y = DOOR_GFX_PAGEY1 + 123;
2868 #if 1
2869     int dst_x = PANEL_XPOS(pos);
2870     int dst_y = PANEL_YPOS(pos);
2871 #else
2872     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2873     int dst_y = PANEL_YPOS(pos);
2874 #endif
2875
2876 #if 1
2877     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2878                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2879                    EL_KEY_1) + i;
2880     int graphic = el2edimg(element);
2881 #endif
2882
2883 #if 1
2884     if (PANEL_DEACTIVATED(pos))
2885       continue;
2886 #endif
2887
2888 #if 0
2889     /* masked blit with tiles from half-size scaled bitmap does not work yet
2890        (no mask bitmap created for these sizes after loading and scaling) --
2891        solution: load without creating mask, scale, then create final mask */
2892
2893     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2894                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2895
2896     if (key[i])
2897     {
2898 #if 0
2899       int graphic = el2edimg(base_key_graphic + i);
2900 #endif
2901       Bitmap *src_bitmap;
2902       int src_x, src_y;
2903
2904       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2905
2906       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2907                     dst_x - src_x, dst_y - src_y);
2908       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2909                        dst_x, dst_y);
2910     }
2911 #else
2912 #if 1
2913     if (key[i])
2914       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2915     else
2916       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2917                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2918 #else
2919     if (key[i])
2920       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2921     else
2922       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2923                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2924 #endif
2925 #endif
2926   }
2927 }
2928
2929 #else
2930
2931 void DrawGameValue_Emeralds(int value)
2932 {
2933   int font_nr = FONT_TEXT_2;
2934   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2935
2936   if (PANEL_DEACTIVATED(game.panel.gems))
2937     return;
2938
2939   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2940 }
2941
2942 void DrawGameValue_Dynamite(int value)
2943 {
2944   int font_nr = FONT_TEXT_2;
2945   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2946
2947   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2948     return;
2949
2950   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2951 }
2952
2953 void DrawGameValue_Score(int value)
2954 {
2955   int font_nr = FONT_TEXT_2;
2956   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2957
2958   if (PANEL_DEACTIVATED(game.panel.score))
2959     return;
2960
2961   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2962 }
2963
2964 void DrawGameValue_Time(int value)
2965 {
2966   int font1_nr = FONT_TEXT_2;
2967 #if 1
2968   int font2_nr = FONT_TEXT_1;
2969 #else
2970   int font2_nr = FONT_LEVEL_NUMBER;
2971 #endif
2972   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2973   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2974
2975   if (PANEL_DEACTIVATED(game.panel.time))
2976     return;
2977
2978   /* clear background if value just changed its size */
2979   if (value == 999 || value == 1000)
2980     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2981
2982   if (value < 1000)
2983     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2984   else
2985     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2986 }
2987
2988 void DrawGameValue_Level(int value)
2989 {
2990   int font1_nr = FONT_TEXT_2;
2991 #if 1
2992   int font2_nr = FONT_TEXT_1;
2993 #else
2994   int font2_nr = FONT_LEVEL_NUMBER;
2995 #endif
2996
2997   if (PANEL_DEACTIVATED(game.panel.level))
2998     return;
2999
3000   if (level_nr < 100)
3001     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3002   else
3003     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3004 }
3005
3006 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3007 {
3008   int base_key_graphic = EL_KEY_1;
3009   int i;
3010
3011   if (PANEL_DEACTIVATED(game.panel.keys))
3012     return;
3013
3014   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3015     base_key_graphic = EL_EM_KEY_1;
3016
3017   /* currently only 4 of 8 possible keys are displayed */
3018   for (i = 0; i < STD_NUM_KEYS; i++)
3019   {
3020     int x = XX_KEYS + i * MINI_TILEX;
3021     int y = YY_KEYS;
3022
3023     if (key[i])
3024       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3025     else
3026       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3027                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3028   }
3029 }
3030
3031 #endif
3032
3033 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3034                        int key_bits)
3035 {
3036   int key[MAX_NUM_KEYS];
3037   int i;
3038
3039   /* prevent EM engine from updating time/score values parallel to GameWon() */
3040   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3041       local_player->LevelSolved)
3042     return;
3043
3044   for (i = 0; i < MAX_NUM_KEYS; i++)
3045     key[i] = key_bits & (1 << i);
3046
3047   DrawGameValue_Level(level_nr);
3048
3049   DrawGameValue_Emeralds(emeralds);
3050   DrawGameValue_Dynamite(dynamite);
3051   DrawGameValue_Score(score);
3052   DrawGameValue_Time(time);
3053
3054   DrawGameValue_Keys(key);
3055 }
3056
3057 void UpdateGameDoorValues()
3058 {
3059   UpdateGameControlValues();
3060 }
3061
3062 void DrawGameDoorValues()
3063 {
3064   DisplayGameControlValues();
3065 }
3066
3067 void DrawGameDoorValues_OLD()
3068 {
3069   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3070   int dynamite_value = 0;
3071   int score_value = (local_player->LevelSolved ? local_player->score_final :
3072                      local_player->score);
3073   int gems_value = local_player->gems_still_needed;
3074   int key_bits = 0;
3075   int i, j;
3076
3077   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3078   {
3079     DrawGameDoorValues_EM();
3080
3081     return;
3082   }
3083
3084   if (game.centered_player_nr == -1)
3085   {
3086     for (i = 0; i < MAX_PLAYERS; i++)
3087     {
3088       for (j = 0; j < MAX_NUM_KEYS; j++)
3089         if (stored_player[i].key[j])
3090           key_bits |= (1 << j);
3091
3092       dynamite_value += stored_player[i].inventory_size;
3093     }
3094   }
3095   else
3096   {
3097     int player_nr = game.centered_player_nr;
3098
3099     for (i = 0; i < MAX_NUM_KEYS; i++)
3100       if (stored_player[player_nr].key[i])
3101         key_bits |= (1 << i);
3102
3103     dynamite_value = stored_player[player_nr].inventory_size;
3104   }
3105
3106   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3107                     key_bits);
3108 }
3109
3110
3111 /*
3112   =============================================================================
3113   InitGameEngine()
3114   -----------------------------------------------------------------------------
3115   initialize game engine due to level / tape version number
3116   =============================================================================
3117 */
3118
3119 static void InitGameEngine()
3120 {
3121   int i, j, k, l, x, y;
3122
3123   /* set game engine from tape file when re-playing, else from level file */
3124   game.engine_version = (tape.playing ? tape.engine_version :
3125                          level.game_version);
3126
3127   /* ---------------------------------------------------------------------- */
3128   /* set flags for bugs and changes according to active game engine version */
3129   /* ---------------------------------------------------------------------- */
3130
3131   /*
3132     Summary of bugfix/change:
3133     Fixed handling for custom elements that change when pushed by the player.
3134
3135     Fixed/changed in version:
3136     3.1.0
3137
3138     Description:
3139     Before 3.1.0, custom elements that "change when pushing" changed directly
3140     after the player started pushing them (until then handled in "DigField()").
3141     Since 3.1.0, these custom elements are not changed until the "pushing"
3142     move of the element is finished (now handled in "ContinueMoving()").
3143
3144     Affected levels/tapes:
3145     The first condition is generally needed for all levels/tapes before version
3146     3.1.0, which might use the old behaviour before it was changed; known tapes
3147     that are affected are some tapes from the level set "Walpurgis Gardens" by
3148     Jamie Cullen.
3149     The second condition is an exception from the above case and is needed for
3150     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3151     above (including some development versions of 3.1.0), but before it was
3152     known that this change would break tapes like the above and was fixed in
3153     3.1.1, so that the changed behaviour was active although the engine version
3154     while recording maybe was before 3.1.0. There is at least one tape that is
3155     affected by this exception, which is the tape for the one-level set "Bug
3156     Machine" by Juergen Bonhagen.
3157   */
3158
3159   game.use_change_when_pushing_bug =
3160     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3161      !(tape.playing &&
3162        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3163        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3164
3165   /*
3166     Summary of bugfix/change:
3167     Fixed handling for blocking the field the player leaves when moving.
3168
3169     Fixed/changed in version:
3170     3.1.1
3171
3172     Description:
3173     Before 3.1.1, when "block last field when moving" was enabled, the field
3174     the player is leaving when moving was blocked for the time of the move,
3175     and was directly unblocked afterwards. This resulted in the last field
3176     being blocked for exactly one less than the number of frames of one player
3177     move. Additionally, even when blocking was disabled, the last field was
3178     blocked for exactly one frame.
3179     Since 3.1.1, due to changes in player movement handling, the last field
3180     is not blocked at all when blocking is disabled. When blocking is enabled,
3181     the last field is blocked for exactly the number of frames of one player
3182     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3183     last field is blocked for exactly one more than the number of frames of
3184     one player move.
3185
3186     Affected levels/tapes:
3187     (!!! yet to be determined -- probably many !!!)
3188   */
3189
3190   game.use_block_last_field_bug =
3191     (game.engine_version < VERSION_IDENT(3,1,1,0));
3192
3193   /*
3194     Summary of bugfix/change:
3195     Changed behaviour of CE changes with multiple changes per single frame.
3196
3197     Fixed/changed in version:
3198     3.2.0-6
3199
3200     Description:
3201     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3202     This resulted in race conditions where CEs seem to behave strange in some
3203     situations (where triggered CE changes were just skipped because there was
3204     already a CE change on that tile in the playfield in that engine frame).
3205     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3206     (The number of changes per frame must be limited in any case, because else
3207     it is easily possible to define CE changes that would result in an infinite
3208     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3209     should be set large enough so that it would only be reached in cases where
3210     the corresponding CE change conditions run into a loop. Therefore, it seems
3211     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3212     maximal number of change pages for custom elements.)
3213
3214     Affected levels/tapes:
3215     Probably many.
3216   */
3217
3218 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3219   game.max_num_changes_per_frame = 1;
3220 #else
3221   game.max_num_changes_per_frame =
3222     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3223 #endif
3224
3225   /* ---------------------------------------------------------------------- */
3226
3227   /* default scan direction: scan playfield from top/left to bottom/right */
3228   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3229
3230   /* dynamically adjust element properties according to game engine version */
3231   InitElementPropertiesEngine(game.engine_version);
3232
3233 #if 0
3234   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3235   printf("          tape version == %06d [%s] [file: %06d]\n",
3236          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3237          tape.file_version);
3238   printf("       => game.engine_version == %06d\n", game.engine_version);
3239 #endif
3240
3241   /* ---------- initialize player's initial move delay --------------------- */
3242
3243   /* dynamically adjust player properties according to level information */
3244   for (i = 0; i < MAX_PLAYERS; i++)
3245     game.initial_move_delay_value[i] =
3246       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3247
3248   /* dynamically adjust player properties according to game engine version */
3249   for (i = 0; i < MAX_PLAYERS; i++)
3250     game.initial_move_delay[i] =
3251       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3252        game.initial_move_delay_value[i] : 0);
3253
3254   /* ---------- initialize player's initial push delay --------------------- */
3255
3256   /* dynamically adjust player properties according to game engine version */
3257   game.initial_push_delay_value =
3258     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3259
3260   /* ---------- initialize changing elements ------------------------------- */
3261
3262   /* initialize changing elements information */
3263   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3264   {
3265     struct ElementInfo *ei = &element_info[i];
3266
3267     /* this pointer might have been changed in the level editor */
3268     ei->change = &ei->change_page[0];
3269
3270     if (!IS_CUSTOM_ELEMENT(i))
3271     {
3272       ei->change->target_element = EL_EMPTY_SPACE;
3273       ei->change->delay_fixed = 0;
3274       ei->change->delay_random = 0;
3275       ei->change->delay_frames = 1;
3276     }
3277
3278     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3279     {
3280       ei->has_change_event[j] = FALSE;
3281
3282       ei->event_page_nr[j] = 0;
3283       ei->event_page[j] = &ei->change_page[0];
3284     }
3285   }
3286
3287   /* add changing elements from pre-defined list */
3288   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3289   {
3290     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3291     struct ElementInfo *ei = &element_info[ch_delay->element];
3292
3293     ei->change->target_element       = ch_delay->target_element;
3294     ei->change->delay_fixed          = ch_delay->change_delay;
3295
3296     ei->change->pre_change_function  = ch_delay->pre_change_function;
3297     ei->change->change_function      = ch_delay->change_function;
3298     ei->change->post_change_function = ch_delay->post_change_function;
3299
3300     ei->change->can_change = TRUE;
3301     ei->change->can_change_or_has_action = TRUE;
3302
3303     ei->has_change_event[CE_DELAY] = TRUE;
3304
3305     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3306     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3307   }
3308
3309   /* ---------- initialize internal run-time variables ------------- */
3310
3311   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3312   {
3313     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3314
3315     for (j = 0; j < ei->num_change_pages; j++)
3316     {
3317       ei->change_page[j].can_change_or_has_action =
3318         (ei->change_page[j].can_change |
3319          ei->change_page[j].has_action);
3320     }
3321   }
3322
3323   /* add change events from custom element configuration */
3324   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3325   {
3326     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3327
3328     for (j = 0; j < ei->num_change_pages; j++)
3329     {
3330       if (!ei->change_page[j].can_change_or_has_action)
3331         continue;
3332
3333       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3334       {
3335         /* only add event page for the first page found with this event */
3336         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3337         {
3338           ei->has_change_event[k] = TRUE;
3339
3340           ei->event_page_nr[k] = j;
3341           ei->event_page[k] = &ei->change_page[j];
3342         }
3343       }
3344     }
3345   }
3346
3347   /* ---------- initialize run-time trigger player and element ------------- */
3348
3349   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3350   {
3351     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3352
3353     for (j = 0; j < ei->num_change_pages; j++)
3354     {
3355       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3356       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3357       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3358       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3359       ei->change_page[j].actual_trigger_ce_value = 0;
3360       ei->change_page[j].actual_trigger_ce_score = 0;
3361     }
3362   }
3363
3364   /* ---------- initialize trigger events ---------------------------------- */
3365
3366   /* initialize trigger events information */
3367   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3368     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3369       trigger_events[i][j] = FALSE;
3370
3371   /* add trigger events from element change event properties */
3372   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3373   {
3374     struct ElementInfo *ei = &element_info[i];
3375
3376     for (j = 0; j < ei->num_change_pages; j++)
3377     {
3378       if (!ei->change_page[j].can_change_or_has_action)
3379         continue;
3380
3381       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3382       {
3383         int trigger_element = ei->change_page[j].trigger_element;
3384
3385         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3386         {
3387           if (ei->change_page[j].has_event[k])
3388           {
3389             if (IS_GROUP_ELEMENT(trigger_element))
3390             {
3391               struct ElementGroupInfo *group =
3392                 element_info[trigger_element].group;
3393
3394               for (l = 0; l < group->num_elements_resolved; l++)
3395                 trigger_events[group->element_resolved[l]][k] = TRUE;
3396             }
3397             else if (trigger_element == EL_ANY_ELEMENT)
3398               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3399                 trigger_events[l][k] = TRUE;
3400             else
3401               trigger_events[trigger_element][k] = TRUE;
3402           }
3403         }
3404       }
3405     }
3406   }
3407
3408   /* ---------- initialize push delay -------------------------------------- */
3409
3410   /* initialize push delay values to default */
3411   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3412   {
3413     if (!IS_CUSTOM_ELEMENT(i))
3414     {
3415       /* set default push delay values (corrected since version 3.0.7-1) */
3416       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3417       {
3418         element_info[i].push_delay_fixed = 2;
3419         element_info[i].push_delay_random = 8;
3420       }
3421       else
3422       {
3423         element_info[i].push_delay_fixed = 8;
3424         element_info[i].push_delay_random = 8;
3425       }
3426     }
3427   }
3428
3429   /* set push delay value for certain elements from pre-defined list */
3430   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3431   {
3432     int e = push_delay_list[i].element;
3433
3434     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3435     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3436   }
3437
3438   /* set push delay value for Supaplex elements for newer engine versions */
3439   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3440   {
3441     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3442     {
3443       if (IS_SP_ELEMENT(i))
3444       {
3445         /* set SP push delay to just enough to push under a falling zonk */
3446         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3447
3448         element_info[i].push_delay_fixed  = delay;
3449         element_info[i].push_delay_random = 0;
3450       }
3451     }
3452   }
3453
3454   /* ---------- initialize move stepsize ----------------------------------- */
3455
3456   /* initialize move stepsize values to default */
3457   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3458     if (!IS_CUSTOM_ELEMENT(i))
3459       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3460
3461   /* set move stepsize value for certain elements from pre-defined list */
3462   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3463   {
3464     int e = move_stepsize_list[i].element;
3465
3466     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3467   }
3468
3469   /* ---------- initialize collect score ----------------------------------- */
3470
3471   /* initialize collect score values for custom elements from initial value */
3472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3473     if (IS_CUSTOM_ELEMENT(i))
3474       element_info[i].collect_score = element_info[i].collect_score_initial;
3475
3476   /* ---------- initialize collect count ----------------------------------- */
3477
3478   /* initialize collect count values for non-custom elements */
3479   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3480     if (!IS_CUSTOM_ELEMENT(i))
3481       element_info[i].collect_count_initial = 0;
3482
3483   /* add collect count values for all elements from pre-defined list */
3484   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3485     element_info[collect_count_list[i].element].collect_count_initial =
3486       collect_count_list[i].count;
3487
3488   /* ---------- initialize access direction -------------------------------- */
3489
3490   /* initialize access direction values to default (access from every side) */
3491   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3492     if (!IS_CUSTOM_ELEMENT(i))
3493       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3494
3495   /* set access direction value for certain elements from pre-defined list */
3496   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3497     element_info[access_direction_list[i].element].access_direction =
3498       access_direction_list[i].direction;
3499
3500   /* ---------- initialize explosion content ------------------------------- */
3501   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3502   {
3503     if (IS_CUSTOM_ELEMENT(i))
3504       continue;
3505
3506     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3507     {
3508       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3509
3510       element_info[i].content.e[x][y] =
3511         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3512          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3513          i == EL_PLAYER_3 ? EL_EMERALD :
3514          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3515          i == EL_MOLE ? EL_EMERALD_RED :
3516          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3517          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3518          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3519          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3520          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3521          i == EL_WALL_EMERALD ? EL_EMERALD :
3522          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3523          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3524          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3525          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3526          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3527          i == EL_WALL_PEARL ? EL_PEARL :
3528          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3529          EL_EMPTY);
3530     }
3531   }
3532
3533   /* ---------- initialize recursion detection ------------------------------ */
3534   recursion_loop_depth = 0;
3535   recursion_loop_detected = FALSE;
3536   recursion_loop_element = EL_UNDEFINED;
3537
3538   /* ---------- initialize graphics engine ---------------------------------- */
3539   game.scroll_delay_value =
3540     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3541      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3542   game.scroll_delay_value =
3543     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3544 }
3545
3546 int get_num_special_action(int element, int action_first, int action_last)
3547 {
3548   int num_special_action = 0;
3549   int i, j;
3550
3551   for (i = action_first; i <= action_last; i++)
3552   {
3553     boolean found = FALSE;
3554
3555     for (j = 0; j < NUM_DIRECTIONS; j++)
3556       if (el_act_dir2img(element, i, j) !=
3557           el_act_dir2img(element, ACTION_DEFAULT, j))
3558         found = TRUE;
3559
3560     if (found)
3561       num_special_action++;
3562     else
3563       break;
3564   }
3565
3566   return num_special_action;
3567 }
3568
3569
3570 /*
3571   =============================================================================
3572   InitGame()
3573   -----------------------------------------------------------------------------
3574   initialize and start new game
3575   =============================================================================
3576 */
3577
3578 void InitGame()
3579 {
3580   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3581   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3582   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3583 #if 0
3584   boolean do_fading = (game_status == GAME_MODE_MAIN);
3585 #endif
3586   int i, j, x, y;
3587
3588   game_status = GAME_MODE_PLAYING;
3589
3590   InitGameEngine();
3591   InitGameControlValues();
3592
3593   /* don't play tapes over network */
3594   network_playing = (options.network && !tape.playing);
3595
3596   for (i = 0; i < MAX_PLAYERS; i++)
3597   {
3598     struct PlayerInfo *player = &stored_player[i];
3599
3600     player->index_nr = i;
3601     player->index_bit = (1 << i);
3602     player->element_nr = EL_PLAYER_1 + i;
3603
3604     player->present = FALSE;
3605     player->active = FALSE;
3606     player->killed = FALSE;
3607
3608     player->action = 0;
3609     player->effective_action = 0;
3610     player->programmed_action = 0;
3611
3612     player->score = 0;
3613     player->score_final = 0;
3614
3615     player->gems_still_needed = level.gems_needed;
3616     player->sokobanfields_still_needed = 0;
3617     player->lights_still_needed = 0;
3618     player->friends_still_needed = 0;
3619
3620     for (j = 0; j < MAX_NUM_KEYS; j++)
3621       player->key[j] = FALSE;
3622
3623     player->num_white_keys = 0;
3624
3625     player->dynabomb_count = 0;
3626     player->dynabomb_size = 1;
3627     player->dynabombs_left = 0;
3628     player->dynabomb_xl = FALSE;
3629
3630     player->MovDir = MV_NONE;
3631     player->MovPos = 0;
3632     player->GfxPos = 0;
3633     player->GfxDir = MV_NONE;
3634     player->GfxAction = ACTION_DEFAULT;
3635     player->Frame = 0;
3636     player->StepFrame = 0;
3637
3638     player->use_murphy = FALSE;
3639     player->artwork_element =
3640       (level.use_artwork_element[i] ? level.artwork_element[i] :
3641        player->element_nr);
3642
3643     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3644     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3645
3646     player->gravity = level.initial_player_gravity[i];
3647
3648     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3649
3650     player->actual_frame_counter = 0;
3651
3652     player->step_counter = 0;
3653
3654     player->last_move_dir = MV_NONE;
3655
3656     player->is_active = FALSE;
3657
3658     player->is_waiting = FALSE;
3659     player->is_moving = FALSE;
3660     player->is_auto_moving = FALSE;
3661     player->is_digging = FALSE;
3662     player->is_snapping = FALSE;
3663     player->is_collecting = FALSE;
3664     player->is_pushing = FALSE;
3665     player->is_switching = FALSE;
3666     player->is_dropping = FALSE;
3667     player->is_dropping_pressed = FALSE;
3668
3669     player->is_bored = FALSE;
3670     player->is_sleeping = FALSE;
3671
3672     player->frame_counter_bored = -1;
3673     player->frame_counter_sleeping = -1;
3674
3675     player->anim_delay_counter = 0;
3676     player->post_delay_counter = 0;
3677
3678     player->dir_waiting = MV_NONE;
3679     player->action_waiting = ACTION_DEFAULT;
3680     player->last_action_waiting = ACTION_DEFAULT;
3681     player->special_action_bored = ACTION_DEFAULT;
3682     player->special_action_sleeping = ACTION_DEFAULT;
3683
3684     player->switch_x = -1;
3685     player->switch_y = -1;
3686
3687     player->drop_x = -1;
3688     player->drop_y = -1;
3689
3690     player->show_envelope = 0;
3691
3692     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3693
3694     player->push_delay       = -1;      /* initialized when pushing starts */
3695     player->push_delay_value = game.initial_push_delay_value;
3696
3697     player->drop_delay = 0;
3698     player->drop_pressed_delay = 0;
3699
3700     player->last_jx = -1;
3701     player->last_jy = -1;
3702     player->jx = -1;
3703     player->jy = -1;
3704
3705     player->shield_normal_time_left = 0;
3706     player->shield_deadly_time_left = 0;
3707
3708     player->inventory_infinite_element = EL_UNDEFINED;
3709     player->inventory_size = 0;
3710
3711     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3712     SnapField(player, 0, 0);
3713
3714     player->LevelSolved = FALSE;
3715     player->GameOver = FALSE;
3716
3717     player->LevelSolved_GameWon = FALSE;
3718     player->LevelSolved_GameEnd = FALSE;
3719     player->LevelSolved_PanelOff = FALSE;
3720     player->LevelSolved_SaveTape = FALSE;
3721     player->LevelSolved_SaveScore = FALSE;
3722     player->LevelSolved_CountingTime = 0;
3723     player->LevelSolved_CountingScore = 0;
3724   }
3725
3726   network_player_action_received = FALSE;
3727
3728 #if defined(NETWORK_AVALIABLE)
3729   /* initial null action */
3730   if (network_playing)
3731     SendToServer_MovePlayer(MV_NONE);
3732 #endif
3733
3734   ZX = ZY = -1;
3735   ExitX = ExitY = -1;
3736
3737   FrameCounter = 0;
3738   TimeFrames = 0;
3739   TimePlayed = 0;
3740   TimeLeft = level.time;
3741   TapeTime = 0;
3742
3743   ScreenMovDir = MV_NONE;
3744   ScreenMovPos = 0;
3745   ScreenGfxPos = 0;
3746
3747   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3748
3749   AllPlayersGone = FALSE;
3750
3751   game.yamyam_content_nr = 0;
3752   game.robot_wheel_active = FALSE;
3753   game.magic_wall_active = FALSE;
3754   game.magic_wall_time_left = 0;
3755   game.light_time_left = 0;
3756   game.timegate_time_left = 0;
3757   game.switchgate_pos = 0;
3758   game.wind_direction = level.wind_direction_initial;
3759
3760 #if !USE_PLAYER_GRAVITY
3761   game.gravity = FALSE;
3762   game.explosions_delayed = TRUE;
3763 #endif
3764
3765   game.lenses_time_left = 0;
3766   game.magnify_time_left = 0;
3767
3768   game.ball_state = level.ball_state_initial;
3769   game.ball_content_nr = 0;
3770
3771   game.envelope_active = FALSE;
3772
3773   /* set focus to local player for network games, else to all players */
3774   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3775   game.centered_player_nr_next = game.centered_player_nr;
3776   game.set_centered_player = FALSE;
3777
3778   if (network_playing && tape.recording)
3779   {
3780     /* store client dependent player focus when recording network games */
3781     tape.centered_player_nr_next = game.centered_player_nr_next;
3782     tape.set_centered_player = TRUE;
3783   }
3784
3785   for (i = 0; i < NUM_BELTS; i++)
3786   {
3787     game.belt_dir[i] = MV_NONE;
3788     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3789   }
3790
3791   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3792     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3793
3794   SCAN_PLAYFIELD(x, y)
3795   {
3796     Feld[x][y] = level.field[x][y];
3797     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3798     ChangeDelay[x][y] = 0;
3799     ChangePage[x][y] = -1;
3800 #if USE_NEW_CUSTOM_VALUE
3801     CustomValue[x][y] = 0;              /* initialized in InitField() */
3802 #endif
3803     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3804     AmoebaNr[x][y] = 0;
3805     WasJustMoving[x][y] = 0;
3806     WasJustFalling[x][y] = 0;
3807     CheckCollision[x][y] = 0;
3808     CheckImpact[x][y] = 0;
3809     Stop[x][y] = FALSE;
3810     Pushed[x][y] = FALSE;
3811
3812     ChangeCount[x][y] = 0;
3813     ChangeEvent[x][y] = -1;
3814
3815     ExplodePhase[x][y] = 0;
3816     ExplodeDelay[x][y] = 0;
3817     ExplodeField[x][y] = EX_TYPE_NONE;
3818
3819     RunnerVisit[x][y] = 0;
3820     PlayerVisit[x][y] = 0;
3821
3822     GfxFrame[x][y] = 0;
3823     GfxRandom[x][y] = INIT_GFX_RANDOM();
3824     GfxElement[x][y] = EL_UNDEFINED;
3825     GfxAction[x][y] = ACTION_DEFAULT;
3826     GfxDir[x][y] = MV_NONE;
3827   }
3828
3829   SCAN_PLAYFIELD(x, y)
3830   {
3831     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3832       emulate_bd = FALSE;
3833     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3834       emulate_sb = FALSE;
3835     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3836       emulate_sp = FALSE;
3837
3838     InitField(x, y, TRUE);
3839
3840     ResetGfxAnimation(x, y);
3841   }
3842
3843   InitBeltMovement();
3844
3845   for (i = 0; i < MAX_PLAYERS; i++)
3846   {
3847     struct PlayerInfo *player = &stored_player[i];
3848
3849     /* set number of special actions for bored and sleeping animation */
3850     player->num_special_action_bored =
3851       get_num_special_action(player->artwork_element,
3852                              ACTION_BORING_1, ACTION_BORING_LAST);
3853     player->num_special_action_sleeping =
3854       get_num_special_action(player->artwork_element,
3855                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3856   }
3857
3858   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3859                     emulate_sb ? EMU_SOKOBAN :
3860                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3861
3862 #if USE_NEW_ALL_SLIPPERY
3863   /* initialize type of slippery elements */
3864   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3865   {
3866     if (!IS_CUSTOM_ELEMENT(i))
3867     {
3868       /* default: elements slip down either to the left or right randomly */
3869       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3870
3871       /* SP style elements prefer to slip down on the left side */
3872       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3873         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3874
3875       /* BD style elements prefer to slip down on the left side */
3876       if (game.emulation == EMU_BOULDERDASH)
3877         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3878     }
3879   }
3880 #endif
3881
3882   /* initialize explosion and ignition delay */
3883   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3884   {
3885     if (!IS_CUSTOM_ELEMENT(i))
3886     {
3887       int num_phase = 8;
3888       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3889                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3890                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3891       int last_phase = (num_phase + 1) * delay;
3892       int half_phase = (num_phase / 2) * delay;
3893
3894       element_info[i].explosion_delay = last_phase - 1;
3895       element_info[i].ignition_delay = half_phase;
3896
3897       if (i == EL_BLACK_ORB)
3898         element_info[i].ignition_delay = 1;
3899     }
3900
3901 #if 0
3902     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3903       element_info[i].explosion_delay = 1;
3904
3905     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3906       element_info[i].ignition_delay = 1;
3907 #endif
3908   }
3909
3910   /* correct non-moving belts to start moving left */
3911   for (i = 0; i < NUM_BELTS; i++)
3912     if (game.belt_dir[i] == MV_NONE)
3913       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3914
3915   /* check if any connected player was not found in playfield */
3916   for (i = 0; i < MAX_PLAYERS; i++)
3917   {
3918     struct PlayerInfo *player = &stored_player[i];
3919
3920     if (player->connected && !player->present)
3921     {
3922       for (j = 0; j < MAX_PLAYERS; j++)
3923       {
3924         struct PlayerInfo *some_player = &stored_player[j];
3925         int jx = some_player->jx, jy = some_player->jy;
3926
3927         /* assign first free player found that is present in the playfield */
3928         if (some_player->present && !some_player->connected)
3929         {
3930           player->present = TRUE;
3931           player->active = TRUE;
3932
3933           some_player->present = FALSE;
3934           some_player->active = FALSE;
3935
3936           player->artwork_element = some_player->artwork_element;
3937
3938           player->block_last_field       = some_player->block_last_field;
3939           player->block_delay_adjustment = some_player->block_delay_adjustment;
3940
3941           StorePlayer[jx][jy] = player->element_nr;
3942           player->jx = player->last_jx = jx;
3943           player->jy = player->last_jy = jy;
3944
3945           break;
3946         }
3947       }
3948     }
3949   }
3950
3951   if (tape.playing)
3952   {
3953     /* when playing a tape, eliminate all players who do not participate */
3954
3955     for (i = 0; i < MAX_PLAYERS; i++)
3956     {
3957       if (stored_player[i].active && !tape.player_participates[i])
3958       {
3959         struct PlayerInfo *player = &stored_player[i];
3960         int jx = player->jx, jy = player->jy;
3961
3962         player->active = FALSE;
3963         StorePlayer[jx][jy] = 0;
3964         Feld[jx][jy] = EL_EMPTY;
3965       }
3966     }
3967   }
3968   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3969   {
3970     /* when in single player mode, eliminate all but the first active player */
3971
3972     for (i = 0; i < MAX_PLAYERS; i++)
3973     {
3974       if (stored_player[i].active)
3975       {
3976         for (j = i + 1; j < MAX_PLAYERS; j++)
3977         {
3978           if (stored_player[j].active)
3979           {
3980             struct PlayerInfo *player = &stored_player[j];
3981             int jx = player->jx, jy = player->jy;
3982
3983             player->active = FALSE;
3984             player->present = FALSE;
3985
3986             StorePlayer[jx][jy] = 0;
3987             Feld[jx][jy] = EL_EMPTY;
3988           }
3989         }
3990       }
3991     }
3992   }
3993
3994   /* when recording the game, store which players take part in the game */
3995   if (tape.recording)
3996   {
3997     for (i = 0; i < MAX_PLAYERS; i++)
3998       if (stored_player[i].active)
3999         tape.player_participates[i] = TRUE;
4000   }
4001
4002   if (options.debug)
4003   {
4004     for (i = 0; i < MAX_PLAYERS; i++)
4005     {
4006       struct PlayerInfo *player = &stored_player[i];
4007
4008       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4009              i+1,
4010              player->present,
4011              player->connected,
4012              player->active);
4013       if (local_player == player)
4014         printf("Player  %d is local player.\n", i+1);
4015     }
4016   }
4017
4018   if (BorderElement == EL_EMPTY)
4019   {
4020     SBX_Left = 0;
4021     SBX_Right = lev_fieldx - SCR_FIELDX;
4022     SBY_Upper = 0;
4023     SBY_Lower = lev_fieldy - SCR_FIELDY;
4024   }
4025   else
4026   {
4027     SBX_Left = -1;
4028     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4029     SBY_Upper = -1;
4030     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4031   }
4032
4033   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4034     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4035
4036   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4037     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4038
4039   /* if local player not found, look for custom element that might create
4040      the player (make some assumptions about the right custom element) */
4041   if (!local_player->present)
4042   {
4043     int start_x = 0, start_y = 0;
4044     int found_rating = 0;
4045     int found_element = EL_UNDEFINED;
4046     int player_nr = local_player->index_nr;
4047
4048     SCAN_PLAYFIELD(x, y)
4049     {
4050       int element = Feld[x][y];
4051       int content;
4052       int xx, yy;
4053       boolean is_player;
4054
4055       if (level.use_start_element[player_nr] &&
4056           level.start_element[player_nr] == element &&
4057           found_rating < 4)
4058       {
4059         start_x = x;
4060         start_y = y;
4061
4062         found_rating = 4;
4063         found_element = element;
4064       }
4065
4066       if (!IS_CUSTOM_ELEMENT(element))
4067         continue;
4068
4069       if (CAN_CHANGE(element))
4070       {
4071         for (i = 0; i < element_info[element].num_change_pages; i++)
4072         {
4073           /* check for player created from custom element as single target */
4074           content = element_info[element].change_page[i].target_element;
4075           is_player = ELEM_IS_PLAYER(content);
4076
4077           if (is_player && (found_rating < 3 ||
4078                             (found_rating == 3 && element < found_element)))
4079           {
4080             start_x = x;
4081             start_y = y;
4082
4083             found_rating = 3;
4084             found_element = element;
4085           }
4086         }
4087       }
4088
4089       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4090       {
4091         /* check for player created from custom element as explosion content */
4092         content = element_info[element].content.e[xx][yy];
4093         is_player = ELEM_IS_PLAYER(content);
4094
4095         if (is_player && (found_rating < 2 ||
4096                           (found_rating == 2 && element < found_element)))
4097         {
4098           start_x = x + xx - 1;
4099           start_y = y + yy - 1;
4100
4101           found_rating = 2;
4102           found_element = element;
4103         }
4104
4105         if (!CAN_CHANGE(element))
4106           continue;
4107
4108         for (i = 0; i < element_info[element].num_change_pages; i++)
4109         {
4110           /* check for player created from custom element as extended target */
4111           content =
4112             element_info[element].change_page[i].target_content.e[xx][yy];
4113
4114           is_player = ELEM_IS_PLAYER(content);
4115
4116           if (is_player && (found_rating < 1 ||
4117                             (found_rating == 1 && element < found_element)))
4118           {
4119             start_x = x + xx - 1;
4120             start_y = y + yy - 1;
4121
4122             found_rating = 1;
4123             found_element = element;
4124           }
4125         }
4126       }
4127     }
4128
4129     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4130                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4131                 start_x - MIDPOSX);
4132
4133     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4134                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4135                 start_y - MIDPOSY);
4136   }
4137   else
4138   {
4139     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4140                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4141                 local_player->jx - MIDPOSX);
4142
4143     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4144                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4145                 local_player->jy - MIDPOSY);
4146   }
4147
4148 #if 0
4149   /* do not use PLAYING mask for fading out from main screen */
4150   game_status = GAME_MODE_MAIN;
4151 #endif
4152
4153   StopAnimation();
4154
4155   if (!game.restart_level)
4156     CloseDoor(DOOR_CLOSE_1);
4157
4158 #if 1
4159   if (level_editor_test_game)
4160     FadeSkipNextFadeIn();
4161   else
4162     FadeSetEnterScreen();
4163 #else
4164   if (level_editor_test_game)
4165     fading = fading_none;
4166   else
4167     fading = menu.destination;
4168 #endif
4169
4170 #if 1
4171   FadeOut(REDRAW_FIELD);
4172 #else
4173   if (do_fading)
4174     FadeOut(REDRAW_FIELD);
4175 #endif
4176
4177 #if 0
4178   game_status = GAME_MODE_PLAYING;
4179 #endif
4180
4181   /* !!! FIX THIS (START) !!! */
4182   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4183   {
4184     InitGameEngine_EM();
4185
4186     /* blit playfield from scroll buffer to normal back buffer for fading in */
4187     BlitScreenToBitmap_EM(backbuffer);
4188   }
4189   else
4190   {
4191     DrawLevel();
4192     DrawAllPlayers();
4193
4194     /* after drawing the level, correct some elements */
4195     if (game.timegate_time_left == 0)
4196       CloseAllOpenTimegates();
4197
4198     /* blit playfield from scroll buffer to normal back buffer for fading in */
4199     if (setup.soft_scrolling)
4200       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4201
4202     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4203   }
4204   /* !!! FIX THIS (END) !!! */
4205
4206 #if 1
4207   FadeIn(REDRAW_FIELD);
4208 #else
4209   if (do_fading)
4210     FadeIn(REDRAW_FIELD);
4211
4212   BackToFront();
4213 #endif
4214
4215   if (!game.restart_level)
4216   {
4217     /* copy default game door content to main double buffer */
4218     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4219                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4220   }
4221
4222   SetPanelBackground();
4223   SetDrawBackgroundMask(REDRAW_DOOR_1);
4224
4225 #if 1
4226   UpdateAndDisplayGameControlValues();
4227 #else
4228   UpdateGameDoorValues();
4229   DrawGameDoorValues();
4230 #endif
4231
4232   if (!game.restart_level)
4233   {
4234     UnmapGameButtons();
4235     UnmapTapeButtons();
4236     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4237     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4238     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4239     MapGameButtons();
4240     MapTapeButtons();
4241
4242     /* copy actual game door content to door double buffer for OpenDoor() */
4243     BlitBitmap(drawto, bitmap_db_door,
4244                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4245
4246     OpenDoor(DOOR_OPEN_ALL);
4247
4248     PlaySound(SND_GAME_STARTING);
4249
4250     if (setup.sound_music)
4251       PlayLevelMusic();
4252
4253     KeyboardAutoRepeatOffUnlessAutoplay();
4254
4255     if (options.debug)
4256     {
4257       for (i = 0; i < MAX_PLAYERS; i++)
4258         printf("Player %d %sactive.\n",
4259                i + 1, (stored_player[i].active ? "" : "not "));
4260     }
4261   }
4262
4263 #if 1
4264   UnmapAllGadgets();
4265
4266   MapGameButtons();
4267   MapTapeButtons();
4268 #endif
4269
4270   game.restart_level = FALSE;
4271 }
4272
4273 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4274 {
4275   /* this is used for non-R'n'D game engines to update certain engine values */
4276
4277   /* needed to determine if sounds are played within the visible screen area */
4278   scroll_x = actual_scroll_x;
4279   scroll_y = actual_scroll_y;
4280 }
4281
4282 void InitMovDir(int x, int y)
4283 {
4284   int i, element = Feld[x][y];
4285   static int xy[4][2] =
4286   {
4287     {  0, +1 },
4288     { +1,  0 },
4289     {  0, -1 },
4290     { -1,  0 }
4291   };
4292   static int direction[3][4] =
4293   {
4294     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4295     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4296     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4297   };
4298
4299   switch (element)
4300   {
4301     case EL_BUG_RIGHT:
4302     case EL_BUG_UP:
4303     case EL_BUG_LEFT:
4304     case EL_BUG_DOWN:
4305       Feld[x][y] = EL_BUG;
4306       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4307       break;
4308
4309     case EL_SPACESHIP_RIGHT:
4310     case EL_SPACESHIP_UP:
4311     case EL_SPACESHIP_LEFT:
4312     case EL_SPACESHIP_DOWN:
4313       Feld[x][y] = EL_SPACESHIP;
4314       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4315       break;
4316
4317     case EL_BD_BUTTERFLY_RIGHT:
4318     case EL_BD_BUTTERFLY_UP:
4319     case EL_BD_BUTTERFLY_LEFT:
4320     case EL_BD_BUTTERFLY_DOWN:
4321       Feld[x][y] = EL_BD_BUTTERFLY;
4322       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4323       break;
4324
4325     case EL_BD_FIREFLY_RIGHT:
4326     case EL_BD_FIREFLY_UP:
4327     case EL_BD_FIREFLY_LEFT:
4328     case EL_BD_FIREFLY_DOWN:
4329       Feld[x][y] = EL_BD_FIREFLY;
4330       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4331       break;
4332
4333     case EL_PACMAN_RIGHT:
4334     case EL_PACMAN_UP:
4335     case EL_PACMAN_LEFT:
4336     case EL_PACMAN_DOWN:
4337       Feld[x][y] = EL_PACMAN;
4338       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4339       break;
4340
4341     case EL_YAMYAM_LEFT:
4342     case EL_YAMYAM_RIGHT:
4343     case EL_YAMYAM_UP:
4344     case EL_YAMYAM_DOWN:
4345       Feld[x][y] = EL_YAMYAM;
4346       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4347       break;
4348
4349     case EL_SP_SNIKSNAK:
4350       MovDir[x][y] = MV_UP;
4351       break;
4352
4353     case EL_SP_ELECTRON:
4354       MovDir[x][y] = MV_LEFT;
4355       break;
4356
4357     case EL_MOLE_LEFT:
4358     case EL_MOLE_RIGHT:
4359     case EL_MOLE_UP:
4360     case EL_MOLE_DOWN:
4361       Feld[x][y] = EL_MOLE;
4362       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4363       break;
4364
4365     default:
4366       if (IS_CUSTOM_ELEMENT(element))
4367       {
4368         struct ElementInfo *ei = &element_info[element];
4369         int move_direction_initial = ei->move_direction_initial;
4370         int move_pattern = ei->move_pattern;
4371
4372         if (move_direction_initial == MV_START_PREVIOUS)
4373         {
4374           if (MovDir[x][y] != MV_NONE)
4375             return;
4376
4377           move_direction_initial = MV_START_AUTOMATIC;
4378         }
4379
4380         if (move_direction_initial == MV_START_RANDOM)
4381           MovDir[x][y] = 1 << RND(4);
4382         else if (move_direction_initial & MV_ANY_DIRECTION)
4383           MovDir[x][y] = move_direction_initial;
4384         else if (move_pattern == MV_ALL_DIRECTIONS ||
4385                  move_pattern == MV_TURNING_LEFT ||
4386                  move_pattern == MV_TURNING_RIGHT ||
4387                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4388                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4389                  move_pattern == MV_TURNING_RANDOM)
4390           MovDir[x][y] = 1 << RND(4);
4391         else if (move_pattern == MV_HORIZONTAL)
4392           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4393         else if (move_pattern == MV_VERTICAL)
4394           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4395         else if (move_pattern & MV_ANY_DIRECTION)
4396           MovDir[x][y] = element_info[element].move_pattern;
4397         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4398                  move_pattern == MV_ALONG_RIGHT_SIDE)
4399         {
4400           /* use random direction as default start direction */
4401           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4402             MovDir[x][y] = 1 << RND(4);
4403
4404           for (i = 0; i < NUM_DIRECTIONS; i++)
4405           {
4406             int x1 = x + xy[i][0];
4407             int y1 = y + xy[i][1];
4408
4409             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4410             {
4411               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4412                 MovDir[x][y] = direction[0][i];
4413               else
4414                 MovDir[x][y] = direction[1][i];
4415
4416               break;
4417             }
4418           }
4419         }                
4420       }
4421       else
4422       {
4423         MovDir[x][y] = 1 << RND(4);
4424
4425         if (element != EL_BUG &&
4426             element != EL_SPACESHIP &&
4427             element != EL_BD_BUTTERFLY &&
4428             element != EL_BD_FIREFLY)
4429           break;
4430
4431         for (i = 0; i < NUM_DIRECTIONS; i++)
4432         {
4433           int x1 = x + xy[i][0];
4434           int y1 = y + xy[i][1];
4435
4436           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4437           {
4438             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4439             {
4440               MovDir[x][y] = direction[0][i];
4441               break;
4442             }
4443             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4444                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4445             {
4446               MovDir[x][y] = direction[1][i];
4447               break;
4448             }
4449           }
4450         }
4451       }
4452       break;
4453   }
4454
4455   GfxDir[x][y] = MovDir[x][y];
4456 }
4457
4458 void InitAmoebaNr(int x, int y)
4459 {
4460   int i;
4461   int group_nr = AmoebeNachbarNr(x, y);
4462
4463   if (group_nr == 0)
4464   {
4465     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4466     {
4467       if (AmoebaCnt[i] == 0)
4468       {
4469         group_nr = i;
4470         break;
4471       }
4472     }
4473   }
4474
4475   AmoebaNr[x][y] = group_nr;
4476   AmoebaCnt[group_nr]++;
4477   AmoebaCnt2[group_nr]++;
4478 }
4479
4480 static void PlayerWins(struct PlayerInfo *player)
4481 {
4482   player->LevelSolved = TRUE;
4483   player->GameOver = TRUE;
4484
4485   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4486                          level.native_em_level->lev->score : player->score);
4487
4488   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4489   player->LevelSolved_CountingScore = player->score_final;
4490 }
4491
4492 void GameWon()
4493 {
4494   static int time, time_final;
4495   static int score, score_final;
4496   static int game_over_delay_1 = 0;
4497   static int game_over_delay_2 = 0;
4498   int game_over_delay_value_1 = 50;
4499   int game_over_delay_value_2 = 50;
4500
4501   if (!local_player->LevelSolved_GameWon)
4502   {
4503     int i;
4504
4505     /* do not start end game actions before the player stops moving (to exit) */
4506     if (local_player->MovPos)
4507       return;
4508
4509     local_player->LevelSolved_GameWon = TRUE;
4510     local_player->LevelSolved_SaveTape = tape.recording;
4511     local_player->LevelSolved_SaveScore = !tape.playing;
4512
4513     if (tape.auto_play)         /* tape might already be stopped here */
4514       tape.auto_play_level_solved = TRUE;
4515
4516 #if 1
4517     TapeStop();
4518 #endif
4519
4520     game_over_delay_1 = game_over_delay_value_1;
4521     game_over_delay_2 = game_over_delay_value_2;
4522
4523     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4524     score = score_final = local_player->score_final;
4525
4526     if (TimeLeft > 0)
4527     {
4528       time_final = 0;
4529       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4530     }
4531     else if (level.time == 0 && TimePlayed < 999)
4532     {
4533       time_final = 999;
4534       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4535     }
4536
4537     local_player->score_final = score_final;
4538
4539     if (level_editor_test_game)
4540     {
4541       time = time_final;
4542       score = score_final;
4543
4544 #if 1
4545       local_player->LevelSolved_CountingTime = time;
4546       local_player->LevelSolved_CountingScore = score;
4547
4548       game_panel_controls[GAME_PANEL_TIME].value = time;
4549       game_panel_controls[GAME_PANEL_SCORE].value = score;
4550
4551       DisplayGameControlValues();
4552 #else
4553       DrawGameValue_Time(time);
4554       DrawGameValue_Score(score);
4555 #endif
4556     }
4557
4558     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4559     {
4560       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4561       {
4562         /* close exit door after last player */
4563         if ((AllPlayersGone &&
4564              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4565               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4566               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4567             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4568             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4569         {
4570           int element = Feld[ExitX][ExitY];
4571
4572 #if 0
4573           if (element == EL_EM_EXIT_OPEN ||
4574               element == EL_EM_STEEL_EXIT_OPEN)
4575           {
4576             Bang(ExitX, ExitY);
4577           }
4578           else
4579 #endif
4580           {
4581             Feld[ExitX][ExitY] =
4582               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4583                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4584                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4585                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4586                EL_EM_STEEL_EXIT_CLOSING);
4587
4588             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4589           }
4590         }
4591
4592         /* player disappears */
4593         DrawLevelField(ExitX, ExitY);
4594       }
4595
4596       for (i = 0; i < MAX_PLAYERS; i++)
4597       {
4598         struct PlayerInfo *player = &stored_player[i];
4599
4600         if (player->present)
4601         {
4602           RemovePlayer(player);
4603
4604           /* player disappears */
4605           DrawLevelField(player->jx, player->jy);
4606         }
4607       }
4608     }
4609
4610     PlaySound(SND_GAME_WINNING);
4611   }
4612
4613   if (game_over_delay_1 > 0)
4614   {
4615     game_over_delay_1--;
4616
4617     return;
4618   }
4619
4620   if (time != time_final)
4621   {
4622     int time_to_go = ABS(time_final - time);
4623     int time_count_dir = (time < time_final ? +1 : -1);
4624     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4625
4626     time  += time_count_steps * time_count_dir;
4627     score += time_count_steps * level.score[SC_TIME_BONUS];
4628
4629 #if 1
4630     local_player->LevelSolved_CountingTime = time;
4631     local_player->LevelSolved_CountingScore = score;
4632
4633     game_panel_controls[GAME_PANEL_TIME].value = time;
4634     game_panel_controls[GAME_PANEL_SCORE].value = score;
4635
4636     DisplayGameControlValues();
4637 #else
4638     DrawGameValue_Time(time);
4639     DrawGameValue_Score(score);
4640 #endif
4641
4642     if (time == time_final)
4643       StopSound(SND_GAME_LEVELTIME_BONUS);
4644     else if (setup.sound_loops)
4645       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4646     else
4647       PlaySound(SND_GAME_LEVELTIME_BONUS);
4648
4649     return;
4650   }
4651
4652   local_player->LevelSolved_PanelOff = TRUE;
4653
4654   if (game_over_delay_2 > 0)
4655   {
4656     game_over_delay_2--;
4657
4658     return;
4659   }
4660
4661 #if 1
4662   GameEnd();
4663 #endif
4664 }
4665
4666 void GameEnd()
4667 {
4668   int hi_pos;
4669   boolean raise_level = FALSE;
4670
4671   local_player->LevelSolved_GameEnd = TRUE;
4672
4673   CloseDoor(DOOR_CLOSE_1);
4674
4675   if (local_player->LevelSolved_SaveTape)
4676   {
4677 #if 0
4678     TapeStop();
4679 #endif
4680
4681 #if 1
4682     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4683 #else
4684     SaveTape(tape.level_nr);            /* ask to save tape */
4685 #endif
4686   }
4687
4688   if (level_editor_test_game)
4689   {
4690     game_status = GAME_MODE_MAIN;
4691
4692 #if 1
4693     DrawAndFadeInMainMenu(REDRAW_FIELD);
4694 #else
4695     DrawMainMenu();
4696 #endif
4697
4698     return;
4699   }
4700
4701   if (!local_player->LevelSolved_SaveScore)
4702   {
4703 #if 1
4704     FadeOut(REDRAW_FIELD);
4705 #endif
4706
4707     game_status = GAME_MODE_MAIN;
4708
4709     DrawAndFadeInMainMenu(REDRAW_FIELD);
4710
4711     return;
4712   }
4713
4714   if (level_nr == leveldir_current->handicap_level)
4715   {
4716     leveldir_current->handicap_level++;
4717     SaveLevelSetup_SeriesInfo();
4718   }
4719
4720   if (level_nr < leveldir_current->last_level)
4721     raise_level = TRUE;                 /* advance to next level */
4722
4723   if ((hi_pos = NewHiScore()) >= 0) 
4724   {
4725     game_status = GAME_MODE_SCORES;
4726
4727     DrawHallOfFame(hi_pos);
4728
4729     if (raise_level)
4730     {
4731       level_nr++;
4732       TapeErase();
4733     }
4734   }
4735   else
4736   {
4737 #if 1
4738     FadeOut(REDRAW_FIELD);
4739 #endif
4740
4741     game_status = GAME_MODE_MAIN;
4742
4743     if (raise_level)
4744     {
4745       level_nr++;
4746       TapeErase();
4747     }
4748
4749     DrawAndFadeInMainMenu(REDRAW_FIELD);
4750   }
4751 }
4752
4753 int NewHiScore()
4754 {
4755   int k, l;
4756   int position = -1;
4757
4758   LoadScore(level_nr);
4759
4760   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4761       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4762     return -1;
4763
4764   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4765   {
4766     if (local_player->score_final > highscore[k].Score)
4767     {
4768       /* player has made it to the hall of fame */
4769
4770       if (k < MAX_SCORE_ENTRIES - 1)
4771       {
4772         int m = MAX_SCORE_ENTRIES - 1;
4773
4774 #ifdef ONE_PER_NAME
4775         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4776           if (strEqual(setup.player_name, highscore[l].Name))
4777             m = l;
4778         if (m == k)     /* player's new highscore overwrites his old one */
4779           goto put_into_list;
4780 #endif
4781
4782         for (l = m; l > k; l--)
4783         {
4784           strcpy(highscore[l].Name, highscore[l - 1].Name);
4785           highscore[l].Score = highscore[l - 1].Score;
4786         }
4787       }
4788
4789 #ifdef ONE_PER_NAME
4790       put_into_list:
4791 #endif
4792       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4793       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4794       highscore[k].Score = local_player->score_final; 
4795       position = k;
4796       break;
4797     }
4798
4799 #ifdef ONE_PER_NAME
4800     else if (!strncmp(setup.player_name, highscore[k].Name,
4801                       MAX_PLAYER_NAME_LEN))
4802       break;    /* player already there with a higher score */
4803 #endif
4804
4805   }
4806
4807   if (position >= 0) 
4808     SaveScore(level_nr);
4809
4810   return position;
4811 }
4812
4813 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4814 {
4815   int element = Feld[x][y];
4816   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4817   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4818   int horiz_move = (dx != 0);
4819   int sign = (horiz_move ? dx : dy);
4820   int step = sign * element_info[element].move_stepsize;
4821
4822   /* special values for move stepsize for spring and things on conveyor belt */
4823   if (horiz_move)
4824   {
4825     if (CAN_FALL(element) &&
4826         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4827       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4828     else if (element == EL_SPRING)
4829       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4830   }
4831
4832   return step;
4833 }
4834
4835 inline static int getElementMoveStepsize(int x, int y)
4836 {
4837   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4838 }
4839
4840 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4841 {
4842   if (player->GfxAction != action || player->GfxDir != dir)
4843   {
4844 #if 0
4845     printf("Player frame reset! (%d => %d, %d => %d)\n",
4846            player->GfxAction, action, player->GfxDir, dir);
4847 #endif
4848
4849     player->GfxAction = action;
4850     player->GfxDir = dir;
4851     player->Frame = 0;
4852     player->StepFrame = 0;
4853   }
4854 }
4855
4856 #if USE_GFX_RESET_GFX_ANIMATION
4857 static void ResetGfxFrame(int x, int y, boolean redraw)
4858 {
4859   int element = Feld[x][y];
4860   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4861   int last_gfx_frame = GfxFrame[x][y];
4862
4863   if (graphic_info[graphic].anim_global_sync)
4864     GfxFrame[x][y] = FrameCounter;
4865   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4866     GfxFrame[x][y] = CustomValue[x][y];
4867   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4868     GfxFrame[x][y] = element_info[element].collect_score;
4869   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4870     GfxFrame[x][y] = ChangeDelay[x][y];
4871
4872   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4873     DrawLevelGraphicAnimation(x, y, graphic);
4874 }
4875 #endif
4876
4877 static void ResetGfxAnimation(int x, int y)
4878 {
4879   GfxAction[x][y] = ACTION_DEFAULT;
4880   GfxDir[x][y] = MovDir[x][y];
4881   GfxFrame[x][y] = 0;
4882
4883 #if USE_GFX_RESET_GFX_ANIMATION
4884   ResetGfxFrame(x, y, FALSE);
4885 #endif
4886 }
4887
4888 static void ResetRandomAnimationValue(int x, int y)
4889 {
4890   GfxRandom[x][y] = INIT_GFX_RANDOM();
4891 }
4892
4893 void InitMovingField(int x, int y, int direction)
4894 {
4895   int element = Feld[x][y];
4896   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4897   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4898   int newx = x + dx;
4899   int newy = y + dy;
4900   boolean is_moving_before, is_moving_after;
4901 #if 0
4902   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4903 #endif
4904
4905   /* check if element was/is moving or being moved before/after mode change */
4906 #if 1
4907 #if 1
4908   is_moving_before = (WasJustMoving[x][y] != 0);
4909 #else
4910   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4911   is_moving_before = WasJustMoving[x][y];
4912 #endif
4913 #else
4914   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4915 #endif
4916   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4917
4918   /* reset animation only for moving elements which change direction of moving
4919      or which just started or stopped moving
4920      (else CEs with property "can move" / "not moving" are reset each frame) */
4921 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4922 #if 1
4923   if (is_moving_before != is_moving_after ||
4924       direction != MovDir[x][y])
4925     ResetGfxAnimation(x, y);
4926 #else
4927   if ((is_moving_before || is_moving_after) && !continues_moving)
4928     ResetGfxAnimation(x, y);
4929 #endif
4930 #else
4931   if (!continues_moving)
4932     ResetGfxAnimation(x, y);
4933 #endif
4934
4935   MovDir[x][y] = direction;
4936   GfxDir[x][y] = direction;
4937
4938 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4939   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4940                      direction == MV_DOWN && CAN_FALL(element) ?
4941                      ACTION_FALLING : ACTION_MOVING);
4942 #else
4943   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4944                      ACTION_FALLING : ACTION_MOVING);
4945 #endif
4946
4947   /* this is needed for CEs with property "can move" / "not moving" */
4948
4949   if (is_moving_after)
4950   {
4951     if (Feld[newx][newy] == EL_EMPTY)
4952       Feld[newx][newy] = EL_BLOCKED;
4953
4954     MovDir[newx][newy] = MovDir[x][y];
4955
4956 #if USE_NEW_CUSTOM_VALUE
4957     CustomValue[newx][newy] = CustomValue[x][y];
4958 #endif
4959
4960     GfxFrame[newx][newy] = GfxFrame[x][y];
4961     GfxRandom[newx][newy] = GfxRandom[x][y];
4962     GfxAction[newx][newy] = GfxAction[x][y];
4963     GfxDir[newx][newy] = GfxDir[x][y];
4964   }
4965 }
4966
4967 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4968 {
4969   int direction = MovDir[x][y];
4970   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4971   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4972
4973   *goes_to_x = newx;
4974   *goes_to_y = newy;
4975 }
4976
4977 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4978 {
4979   int oldx = x, oldy = y;
4980   int direction = MovDir[x][y];
4981
4982   if (direction == MV_LEFT)
4983     oldx++;
4984   else if (direction == MV_RIGHT)
4985     oldx--;
4986   else if (direction == MV_UP)
4987     oldy++;
4988   else if (direction == MV_DOWN)
4989     oldy--;
4990
4991   *comes_from_x = oldx;
4992   *comes_from_y = oldy;
4993 }
4994
4995 int MovingOrBlocked2Element(int x, int y)
4996 {
4997   int element = Feld[x][y];
4998
4999   if (element == EL_BLOCKED)
5000   {
5001     int oldx, oldy;
5002
5003     Blocked2Moving(x, y, &oldx, &oldy);
5004     return Feld[oldx][oldy];
5005   }
5006   else
5007     return element;
5008 }
5009
5010 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5011 {
5012   /* like MovingOrBlocked2Element(), but if element is moving
5013      and (x,y) is the field the moving element is just leaving,
5014      return EL_BLOCKED instead of the element value */
5015   int element = Feld[x][y];
5016
5017   if (IS_MOVING(x, y))
5018   {
5019     if (element == EL_BLOCKED)
5020     {
5021       int oldx, oldy;
5022
5023       Blocked2Moving(x, y, &oldx, &oldy);
5024       return Feld[oldx][oldy];
5025     }
5026     else
5027       return EL_BLOCKED;
5028   }
5029   else
5030     return element;
5031 }
5032
5033 static void RemoveField(int x, int y)
5034 {
5035   Feld[x][y] = EL_EMPTY;
5036
5037   MovPos[x][y] = 0;
5038   MovDir[x][y] = 0;
5039   MovDelay[x][y] = 0;
5040
5041 #if USE_NEW_CUSTOM_VALUE
5042   CustomValue[x][y] = 0;
5043 #endif
5044
5045   AmoebaNr[x][y] = 0;
5046   ChangeDelay[x][y] = 0;
5047   ChangePage[x][y] = -1;
5048   Pushed[x][y] = FALSE;
5049
5050 #if 0
5051   ExplodeField[x][y] = EX_TYPE_NONE;
5052 #endif
5053
5054   GfxElement[x][y] = EL_UNDEFINED;
5055   GfxAction[x][y] = ACTION_DEFAULT;
5056   GfxDir[x][y] = MV_NONE;
5057 }
5058
5059 void RemoveMovingField(int x, int y)
5060 {
5061   int oldx = x, oldy = y, newx = x, newy = y;
5062   int element = Feld[x][y];
5063   int next_element = EL_UNDEFINED;
5064
5065   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5066     return;
5067
5068   if (IS_MOVING(x, y))
5069   {
5070     Moving2Blocked(x, y, &newx, &newy);
5071
5072     if (Feld[newx][newy] != EL_BLOCKED)
5073     {
5074       /* element is moving, but target field is not free (blocked), but
5075          already occupied by something different (example: acid pool);
5076          in this case, only remove the moving field, but not the target */
5077
5078       RemoveField(oldx, oldy);
5079
5080       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5081
5082       DrawLevelField(oldx, oldy);
5083
5084       return;
5085     }
5086   }
5087   else if (element == EL_BLOCKED)
5088   {
5089     Blocked2Moving(x, y, &oldx, &oldy);
5090     if (!IS_MOVING(oldx, oldy))
5091       return;
5092   }
5093
5094   if (element == EL_BLOCKED &&
5095       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5096        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5097        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5098        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5099        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5100        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5101     next_element = get_next_element(Feld[oldx][oldy]);
5102
5103   RemoveField(oldx, oldy);
5104   RemoveField(newx, newy);
5105
5106   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5107
5108   if (next_element != EL_UNDEFINED)
5109     Feld[oldx][oldy] = next_element;
5110
5111   DrawLevelField(oldx, oldy);
5112   DrawLevelField(newx, newy);
5113 }
5114
5115 void DrawDynamite(int x, int y)
5116 {
5117   int sx = SCREENX(x), sy = SCREENY(y);
5118   int graphic = el2img(Feld[x][y]);
5119   int frame;
5120
5121   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5122     return;
5123
5124   if (IS_WALKABLE_INSIDE(Back[x][y]))
5125     return;
5126
5127   if (Back[x][y])
5128     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5129   else if (Store[x][y])
5130     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5131
5132   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5133
5134   if (Back[x][y] || Store[x][y])
5135     DrawGraphicThruMask(sx, sy, graphic, frame);
5136   else
5137     DrawGraphic(sx, sy, graphic, frame);
5138 }
5139
5140 void CheckDynamite(int x, int y)
5141 {
5142   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5143   {
5144     MovDelay[x][y]--;
5145
5146     if (MovDelay[x][y] != 0)
5147     {
5148       DrawDynamite(x, y);
5149       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5150
5151       return;
5152     }
5153   }
5154
5155   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5156
5157   Bang(x, y);
5158 }
5159
5160 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5161 {
5162   boolean num_checked_players = 0;
5163   int i;
5164
5165   for (i = 0; i < MAX_PLAYERS; i++)
5166   {
5167     if (stored_player[i].active)
5168     {
5169       int sx = stored_player[i].jx;
5170       int sy = stored_player[i].jy;
5171
5172       if (num_checked_players == 0)
5173       {
5174         *sx1 = *sx2 = sx;
5175         *sy1 = *sy2 = sy;
5176       }
5177       else
5178       {
5179         *sx1 = MIN(*sx1, sx);
5180         *sy1 = MIN(*sy1, sy);
5181         *sx2 = MAX(*sx2, sx);
5182         *sy2 = MAX(*sy2, sy);
5183       }
5184
5185       num_checked_players++;
5186     }
5187   }
5188 }
5189
5190 static boolean checkIfAllPlayersFitToScreen_RND()
5191 {
5192   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5193
5194   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5195
5196   return (sx2 - sx1 < SCR_FIELDX &&
5197           sy2 - sy1 < SCR_FIELDY);
5198 }
5199
5200 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5201 {
5202   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5203
5204   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5205
5206   *sx = (sx1 + sx2) / 2;
5207   *sy = (sy1 + sy2) / 2;
5208 }
5209
5210 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5211                         boolean center_screen, boolean quick_relocation)
5212 {
5213   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5214   boolean no_delay = (tape.warp_forward);
5215   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5216   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5217
5218   if (quick_relocation)
5219   {
5220     int offset = game.scroll_delay_value;
5221
5222     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5223     {
5224       if (!level.shifted_relocation || center_screen)
5225       {
5226         /* quick relocation (without scrolling), with centering of screen */
5227
5228         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5229                     x > SBX_Right + MIDPOSX ? SBX_Right :
5230                     x - MIDPOSX);
5231
5232         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5233                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5234                     y - MIDPOSY);
5235       }
5236       else
5237       {
5238         /* quick relocation (without scrolling), but do not center screen */
5239
5240         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5241                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5242                                old_x - MIDPOSX);
5243
5244         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5245                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5246                                old_y - MIDPOSY);
5247
5248         int offset_x = x + (scroll_x - center_scroll_x);
5249         int offset_y = y + (scroll_y - center_scroll_y);
5250
5251         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5252                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5253                     offset_x - MIDPOSX);
5254
5255         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5256                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5257                     offset_y - MIDPOSY);
5258       }
5259     }
5260     else
5261     {
5262       /* quick relocation (without scrolling), inside visible screen area */
5263
5264       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5265           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5266         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5267
5268       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5269           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5270         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5271
5272       /* don't scroll over playfield boundaries */
5273       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5274         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5275
5276       /* don't scroll over playfield boundaries */
5277       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5278         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5279     }
5280
5281     RedrawPlayfield(TRUE, 0,0,0,0);
5282   }
5283   else
5284   {
5285 #if 1
5286     int scroll_xx, scroll_yy;
5287
5288     if (!level.shifted_relocation || center_screen)
5289     {
5290       /* visible relocation (with scrolling), with centering of screen */
5291
5292       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5293                    x > SBX_Right + MIDPOSX ? SBX_Right :
5294                    x - MIDPOSX);
5295
5296       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5297                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5298                    y - MIDPOSY);
5299     }
5300     else
5301     {
5302       /* visible relocation (with scrolling), but do not center screen */
5303
5304       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5305                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5306                              old_x - MIDPOSX);
5307
5308       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5309                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5310                              old_y - MIDPOSY);
5311
5312       int offset_x = x + (scroll_x - center_scroll_x);
5313       int offset_y = y + (scroll_y - center_scroll_y);
5314
5315       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5316                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5317                    offset_x - MIDPOSX);
5318
5319       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5320                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5321                    offset_y - MIDPOSY);
5322     }
5323
5324 #else
5325
5326     /* visible relocation (with scrolling), with centering of screen */
5327
5328     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5329                      x > SBX_Right + MIDPOSX ? SBX_Right :
5330                      x - MIDPOSX);
5331
5332     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5333                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5334                      y - MIDPOSY);
5335 #endif
5336
5337     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5338
5339     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5340     {
5341       int dx = 0, dy = 0;
5342       int fx = FX, fy = FY;
5343
5344       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5345       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5346
5347       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5348         break;
5349
5350       scroll_x -= dx;
5351       scroll_y -= dy;
5352
5353       fx += dx * TILEX / 2;
5354       fy += dy * TILEY / 2;
5355
5356       ScrollLevel(dx, dy);
5357       DrawAllPlayers();
5358
5359       /* scroll in two steps of half tile size to make things smoother */
5360       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5361       FlushDisplay();
5362       Delay(wait_delay_value);
5363
5364       /* scroll second step to align at full tile size */
5365       BackToFront();
5366       Delay(wait_delay_value);
5367     }
5368
5369     DrawAllPlayers();
5370     BackToFront();
5371     Delay(wait_delay_value);
5372   }
5373 }
5374
5375 void RelocatePlayer(int jx, int jy, int el_player_raw)
5376 {
5377   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5378   int player_nr = GET_PLAYER_NR(el_player);
5379   struct PlayerInfo *player = &stored_player[player_nr];
5380   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5381   boolean no_delay = (tape.warp_forward);
5382   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5383   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5384   int old_jx = player->jx;
5385   int old_jy = player->jy;
5386   int old_element = Feld[old_jx][old_jy];
5387   int element = Feld[jx][jy];
5388   boolean player_relocated = (old_jx != jx || old_jy != jy);
5389
5390   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5391   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5392   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5393   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5394   int leave_side_horiz = move_dir_horiz;
5395   int leave_side_vert  = move_dir_vert;
5396   int enter_side = enter_side_horiz | enter_side_vert;
5397   int leave_side = leave_side_horiz | leave_side_vert;
5398
5399   if (player->GameOver)         /* do not reanimate dead player */
5400     return;
5401
5402   if (!player_relocated)        /* no need to relocate the player */
5403     return;
5404
5405   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5406   {
5407     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5408     DrawLevelField(jx, jy);
5409   }
5410
5411   if (player->present)
5412   {
5413     while (player->MovPos)
5414     {
5415       ScrollPlayer(player, SCROLL_GO_ON);
5416       ScrollScreen(NULL, SCROLL_GO_ON);
5417
5418       AdvanceFrameAndPlayerCounters(player->index_nr);
5419
5420       DrawPlayer(player);
5421
5422       BackToFront();
5423       Delay(wait_delay_value);
5424     }
5425
5426     DrawPlayer(player);         /* needed here only to cleanup last field */
5427     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5428
5429     player->is_moving = FALSE;
5430   }
5431
5432   if (IS_CUSTOM_ELEMENT(old_element))
5433     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5434                                CE_LEFT_BY_PLAYER,
5435                                player->index_bit, leave_side);
5436
5437   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5438                                       CE_PLAYER_LEAVES_X,
5439                                       player->index_bit, leave_side);
5440
5441   Feld[jx][jy] = el_player;
5442   InitPlayerField(jx, jy, el_player, TRUE);
5443
5444   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5445   {
5446     Feld[jx][jy] = element;
5447     InitField(jx, jy, FALSE);
5448   }
5449
5450   /* only visually relocate centered player */
5451   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5452                      FALSE, level.instant_relocation);
5453
5454   TestIfPlayerTouchesBadThing(jx, jy);
5455   TestIfPlayerTouchesCustomElement(jx, jy);
5456
5457   if (IS_CUSTOM_ELEMENT(element))
5458     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5459                                player->index_bit, enter_side);
5460
5461   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5462                                       player->index_bit, enter_side);
5463 }
5464
5465 void Explode(int ex, int ey, int phase, int mode)
5466 {
5467   int x, y;
5468   int last_phase;
5469   int border_element;
5470
5471   /* !!! eliminate this variable !!! */
5472   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5473
5474   if (game.explosions_delayed)
5475   {
5476     ExplodeField[ex][ey] = mode;
5477     return;
5478   }
5479
5480   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5481   {
5482     int center_element = Feld[ex][ey];
5483     int artwork_element, explosion_element;     /* set these values later */
5484
5485 #if 0
5486     /* --- This is only really needed (and now handled) in "Impact()". --- */
5487     /* do not explode moving elements that left the explode field in time */
5488     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5489         center_element == EL_EMPTY &&
5490         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5491       return;
5492 #endif
5493
5494 #if 0
5495     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5496     if (mode == EX_TYPE_NORMAL ||
5497         mode == EX_TYPE_CENTER ||
5498         mode == EX_TYPE_CROSS)
5499       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5500 #endif
5501
5502     /* remove things displayed in background while burning dynamite */
5503     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5504       Back[ex][ey] = 0;
5505
5506     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5507     {
5508       /* put moving element to center field (and let it explode there) */
5509       center_element = MovingOrBlocked2Element(ex, ey);
5510       RemoveMovingField(ex, ey);
5511       Feld[ex][ey] = center_element;
5512     }
5513
5514     /* now "center_element" is finally determined -- set related values now */
5515     artwork_element = center_element;           /* for custom player artwork */
5516     explosion_element = center_element;         /* for custom player artwork */
5517
5518     if (IS_PLAYER(ex, ey))
5519     {
5520       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5521
5522       artwork_element = stored_player[player_nr].artwork_element;
5523
5524       if (level.use_explosion_element[player_nr])
5525       {
5526         explosion_element = level.explosion_element[player_nr];
5527         artwork_element = explosion_element;
5528       }
5529     }
5530
5531 #if 1
5532     if (mode == EX_TYPE_NORMAL ||
5533         mode == EX_TYPE_CENTER ||
5534         mode == EX_TYPE_CROSS)
5535       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5536 #endif
5537
5538     last_phase = element_info[explosion_element].explosion_delay + 1;
5539
5540     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5541     {
5542       int xx = x - ex + 1;
5543       int yy = y - ey + 1;
5544       int element;
5545
5546       if (!IN_LEV_FIELD(x, y) ||
5547           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5548           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5549         continue;
5550
5551       element = Feld[x][y];
5552
5553       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5554       {
5555         element = MovingOrBlocked2Element(x, y);
5556
5557         if (!IS_EXPLOSION_PROOF(element))
5558           RemoveMovingField(x, y);
5559       }
5560
5561       /* indestructible elements can only explode in center (but not flames) */
5562       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5563                                            mode == EX_TYPE_BORDER)) ||
5564           element == EL_FLAMES)
5565         continue;
5566
5567       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5568          behaviour, for example when touching a yamyam that explodes to rocks
5569          with active deadly shield, a rock is created under the player !!! */
5570       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5571 #if 0
5572       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5573           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5574            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5575 #else
5576       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5577 #endif
5578       {
5579         if (IS_ACTIVE_BOMB(element))
5580         {
5581           /* re-activate things under the bomb like gate or penguin */
5582           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5583           Back[x][y] = 0;
5584         }
5585
5586         continue;
5587       }
5588
5589       /* save walkable background elements while explosion on same tile */
5590       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5591           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5592         Back[x][y] = element;
5593
5594       /* ignite explodable elements reached by other explosion */
5595       if (element == EL_EXPLOSION)
5596         element = Store2[x][y];
5597
5598       if (AmoebaNr[x][y] &&
5599           (element == EL_AMOEBA_FULL ||
5600            element == EL_BD_AMOEBA ||
5601            element == EL_AMOEBA_GROWING))
5602       {
5603         AmoebaCnt[AmoebaNr[x][y]]--;
5604         AmoebaCnt2[AmoebaNr[x][y]]--;
5605       }
5606
5607       RemoveField(x, y);
5608
5609       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5610       {
5611         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5612
5613         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5614
5615         if (PLAYERINFO(ex, ey)->use_murphy)
5616           Store[x][y] = EL_EMPTY;
5617       }
5618
5619       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5620          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5621       else if (ELEM_IS_PLAYER(center_element))
5622         Store[x][y] = EL_EMPTY;
5623       else if (center_element == EL_YAMYAM)
5624         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5625       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5626         Store[x][y] = element_info[center_element].content.e[xx][yy];
5627 #if 1
5628       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5629          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5630          otherwise) -- FIX THIS !!! */
5631       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5632         Store[x][y] = element_info[element].content.e[1][1];
5633 #else
5634       else if (!CAN_EXPLODE(element))
5635         Store[x][y] = element_info[element].content.e[1][1];
5636 #endif
5637       else
5638         Store[x][y] = EL_EMPTY;
5639
5640       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5641           center_element == EL_AMOEBA_TO_DIAMOND)
5642         Store2[x][y] = element;
5643
5644       Feld[x][y] = EL_EXPLOSION;
5645       GfxElement[x][y] = artwork_element;
5646
5647       ExplodePhase[x][y] = 1;
5648       ExplodeDelay[x][y] = last_phase;
5649
5650       Stop[x][y] = TRUE;
5651     }
5652
5653     if (center_element == EL_YAMYAM)
5654       game.yamyam_content_nr =
5655         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5656
5657     return;
5658   }
5659
5660   if (Stop[ex][ey])
5661     return;
5662
5663   x = ex;
5664   y = ey;
5665
5666   if (phase == 1)
5667     GfxFrame[x][y] = 0;         /* restart explosion animation */
5668
5669   last_phase = ExplodeDelay[x][y];
5670
5671   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5672
5673 #ifdef DEBUG
5674
5675   /* activate this even in non-DEBUG version until cause for crash in
5676      getGraphicAnimationFrame() (see below) is found and eliminated */
5677
5678 #endif
5679 #if 1
5680
5681 #if 1
5682   /* this can happen if the player leaves an explosion just in time */
5683   if (GfxElement[x][y] == EL_UNDEFINED)
5684     GfxElement[x][y] = EL_EMPTY;
5685 #else
5686   if (GfxElement[x][y] == EL_UNDEFINED)
5687   {
5688     printf("\n\n");
5689     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5690     printf("Explode(): This should never happen!\n");
5691     printf("\n\n");
5692
5693     GfxElement[x][y] = EL_EMPTY;
5694   }
5695 #endif
5696
5697 #endif
5698
5699   border_element = Store2[x][y];
5700   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5701     border_element = StorePlayer[x][y];
5702
5703   if (phase == element_info[border_element].ignition_delay ||
5704       phase == last_phase)
5705   {
5706     boolean border_explosion = FALSE;
5707
5708     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5709         !PLAYER_EXPLOSION_PROTECTED(x, y))
5710     {
5711       KillPlayerUnlessExplosionProtected(x, y);
5712       border_explosion = TRUE;
5713     }
5714     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5715     {
5716       Feld[x][y] = Store2[x][y];
5717       Store2[x][y] = 0;
5718       Bang(x, y);
5719       border_explosion = TRUE;
5720     }
5721     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5722     {
5723       AmoebeUmwandeln(x, y);
5724       Store2[x][y] = 0;
5725       border_explosion = TRUE;
5726     }
5727
5728     /* if an element just explodes due to another explosion (chain-reaction),
5729        do not immediately end the new explosion when it was the last frame of
5730        the explosion (as it would be done in the following "if"-statement!) */
5731     if (border_explosion && phase == last_phase)
5732       return;
5733   }
5734
5735   if (phase == last_phase)
5736   {
5737     int element;
5738
5739     element = Feld[x][y] = Store[x][y];
5740     Store[x][y] = Store2[x][y] = 0;
5741     GfxElement[x][y] = EL_UNDEFINED;
5742
5743     /* player can escape from explosions and might therefore be still alive */
5744     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5745         element <= EL_PLAYER_IS_EXPLODING_4)
5746     {
5747       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5748       int explosion_element = EL_PLAYER_1 + player_nr;
5749       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5750       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5751
5752       if (level.use_explosion_element[player_nr])
5753         explosion_element = level.explosion_element[player_nr];
5754
5755       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5756                     element_info[explosion_element].content.e[xx][yy]);
5757     }
5758
5759     /* restore probably existing indestructible background element */
5760     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5761       element = Feld[x][y] = Back[x][y];
5762     Back[x][y] = 0;
5763
5764     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5765     GfxDir[x][y] = MV_NONE;
5766     ChangeDelay[x][y] = 0;
5767     ChangePage[x][y] = -1;
5768
5769 #if USE_NEW_CUSTOM_VALUE
5770     CustomValue[x][y] = 0;
5771 #endif
5772
5773     InitField_WithBug2(x, y, FALSE);
5774
5775     DrawLevelField(x, y);
5776
5777     TestIfElementTouchesCustomElement(x, y);
5778
5779     if (GFX_CRUMBLED(element))
5780       DrawLevelFieldCrumbledSandNeighbours(x, y);
5781
5782     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5783       StorePlayer[x][y] = 0;
5784
5785     if (ELEM_IS_PLAYER(element))
5786       RelocatePlayer(x, y, element);
5787   }
5788   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5789   {
5790     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5791     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5792
5793     if (phase == delay)
5794       DrawLevelFieldCrumbledSand(x, y);
5795
5796     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5797     {
5798       DrawLevelElement(x, y, Back[x][y]);
5799       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5800     }
5801     else if (IS_WALKABLE_UNDER(Back[x][y]))
5802     {
5803       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5804       DrawLevelElementThruMask(x, y, Back[x][y]);
5805     }
5806     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5807       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5808   }
5809 }
5810
5811 void DynaExplode(int ex, int ey)
5812 {
5813   int i, j;
5814   int dynabomb_element = Feld[ex][ey];
5815   int dynabomb_size = 1;
5816   boolean dynabomb_xl = FALSE;
5817   struct PlayerInfo *player;
5818   static int xy[4][2] =
5819   {
5820     { 0, -1 },
5821     { -1, 0 },
5822     { +1, 0 },
5823     { 0, +1 }
5824   };
5825
5826   if (IS_ACTIVE_BOMB(dynabomb_element))
5827   {
5828     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5829     dynabomb_size = player->dynabomb_size;
5830     dynabomb_xl = player->dynabomb_xl;
5831     player->dynabombs_left++;
5832   }
5833
5834   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5835
5836   for (i = 0; i < NUM_DIRECTIONS; i++)
5837   {
5838     for (j = 1; j <= dynabomb_size; j++)
5839     {
5840       int x = ex + j * xy[i][0];
5841       int y = ey + j * xy[i][1];
5842       int element;
5843
5844       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5845         break;
5846
5847       element = Feld[x][y];
5848
5849       /* do not restart explosions of fields with active bombs */
5850       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5851         continue;
5852
5853       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5854
5855       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5856           !IS_DIGGABLE(element) && !dynabomb_xl)
5857         break;
5858     }
5859   }
5860 }
5861
5862 void Bang(int x, int y)
5863 {
5864   int element = MovingOrBlocked2Element(x, y);
5865   int explosion_type = EX_TYPE_NORMAL;
5866
5867   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5868   {
5869     struct PlayerInfo *player = PLAYERINFO(x, y);
5870
5871     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5872                             player->element_nr);
5873
5874     if (level.use_explosion_element[player->index_nr])
5875     {
5876       int explosion_element = level.explosion_element[player->index_nr];
5877
5878       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5879         explosion_type = EX_TYPE_CROSS;
5880       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5881         explosion_type = EX_TYPE_CENTER;
5882     }
5883   }
5884
5885   switch (element)
5886   {
5887     case EL_BUG:
5888     case EL_SPACESHIP:
5889     case EL_BD_BUTTERFLY:
5890     case EL_BD_FIREFLY:
5891     case EL_YAMYAM:
5892     case EL_DARK_YAMYAM:
5893     case EL_ROBOT:
5894     case EL_PACMAN:
5895     case EL_MOLE:
5896       RaiseScoreElement(element);
5897       break;
5898
5899     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5900     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5901     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5902     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5903     case EL_DYNABOMB_INCREASE_NUMBER:
5904     case EL_DYNABOMB_INCREASE_SIZE:
5905     case EL_DYNABOMB_INCREASE_POWER:
5906       explosion_type = EX_TYPE_DYNA;
5907       break;
5908
5909     case EL_DC_LANDMINE:
5910 #if 0
5911     case EL_EM_EXIT_OPEN:
5912     case EL_EM_STEEL_EXIT_OPEN:
5913 #endif
5914       explosion_type = EX_TYPE_CENTER;
5915       break;
5916
5917     case EL_PENGUIN:
5918     case EL_LAMP:
5919     case EL_LAMP_ACTIVE:
5920     case EL_AMOEBA_TO_DIAMOND:
5921       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5922         explosion_type = EX_TYPE_CENTER;
5923       break;
5924
5925     default:
5926       if (element_info[element].explosion_type == EXPLODES_CROSS)
5927         explosion_type = EX_TYPE_CROSS;
5928       else if (element_info[element].explosion_type == EXPLODES_1X1)
5929         explosion_type = EX_TYPE_CENTER;
5930       break;
5931   }
5932
5933   if (explosion_type == EX_TYPE_DYNA)
5934     DynaExplode(x, y);
5935   else
5936     Explode(x, y, EX_PHASE_START, explosion_type);
5937
5938   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5939 }
5940
5941 void SplashAcid(int x, int y)
5942 {
5943   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5944       (!IN_LEV_FIELD(x - 1, y - 2) ||
5945        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5946     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5947
5948   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5949       (!IN_LEV_FIELD(x + 1, y - 2) ||
5950        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5951     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5952
5953   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5954 }
5955
5956 static void InitBeltMovement()
5957 {
5958   static int belt_base_element[4] =
5959   {
5960     EL_CONVEYOR_BELT_1_LEFT,
5961     EL_CONVEYOR_BELT_2_LEFT,
5962     EL_CONVEYOR_BELT_3_LEFT,
5963     EL_CONVEYOR_BELT_4_LEFT
5964   };
5965   static int belt_base_active_element[4] =
5966   {
5967     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5968     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5969     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5970     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5971   };
5972
5973   int x, y, i, j;
5974
5975   /* set frame order for belt animation graphic according to belt direction */
5976   for (i = 0; i < NUM_BELTS; i++)
5977   {
5978     int belt_nr = i;
5979
5980     for (j = 0; j < NUM_BELT_PARTS; j++)
5981     {
5982       int element = belt_base_active_element[belt_nr] + j;
5983       int graphic_1 = el2img(element);
5984       int graphic_2 = el2panelimg(element);
5985
5986       if (game.belt_dir[i] == MV_LEFT)
5987       {
5988         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5989         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5990       }
5991       else
5992       {
5993         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5994         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5995       }
5996     }
5997   }
5998
5999   SCAN_PLAYFIELD(x, y)
6000   {
6001     int element = Feld[x][y];
6002
6003     for (i = 0; i < NUM_BELTS; i++)
6004     {
6005       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6006       {
6007         int e_belt_nr = getBeltNrFromBeltElement(element);
6008         int belt_nr = i;
6009
6010         if (e_belt_nr == belt_nr)
6011         {
6012           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6013
6014           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6015         }
6016       }
6017     }
6018   }
6019 }
6020
6021 static void ToggleBeltSwitch(int x, int y)
6022 {
6023   static int belt_base_element[4] =
6024   {
6025     EL_CONVEYOR_BELT_1_LEFT,
6026     EL_CONVEYOR_BELT_2_LEFT,
6027     EL_CONVEYOR_BELT_3_LEFT,
6028     EL_CONVEYOR_BELT_4_LEFT
6029   };
6030   static int belt_base_active_element[4] =
6031   {
6032     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6033     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6034     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6035     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6036   };
6037   static int belt_base_switch_element[4] =
6038   {
6039     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6040     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6041     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6042     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6043   };
6044   static int belt_move_dir[4] =
6045   {
6046     MV_LEFT,
6047     MV_NONE,
6048     MV_RIGHT,
6049     MV_NONE,
6050   };
6051
6052   int element = Feld[x][y];
6053   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6054   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6055   int belt_dir = belt_move_dir[belt_dir_nr];
6056   int xx, yy, i;
6057
6058   if (!IS_BELT_SWITCH(element))
6059     return;
6060
6061   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6062   game.belt_dir[belt_nr] = belt_dir;
6063
6064   if (belt_dir_nr == 3)
6065     belt_dir_nr = 1;
6066
6067   /* set frame order for belt animation graphic according to belt direction */
6068   for (i = 0; i < NUM_BELT_PARTS; i++)
6069   {
6070     int element = belt_base_active_element[belt_nr] + i;
6071     int graphic_1 = el2img(element);
6072     int graphic_2 = el2panelimg(element);
6073
6074     if (belt_dir == MV_LEFT)
6075     {
6076       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6077       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6078     }
6079     else
6080     {
6081       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6082       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6083     }
6084   }
6085
6086   SCAN_PLAYFIELD(xx, yy)
6087   {
6088     int element = Feld[xx][yy];
6089
6090     if (IS_BELT_SWITCH(element))
6091     {
6092       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6093
6094       if (e_belt_nr == belt_nr)
6095       {
6096         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6097         DrawLevelField(xx, yy);
6098       }
6099     }
6100     else if (IS_BELT(element) && belt_dir != MV_NONE)
6101     {
6102       int e_belt_nr = getBeltNrFromBeltElement(element);
6103
6104       if (e_belt_nr == belt_nr)
6105       {
6106         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6107
6108         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6109         DrawLevelField(xx, yy);
6110       }
6111     }
6112     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6113     {
6114       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6115
6116       if (e_belt_nr == belt_nr)
6117       {
6118         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6119
6120         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6121         DrawLevelField(xx, yy);
6122       }
6123     }
6124   }
6125 }
6126
6127 static void ToggleSwitchgateSwitch(int x, int y)
6128 {
6129   int xx, yy;
6130
6131   game.switchgate_pos = !game.switchgate_pos;
6132
6133   SCAN_PLAYFIELD(xx, yy)
6134   {
6135     int element = Feld[xx][yy];
6136
6137 #if !USE_BOTH_SWITCHGATE_SWITCHES
6138     if (element == EL_SWITCHGATE_SWITCH_UP ||
6139         element == EL_SWITCHGATE_SWITCH_DOWN)
6140     {
6141       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6142       DrawLevelField(xx, yy);
6143     }
6144     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6145              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6146     {
6147       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6148       DrawLevelField(xx, yy);
6149     }
6150 #else
6151     if (element == EL_SWITCHGATE_SWITCH_UP)
6152     {
6153       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6154       DrawLevelField(xx, yy);
6155     }
6156     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6157     {
6158       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6159       DrawLevelField(xx, yy);
6160     }
6161     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6162     {
6163       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6164       DrawLevelField(xx, yy);
6165     }
6166     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6167     {
6168       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6169       DrawLevelField(xx, yy);
6170     }
6171 #endif
6172     else if (element == EL_SWITCHGATE_OPEN ||
6173              element == EL_SWITCHGATE_OPENING)
6174     {
6175       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6176
6177       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6178     }
6179     else if (element == EL_SWITCHGATE_CLOSED ||
6180              element == EL_SWITCHGATE_CLOSING)
6181     {
6182       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6183
6184       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6185     }
6186   }
6187 }
6188
6189 static int getInvisibleActiveFromInvisibleElement(int element)
6190 {
6191   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6192           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6193           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6194           element);
6195 }
6196
6197 static int getInvisibleFromInvisibleActiveElement(int element)
6198 {
6199   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6200           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6201           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6202           element);
6203 }
6204
6205 static void RedrawAllLightSwitchesAndInvisibleElements()
6206 {
6207   int x, y;
6208
6209   SCAN_PLAYFIELD(x, y)
6210   {
6211     int element = Feld[x][y];
6212
6213     if (element == EL_LIGHT_SWITCH &&
6214         game.light_time_left > 0)
6215     {
6216       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6217       DrawLevelField(x, y);
6218     }
6219     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6220              game.light_time_left == 0)
6221     {
6222       Feld[x][y] = EL_LIGHT_SWITCH;
6223       DrawLevelField(x, y);
6224     }
6225     else if (element == EL_EMC_DRIPPER &&
6226              game.light_time_left > 0)
6227     {
6228       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6229       DrawLevelField(x, y);
6230     }
6231     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6232              game.light_time_left == 0)
6233     {
6234       Feld[x][y] = EL_EMC_DRIPPER;
6235       DrawLevelField(x, y);
6236     }
6237     else if (element == EL_INVISIBLE_STEELWALL ||
6238              element == EL_INVISIBLE_WALL ||
6239              element == EL_INVISIBLE_SAND)
6240     {
6241       if (game.light_time_left > 0)
6242         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6243
6244       DrawLevelField(x, y);
6245
6246       /* uncrumble neighbour fields, if needed */
6247       if (element == EL_INVISIBLE_SAND)
6248         DrawLevelFieldCrumbledSandNeighbours(x, y);
6249     }
6250     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6251              element == EL_INVISIBLE_WALL_ACTIVE ||
6252              element == EL_INVISIBLE_SAND_ACTIVE)
6253     {
6254       if (game.light_time_left == 0)
6255         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6256
6257       DrawLevelField(x, y);
6258
6259       /* re-crumble neighbour fields, if needed */
6260       if (element == EL_INVISIBLE_SAND)
6261         DrawLevelFieldCrumbledSandNeighbours(x, y);
6262     }
6263   }
6264 }
6265
6266 static void RedrawAllInvisibleElementsForLenses()
6267 {
6268   int x, y;
6269
6270   SCAN_PLAYFIELD(x, y)
6271   {
6272     int element = Feld[x][y];
6273
6274     if (element == EL_EMC_DRIPPER &&
6275         game.lenses_time_left > 0)
6276     {
6277       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6278       DrawLevelField(x, y);
6279     }
6280     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6281              game.lenses_time_left == 0)
6282     {
6283       Feld[x][y] = EL_EMC_DRIPPER;
6284       DrawLevelField(x, y);
6285     }
6286     else if (element == EL_INVISIBLE_STEELWALL ||
6287              element == EL_INVISIBLE_WALL ||
6288              element == EL_INVISIBLE_SAND)
6289     {
6290       if (game.lenses_time_left > 0)
6291         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6292
6293       DrawLevelField(x, y);
6294
6295       /* uncrumble neighbour fields, if needed */
6296       if (element == EL_INVISIBLE_SAND)
6297         DrawLevelFieldCrumbledSandNeighbours(x, y);
6298     }
6299     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6300              element == EL_INVISIBLE_WALL_ACTIVE ||
6301              element == EL_INVISIBLE_SAND_ACTIVE)
6302     {
6303       if (game.lenses_time_left == 0)
6304         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6305
6306       DrawLevelField(x, y);
6307
6308       /* re-crumble neighbour fields, if needed */
6309       if (element == EL_INVISIBLE_SAND)
6310         DrawLevelFieldCrumbledSandNeighbours(x, y);
6311     }
6312   }
6313 }
6314
6315 static void RedrawAllInvisibleElementsForMagnifier()
6316 {
6317   int x, y;
6318
6319   SCAN_PLAYFIELD(x, y)
6320   {
6321     int element = Feld[x][y];
6322
6323     if (element == EL_EMC_FAKE_GRASS &&
6324         game.magnify_time_left > 0)
6325     {
6326       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6327       DrawLevelField(x, y);
6328     }
6329     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6330              game.magnify_time_left == 0)
6331     {
6332       Feld[x][y] = EL_EMC_FAKE_GRASS;
6333       DrawLevelField(x, y);
6334     }
6335     else if (IS_GATE_GRAY(element) &&
6336              game.magnify_time_left > 0)
6337     {
6338       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6339                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6340                     IS_EM_GATE_GRAY(element) ?
6341                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6342                     IS_EMC_GATE_GRAY(element) ?
6343                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6344                     element);
6345       DrawLevelField(x, y);
6346     }
6347     else if (IS_GATE_GRAY_ACTIVE(element) &&
6348              game.magnify_time_left == 0)
6349     {
6350       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6351                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6352                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6353                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6354                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6355                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6356                     element);
6357       DrawLevelField(x, y);
6358     }
6359   }
6360 }
6361
6362 static void ToggleLightSwitch(int x, int y)
6363 {
6364   int element = Feld[x][y];
6365
6366   game.light_time_left =
6367     (element == EL_LIGHT_SWITCH ?
6368      level.time_light * FRAMES_PER_SECOND : 0);
6369
6370   RedrawAllLightSwitchesAndInvisibleElements();
6371 }
6372
6373 static void ActivateTimegateSwitch(int x, int y)
6374 {
6375   int xx, yy;
6376
6377   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6378
6379   SCAN_PLAYFIELD(xx, yy)
6380   {
6381     int element = Feld[xx][yy];
6382
6383     if (element == EL_TIMEGATE_CLOSED ||
6384         element == EL_TIMEGATE_CLOSING)
6385     {
6386       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6387       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6388     }
6389
6390     /*
6391     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6392     {
6393       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6394       DrawLevelField(xx, yy);
6395     }
6396     */
6397
6398   }
6399
6400 #if 1
6401   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6402                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6403 #else
6404   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6405 #endif
6406 }
6407
6408 void Impact(int x, int y)
6409 {
6410   boolean last_line = (y == lev_fieldy - 1);
6411   boolean object_hit = FALSE;
6412   boolean impact = (last_line || object_hit);
6413   int element = Feld[x][y];
6414   int smashed = EL_STEELWALL;
6415
6416   if (!last_line)       /* check if element below was hit */
6417   {
6418     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6419       return;
6420
6421     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6422                                          MovDir[x][y + 1] != MV_DOWN ||
6423                                          MovPos[x][y + 1] <= TILEY / 2));
6424
6425     /* do not smash moving elements that left the smashed field in time */
6426     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6427         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6428       object_hit = FALSE;
6429
6430 #if USE_QUICKSAND_IMPACT_BUGFIX
6431     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6432     {
6433       RemoveMovingField(x, y + 1);
6434       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6435       Feld[x][y + 2] = EL_ROCK;
6436       DrawLevelField(x, y + 2);
6437
6438       object_hit = TRUE;
6439     }
6440
6441     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6442     {
6443       RemoveMovingField(x, y + 1);
6444       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6445       Feld[x][y + 2] = EL_ROCK;
6446       DrawLevelField(x, y + 2);
6447
6448       object_hit = TRUE;
6449     }
6450 #endif
6451
6452     if (object_hit)
6453       smashed = MovingOrBlocked2Element(x, y + 1);
6454
6455     impact = (last_line || object_hit);
6456   }
6457
6458   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6459   {
6460     SplashAcid(x, y + 1);
6461     return;
6462   }
6463
6464   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6465   /* only reset graphic animation if graphic really changes after impact */
6466   if (impact &&
6467       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6468   {
6469     ResetGfxAnimation(x, y);
6470     DrawLevelField(x, y);
6471   }
6472
6473   if (impact && CAN_EXPLODE_IMPACT(element))
6474   {
6475     Bang(x, y);
6476     return;
6477   }
6478   else if (impact && element == EL_PEARL &&
6479            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6480   {
6481     ResetGfxAnimation(x, y);
6482
6483     Feld[x][y] = EL_PEARL_BREAKING;
6484     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6485     return;
6486   }
6487   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6488   {
6489     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6490
6491     return;
6492   }
6493
6494   if (impact && element == EL_AMOEBA_DROP)
6495   {
6496     if (object_hit && IS_PLAYER(x, y + 1))
6497       KillPlayerUnlessEnemyProtected(x, y + 1);
6498     else if (object_hit && smashed == EL_PENGUIN)
6499       Bang(x, y + 1);
6500     else
6501     {
6502       Feld[x][y] = EL_AMOEBA_GROWING;
6503       Store[x][y] = EL_AMOEBA_WET;
6504
6505       ResetRandomAnimationValue(x, y);
6506     }
6507     return;
6508   }
6509
6510   if (object_hit)               /* check which object was hit */
6511   {
6512     if ((CAN_PASS_MAGIC_WALL(element) && 
6513          (smashed == EL_MAGIC_WALL ||
6514           smashed == EL_BD_MAGIC_WALL)) ||
6515         (CAN_PASS_DC_MAGIC_WALL(element) &&
6516          smashed == EL_DC_MAGIC_WALL))
6517     {
6518       int xx, yy;
6519       int activated_magic_wall =
6520         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6521          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6522          EL_DC_MAGIC_WALL_ACTIVE);
6523
6524       /* activate magic wall / mill */
6525       SCAN_PLAYFIELD(xx, yy)
6526       {
6527         if (Feld[xx][yy] == smashed)
6528           Feld[xx][yy] = activated_magic_wall;
6529       }
6530
6531       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6532       game.magic_wall_active = TRUE;
6533
6534       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6535                             SND_MAGIC_WALL_ACTIVATING :
6536                             smashed == EL_BD_MAGIC_WALL ?
6537                             SND_BD_MAGIC_WALL_ACTIVATING :
6538                             SND_DC_MAGIC_WALL_ACTIVATING));
6539     }
6540
6541     if (IS_PLAYER(x, y + 1))
6542     {
6543       if (CAN_SMASH_PLAYER(element))
6544       {
6545         KillPlayerUnlessEnemyProtected(x, y + 1);
6546         return;
6547       }
6548     }
6549     else if (smashed == EL_PENGUIN)
6550     {
6551       if (CAN_SMASH_PLAYER(element))
6552       {
6553         Bang(x, y + 1);
6554         return;
6555       }
6556     }
6557     else if (element == EL_BD_DIAMOND)
6558     {
6559       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6560       {
6561         Bang(x, y + 1);
6562         return;
6563       }
6564     }
6565     else if (((element == EL_SP_INFOTRON ||
6566                element == EL_SP_ZONK) &&
6567               (smashed == EL_SP_SNIKSNAK ||
6568                smashed == EL_SP_ELECTRON ||
6569                smashed == EL_SP_DISK_ORANGE)) ||
6570              (element == EL_SP_INFOTRON &&
6571               smashed == EL_SP_DISK_YELLOW))
6572     {
6573       Bang(x, y + 1);
6574       return;
6575     }
6576     else if (CAN_SMASH_EVERYTHING(element))
6577     {
6578       if (IS_CLASSIC_ENEMY(smashed) ||
6579           CAN_EXPLODE_SMASHED(smashed))
6580       {
6581         Bang(x, y + 1);
6582         return;
6583       }
6584       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6585       {
6586         if (smashed == EL_LAMP ||
6587             smashed == EL_LAMP_ACTIVE)
6588         {
6589           Bang(x, y + 1);
6590           return;
6591         }
6592         else if (smashed == EL_NUT)
6593         {
6594           Feld[x][y + 1] = EL_NUT_BREAKING;
6595           PlayLevelSound(x, y, SND_NUT_BREAKING);
6596           RaiseScoreElement(EL_NUT);
6597           return;
6598         }
6599         else if (smashed == EL_PEARL)
6600         {
6601           ResetGfxAnimation(x, y);
6602
6603           Feld[x][y + 1] = EL_PEARL_BREAKING;
6604           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6605           return;
6606         }
6607         else if (smashed == EL_DIAMOND)
6608         {
6609           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6610           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6611           return;
6612         }
6613         else if (IS_BELT_SWITCH(smashed))
6614         {
6615           ToggleBeltSwitch(x, y + 1);
6616         }
6617         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6618                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6619                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6620                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6621         {
6622           ToggleSwitchgateSwitch(x, y + 1);
6623         }
6624         else if (smashed == EL_LIGHT_SWITCH ||
6625                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6626         {
6627           ToggleLightSwitch(x, y + 1);
6628         }
6629         else
6630         {
6631 #if 0
6632           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6633 #endif
6634
6635           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6636
6637           CheckElementChangeBySide(x, y + 1, smashed, element,
6638                                    CE_SWITCHED, CH_SIDE_TOP);
6639           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6640                                             CH_SIDE_TOP);
6641         }
6642       }
6643       else
6644       {
6645         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6646       }
6647     }
6648   }
6649
6650   /* play sound of magic wall / mill */
6651   if (!last_line &&
6652       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6653        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6654        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6655   {
6656     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6657       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6658     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6659       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6660     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6661       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6662
6663     return;
6664   }
6665
6666   /* play sound of object that hits the ground */
6667   if (last_line || object_hit)
6668     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6669 }
6670
6671 inline static void TurnRoundExt(int x, int y)
6672 {
6673   static struct
6674   {
6675     int dx, dy;
6676   } move_xy[] =
6677   {
6678     {  0,  0 },
6679     { -1,  0 },
6680     { +1,  0 },
6681     {  0,  0 },
6682     {  0, -1 },
6683     {  0,  0 }, { 0, 0 }, { 0, 0 },
6684     {  0, +1 }
6685   };
6686   static struct
6687   {
6688     int left, right, back;
6689   } turn[] =
6690   {
6691     { 0,        0,              0        },
6692     { MV_DOWN,  MV_UP,          MV_RIGHT },
6693     { MV_UP,    MV_DOWN,        MV_LEFT  },
6694     { 0,        0,              0        },
6695     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6696     { 0,        0,              0        },
6697     { 0,        0,              0        },
6698     { 0,        0,              0        },
6699     { MV_RIGHT, MV_LEFT,        MV_UP    }
6700   };
6701
6702   int element = Feld[x][y];
6703   int move_pattern = element_info[element].move_pattern;
6704
6705   int old_move_dir = MovDir[x][y];
6706   int left_dir  = turn[old_move_dir].left;
6707   int right_dir = turn[old_move_dir].right;
6708   int back_dir  = turn[old_move_dir].back;
6709
6710   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6711   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6712   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6713   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6714
6715   int left_x  = x + left_dx,  left_y  = y + left_dy;
6716   int right_x = x + right_dx, right_y = y + right_dy;
6717   int move_x  = x + move_dx,  move_y  = y + move_dy;
6718
6719   int xx, yy;
6720
6721   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6722   {
6723     TestIfBadThingTouchesOtherBadThing(x, y);
6724
6725     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6726       MovDir[x][y] = right_dir;
6727     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6728       MovDir[x][y] = left_dir;
6729
6730     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6731       MovDelay[x][y] = 9;
6732     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6733       MovDelay[x][y] = 1;
6734   }
6735   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6736   {
6737     TestIfBadThingTouchesOtherBadThing(x, y);
6738
6739     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6740       MovDir[x][y] = left_dir;
6741     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6742       MovDir[x][y] = right_dir;
6743
6744     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6745       MovDelay[x][y] = 9;
6746     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6747       MovDelay[x][y] = 1;
6748   }
6749   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6750   {
6751     TestIfBadThingTouchesOtherBadThing(x, y);
6752
6753     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6754       MovDir[x][y] = left_dir;
6755     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6756       MovDir[x][y] = right_dir;
6757
6758     if (MovDir[x][y] != old_move_dir)
6759       MovDelay[x][y] = 9;
6760   }
6761   else if (element == EL_YAMYAM)
6762   {
6763     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6764     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6765
6766     if (can_turn_left && can_turn_right)
6767       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6768     else if (can_turn_left)
6769       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6770     else if (can_turn_right)
6771       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6772     else
6773       MovDir[x][y] = back_dir;
6774
6775     MovDelay[x][y] = 16 + 16 * RND(3);
6776   }
6777   else if (element == EL_DARK_YAMYAM)
6778   {
6779     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6780                                                          left_x, left_y);
6781     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6782                                                          right_x, right_y);
6783
6784     if (can_turn_left && can_turn_right)
6785       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6786     else if (can_turn_left)
6787       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6788     else if (can_turn_right)
6789       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6790     else
6791       MovDir[x][y] = back_dir;
6792
6793     MovDelay[x][y] = 16 + 16 * RND(3);
6794   }
6795   else if (element == EL_PACMAN)
6796   {
6797     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6798     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6799
6800     if (can_turn_left && can_turn_right)
6801       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6802     else if (can_turn_left)
6803       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6804     else if (can_turn_right)
6805       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6806     else
6807       MovDir[x][y] = back_dir;
6808
6809     MovDelay[x][y] = 6 + RND(40);
6810   }
6811   else if (element == EL_PIG)
6812   {
6813     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6814     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6815     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6816     boolean should_turn_left, should_turn_right, should_move_on;
6817     int rnd_value = 24;
6818     int rnd = RND(rnd_value);
6819
6820     should_turn_left = (can_turn_left &&
6821                         (!can_move_on ||
6822                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6823                                                    y + back_dy + left_dy)));
6824     should_turn_right = (can_turn_right &&
6825                          (!can_move_on ||
6826                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6827                                                     y + back_dy + right_dy)));
6828     should_move_on = (can_move_on &&
6829                       (!can_turn_left ||
6830                        !can_turn_right ||
6831                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6832                                                  y + move_dy + left_dy) ||
6833                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6834                                                  y + move_dy + right_dy)));
6835
6836     if (should_turn_left || should_turn_right || should_move_on)
6837     {
6838       if (should_turn_left && should_turn_right && should_move_on)
6839         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6840                         rnd < 2 * rnd_value / 3 ? right_dir :
6841                         old_move_dir);
6842       else if (should_turn_left && should_turn_right)
6843         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6844       else if (should_turn_left && should_move_on)
6845         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6846       else if (should_turn_right && should_move_on)
6847         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6848       else if (should_turn_left)
6849         MovDir[x][y] = left_dir;
6850       else if (should_turn_right)
6851         MovDir[x][y] = right_dir;
6852       else if (should_move_on)
6853         MovDir[x][y] = old_move_dir;
6854     }
6855     else if (can_move_on && rnd > rnd_value / 8)
6856       MovDir[x][y] = old_move_dir;
6857     else if (can_turn_left && can_turn_right)
6858       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6859     else if (can_turn_left && rnd > rnd_value / 8)
6860       MovDir[x][y] = left_dir;
6861     else if (can_turn_right && rnd > rnd_value/8)
6862       MovDir[x][y] = right_dir;
6863     else
6864       MovDir[x][y] = back_dir;
6865
6866     xx = x + move_xy[MovDir[x][y]].dx;
6867     yy = y + move_xy[MovDir[x][y]].dy;
6868
6869     if (!IN_LEV_FIELD(xx, yy) ||
6870         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6871       MovDir[x][y] = old_move_dir;
6872
6873     MovDelay[x][y] = 0;
6874   }
6875   else if (element == EL_DRAGON)
6876   {
6877     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6878     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6879     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6880     int rnd_value = 24;
6881     int rnd = RND(rnd_value);
6882
6883     if (can_move_on && rnd > rnd_value / 8)
6884       MovDir[x][y] = old_move_dir;
6885     else if (can_turn_left && can_turn_right)
6886       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6887     else if (can_turn_left && rnd > rnd_value / 8)
6888       MovDir[x][y] = left_dir;
6889     else if (can_turn_right && rnd > rnd_value / 8)
6890       MovDir[x][y] = right_dir;
6891     else
6892       MovDir[x][y] = back_dir;
6893
6894     xx = x + move_xy[MovDir[x][y]].dx;
6895     yy = y + move_xy[MovDir[x][y]].dy;
6896
6897     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6898       MovDir[x][y] = old_move_dir;
6899
6900     MovDelay[x][y] = 0;
6901   }
6902   else if (element == EL_MOLE)
6903   {
6904     boolean can_move_on =
6905       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6906                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6907                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6908     if (!can_move_on)
6909     {
6910       boolean can_turn_left =
6911         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6912                               IS_AMOEBOID(Feld[left_x][left_y])));
6913
6914       boolean can_turn_right =
6915         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6916                               IS_AMOEBOID(Feld[right_x][right_y])));
6917
6918       if (can_turn_left && can_turn_right)
6919         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6920       else if (can_turn_left)
6921         MovDir[x][y] = left_dir;
6922       else
6923         MovDir[x][y] = right_dir;
6924     }
6925
6926     if (MovDir[x][y] != old_move_dir)
6927       MovDelay[x][y] = 9;
6928   }
6929   else if (element == EL_BALLOON)
6930   {
6931     MovDir[x][y] = game.wind_direction;
6932     MovDelay[x][y] = 0;
6933   }
6934   else if (element == EL_SPRING)
6935   {
6936 #if USE_NEW_SPRING_BUMPER
6937     if (MovDir[x][y] & MV_HORIZONTAL)
6938     {
6939       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6940           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6941       {
6942         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6943         ResetGfxAnimation(move_x, move_y);
6944         DrawLevelField(move_x, move_y);
6945
6946         MovDir[x][y] = back_dir;
6947       }
6948       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6949                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6950         MovDir[x][y] = MV_NONE;
6951     }
6952 #else
6953     if (MovDir[x][y] & MV_HORIZONTAL &&
6954         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6955          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6956       MovDir[x][y] = MV_NONE;
6957 #endif
6958
6959     MovDelay[x][y] = 0;
6960   }
6961   else if (element == EL_ROBOT ||
6962            element == EL_SATELLITE ||
6963            element == EL_PENGUIN ||
6964            element == EL_EMC_ANDROID)
6965   {
6966     int attr_x = -1, attr_y = -1;
6967
6968     if (AllPlayersGone)
6969     {
6970       attr_x = ExitX;
6971       attr_y = ExitY;
6972     }
6973     else
6974     {
6975       int i;
6976
6977       for (i = 0; i < MAX_PLAYERS; i++)
6978       {
6979         struct PlayerInfo *player = &stored_player[i];
6980         int jx = player->jx, jy = player->jy;
6981
6982         if (!player->active)
6983           continue;
6984
6985         if (attr_x == -1 ||
6986             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6987         {
6988           attr_x = jx;
6989           attr_y = jy;
6990         }
6991       }
6992     }
6993
6994     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6995         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6996          game.engine_version < VERSION_IDENT(3,1,0,0)))
6997     {
6998       attr_x = ZX;
6999       attr_y = ZY;
7000     }
7001
7002     if (element == EL_PENGUIN)
7003     {
7004       int i;
7005       static int xy[4][2] =
7006       {
7007         { 0, -1 },
7008         { -1, 0 },
7009         { +1, 0 },
7010         { 0, +1 }
7011       };
7012
7013       for (i = 0; i < NUM_DIRECTIONS; i++)
7014       {
7015         int ex = x + xy[i][0];
7016         int ey = y + xy[i][1];
7017
7018         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7019                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7020                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7021                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7022         {
7023           attr_x = ex;
7024           attr_y = ey;
7025           break;
7026         }
7027       }
7028     }
7029
7030     MovDir[x][y] = MV_NONE;
7031     if (attr_x < x)
7032       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7033     else if (attr_x > x)
7034       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7035     if (attr_y < y)
7036       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7037     else if (attr_y > y)
7038       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7039
7040     if (element == EL_ROBOT)
7041     {
7042       int newx, newy;
7043
7044       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7045         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7046       Moving2Blocked(x, y, &newx, &newy);
7047
7048       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7049         MovDelay[x][y] = 8 + 8 * !RND(3);
7050       else
7051         MovDelay[x][y] = 16;
7052     }
7053     else if (element == EL_PENGUIN)
7054     {
7055       int newx, newy;
7056
7057       MovDelay[x][y] = 1;
7058
7059       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7060       {
7061         boolean first_horiz = RND(2);
7062         int new_move_dir = MovDir[x][y];
7063
7064         MovDir[x][y] =
7065           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7066         Moving2Blocked(x, y, &newx, &newy);
7067
7068         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7069           return;
7070
7071         MovDir[x][y] =
7072           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7073         Moving2Blocked(x, y, &newx, &newy);
7074
7075         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7076           return;
7077
7078         MovDir[x][y] = old_move_dir;
7079         return;
7080       }
7081     }
7082     else if (element == EL_SATELLITE)
7083     {
7084       int newx, newy;
7085
7086       MovDelay[x][y] = 1;
7087
7088       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7089       {
7090         boolean first_horiz = RND(2);
7091         int new_move_dir = MovDir[x][y];
7092
7093         MovDir[x][y] =
7094           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7095         Moving2Blocked(x, y, &newx, &newy);
7096
7097         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7098           return;
7099
7100         MovDir[x][y] =
7101           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7102         Moving2Blocked(x, y, &newx, &newy);
7103
7104         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7105           return;
7106
7107         MovDir[x][y] = old_move_dir;
7108         return;
7109       }
7110     }
7111     else if (element == EL_EMC_ANDROID)
7112     {
7113       static int check_pos[16] =
7114       {
7115         -1,             /*  0 => (invalid)          */
7116         7,              /*  1 => MV_LEFT            */
7117         3,              /*  2 => MV_RIGHT           */
7118         -1,             /*  3 => (invalid)          */
7119         1,              /*  4 =>            MV_UP   */
7120         0,              /*  5 => MV_LEFT  | MV_UP   */
7121         2,              /*  6 => MV_RIGHT | MV_UP   */
7122         -1,             /*  7 => (invalid)          */
7123         5,              /*  8 =>            MV_DOWN */
7124         6,              /*  9 => MV_LEFT  | MV_DOWN */
7125         4,              /* 10 => MV_RIGHT | MV_DOWN */
7126         -1,             /* 11 => (invalid)          */
7127         -1,             /* 12 => (invalid)          */
7128         -1,             /* 13 => (invalid)          */
7129         -1,             /* 14 => (invalid)          */
7130         -1,             /* 15 => (invalid)          */
7131       };
7132       static struct
7133       {
7134         int dx, dy;
7135         int dir;
7136       } check_xy[8] =
7137       {
7138         { -1, -1,       MV_LEFT  | MV_UP   },
7139         {  0, -1,                  MV_UP   },
7140         { +1, -1,       MV_RIGHT | MV_UP   },
7141         { +1,  0,       MV_RIGHT           },
7142         { +1, +1,       MV_RIGHT | MV_DOWN },
7143         {  0, +1,                  MV_DOWN },
7144         { -1, +1,       MV_LEFT  | MV_DOWN },
7145         { -1,  0,       MV_LEFT            },
7146       };
7147       int start_pos, check_order;
7148       boolean can_clone = FALSE;
7149       int i;
7150
7151       /* check if there is any free field around current position */
7152       for (i = 0; i < 8; i++)
7153       {
7154         int newx = x + check_xy[i].dx;
7155         int newy = y + check_xy[i].dy;
7156
7157         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7158         {
7159           can_clone = TRUE;
7160
7161           break;
7162         }
7163       }
7164
7165       if (can_clone)            /* randomly find an element to clone */
7166       {
7167         can_clone = FALSE;
7168
7169         start_pos = check_pos[RND(8)];
7170         check_order = (RND(2) ? -1 : +1);
7171
7172         for (i = 0; i < 8; i++)
7173         {
7174           int pos_raw = start_pos + i * check_order;
7175           int pos = (pos_raw + 8) % 8;
7176           int newx = x + check_xy[pos].dx;
7177           int newy = y + check_xy[pos].dy;
7178
7179           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7180           {
7181             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7182             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7183
7184             Store[x][y] = Feld[newx][newy];
7185
7186             can_clone = TRUE;
7187
7188             break;
7189           }
7190         }
7191       }
7192
7193       if (can_clone)            /* randomly find a direction to move */
7194       {
7195         can_clone = FALSE;
7196
7197         start_pos = check_pos[RND(8)];
7198         check_order = (RND(2) ? -1 : +1);
7199
7200         for (i = 0; i < 8; i++)
7201         {
7202           int pos_raw = start_pos + i * check_order;
7203           int pos = (pos_raw + 8) % 8;
7204           int newx = x + check_xy[pos].dx;
7205           int newy = y + check_xy[pos].dy;
7206           int new_move_dir = check_xy[pos].dir;
7207
7208           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7209           {
7210             MovDir[x][y] = new_move_dir;
7211             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7212
7213             can_clone = TRUE;
7214
7215             break;
7216           }
7217         }
7218       }
7219
7220       if (can_clone)            /* cloning and moving successful */
7221         return;
7222
7223       /* cannot clone -- try to move towards player */
7224
7225       start_pos = check_pos[MovDir[x][y] & 0x0f];
7226       check_order = (RND(2) ? -1 : +1);
7227
7228       for (i = 0; i < 3; i++)
7229       {
7230         /* first check start_pos, then previous/next or (next/previous) pos */
7231         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7232         int pos = (pos_raw + 8) % 8;
7233         int newx = x + check_xy[pos].dx;
7234         int newy = y + check_xy[pos].dy;
7235         int new_move_dir = check_xy[pos].dir;
7236
7237         if (IS_PLAYER(newx, newy))
7238           break;
7239
7240         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7241         {
7242           MovDir[x][y] = new_move_dir;
7243           MovDelay[x][y] = level.android_move_time * 8 + 1;
7244
7245           break;
7246         }
7247       }
7248     }
7249   }
7250   else if (move_pattern == MV_TURNING_LEFT ||
7251            move_pattern == MV_TURNING_RIGHT ||
7252            move_pattern == MV_TURNING_LEFT_RIGHT ||
7253            move_pattern == MV_TURNING_RIGHT_LEFT ||
7254            move_pattern == MV_TURNING_RANDOM ||
7255            move_pattern == MV_ALL_DIRECTIONS)
7256   {
7257     boolean can_turn_left =
7258       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7259     boolean can_turn_right =
7260       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7261
7262     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7263       return;
7264
7265     if (move_pattern == MV_TURNING_LEFT)
7266       MovDir[x][y] = left_dir;
7267     else if (move_pattern == MV_TURNING_RIGHT)
7268       MovDir[x][y] = right_dir;
7269     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7270       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7271     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7272       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7273     else if (move_pattern == MV_TURNING_RANDOM)
7274       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7275                       can_turn_right && !can_turn_left ? right_dir :
7276                       RND(2) ? left_dir : right_dir);
7277     else if (can_turn_left && can_turn_right)
7278       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7279     else if (can_turn_left)
7280       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7281     else if (can_turn_right)
7282       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7283     else
7284       MovDir[x][y] = back_dir;
7285
7286     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7287   }
7288   else if (move_pattern == MV_HORIZONTAL ||
7289            move_pattern == MV_VERTICAL)
7290   {
7291     if (move_pattern & old_move_dir)
7292       MovDir[x][y] = back_dir;
7293     else if (move_pattern == MV_HORIZONTAL)
7294       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7295     else if (move_pattern == MV_VERTICAL)
7296       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7297
7298     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7299   }
7300   else if (move_pattern & MV_ANY_DIRECTION)
7301   {
7302     MovDir[x][y] = move_pattern;
7303     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7304   }
7305   else if (move_pattern & MV_WIND_DIRECTION)
7306   {
7307     MovDir[x][y] = game.wind_direction;
7308     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7309   }
7310   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7311   {
7312     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7313       MovDir[x][y] = left_dir;
7314     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7315       MovDir[x][y] = right_dir;
7316
7317     if (MovDir[x][y] != old_move_dir)
7318       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7319   }
7320   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7321   {
7322     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7323       MovDir[x][y] = right_dir;
7324     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7325       MovDir[x][y] = left_dir;
7326
7327     if (MovDir[x][y] != old_move_dir)
7328       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7329   }
7330   else if (move_pattern == MV_TOWARDS_PLAYER ||
7331            move_pattern == MV_AWAY_FROM_PLAYER)
7332   {
7333     int attr_x = -1, attr_y = -1;
7334     int newx, newy;
7335     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7336
7337     if (AllPlayersGone)
7338     {
7339       attr_x = ExitX;
7340       attr_y = ExitY;
7341     }
7342     else
7343     {
7344       int i;
7345
7346       for (i = 0; i < MAX_PLAYERS; i++)
7347       {
7348         struct PlayerInfo *player = &stored_player[i];
7349         int jx = player->jx, jy = player->jy;
7350
7351         if (!player->active)
7352           continue;
7353
7354         if (attr_x == -1 ||
7355             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7356         {
7357           attr_x = jx;
7358           attr_y = jy;
7359         }
7360       }
7361     }
7362
7363     MovDir[x][y] = MV_NONE;
7364     if (attr_x < x)
7365       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7366     else if (attr_x > x)
7367       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7368     if (attr_y < y)
7369       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7370     else if (attr_y > y)
7371       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7372
7373     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7374
7375     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7376     {
7377       boolean first_horiz = RND(2);
7378       int new_move_dir = MovDir[x][y];
7379
7380       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7381       {
7382         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7383         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7384
7385         return;
7386       }
7387
7388       MovDir[x][y] =
7389         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7390       Moving2Blocked(x, y, &newx, &newy);
7391
7392       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7393         return;
7394
7395       MovDir[x][y] =
7396         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7397       Moving2Blocked(x, y, &newx, &newy);
7398
7399       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7400         return;
7401
7402       MovDir[x][y] = old_move_dir;
7403     }
7404   }
7405   else if (move_pattern == MV_WHEN_PUSHED ||
7406            move_pattern == MV_WHEN_DROPPED)
7407   {
7408     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7409       MovDir[x][y] = MV_NONE;
7410
7411     MovDelay[x][y] = 0;
7412   }
7413   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7414   {
7415     static int test_xy[7][2] =
7416     {
7417       { 0, -1 },
7418       { -1, 0 },
7419       { +1, 0 },
7420       { 0, +1 },
7421       { 0, -1 },
7422       { -1, 0 },
7423       { +1, 0 },
7424     };
7425     static int test_dir[7] =
7426     {
7427       MV_UP,
7428       MV_LEFT,
7429       MV_RIGHT,
7430       MV_DOWN,
7431       MV_UP,
7432       MV_LEFT,
7433       MV_RIGHT,
7434     };
7435     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7436     int move_preference = -1000000;     /* start with very low preference */
7437     int new_move_dir = MV_NONE;
7438     int start_test = RND(4);
7439     int i;
7440
7441     for (i = 0; i < NUM_DIRECTIONS; i++)
7442     {
7443       int move_dir = test_dir[start_test + i];
7444       int move_dir_preference;
7445
7446       xx = x + test_xy[start_test + i][0];
7447       yy = y + test_xy[start_test + i][1];
7448
7449       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7450           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7451       {
7452         new_move_dir = move_dir;
7453
7454         break;
7455       }
7456
7457       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7458         continue;
7459
7460       move_dir_preference = -1 * RunnerVisit[xx][yy];
7461       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7462         move_dir_preference = PlayerVisit[xx][yy];
7463
7464       if (move_dir_preference > move_preference)
7465       {
7466         /* prefer field that has not been visited for the longest time */
7467         move_preference = move_dir_preference;
7468         new_move_dir = move_dir;
7469       }
7470       else if (move_dir_preference == move_preference &&
7471                move_dir == old_move_dir)
7472       {
7473         /* prefer last direction when all directions are preferred equally */
7474         move_preference = move_dir_preference;
7475         new_move_dir = move_dir;
7476       }
7477     }
7478
7479     MovDir[x][y] = new_move_dir;
7480     if (old_move_dir != new_move_dir)
7481       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7482   }
7483 }
7484
7485 static void TurnRound(int x, int y)
7486 {
7487   int direction = MovDir[x][y];
7488
7489   TurnRoundExt(x, y);
7490
7491   GfxDir[x][y] = MovDir[x][y];
7492
7493   if (direction != MovDir[x][y])
7494     GfxFrame[x][y] = 0;
7495
7496   if (MovDelay[x][y])
7497     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7498
7499   ResetGfxFrame(x, y, FALSE);
7500 }
7501
7502 static boolean JustBeingPushed(int x, int y)
7503 {
7504   int i;
7505
7506   for (i = 0; i < MAX_PLAYERS; i++)
7507   {
7508     struct PlayerInfo *player = &stored_player[i];
7509
7510     if (player->active && player->is_pushing && player->MovPos)
7511     {
7512       int next_jx = player->jx + (player->jx - player->last_jx);
7513       int next_jy = player->jy + (player->jy - player->last_jy);
7514
7515       if (x == next_jx && y == next_jy)
7516         return TRUE;
7517     }
7518   }
7519
7520   return FALSE;
7521 }
7522
7523 void StartMoving(int x, int y)
7524 {
7525   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7526   int element = Feld[x][y];
7527
7528   if (Stop[x][y])
7529     return;
7530
7531   if (MovDelay[x][y] == 0)
7532     GfxAction[x][y] = ACTION_DEFAULT;
7533
7534   if (CAN_FALL(element) && y < lev_fieldy - 1)
7535   {
7536     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7537         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7538       if (JustBeingPushed(x, y))
7539         return;
7540
7541     if (element == EL_QUICKSAND_FULL)
7542     {
7543       if (IS_FREE(x, y + 1))
7544       {
7545         InitMovingField(x, y, MV_DOWN);
7546         started_moving = TRUE;
7547
7548         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7549 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7550         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7551           Store[x][y] = EL_ROCK;
7552 #else
7553         Store[x][y] = EL_ROCK;
7554 #endif
7555
7556         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7557       }
7558       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7559       {
7560         if (!MovDelay[x][y])
7561           MovDelay[x][y] = TILEY + 1;
7562
7563         if (MovDelay[x][y])
7564         {
7565           MovDelay[x][y]--;
7566           if (MovDelay[x][y])
7567             return;
7568         }
7569
7570         Feld[x][y] = EL_QUICKSAND_EMPTY;
7571         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7572         Store[x][y + 1] = Store[x][y];
7573         Store[x][y] = 0;
7574
7575         PlayLevelSoundAction(x, y, ACTION_FILLING);
7576       }
7577     }
7578     else if (element == EL_QUICKSAND_FAST_FULL)
7579     {
7580       if (IS_FREE(x, y + 1))
7581       {
7582         InitMovingField(x, y, MV_DOWN);
7583         started_moving = TRUE;
7584
7585         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7586 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7587         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7588           Store[x][y] = EL_ROCK;
7589 #else
7590         Store[x][y] = EL_ROCK;
7591 #endif
7592
7593         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7594       }
7595       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7596       {
7597         if (!MovDelay[x][y])
7598           MovDelay[x][y] = TILEY + 1;
7599
7600         if (MovDelay[x][y])
7601         {
7602           MovDelay[x][y]--;
7603           if (MovDelay[x][y])
7604             return;
7605         }
7606
7607         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7608         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7609         Store[x][y + 1] = Store[x][y];
7610         Store[x][y] = 0;
7611
7612         PlayLevelSoundAction(x, y, ACTION_FILLING);
7613       }
7614     }
7615     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7616              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7617     {
7618       InitMovingField(x, y, MV_DOWN);
7619       started_moving = TRUE;
7620
7621       Feld[x][y] = EL_QUICKSAND_FILLING;
7622       Store[x][y] = element;
7623
7624       PlayLevelSoundAction(x, y, ACTION_FILLING);
7625     }
7626     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7627              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7628     {
7629       InitMovingField(x, y, MV_DOWN);
7630       started_moving = TRUE;
7631
7632       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7633       Store[x][y] = element;
7634
7635       PlayLevelSoundAction(x, y, ACTION_FILLING);
7636     }
7637     else if (element == EL_MAGIC_WALL_FULL)
7638     {
7639       if (IS_FREE(x, y + 1))
7640       {
7641         InitMovingField(x, y, MV_DOWN);
7642         started_moving = TRUE;
7643
7644         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7645         Store[x][y] = EL_CHANGED(Store[x][y]);
7646       }
7647       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7648       {
7649         if (!MovDelay[x][y])
7650           MovDelay[x][y] = TILEY/4 + 1;
7651
7652         if (MovDelay[x][y])
7653         {
7654           MovDelay[x][y]--;
7655           if (MovDelay[x][y])
7656             return;
7657         }
7658
7659         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7660         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7661         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7662         Store[x][y] = 0;
7663       }
7664     }
7665     else if (element == EL_BD_MAGIC_WALL_FULL)
7666     {
7667       if (IS_FREE(x, y + 1))
7668       {
7669         InitMovingField(x, y, MV_DOWN);
7670         started_moving = TRUE;
7671
7672         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7673         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7674       }
7675       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7676       {
7677         if (!MovDelay[x][y])
7678           MovDelay[x][y] = TILEY/4 + 1;
7679
7680         if (MovDelay[x][y])
7681         {
7682           MovDelay[x][y]--;
7683           if (MovDelay[x][y])
7684             return;
7685         }
7686
7687         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7688         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7689         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7690         Store[x][y] = 0;
7691       }
7692     }
7693     else if (element == EL_DC_MAGIC_WALL_FULL)
7694     {
7695       if (IS_FREE(x, y + 1))
7696       {
7697         InitMovingField(x, y, MV_DOWN);
7698         started_moving = TRUE;
7699
7700         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7701         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7702       }
7703       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7704       {
7705         if (!MovDelay[x][y])
7706           MovDelay[x][y] = TILEY/4 + 1;
7707
7708         if (MovDelay[x][y])
7709         {
7710           MovDelay[x][y]--;
7711           if (MovDelay[x][y])
7712             return;
7713         }
7714
7715         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7716         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7717         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7718         Store[x][y] = 0;
7719       }
7720     }
7721     else if ((CAN_PASS_MAGIC_WALL(element) &&
7722               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7723                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7724              (CAN_PASS_DC_MAGIC_WALL(element) &&
7725               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7726
7727     {
7728       InitMovingField(x, y, MV_DOWN);
7729       started_moving = TRUE;
7730
7731       Feld[x][y] =
7732         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7733          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7734          EL_DC_MAGIC_WALL_FILLING);
7735       Store[x][y] = element;
7736     }
7737     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7738     {
7739       SplashAcid(x, y + 1);
7740
7741       InitMovingField(x, y, MV_DOWN);
7742       started_moving = TRUE;
7743
7744       Store[x][y] = EL_ACID;
7745     }
7746     else if (
7747 #if USE_FIX_IMPACT_COLLISION
7748              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7749               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7750 #else
7751              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7752               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7753 #endif
7754              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7755               CAN_FALL(element) && WasJustFalling[x][y] &&
7756               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7757
7758              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7759               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7760               (Feld[x][y + 1] == EL_BLOCKED)))
7761     {
7762       /* this is needed for a special case not covered by calling "Impact()"
7763          from "ContinueMoving()": if an element moves to a tile directly below
7764          another element which was just falling on that tile (which was empty
7765          in the previous frame), the falling element above would just stop
7766          instead of smashing the element below (in previous version, the above
7767          element was just checked for "moving" instead of "falling", resulting
7768          in incorrect smashes caused by horizontal movement of the above
7769          element; also, the case of the player being the element to smash was
7770          simply not covered here... :-/ ) */
7771
7772       CheckCollision[x][y] = 0;
7773       CheckImpact[x][y] = 0;
7774
7775       Impact(x, y);
7776     }
7777     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7778     {
7779       if (MovDir[x][y] == MV_NONE)
7780       {
7781         InitMovingField(x, y, MV_DOWN);
7782         started_moving = TRUE;
7783       }
7784     }
7785     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7786     {
7787       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7788         MovDir[x][y] = MV_DOWN;
7789
7790       InitMovingField(x, y, MV_DOWN);
7791       started_moving = TRUE;
7792     }
7793     else if (element == EL_AMOEBA_DROP)
7794     {
7795       Feld[x][y] = EL_AMOEBA_GROWING;
7796       Store[x][y] = EL_AMOEBA_WET;
7797     }
7798     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7799               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7800              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7801              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7802     {
7803       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7804                                 (IS_FREE(x - 1, y + 1) ||
7805                                  Feld[x - 1][y + 1] == EL_ACID));
7806       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7807                                 (IS_FREE(x + 1, y + 1) ||
7808                                  Feld[x + 1][y + 1] == EL_ACID));
7809       boolean can_fall_any  = (can_fall_left || can_fall_right);
7810       boolean can_fall_both = (can_fall_left && can_fall_right);
7811       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7812
7813 #if USE_NEW_ALL_SLIPPERY
7814       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7815       {
7816         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7817           can_fall_right = FALSE;
7818         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7819           can_fall_left = FALSE;
7820         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7821           can_fall_right = FALSE;
7822         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7823           can_fall_left = FALSE;
7824
7825         can_fall_any  = (can_fall_left || can_fall_right);
7826         can_fall_both = FALSE;
7827       }
7828 #else
7829       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7830       {
7831         if (slippery_type == SLIPPERY_ONLY_LEFT)
7832           can_fall_right = FALSE;
7833         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7834           can_fall_left = FALSE;
7835         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7836           can_fall_right = FALSE;
7837         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7838           can_fall_left = FALSE;
7839
7840         can_fall_any  = (can_fall_left || can_fall_right);
7841         can_fall_both = (can_fall_left && can_fall_right);
7842       }
7843 #endif
7844
7845 #if USE_NEW_ALL_SLIPPERY
7846 #else
7847 #if USE_NEW_SP_SLIPPERY
7848       /* !!! better use the same properties as for custom elements here !!! */
7849       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7850                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7851       {
7852         can_fall_right = FALSE;         /* slip down on left side */
7853         can_fall_both = FALSE;
7854       }
7855 #endif
7856 #endif
7857
7858 #if USE_NEW_ALL_SLIPPERY
7859       if (can_fall_both)
7860       {
7861         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7862           can_fall_right = FALSE;       /* slip down on left side */
7863         else
7864           can_fall_left = !(can_fall_right = RND(2));
7865
7866         can_fall_both = FALSE;
7867       }
7868 #else
7869       if (can_fall_both)
7870       {
7871         if (game.emulation == EMU_BOULDERDASH ||
7872             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7873           can_fall_right = FALSE;       /* slip down on left side */
7874         else
7875           can_fall_left = !(can_fall_right = RND(2));
7876
7877         can_fall_both = FALSE;
7878       }
7879 #endif
7880
7881       if (can_fall_any)
7882       {
7883         /* if not determined otherwise, prefer left side for slipping down */
7884         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7885         started_moving = TRUE;
7886       }
7887     }
7888 #if 0
7889     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7890 #else
7891     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7892 #endif
7893     {
7894       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7895       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7896       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7897       int belt_dir = game.belt_dir[belt_nr];
7898
7899       if ((belt_dir == MV_LEFT  && left_is_free) ||
7900           (belt_dir == MV_RIGHT && right_is_free))
7901       {
7902         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7903
7904         InitMovingField(x, y, belt_dir);
7905         started_moving = TRUE;
7906
7907         Pushed[x][y] = TRUE;
7908         Pushed[nextx][y] = TRUE;
7909
7910         GfxAction[x][y] = ACTION_DEFAULT;
7911       }
7912       else
7913       {
7914         MovDir[x][y] = 0;       /* if element was moving, stop it */
7915       }
7916     }
7917   }
7918
7919   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7920 #if 0
7921   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7922 #else
7923   if (CAN_MOVE(element) && !started_moving)
7924 #endif
7925   {
7926     int move_pattern = element_info[element].move_pattern;
7927     int newx, newy;
7928
7929 #if 0
7930 #if DEBUG
7931     if (MovDir[x][y] == MV_NONE)
7932     {
7933       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7934              x, y, element, element_info[element].token_name);
7935       printf("StartMoving(): This should never happen!\n");
7936     }
7937 #endif
7938 #endif
7939
7940     Moving2Blocked(x, y, &newx, &newy);
7941
7942     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7943       return;
7944
7945     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7946         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7947     {
7948       WasJustMoving[x][y] = 0;
7949       CheckCollision[x][y] = 0;
7950
7951       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7952
7953       if (Feld[x][y] != element)        /* element has changed */
7954         return;
7955     }
7956
7957     if (!MovDelay[x][y])        /* start new movement phase */
7958     {
7959       /* all objects that can change their move direction after each step
7960          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7961
7962       if (element != EL_YAMYAM &&
7963           element != EL_DARK_YAMYAM &&
7964           element != EL_PACMAN &&
7965           !(move_pattern & MV_ANY_DIRECTION) &&
7966           move_pattern != MV_TURNING_LEFT &&
7967           move_pattern != MV_TURNING_RIGHT &&
7968           move_pattern != MV_TURNING_LEFT_RIGHT &&
7969           move_pattern != MV_TURNING_RIGHT_LEFT &&
7970           move_pattern != MV_TURNING_RANDOM)
7971       {
7972         TurnRound(x, y);
7973
7974         if (MovDelay[x][y] && (element == EL_BUG ||
7975                                element == EL_SPACESHIP ||
7976                                element == EL_SP_SNIKSNAK ||
7977                                element == EL_SP_ELECTRON ||
7978                                element == EL_MOLE))
7979           DrawLevelField(x, y);
7980       }
7981     }
7982
7983     if (MovDelay[x][y])         /* wait some time before next movement */
7984     {
7985       MovDelay[x][y]--;
7986
7987       if (element == EL_ROBOT ||
7988           element == EL_YAMYAM ||
7989           element == EL_DARK_YAMYAM)
7990       {
7991         DrawLevelElementAnimationIfNeeded(x, y, element);
7992         PlayLevelSoundAction(x, y, ACTION_WAITING);
7993       }
7994       else if (element == EL_SP_ELECTRON)
7995         DrawLevelElementAnimationIfNeeded(x, y, element);
7996       else if (element == EL_DRAGON)
7997       {
7998         int i;
7999         int dir = MovDir[x][y];
8000         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8001         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8002         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8003                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8004                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8005                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8006         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8007
8008         GfxAction[x][y] = ACTION_ATTACKING;
8009
8010         if (IS_PLAYER(x, y))
8011           DrawPlayerField(x, y);
8012         else
8013           DrawLevelField(x, y);
8014
8015         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8016
8017         for (i = 1; i <= 3; i++)
8018         {
8019           int xx = x + i * dx;
8020           int yy = y + i * dy;
8021           int sx = SCREENX(xx);
8022           int sy = SCREENY(yy);
8023           int flame_graphic = graphic + (i - 1);
8024
8025           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8026             break;
8027
8028           if (MovDelay[x][y])
8029           {
8030             int flamed = MovingOrBlocked2Element(xx, yy);
8031
8032             /* !!! */
8033 #if 0
8034             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8035               Bang(xx, yy);
8036             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8037               RemoveMovingField(xx, yy);
8038             else
8039               RemoveField(xx, yy);
8040 #else
8041             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8042               Bang(xx, yy);
8043             else
8044               RemoveMovingField(xx, yy);
8045 #endif
8046
8047             ChangeDelay[xx][yy] = 0;
8048
8049             Feld[xx][yy] = EL_FLAMES;
8050
8051             if (IN_SCR_FIELD(sx, sy))
8052             {
8053               DrawLevelFieldCrumbledSand(xx, yy);
8054               DrawGraphic(sx, sy, flame_graphic, frame);
8055             }
8056           }
8057           else
8058           {
8059             if (Feld[xx][yy] == EL_FLAMES)
8060               Feld[xx][yy] = EL_EMPTY;
8061             DrawLevelField(xx, yy);
8062           }
8063         }
8064       }
8065
8066       if (MovDelay[x][y])       /* element still has to wait some time */
8067       {
8068         PlayLevelSoundAction(x, y, ACTION_WAITING);
8069
8070         return;
8071       }
8072     }
8073
8074     /* now make next step */
8075
8076     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8077
8078     if (DONT_COLLIDE_WITH(element) &&
8079         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8080         !PLAYER_ENEMY_PROTECTED(newx, newy))
8081     {
8082       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8083
8084       return;
8085     }
8086
8087     else if (CAN_MOVE_INTO_ACID(element) &&
8088              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8089              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8090              (MovDir[x][y] == MV_DOWN ||
8091               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8092     {
8093       SplashAcid(newx, newy);
8094       Store[x][y] = EL_ACID;
8095     }
8096     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8097     {
8098       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8099           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8100           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8101           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8102       {
8103         RemoveField(x, y);
8104         DrawLevelField(x, y);
8105
8106         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8107         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8108           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8109
8110         local_player->friends_still_needed--;
8111         if (!local_player->friends_still_needed &&
8112             !local_player->GameOver && AllPlayersGone)
8113           PlayerWins(local_player);
8114
8115         return;
8116       }
8117       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8118       {
8119         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8120           DrawLevelField(newx, newy);
8121         else
8122           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8123       }
8124       else if (!IS_FREE(newx, newy))
8125       {
8126         GfxAction[x][y] = ACTION_WAITING;
8127
8128         if (IS_PLAYER(x, y))
8129           DrawPlayerField(x, y);
8130         else
8131           DrawLevelField(x, y);
8132
8133         return;
8134       }
8135     }
8136     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8137     {
8138       if (IS_FOOD_PIG(Feld[newx][newy]))
8139       {
8140         if (IS_MOVING(newx, newy))
8141           RemoveMovingField(newx, newy);
8142         else
8143         {
8144           Feld[newx][newy] = EL_EMPTY;
8145           DrawLevelField(newx, newy);
8146         }
8147
8148         PlayLevelSound(x, y, SND_PIG_DIGGING);
8149       }
8150       else if (!IS_FREE(newx, newy))
8151       {
8152         if (IS_PLAYER(x, y))
8153           DrawPlayerField(x, y);
8154         else
8155           DrawLevelField(x, y);
8156
8157         return;
8158       }
8159     }
8160     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8161     {
8162       if (Store[x][y] != EL_EMPTY)
8163       {
8164         boolean can_clone = FALSE;
8165         int xx, yy;
8166
8167         /* check if element to clone is still there */
8168         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8169         {
8170           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8171           {
8172             can_clone = TRUE;
8173
8174             break;
8175           }
8176         }
8177
8178         /* cannot clone or target field not free anymore -- do not clone */
8179         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8180           Store[x][y] = EL_EMPTY;
8181       }
8182
8183       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8184       {
8185         if (IS_MV_DIAGONAL(MovDir[x][y]))
8186         {
8187           int diagonal_move_dir = MovDir[x][y];
8188           int stored = Store[x][y];
8189           int change_delay = 8;
8190           int graphic;
8191
8192           /* android is moving diagonally */
8193
8194           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8195
8196           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8197           GfxElement[x][y] = EL_EMC_ANDROID;
8198           GfxAction[x][y] = ACTION_SHRINKING;
8199           GfxDir[x][y] = diagonal_move_dir;
8200           ChangeDelay[x][y] = change_delay;
8201
8202           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8203                                    GfxDir[x][y]);
8204
8205           DrawLevelGraphicAnimation(x, y, graphic);
8206           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8207
8208           if (Feld[newx][newy] == EL_ACID)
8209           {
8210             SplashAcid(newx, newy);
8211
8212             return;
8213           }
8214
8215           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8216
8217           Store[newx][newy] = EL_EMC_ANDROID;
8218           GfxElement[newx][newy] = EL_EMC_ANDROID;
8219           GfxAction[newx][newy] = ACTION_GROWING;
8220           GfxDir[newx][newy] = diagonal_move_dir;
8221           ChangeDelay[newx][newy] = change_delay;
8222
8223           graphic = el_act_dir2img(GfxElement[newx][newy],
8224                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8225
8226           DrawLevelGraphicAnimation(newx, newy, graphic);
8227           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8228
8229           return;
8230         }
8231         else
8232         {
8233           Feld[newx][newy] = EL_EMPTY;
8234           DrawLevelField(newx, newy);
8235
8236           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8237         }
8238       }
8239       else if (!IS_FREE(newx, newy))
8240       {
8241 #if 0
8242         if (IS_PLAYER(x, y))
8243           DrawPlayerField(x, y);
8244         else
8245           DrawLevelField(x, y);
8246 #endif
8247
8248         return;
8249       }
8250     }
8251     else if (IS_CUSTOM_ELEMENT(element) &&
8252              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8253     {
8254       int new_element = Feld[newx][newy];
8255
8256       if (!IS_FREE(newx, newy))
8257       {
8258         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8259                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8260                       ACTION_BREAKING);
8261
8262         /* no element can dig solid indestructible elements */
8263         if (IS_INDESTRUCTIBLE(new_element) &&
8264             !IS_DIGGABLE(new_element) &&
8265             !IS_COLLECTIBLE(new_element))
8266           return;
8267
8268         if (AmoebaNr[newx][newy] &&
8269             (new_element == EL_AMOEBA_FULL ||
8270              new_element == EL_BD_AMOEBA ||
8271              new_element == EL_AMOEBA_GROWING))
8272         {
8273           AmoebaCnt[AmoebaNr[newx][newy]]--;
8274           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8275         }
8276
8277         if (IS_MOVING(newx, newy))
8278           RemoveMovingField(newx, newy);
8279         else
8280         {
8281           RemoveField(newx, newy);
8282           DrawLevelField(newx, newy);
8283         }
8284
8285         /* if digged element was about to explode, prevent the explosion */
8286         ExplodeField[newx][newy] = EX_TYPE_NONE;
8287
8288         PlayLevelSoundAction(x, y, action);
8289       }
8290
8291       Store[newx][newy] = EL_EMPTY;
8292 #if 1
8293       /* this makes it possible to leave the removed element again */
8294       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8295         Store[newx][newy] = new_element;
8296 #else
8297       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8298       {
8299         int move_leave_element = element_info[element].move_leave_element;
8300
8301         /* this makes it possible to leave the removed element again */
8302         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8303                              new_element : move_leave_element);
8304       }
8305 #endif
8306
8307       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8308       {
8309         RunnerVisit[x][y] = FrameCounter;
8310         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8311       }
8312     }
8313     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8314     {
8315       if (!IS_FREE(newx, newy))
8316       {
8317         if (IS_PLAYER(x, y))
8318           DrawPlayerField(x, y);
8319         else
8320           DrawLevelField(x, y);
8321
8322         return;
8323       }
8324       else
8325       {
8326         boolean wanna_flame = !RND(10);
8327         int dx = newx - x, dy = newy - y;
8328         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8329         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8330         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8331                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8332         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8333                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8334
8335         if ((wanna_flame ||
8336              IS_CLASSIC_ENEMY(element1) ||
8337              IS_CLASSIC_ENEMY(element2)) &&
8338             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8339             element1 != EL_FLAMES && element2 != EL_FLAMES)
8340         {
8341           ResetGfxAnimation(x, y);
8342           GfxAction[x][y] = ACTION_ATTACKING;
8343
8344           if (IS_PLAYER(x, y))
8345             DrawPlayerField(x, y);
8346           else
8347             DrawLevelField(x, y);
8348
8349           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8350
8351           MovDelay[x][y] = 50;
8352
8353           /* !!! */
8354 #if 0
8355           RemoveField(newx, newy);
8356 #endif
8357           Feld[newx][newy] = EL_FLAMES;
8358           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8359           {
8360 #if 0
8361             RemoveField(newx1, newy1);
8362 #endif
8363             Feld[newx1][newy1] = EL_FLAMES;
8364           }
8365           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8366           {
8367 #if 0
8368             RemoveField(newx2, newy2);
8369 #endif
8370             Feld[newx2][newy2] = EL_FLAMES;
8371           }
8372
8373           return;
8374         }
8375       }
8376     }
8377     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8378              Feld[newx][newy] == EL_DIAMOND)
8379     {
8380       if (IS_MOVING(newx, newy))
8381         RemoveMovingField(newx, newy);
8382       else
8383       {
8384         Feld[newx][newy] = EL_EMPTY;
8385         DrawLevelField(newx, newy);
8386       }
8387
8388       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8389     }
8390     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8391              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8392     {
8393       if (AmoebaNr[newx][newy])
8394       {
8395         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8396         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8397             Feld[newx][newy] == EL_BD_AMOEBA)
8398           AmoebaCnt[AmoebaNr[newx][newy]]--;
8399       }
8400
8401 #if 0
8402       /* !!! test !!! */
8403       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8404       {
8405         RemoveMovingField(newx, newy);
8406       }
8407 #else
8408       if (IS_MOVING(newx, newy))
8409       {
8410         RemoveMovingField(newx, newy);
8411       }
8412 #endif
8413       else
8414       {
8415         Feld[newx][newy] = EL_EMPTY;
8416         DrawLevelField(newx, newy);
8417       }
8418
8419       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8420     }
8421     else if ((element == EL_PACMAN || element == EL_MOLE)
8422              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8423     {
8424       if (AmoebaNr[newx][newy])
8425       {
8426         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8427         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8428             Feld[newx][newy] == EL_BD_AMOEBA)
8429           AmoebaCnt[AmoebaNr[newx][newy]]--;
8430       }
8431
8432       if (element == EL_MOLE)
8433       {
8434         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8435         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8436
8437         ResetGfxAnimation(x, y);
8438         GfxAction[x][y] = ACTION_DIGGING;
8439         DrawLevelField(x, y);
8440
8441         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8442
8443         return;                         /* wait for shrinking amoeba */
8444       }
8445       else      /* element == EL_PACMAN */
8446       {
8447         Feld[newx][newy] = EL_EMPTY;
8448         DrawLevelField(newx, newy);
8449         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8450       }
8451     }
8452     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8453              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8454               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8455     {
8456       /* wait for shrinking amoeba to completely disappear */
8457       return;
8458     }
8459     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8460     {
8461       /* object was running against a wall */
8462
8463       TurnRound(x, y);
8464
8465 #if 0
8466       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8467       if (move_pattern & MV_ANY_DIRECTION &&
8468           move_pattern == MovDir[x][y])
8469       {
8470         int blocking_element =
8471           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8472
8473         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8474                                  MovDir[x][y]);
8475
8476         element = Feld[x][y];   /* element might have changed */
8477       }
8478 #endif
8479
8480       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8481         DrawLevelElementAnimation(x, y, element);
8482
8483       if (DONT_TOUCH(element))
8484         TestIfBadThingTouchesPlayer(x, y);
8485
8486       return;
8487     }
8488
8489     InitMovingField(x, y, MovDir[x][y]);
8490
8491     PlayLevelSoundAction(x, y, ACTION_MOVING);
8492   }
8493
8494   if (MovDir[x][y])
8495     ContinueMoving(x, y);
8496 }
8497
8498 void ContinueMoving(int x, int y)
8499 {
8500   int element = Feld[x][y];
8501   struct ElementInfo *ei = &element_info[element];
8502   int direction = MovDir[x][y];
8503   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8504   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8505   int newx = x + dx, newy = y + dy;
8506   int stored = Store[x][y];
8507   int stored_new = Store[newx][newy];
8508   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8509   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8510   boolean last_line = (newy == lev_fieldy - 1);
8511
8512   MovPos[x][y] += getElementMoveStepsize(x, y);
8513
8514   if (pushed_by_player) /* special case: moving object pushed by player */
8515     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8516
8517   if (ABS(MovPos[x][y]) < TILEX)
8518   {
8519 #if 0
8520     int ee = Feld[x][y];
8521     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8522     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8523
8524     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8525            x, y, ABS(MovPos[x][y]),
8526            ee, gg, ff,
8527            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8528 #endif
8529
8530     DrawLevelField(x, y);
8531
8532     return;     /* element is still moving */
8533   }
8534
8535   /* element reached destination field */
8536
8537   Feld[x][y] = EL_EMPTY;
8538   Feld[newx][newy] = element;
8539   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8540
8541   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8542   {
8543     element = Feld[newx][newy] = EL_ACID;
8544   }
8545   else if (element == EL_MOLE)
8546   {
8547     Feld[x][y] = EL_SAND;
8548
8549     DrawLevelFieldCrumbledSandNeighbours(x, y);
8550   }
8551   else if (element == EL_QUICKSAND_FILLING)
8552   {
8553     element = Feld[newx][newy] = get_next_element(element);
8554     Store[newx][newy] = Store[x][y];
8555   }
8556   else if (element == EL_QUICKSAND_EMPTYING)
8557   {
8558     Feld[x][y] = get_next_element(element);
8559     element = Feld[newx][newy] = Store[x][y];
8560   }
8561   else if (element == EL_QUICKSAND_FAST_FILLING)
8562   {
8563     element = Feld[newx][newy] = get_next_element(element);
8564     Store[newx][newy] = Store[x][y];
8565   }
8566   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8567   {
8568     Feld[x][y] = get_next_element(element);
8569     element = Feld[newx][newy] = Store[x][y];
8570   }
8571   else if (element == EL_MAGIC_WALL_FILLING)
8572   {
8573     element = Feld[newx][newy] = get_next_element(element);
8574     if (!game.magic_wall_active)
8575       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8576     Store[newx][newy] = Store[x][y];
8577   }
8578   else if (element == EL_MAGIC_WALL_EMPTYING)
8579   {
8580     Feld[x][y] = get_next_element(element);
8581     if (!game.magic_wall_active)
8582       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8583     element = Feld[newx][newy] = Store[x][y];
8584
8585 #if USE_NEW_CUSTOM_VALUE
8586     InitField(newx, newy, FALSE);
8587 #endif
8588   }
8589   else if (element == EL_BD_MAGIC_WALL_FILLING)
8590   {
8591     element = Feld[newx][newy] = get_next_element(element);
8592     if (!game.magic_wall_active)
8593       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8594     Store[newx][newy] = Store[x][y];
8595   }
8596   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8597   {
8598     Feld[x][y] = get_next_element(element);
8599     if (!game.magic_wall_active)
8600       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8601     element = Feld[newx][newy] = Store[x][y];
8602
8603 #if USE_NEW_CUSTOM_VALUE
8604     InitField(newx, newy, FALSE);
8605 #endif
8606   }
8607   else if (element == EL_DC_MAGIC_WALL_FILLING)
8608   {
8609     element = Feld[newx][newy] = get_next_element(element);
8610     if (!game.magic_wall_active)
8611       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8612     Store[newx][newy] = Store[x][y];
8613   }
8614   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8615   {
8616     Feld[x][y] = get_next_element(element);
8617     if (!game.magic_wall_active)
8618       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8619     element = Feld[newx][newy] = Store[x][y];
8620
8621 #if USE_NEW_CUSTOM_VALUE
8622     InitField(newx, newy, FALSE);
8623 #endif
8624   }
8625   else if (element == EL_AMOEBA_DROPPING)
8626   {
8627     Feld[x][y] = get_next_element(element);
8628     element = Feld[newx][newy] = Store[x][y];
8629   }
8630   else if (element == EL_SOKOBAN_OBJECT)
8631   {
8632     if (Back[x][y])
8633       Feld[x][y] = Back[x][y];
8634
8635     if (Back[newx][newy])
8636       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8637
8638     Back[x][y] = Back[newx][newy] = 0;
8639   }
8640
8641   Store[x][y] = EL_EMPTY;
8642   MovPos[x][y] = 0;
8643   MovDir[x][y] = 0;
8644   MovDelay[x][y] = 0;
8645
8646   MovDelay[newx][newy] = 0;
8647
8648   if (CAN_CHANGE_OR_HAS_ACTION(element))
8649   {
8650     /* copy element change control values to new field */
8651     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8652     ChangePage[newx][newy]  = ChangePage[x][y];
8653     ChangeCount[newx][newy] = ChangeCount[x][y];
8654     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8655   }
8656
8657 #if USE_NEW_CUSTOM_VALUE
8658     CustomValue[newx][newy] = CustomValue[x][y];
8659 #endif
8660
8661   ChangeDelay[x][y] = 0;
8662   ChangePage[x][y] = -1;
8663   ChangeCount[x][y] = 0;
8664   ChangeEvent[x][y] = -1;
8665
8666 #if USE_NEW_CUSTOM_VALUE
8667   CustomValue[x][y] = 0;
8668 #endif
8669
8670   /* copy animation control values to new field */
8671   GfxFrame[newx][newy]  = GfxFrame[x][y];
8672   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8673   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8674   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8675
8676   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8677
8678   /* some elements can leave other elements behind after moving */
8679 #if 1
8680   if (ei->move_leave_element != EL_EMPTY &&
8681       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8682       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8683 #else
8684   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8685       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8686       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8687 #endif
8688   {
8689     int move_leave_element = ei->move_leave_element;
8690
8691 #if 1
8692 #if 1
8693     /* this makes it possible to leave the removed element again */
8694     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8695       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8696 #else
8697     /* this makes it possible to leave the removed element again */
8698     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8699       move_leave_element = stored;
8700 #endif
8701 #else
8702     /* this makes it possible to leave the removed element again */
8703     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8704         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8705       move_leave_element = stored;
8706 #endif
8707
8708     Feld[x][y] = move_leave_element;
8709
8710     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8711       MovDir[x][y] = direction;
8712
8713     InitField(x, y, FALSE);
8714
8715     if (GFX_CRUMBLED(Feld[x][y]))
8716       DrawLevelFieldCrumbledSandNeighbours(x, y);
8717
8718     if (ELEM_IS_PLAYER(move_leave_element))
8719       RelocatePlayer(x, y, move_leave_element);
8720   }
8721
8722   /* do this after checking for left-behind element */
8723   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8724
8725   if (!CAN_MOVE(element) ||
8726       (CAN_FALL(element) && direction == MV_DOWN &&
8727        (element == EL_SPRING ||
8728         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8729         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8730     GfxDir[x][y] = MovDir[newx][newy] = 0;
8731
8732   DrawLevelField(x, y);
8733   DrawLevelField(newx, newy);
8734
8735   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8736
8737   /* prevent pushed element from moving on in pushed direction */
8738   if (pushed_by_player && CAN_MOVE(element) &&
8739       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8740       !(element_info[element].move_pattern & direction))
8741     TurnRound(newx, newy);
8742
8743   /* prevent elements on conveyor belt from moving on in last direction */
8744   if (pushed_by_conveyor && CAN_FALL(element) &&
8745       direction & MV_HORIZONTAL)
8746     MovDir[newx][newy] = 0;
8747
8748   if (!pushed_by_player)
8749   {
8750     int nextx = newx + dx, nexty = newy + dy;
8751     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8752
8753     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8754
8755     if (CAN_FALL(element) && direction == MV_DOWN)
8756       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8757
8758     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8759       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8760
8761 #if USE_FIX_IMPACT_COLLISION
8762     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8763       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8764 #endif
8765   }
8766
8767   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8768   {
8769     TestIfBadThingTouchesPlayer(newx, newy);
8770     TestIfBadThingTouchesFriend(newx, newy);
8771
8772     if (!IS_CUSTOM_ELEMENT(element))
8773       TestIfBadThingTouchesOtherBadThing(newx, newy);
8774   }
8775   else if (element == EL_PENGUIN)
8776     TestIfFriendTouchesBadThing(newx, newy);
8777
8778   /* give the player one last chance (one more frame) to move away */
8779   if (CAN_FALL(element) && direction == MV_DOWN &&
8780       (last_line || (!IS_FREE(x, newy + 1) &&
8781                      (!IS_PLAYER(x, newy + 1) ||
8782                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8783     Impact(x, newy);
8784
8785   if (pushed_by_player && !game.use_change_when_pushing_bug)
8786   {
8787     int push_side = MV_DIR_OPPOSITE(direction);
8788     struct PlayerInfo *player = PLAYERINFO(x, y);
8789
8790     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8791                                player->index_bit, push_side);
8792     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8793                                         player->index_bit, push_side);
8794   }
8795
8796   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8797     MovDelay[newx][newy] = 1;
8798
8799   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8800
8801   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8802
8803 #if 0
8804   if (ChangePage[newx][newy] != -1)             /* delayed change */
8805   {
8806     int page = ChangePage[newx][newy];
8807     struct ElementChangeInfo *change = &ei->change_page[page];
8808
8809     ChangePage[newx][newy] = -1;
8810
8811     if (change->can_change)
8812     {
8813       if (ChangeElement(newx, newy, element, page))
8814       {
8815         if (change->post_change_function)
8816           change->post_change_function(newx, newy);
8817       }
8818     }
8819
8820     if (change->has_action)
8821       ExecuteCustomElementAction(newx, newy, element, page);
8822   }
8823 #endif
8824
8825   TestIfElementHitsCustomElement(newx, newy, direction);
8826   TestIfPlayerTouchesCustomElement(newx, newy);
8827   TestIfElementTouchesCustomElement(newx, newy);
8828
8829   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8830       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8831     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8832                              MV_DIR_OPPOSITE(direction));
8833 }
8834
8835 int AmoebeNachbarNr(int ax, int ay)
8836 {
8837   int i;
8838   int element = Feld[ax][ay];
8839   int group_nr = 0;
8840   static int xy[4][2] =
8841   {
8842     { 0, -1 },
8843     { -1, 0 },
8844     { +1, 0 },
8845     { 0, +1 }
8846   };
8847
8848   for (i = 0; i < NUM_DIRECTIONS; i++)
8849   {
8850     int x = ax + xy[i][0];
8851     int y = ay + xy[i][1];
8852
8853     if (!IN_LEV_FIELD(x, y))
8854       continue;
8855
8856     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8857       group_nr = AmoebaNr[x][y];
8858   }
8859
8860   return group_nr;
8861 }
8862
8863 void AmoebenVereinigen(int ax, int ay)
8864 {
8865   int i, x, y, xx, yy;
8866   int new_group_nr = AmoebaNr[ax][ay];
8867   static int xy[4][2] =
8868   {
8869     { 0, -1 },
8870     { -1, 0 },
8871     { +1, 0 },
8872     { 0, +1 }
8873   };
8874
8875   if (new_group_nr == 0)
8876     return;
8877
8878   for (i = 0; i < NUM_DIRECTIONS; i++)
8879   {
8880     x = ax + xy[i][0];
8881     y = ay + xy[i][1];
8882
8883     if (!IN_LEV_FIELD(x, y))
8884       continue;
8885
8886     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8887          Feld[x][y] == EL_BD_AMOEBA ||
8888          Feld[x][y] == EL_AMOEBA_DEAD) &&
8889         AmoebaNr[x][y] != new_group_nr)
8890     {
8891       int old_group_nr = AmoebaNr[x][y];
8892
8893       if (old_group_nr == 0)
8894         return;
8895
8896       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8897       AmoebaCnt[old_group_nr] = 0;
8898       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8899       AmoebaCnt2[old_group_nr] = 0;
8900
8901       SCAN_PLAYFIELD(xx, yy)
8902       {
8903         if (AmoebaNr[xx][yy] == old_group_nr)
8904           AmoebaNr[xx][yy] = new_group_nr;
8905       }
8906     }
8907   }
8908 }
8909
8910 void AmoebeUmwandeln(int ax, int ay)
8911 {
8912   int i, x, y;
8913
8914   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8915   {
8916     int group_nr = AmoebaNr[ax][ay];
8917
8918 #ifdef DEBUG
8919     if (group_nr == 0)
8920     {
8921       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8922       printf("AmoebeUmwandeln(): This should never happen!\n");
8923       return;
8924     }
8925 #endif
8926
8927     SCAN_PLAYFIELD(x, y)
8928     {
8929       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8930       {
8931         AmoebaNr[x][y] = 0;
8932         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8933       }
8934     }
8935
8936     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8937                             SND_AMOEBA_TURNING_TO_GEM :
8938                             SND_AMOEBA_TURNING_TO_ROCK));
8939     Bang(ax, ay);
8940   }
8941   else
8942   {
8943     static int xy[4][2] =
8944     {
8945       { 0, -1 },
8946       { -1, 0 },
8947       { +1, 0 },
8948       { 0, +1 }
8949     };
8950
8951     for (i = 0; i < NUM_DIRECTIONS; i++)
8952     {
8953       x = ax + xy[i][0];
8954       y = ay + xy[i][1];
8955
8956       if (!IN_LEV_FIELD(x, y))
8957         continue;
8958
8959       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8960       {
8961         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8962                               SND_AMOEBA_TURNING_TO_GEM :
8963                               SND_AMOEBA_TURNING_TO_ROCK));
8964         Bang(x, y);
8965       }
8966     }
8967   }
8968 }
8969
8970 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8971 {
8972   int x, y;
8973   int group_nr = AmoebaNr[ax][ay];
8974   boolean done = FALSE;
8975
8976 #ifdef DEBUG
8977   if (group_nr == 0)
8978   {
8979     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8980     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8981     return;
8982   }
8983 #endif
8984
8985   SCAN_PLAYFIELD(x, y)
8986   {
8987     if (AmoebaNr[x][y] == group_nr &&
8988         (Feld[x][y] == EL_AMOEBA_DEAD ||
8989          Feld[x][y] == EL_BD_AMOEBA ||
8990          Feld[x][y] == EL_AMOEBA_GROWING))
8991     {
8992       AmoebaNr[x][y] = 0;
8993       Feld[x][y] = new_element;
8994       InitField(x, y, FALSE);
8995       DrawLevelField(x, y);
8996       done = TRUE;
8997     }
8998   }
8999
9000   if (done)
9001     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9002                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9003                             SND_BD_AMOEBA_TURNING_TO_GEM));
9004 }
9005
9006 void AmoebeWaechst(int x, int y)
9007 {
9008   static unsigned long sound_delay = 0;
9009   static unsigned long sound_delay_value = 0;
9010
9011   if (!MovDelay[x][y])          /* start new growing cycle */
9012   {
9013     MovDelay[x][y] = 7;
9014
9015     if (DelayReached(&sound_delay, sound_delay_value))
9016     {
9017       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9018       sound_delay_value = 30;
9019     }
9020   }
9021
9022   if (MovDelay[x][y])           /* wait some time before growing bigger */
9023   {
9024     MovDelay[x][y]--;
9025     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9026     {
9027       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9028                                            6 - MovDelay[x][y]);
9029
9030       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9031     }
9032
9033     if (!MovDelay[x][y])
9034     {
9035       Feld[x][y] = Store[x][y];
9036       Store[x][y] = 0;
9037       DrawLevelField(x, y);
9038     }
9039   }
9040 }
9041
9042 void AmoebaDisappearing(int x, int y)
9043 {
9044   static unsigned long sound_delay = 0;
9045   static unsigned long sound_delay_value = 0;
9046
9047   if (!MovDelay[x][y])          /* start new shrinking cycle */
9048   {
9049     MovDelay[x][y] = 7;
9050
9051     if (DelayReached(&sound_delay, sound_delay_value))
9052       sound_delay_value = 30;
9053   }
9054
9055   if (MovDelay[x][y])           /* wait some time before shrinking */
9056   {
9057     MovDelay[x][y]--;
9058     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9059     {
9060       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9061                                            6 - MovDelay[x][y]);
9062
9063       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9064     }
9065
9066     if (!MovDelay[x][y])
9067     {
9068       Feld[x][y] = EL_EMPTY;
9069       DrawLevelField(x, y);
9070
9071       /* don't let mole enter this field in this cycle;
9072          (give priority to objects falling to this field from above) */
9073       Stop[x][y] = TRUE;
9074     }
9075   }
9076 }
9077
9078 void AmoebeAbleger(int ax, int ay)
9079 {
9080   int i;
9081   int element = Feld[ax][ay];
9082   int graphic = el2img(element);
9083   int newax = ax, neway = ay;
9084   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9085   static int xy[4][2] =
9086   {
9087     { 0, -1 },
9088     { -1, 0 },
9089     { +1, 0 },
9090     { 0, +1 }
9091   };
9092
9093   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9094   {
9095     Feld[ax][ay] = EL_AMOEBA_DEAD;
9096     DrawLevelField(ax, ay);
9097     return;
9098   }
9099
9100   if (IS_ANIMATED(graphic))
9101     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9102
9103   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9104     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9105
9106   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9107   {
9108     MovDelay[ax][ay]--;
9109     if (MovDelay[ax][ay])
9110       return;
9111   }
9112
9113   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9114   {
9115     int start = RND(4);
9116     int x = ax + xy[start][0];
9117     int y = ay + xy[start][1];
9118
9119     if (!IN_LEV_FIELD(x, y))
9120       return;
9121
9122     if (IS_FREE(x, y) ||
9123         CAN_GROW_INTO(Feld[x][y]) ||
9124         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9125         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9126     {
9127       newax = x;
9128       neway = y;
9129     }
9130
9131     if (newax == ax && neway == ay)
9132       return;
9133   }
9134   else                          /* normal or "filled" (BD style) amoeba */
9135   {
9136     int start = RND(4);
9137     boolean waiting_for_player = FALSE;
9138
9139     for (i = 0; i < NUM_DIRECTIONS; i++)
9140     {
9141       int j = (start + i) % 4;
9142       int x = ax + xy[j][0];
9143       int y = ay + xy[j][1];
9144
9145       if (!IN_LEV_FIELD(x, y))
9146         continue;
9147
9148       if (IS_FREE(x, y) ||
9149           CAN_GROW_INTO(Feld[x][y]) ||
9150           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9151           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9152       {
9153         newax = x;
9154         neway = y;
9155         break;
9156       }
9157       else if (IS_PLAYER(x, y))
9158         waiting_for_player = TRUE;
9159     }
9160
9161     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9162     {
9163       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9164       {
9165         Feld[ax][ay] = EL_AMOEBA_DEAD;
9166         DrawLevelField(ax, ay);
9167         AmoebaCnt[AmoebaNr[ax][ay]]--;
9168
9169         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9170         {
9171           if (element == EL_AMOEBA_FULL)
9172             AmoebeUmwandeln(ax, ay);
9173           else if (element == EL_BD_AMOEBA)
9174             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9175         }
9176       }
9177       return;
9178     }
9179     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9180     {
9181       /* amoeba gets larger by growing in some direction */
9182
9183       int new_group_nr = AmoebaNr[ax][ay];
9184
9185 #ifdef DEBUG
9186   if (new_group_nr == 0)
9187   {
9188     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9189     printf("AmoebeAbleger(): This should never happen!\n");
9190     return;
9191   }
9192 #endif
9193
9194       AmoebaNr[newax][neway] = new_group_nr;
9195       AmoebaCnt[new_group_nr]++;
9196       AmoebaCnt2[new_group_nr]++;
9197
9198       /* if amoeba touches other amoeba(s) after growing, unify them */
9199       AmoebenVereinigen(newax, neway);
9200
9201       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9202       {
9203         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9204         return;
9205       }
9206     }
9207   }
9208
9209   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9210       (neway == lev_fieldy - 1 && newax != ax))
9211   {
9212     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9213     Store[newax][neway] = element;
9214   }
9215   else if (neway == ay || element == EL_EMC_DRIPPER)
9216   {
9217     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9218
9219     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9220   }
9221   else
9222   {
9223     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9224     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9225     Store[ax][ay] = EL_AMOEBA_DROP;
9226     ContinueMoving(ax, ay);
9227     return;
9228   }
9229
9230   DrawLevelField(newax, neway);
9231 }
9232
9233 void Life(int ax, int ay)
9234 {
9235   int x1, y1, x2, y2;
9236   int life_time = 40;
9237   int element = Feld[ax][ay];
9238   int graphic = el2img(element);
9239   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9240                          level.biomaze);
9241   boolean changed = FALSE;
9242
9243   if (IS_ANIMATED(graphic))
9244     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9245
9246   if (Stop[ax][ay])
9247     return;
9248
9249   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9250     MovDelay[ax][ay] = life_time;
9251
9252   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9253   {
9254     MovDelay[ax][ay]--;
9255     if (MovDelay[ax][ay])
9256       return;
9257   }
9258
9259   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9260   {
9261     int xx = ax+x1, yy = ay+y1;
9262     int nachbarn = 0;
9263
9264     if (!IN_LEV_FIELD(xx, yy))
9265       continue;
9266
9267     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9268     {
9269       int x = xx+x2, y = yy+y2;
9270
9271       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9272         continue;
9273
9274       if (((Feld[x][y] == element ||
9275             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9276            !Stop[x][y]) ||
9277           (IS_FREE(x, y) && Stop[x][y]))
9278         nachbarn++;
9279     }
9280
9281     if (xx == ax && yy == ay)           /* field in the middle */
9282     {
9283       if (nachbarn < life_parameter[0] ||
9284           nachbarn > life_parameter[1])
9285       {
9286         Feld[xx][yy] = EL_EMPTY;
9287         if (!Stop[xx][yy])
9288           DrawLevelField(xx, yy);
9289         Stop[xx][yy] = TRUE;
9290         changed = TRUE;
9291       }
9292     }
9293     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9294     {                                   /* free border field */
9295       if (nachbarn >= life_parameter[2] &&
9296           nachbarn <= life_parameter[3])
9297       {
9298         Feld[xx][yy] = element;
9299         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9300         if (!Stop[xx][yy])
9301           DrawLevelField(xx, yy);
9302         Stop[xx][yy] = TRUE;
9303         changed = TRUE;
9304       }
9305     }
9306   }
9307
9308   if (changed)
9309     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9310                    SND_GAME_OF_LIFE_GROWING);
9311 }
9312
9313 static void InitRobotWheel(int x, int y)
9314 {
9315   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9316 }
9317
9318 static void RunRobotWheel(int x, int y)
9319 {
9320   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9321 }
9322
9323 static void StopRobotWheel(int x, int y)
9324 {
9325   if (ZX == x && ZY == y)
9326   {
9327     ZX = ZY = -1;
9328
9329     game.robot_wheel_active = FALSE;
9330   }
9331 }
9332
9333 static void InitTimegateWheel(int x, int y)
9334 {
9335   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9336 }
9337
9338 static void RunTimegateWheel(int x, int y)
9339 {
9340   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9341 }
9342
9343 static void InitMagicBallDelay(int x, int y)
9344 {
9345 #if 1
9346   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9347 #else
9348   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9349 #endif
9350 }
9351
9352 static void ActivateMagicBall(int bx, int by)
9353 {
9354   int x, y;
9355
9356   if (level.ball_random)
9357   {
9358     int pos_border = RND(8);    /* select one of the eight border elements */
9359     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9360     int xx = pos_content % 3;
9361     int yy = pos_content / 3;
9362
9363     x = bx - 1 + xx;
9364     y = by - 1 + yy;
9365
9366     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9367       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9368   }
9369   else
9370   {
9371     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9372     {
9373       int xx = x - bx + 1;
9374       int yy = y - by + 1;
9375
9376       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9377         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9378     }
9379   }
9380
9381   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9382 }
9383
9384 void CheckExit(int x, int y)
9385 {
9386   if (local_player->gems_still_needed > 0 ||
9387       local_player->sokobanfields_still_needed > 0 ||
9388       local_player->lights_still_needed > 0)
9389   {
9390     int element = Feld[x][y];
9391     int graphic = el2img(element);
9392
9393     if (IS_ANIMATED(graphic))
9394       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9395
9396     return;
9397   }
9398
9399   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9400     return;
9401
9402   Feld[x][y] = EL_EXIT_OPENING;
9403
9404   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9405 }
9406
9407 void CheckExitEM(int x, int y)
9408 {
9409   if (local_player->gems_still_needed > 0 ||
9410       local_player->sokobanfields_still_needed > 0 ||
9411       local_player->lights_still_needed > 0)
9412   {
9413     int element = Feld[x][y];
9414     int graphic = el2img(element);
9415
9416     if (IS_ANIMATED(graphic))
9417       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9418
9419     return;
9420   }
9421
9422   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9423     return;
9424
9425   Feld[x][y] = EL_EM_EXIT_OPENING;
9426
9427   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9428 }
9429
9430 void CheckExitSteel(int x, int y)
9431 {
9432   if (local_player->gems_still_needed > 0 ||
9433       local_player->sokobanfields_still_needed > 0 ||
9434       local_player->lights_still_needed > 0)
9435   {
9436     int element = Feld[x][y];
9437     int graphic = el2img(element);
9438
9439     if (IS_ANIMATED(graphic))
9440       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9441
9442     return;
9443   }
9444
9445   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9446     return;
9447
9448   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9449
9450   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9451 }
9452
9453 void CheckExitSteelEM(int x, int y)
9454 {
9455   if (local_player->gems_still_needed > 0 ||
9456       local_player->sokobanfields_still_needed > 0 ||
9457       local_player->lights_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_EM_STEEL_EXIT_OPENING;
9472
9473   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9474 }
9475
9476 void CheckExitSP(int x, int y)
9477 {
9478   if (local_player->gems_still_needed > 0)
9479   {
9480     int element = Feld[x][y];
9481     int graphic = el2img(element);
9482
9483     if (IS_ANIMATED(graphic))
9484       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9485
9486     return;
9487   }
9488
9489   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9490     return;
9491
9492   Feld[x][y] = EL_SP_EXIT_OPENING;
9493
9494   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9495 }
9496
9497 static void CloseAllOpenTimegates()
9498 {
9499   int x, y;
9500
9501   SCAN_PLAYFIELD(x, y)
9502   {
9503     int element = Feld[x][y];
9504
9505     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9506     {
9507       Feld[x][y] = EL_TIMEGATE_CLOSING;
9508
9509       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9510     }
9511   }
9512 }
9513
9514 void DrawTwinkleOnField(int x, int y)
9515 {
9516   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9517     return;
9518
9519   if (Feld[x][y] == EL_BD_DIAMOND)
9520     return;
9521
9522   if (MovDelay[x][y] == 0)      /* next animation frame */
9523     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9524
9525   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9526   {
9527     MovDelay[x][y]--;
9528
9529     DrawLevelElementAnimation(x, y, Feld[x][y]);
9530
9531     if (MovDelay[x][y] != 0)
9532     {
9533       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9534                                            10 - MovDelay[x][y]);
9535
9536       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9537     }
9538   }
9539 }
9540
9541 void MauerWaechst(int x, int y)
9542 {
9543   int delay = 6;
9544
9545   if (!MovDelay[x][y])          /* next animation frame */
9546     MovDelay[x][y] = 3 * delay;
9547
9548   if (MovDelay[x][y])           /* wait some time before next frame */
9549   {
9550     MovDelay[x][y]--;
9551
9552     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9553     {
9554       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9555       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9556
9557       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9558     }
9559
9560     if (!MovDelay[x][y])
9561     {
9562       if (MovDir[x][y] == MV_LEFT)
9563       {
9564         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9565           DrawLevelField(x - 1, y);
9566       }
9567       else if (MovDir[x][y] == MV_RIGHT)
9568       {
9569         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9570           DrawLevelField(x + 1, y);
9571       }
9572       else if (MovDir[x][y] == MV_UP)
9573       {
9574         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9575           DrawLevelField(x, y - 1);
9576       }
9577       else
9578       {
9579         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9580           DrawLevelField(x, y + 1);
9581       }
9582
9583       Feld[x][y] = Store[x][y];
9584       Store[x][y] = 0;
9585       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9586       DrawLevelField(x, y);
9587     }
9588   }
9589 }
9590
9591 void MauerAbleger(int ax, int ay)
9592 {
9593   int element = Feld[ax][ay];
9594   int graphic = el2img(element);
9595   boolean oben_frei = FALSE, unten_frei = FALSE;
9596   boolean links_frei = FALSE, rechts_frei = FALSE;
9597   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9598   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9599   boolean new_wall = FALSE;
9600
9601   if (IS_ANIMATED(graphic))
9602     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9603
9604   if (!MovDelay[ax][ay])        /* start building new wall */
9605     MovDelay[ax][ay] = 6;
9606
9607   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9608   {
9609     MovDelay[ax][ay]--;
9610     if (MovDelay[ax][ay])
9611       return;
9612   }
9613
9614   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9615     oben_frei = TRUE;
9616   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9617     unten_frei = TRUE;
9618   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9619     links_frei = TRUE;
9620   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9621     rechts_frei = TRUE;
9622
9623   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9624       element == EL_EXPANDABLE_WALL_ANY)
9625   {
9626     if (oben_frei)
9627     {
9628       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9629       Store[ax][ay-1] = element;
9630       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9631       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9632         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9633                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9634       new_wall = TRUE;
9635     }
9636     if (unten_frei)
9637     {
9638       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9639       Store[ax][ay+1] = element;
9640       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9641       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9642         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9643                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9644       new_wall = TRUE;
9645     }
9646   }
9647
9648   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9649       element == EL_EXPANDABLE_WALL_ANY ||
9650       element == EL_EXPANDABLE_WALL ||
9651       element == EL_BD_EXPANDABLE_WALL)
9652   {
9653     if (links_frei)
9654     {
9655       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9656       Store[ax-1][ay] = element;
9657       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9658       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9659         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9660                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9661       new_wall = TRUE;
9662     }
9663
9664     if (rechts_frei)
9665     {
9666       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9667       Store[ax+1][ay] = element;
9668       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9669       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9670         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9671                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9672       new_wall = TRUE;
9673     }
9674   }
9675
9676   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9677     DrawLevelField(ax, ay);
9678
9679   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9680     oben_massiv = TRUE;
9681   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9682     unten_massiv = TRUE;
9683   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9684     links_massiv = TRUE;
9685   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9686     rechts_massiv = TRUE;
9687
9688   if (((oben_massiv && unten_massiv) ||
9689        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9690        element == EL_EXPANDABLE_WALL) &&
9691       ((links_massiv && rechts_massiv) ||
9692        element == EL_EXPANDABLE_WALL_VERTICAL))
9693     Feld[ax][ay] = EL_WALL;
9694
9695   if (new_wall)
9696     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9697 }
9698
9699 void MauerAblegerStahl(int ax, int ay)
9700 {
9701   int element = Feld[ax][ay];
9702   int graphic = el2img(element);
9703   boolean oben_frei = FALSE, unten_frei = FALSE;
9704   boolean links_frei = FALSE, rechts_frei = FALSE;
9705   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9706   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9707   boolean new_wall = FALSE;
9708
9709   if (IS_ANIMATED(graphic))
9710     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9711
9712   if (!MovDelay[ax][ay])        /* start building new wall */
9713     MovDelay[ax][ay] = 6;
9714
9715   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9716   {
9717     MovDelay[ax][ay]--;
9718     if (MovDelay[ax][ay])
9719       return;
9720   }
9721
9722   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9723     oben_frei = TRUE;
9724   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9725     unten_frei = TRUE;
9726   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9727     links_frei = TRUE;
9728   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9729     rechts_frei = TRUE;
9730
9731   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9732       element == EL_EXPANDABLE_STEELWALL_ANY)
9733   {
9734     if (oben_frei)
9735     {
9736       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9737       Store[ax][ay-1] = element;
9738       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9739       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9740         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9741                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9742       new_wall = TRUE;
9743     }
9744     if (unten_frei)
9745     {
9746       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9747       Store[ax][ay+1] = element;
9748       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9749       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9750         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9751                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9752       new_wall = TRUE;
9753     }
9754   }
9755
9756   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9757       element == EL_EXPANDABLE_STEELWALL_ANY)
9758   {
9759     if (links_frei)
9760     {
9761       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9762       Store[ax-1][ay] = element;
9763       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9764       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9765         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9766                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9767       new_wall = TRUE;
9768     }
9769
9770     if (rechts_frei)
9771     {
9772       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9773       Store[ax+1][ay] = element;
9774       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9775       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9776         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9777                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9778       new_wall = TRUE;
9779     }
9780   }
9781
9782   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9783     oben_massiv = TRUE;
9784   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9785     unten_massiv = TRUE;
9786   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9787     links_massiv = TRUE;
9788   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9789     rechts_massiv = TRUE;
9790
9791   if (((oben_massiv && unten_massiv) ||
9792        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9793       ((links_massiv && rechts_massiv) ||
9794        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9795     Feld[ax][ay] = EL_WALL;
9796
9797   if (new_wall)
9798     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9799 }
9800
9801 void CheckForDragon(int x, int y)
9802 {
9803   int i, j;
9804   boolean dragon_found = FALSE;
9805   static int xy[4][2] =
9806   {
9807     { 0, -1 },
9808     { -1, 0 },
9809     { +1, 0 },
9810     { 0, +1 }
9811   };
9812
9813   for (i = 0; i < NUM_DIRECTIONS; i++)
9814   {
9815     for (j = 0; j < 4; j++)
9816     {
9817       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9818
9819       if (IN_LEV_FIELD(xx, yy) &&
9820           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9821       {
9822         if (Feld[xx][yy] == EL_DRAGON)
9823           dragon_found = TRUE;
9824       }
9825       else
9826         break;
9827     }
9828   }
9829
9830   if (!dragon_found)
9831   {
9832     for (i = 0; i < NUM_DIRECTIONS; i++)
9833     {
9834       for (j = 0; j < 3; j++)
9835       {
9836         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9837   
9838         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9839         {
9840           Feld[xx][yy] = EL_EMPTY;
9841           DrawLevelField(xx, yy);
9842         }
9843         else
9844           break;
9845       }
9846     }
9847   }
9848 }
9849
9850 static void InitBuggyBase(int x, int y)
9851 {
9852   int element = Feld[x][y];
9853   int activating_delay = FRAMES_PER_SECOND / 4;
9854
9855   ChangeDelay[x][y] =
9856     (element == EL_SP_BUGGY_BASE ?
9857      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9858      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9859      activating_delay :
9860      element == EL_SP_BUGGY_BASE_ACTIVE ?
9861      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9862 }
9863
9864 static void WarnBuggyBase(int x, int y)
9865 {
9866   int i;
9867   static int xy[4][2] =
9868   {
9869     { 0, -1 },
9870     { -1, 0 },
9871     { +1, 0 },
9872     { 0, +1 }
9873   };
9874
9875   for (i = 0; i < NUM_DIRECTIONS; i++)
9876   {
9877     int xx = x + xy[i][0];
9878     int yy = y + xy[i][1];
9879
9880     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9881     {
9882       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9883
9884       break;
9885     }
9886   }
9887 }
9888
9889 static void InitTrap(int x, int y)
9890 {
9891   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9892 }
9893
9894 static void ActivateTrap(int x, int y)
9895 {
9896   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9897 }
9898
9899 static void ChangeActiveTrap(int x, int y)
9900 {
9901   int graphic = IMG_TRAP_ACTIVE;
9902
9903   /* if new animation frame was drawn, correct crumbled sand border */
9904   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9905     DrawLevelFieldCrumbledSand(x, y);
9906 }
9907
9908 static int getSpecialActionElement(int element, int number, int base_element)
9909 {
9910   return (element != EL_EMPTY ? element :
9911           number != -1 ? base_element + number - 1 :
9912           EL_EMPTY);
9913 }
9914
9915 static int getModifiedActionNumber(int value_old, int operator, int operand,
9916                                    int value_min, int value_max)
9917 {
9918   int value_new = (operator == CA_MODE_SET      ? operand :
9919                    operator == CA_MODE_ADD      ? value_old + operand :
9920                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9921                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9922                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9923                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9924                    value_old);
9925
9926   return (value_new < value_min ? value_min :
9927           value_new > value_max ? value_max :
9928           value_new);
9929 }
9930
9931 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9932 {
9933   struct ElementInfo *ei = &element_info[element];
9934   struct ElementChangeInfo *change = &ei->change_page[page];
9935   int target_element = change->target_element;
9936   int action_type = change->action_type;
9937   int action_mode = change->action_mode;
9938   int action_arg = change->action_arg;
9939   int i;
9940
9941   if (!change->has_action)
9942     return;
9943
9944   /* ---------- determine action paramater values -------------------------- */
9945
9946   int level_time_value =
9947     (level.time > 0 ? TimeLeft :
9948      TimePlayed);
9949
9950   int action_arg_element =
9951     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9952      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9953      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9954      EL_EMPTY);
9955
9956   int action_arg_direction =
9957     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9958      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9959      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9960      change->actual_trigger_side :
9961      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9962      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9963      MV_NONE);
9964
9965   int action_arg_number_min =
9966     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9967      CA_ARG_MIN);
9968
9969   int action_arg_number_max =
9970     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9971      action_type == CA_SET_LEVEL_GEMS ? 999 :
9972      action_type == CA_SET_LEVEL_TIME ? 9999 :
9973      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9974      action_type == CA_SET_CE_VALUE ? 9999 :
9975      action_type == CA_SET_CE_SCORE ? 9999 :
9976      CA_ARG_MAX);
9977
9978   int action_arg_number_reset =
9979     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9980      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9981      action_type == CA_SET_LEVEL_TIME ? level.time :
9982      action_type == CA_SET_LEVEL_SCORE ? 0 :
9983 #if USE_NEW_CUSTOM_VALUE
9984      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9985 #else
9986      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9987 #endif
9988      action_type == CA_SET_CE_SCORE ? 0 :
9989      0);
9990
9991   int action_arg_number =
9992     (action_arg <= CA_ARG_MAX ? action_arg :
9993      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9994      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9995      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9996      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9997      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9998      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9999 #if USE_NEW_CUSTOM_VALUE
10000      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10001 #else
10002      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10003 #endif
10004      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10005      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10006      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10007      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10008      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10009      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10010      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10011      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10012      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10013      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10014      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10015      -1);
10016
10017   int action_arg_number_old =
10018     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10019      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10020      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10021      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10022      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10023      0);
10024
10025   int action_arg_number_new =
10026     getModifiedActionNumber(action_arg_number_old,
10027                             action_mode, action_arg_number,
10028                             action_arg_number_min, action_arg_number_max);
10029
10030 #if 1
10031   int trigger_player_bits = change->actual_trigger_player_bits;
10032 #else
10033   int trigger_player_bits =
10034     (change->actual_trigger_player >= EL_PLAYER_1 &&
10035      change->actual_trigger_player <= EL_PLAYER_4 ?
10036      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10037      PLAYER_BITS_ANY);
10038 #endif
10039
10040   int action_arg_player_bits =
10041     (action_arg >= CA_ARG_PLAYER_1 &&
10042      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10043      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10044      PLAYER_BITS_ANY);
10045
10046   /* ---------- execute action  -------------------------------------------- */
10047
10048   switch (action_type)
10049   {
10050     case CA_NO_ACTION:
10051     {
10052       return;
10053     }
10054
10055     /* ---------- level actions  ------------------------------------------- */
10056
10057     case CA_RESTART_LEVEL:
10058     {
10059       game.restart_level = TRUE;
10060
10061       break;
10062     }
10063
10064     case CA_SHOW_ENVELOPE:
10065     {
10066       int element = getSpecialActionElement(action_arg_element,
10067                                             action_arg_number, EL_ENVELOPE_1);
10068
10069       if (IS_ENVELOPE(element))
10070         local_player->show_envelope = element;
10071
10072       break;
10073     }
10074
10075     case CA_SET_LEVEL_TIME:
10076     {
10077       if (level.time > 0)       /* only modify limited time value */
10078       {
10079         TimeLeft = action_arg_number_new;
10080
10081 #if 1
10082         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10083
10084         DisplayGameControlValues();
10085 #else
10086         DrawGameValue_Time(TimeLeft);
10087 #endif
10088
10089         if (!TimeLeft && setup.time_limit)
10090           for (i = 0; i < MAX_PLAYERS; i++)
10091             KillPlayer(&stored_player[i]);
10092       }
10093
10094       break;
10095     }
10096
10097     case CA_SET_LEVEL_SCORE:
10098     {
10099       local_player->score = action_arg_number_new;
10100
10101 #if 1
10102       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10103
10104       DisplayGameControlValues();
10105 #else
10106       DrawGameValue_Score(local_player->score);
10107 #endif
10108
10109       break;
10110     }
10111
10112     case CA_SET_LEVEL_GEMS:
10113     {
10114       local_player->gems_still_needed = action_arg_number_new;
10115
10116 #if 1
10117       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10118
10119       DisplayGameControlValues();
10120 #else
10121       DrawGameValue_Emeralds(local_player->gems_still_needed);
10122 #endif
10123
10124       break;
10125     }
10126
10127 #if !USE_PLAYER_GRAVITY
10128     case CA_SET_LEVEL_GRAVITY:
10129     {
10130       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10131                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10132                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10133                       game.gravity);
10134       break;
10135     }
10136 #endif
10137
10138     case CA_SET_LEVEL_WIND:
10139     {
10140       game.wind_direction = action_arg_direction;
10141
10142       break;
10143     }
10144
10145     /* ---------- player actions  ------------------------------------------ */
10146
10147     case CA_MOVE_PLAYER:
10148     {
10149       /* automatically move to the next field in specified direction */
10150       for (i = 0; i < MAX_PLAYERS; i++)
10151         if (trigger_player_bits & (1 << i))
10152           stored_player[i].programmed_action = action_arg_direction;
10153
10154       break;
10155     }
10156
10157     case CA_EXIT_PLAYER:
10158     {
10159       for (i = 0; i < MAX_PLAYERS; i++)
10160         if (action_arg_player_bits & (1 << i))
10161           PlayerWins(&stored_player[i]);
10162
10163       break;
10164     }
10165
10166     case CA_KILL_PLAYER:
10167     {
10168       for (i = 0; i < MAX_PLAYERS; i++)
10169         if (action_arg_player_bits & (1 << i))
10170           KillPlayer(&stored_player[i]);
10171
10172       break;
10173     }
10174
10175     case CA_SET_PLAYER_KEYS:
10176     {
10177       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10178       int element = getSpecialActionElement(action_arg_element,
10179                                             action_arg_number, EL_KEY_1);
10180
10181       if (IS_KEY(element))
10182       {
10183         for (i = 0; i < MAX_PLAYERS; i++)
10184         {
10185           if (trigger_player_bits & (1 << i))
10186           {
10187             stored_player[i].key[KEY_NR(element)] = key_state;
10188
10189             DrawGameDoorValues();
10190           }
10191         }
10192       }
10193
10194       break;
10195     }
10196
10197     case CA_SET_PLAYER_SPEED:
10198     {
10199       for (i = 0; i < MAX_PLAYERS; i++)
10200       {
10201         if (trigger_player_bits & (1 << i))
10202         {
10203           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10204
10205           if (action_arg == CA_ARG_SPEED_FASTER &&
10206               stored_player[i].cannot_move)
10207           {
10208             action_arg_number = STEPSIZE_VERY_SLOW;
10209           }
10210           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10211                    action_arg == CA_ARG_SPEED_FASTER)
10212           {
10213             action_arg_number = 2;
10214             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10215                            CA_MODE_MULTIPLY);
10216           }
10217           else if (action_arg == CA_ARG_NUMBER_RESET)
10218           {
10219             action_arg_number = level.initial_player_stepsize[i];
10220           }
10221
10222           move_stepsize =
10223             getModifiedActionNumber(move_stepsize,
10224                                     action_mode,
10225                                     action_arg_number,
10226                                     action_arg_number_min,
10227                                     action_arg_number_max);
10228
10229           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10230         }
10231       }
10232
10233       break;
10234     }
10235
10236     case CA_SET_PLAYER_SHIELD:
10237     {
10238       for (i = 0; i < MAX_PLAYERS; i++)
10239       {
10240         if (trigger_player_bits & (1 << i))
10241         {
10242           if (action_arg == CA_ARG_SHIELD_OFF)
10243           {
10244             stored_player[i].shield_normal_time_left = 0;
10245             stored_player[i].shield_deadly_time_left = 0;
10246           }
10247           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10248           {
10249             stored_player[i].shield_normal_time_left = 999999;
10250           }
10251           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10252           {
10253             stored_player[i].shield_normal_time_left = 999999;
10254             stored_player[i].shield_deadly_time_left = 999999;
10255           }
10256         }
10257       }
10258
10259       break;
10260     }
10261
10262 #if USE_PLAYER_GRAVITY
10263     case CA_SET_PLAYER_GRAVITY:
10264     {
10265       for (i = 0; i < MAX_PLAYERS; i++)
10266       {
10267         if (trigger_player_bits & (1 << i))
10268         {
10269           stored_player[i].gravity =
10270             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10271              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10272              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10273              stored_player[i].gravity);
10274         }
10275       }
10276
10277       break;
10278     }
10279 #endif
10280
10281     case CA_SET_PLAYER_ARTWORK:
10282     {
10283       for (i = 0; i < MAX_PLAYERS; i++)
10284       {
10285         if (trigger_player_bits & (1 << i))
10286         {
10287           int artwork_element = action_arg_element;
10288
10289           if (action_arg == CA_ARG_ELEMENT_RESET)
10290             artwork_element =
10291               (level.use_artwork_element[i] ? level.artwork_element[i] :
10292                stored_player[i].element_nr);
10293
10294 #if USE_GFX_RESET_PLAYER_ARTWORK
10295           if (stored_player[i].artwork_element != artwork_element)
10296             stored_player[i].Frame = 0;
10297 #endif
10298
10299           stored_player[i].artwork_element = artwork_element;
10300
10301           SetPlayerWaiting(&stored_player[i], FALSE);
10302
10303           /* set number of special actions for bored and sleeping animation */
10304           stored_player[i].num_special_action_bored =
10305             get_num_special_action(artwork_element,
10306                                    ACTION_BORING_1, ACTION_BORING_LAST);
10307           stored_player[i].num_special_action_sleeping =
10308             get_num_special_action(artwork_element,
10309                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10310         }
10311       }
10312
10313       break;
10314     }
10315
10316     /* ---------- CE actions  ---------------------------------------------- */
10317
10318     case CA_SET_CE_VALUE:
10319     {
10320 #if USE_NEW_CUSTOM_VALUE
10321       int last_ce_value = CustomValue[x][y];
10322
10323       CustomValue[x][y] = action_arg_number_new;
10324
10325       if (CustomValue[x][y] != last_ce_value)
10326       {
10327         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10328         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10329
10330         if (CustomValue[x][y] == 0)
10331         {
10332           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10333           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10334         }
10335       }
10336 #endif
10337
10338       break;
10339     }
10340
10341     case CA_SET_CE_SCORE:
10342     {
10343 #if USE_NEW_CUSTOM_VALUE
10344       int last_ce_score = ei->collect_score;
10345
10346       ei->collect_score = action_arg_number_new;
10347
10348       if (ei->collect_score != last_ce_score)
10349       {
10350         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10351         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10352
10353         if (ei->collect_score == 0)
10354         {
10355           int xx, yy;
10356
10357           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10358           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10359
10360           /*
10361             This is a very special case that seems to be a mixture between
10362             CheckElementChange() and CheckTriggeredElementChange(): while
10363             the first one only affects single elements that are triggered
10364             directly, the second one affects multiple elements in the playfield
10365             that are triggered indirectly by another element. This is a third
10366             case: Changing the CE score always affects multiple identical CEs,
10367             so every affected CE must be checked, not only the single CE for
10368             which the CE score was changed in the first place (as every instance
10369             of that CE shares the same CE score, and therefore also can change)!
10370           */
10371           SCAN_PLAYFIELD(xx, yy)
10372           {
10373             if (Feld[xx][yy] == element)
10374               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10375                                  CE_SCORE_GETS_ZERO);
10376           }
10377         }
10378       }
10379 #endif
10380
10381       break;
10382     }
10383
10384     /* ---------- engine actions  ------------------------------------------ */
10385
10386     case CA_SET_ENGINE_SCAN_MODE:
10387     {
10388       InitPlayfieldScanMode(action_arg);
10389
10390       break;
10391     }
10392
10393     default:
10394       break;
10395   }
10396 }
10397
10398 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10399 {
10400   int old_element = Feld[x][y];
10401   int new_element = GetElementFromGroupElement(element);
10402   int previous_move_direction = MovDir[x][y];
10403 #if USE_NEW_CUSTOM_VALUE
10404   int last_ce_value = CustomValue[x][y];
10405 #endif
10406   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10407   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10408   boolean add_player_onto_element = (new_element_is_player &&
10409 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10410                                      /* this breaks SnakeBite when a snake is
10411                                         halfway through a door that closes */
10412                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10413                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10414 #endif
10415                                      IS_WALKABLE(old_element));
10416
10417 #if 0
10418   /* check if element under the player changes from accessible to unaccessible
10419      (needed for special case of dropping element which then changes) */
10420   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10421       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10422   {
10423     Bang(x, y);
10424
10425     return;
10426   }
10427 #endif
10428
10429   if (!add_player_onto_element)
10430   {
10431     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10432       RemoveMovingField(x, y);
10433     else
10434       RemoveField(x, y);
10435
10436     Feld[x][y] = new_element;
10437
10438 #if !USE_GFX_RESET_GFX_ANIMATION
10439     ResetGfxAnimation(x, y);
10440     ResetRandomAnimationValue(x, y);
10441 #endif
10442
10443     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10444       MovDir[x][y] = previous_move_direction;
10445
10446 #if USE_NEW_CUSTOM_VALUE
10447     if (element_info[new_element].use_last_ce_value)
10448       CustomValue[x][y] = last_ce_value;
10449 #endif
10450
10451     InitField_WithBug1(x, y, FALSE);
10452
10453     new_element = Feld[x][y];   /* element may have changed */
10454
10455 #if USE_GFX_RESET_GFX_ANIMATION
10456     ResetGfxAnimation(x, y);
10457     ResetRandomAnimationValue(x, y);
10458 #endif
10459
10460     DrawLevelField(x, y);
10461
10462     if (GFX_CRUMBLED(new_element))
10463       DrawLevelFieldCrumbledSandNeighbours(x, y);
10464   }
10465
10466 #if 1
10467   /* check if element under the player changes from accessible to unaccessible
10468      (needed for special case of dropping element which then changes) */
10469   /* (must be checked after creating new element for walkable group elements) */
10470 #if USE_FIX_KILLED_BY_NON_WALKABLE
10471   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10472       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10473   {
10474     Bang(x, y);
10475
10476     return;
10477   }
10478 #else
10479   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10480       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10481   {
10482     Bang(x, y);
10483
10484     return;
10485   }
10486 #endif
10487 #endif
10488
10489   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10490   if (new_element_is_player)
10491     RelocatePlayer(x, y, new_element);
10492
10493   if (is_change)
10494     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10495
10496   TestIfBadThingTouchesPlayer(x, y);
10497   TestIfPlayerTouchesCustomElement(x, y);
10498   TestIfElementTouchesCustomElement(x, y);
10499 }
10500
10501 static void CreateField(int x, int y, int element)
10502 {
10503   CreateFieldExt(x, y, element, FALSE);
10504 }
10505
10506 static void CreateElementFromChange(int x, int y, int element)
10507 {
10508   element = GET_VALID_RUNTIME_ELEMENT(element);
10509
10510 #if USE_STOP_CHANGED_ELEMENTS
10511   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10512   {
10513     int old_element = Feld[x][y];
10514
10515     /* prevent changed element from moving in same engine frame
10516        unless both old and new element can either fall or move */
10517     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10518         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10519       Stop[x][y] = TRUE;
10520   }
10521 #endif
10522
10523   CreateFieldExt(x, y, element, TRUE);
10524 }
10525
10526 static boolean ChangeElement(int x, int y, int element, int page)
10527 {
10528   struct ElementInfo *ei = &element_info[element];
10529   struct ElementChangeInfo *change = &ei->change_page[page];
10530   int ce_value = CustomValue[x][y];
10531   int ce_score = ei->collect_score;
10532   int target_element;
10533   int old_element = Feld[x][y];
10534
10535   /* always use default change event to prevent running into a loop */
10536   if (ChangeEvent[x][y] == -1)
10537     ChangeEvent[x][y] = CE_DELAY;
10538
10539   if (ChangeEvent[x][y] == CE_DELAY)
10540   {
10541     /* reset actual trigger element, trigger player and action element */
10542     change->actual_trigger_element = EL_EMPTY;
10543     change->actual_trigger_player = EL_PLAYER_1;
10544     change->actual_trigger_player_bits = CH_PLAYER_1;
10545     change->actual_trigger_side = CH_SIDE_NONE;
10546     change->actual_trigger_ce_value = 0;
10547     change->actual_trigger_ce_score = 0;
10548   }
10549
10550   /* do not change elements more than a specified maximum number of changes */
10551   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10552     return FALSE;
10553
10554   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10555
10556   if (change->explode)
10557   {
10558     Bang(x, y);
10559
10560     return TRUE;
10561   }
10562
10563   if (change->use_target_content)
10564   {
10565     boolean complete_replace = TRUE;
10566     boolean can_replace[3][3];
10567     int xx, yy;
10568
10569     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10570     {
10571       boolean is_empty;
10572       boolean is_walkable;
10573       boolean is_diggable;
10574       boolean is_collectible;
10575       boolean is_removable;
10576       boolean is_destructible;
10577       int ex = x + xx - 1;
10578       int ey = y + yy - 1;
10579       int content_element = change->target_content.e[xx][yy];
10580       int e;
10581
10582       can_replace[xx][yy] = TRUE;
10583
10584       if (ex == x && ey == y)   /* do not check changing element itself */
10585         continue;
10586
10587       if (content_element == EL_EMPTY_SPACE)
10588       {
10589         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10590
10591         continue;
10592       }
10593
10594       if (!IN_LEV_FIELD(ex, ey))
10595       {
10596         can_replace[xx][yy] = FALSE;
10597         complete_replace = FALSE;
10598
10599         continue;
10600       }
10601
10602       e = Feld[ex][ey];
10603
10604       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10605         e = MovingOrBlocked2Element(ex, ey);
10606
10607       is_empty = (IS_FREE(ex, ey) ||
10608                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10609
10610       is_walkable     = (is_empty || IS_WALKABLE(e));
10611       is_diggable     = (is_empty || IS_DIGGABLE(e));
10612       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10613       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10614       is_removable    = (is_diggable || is_collectible);
10615
10616       can_replace[xx][yy] =
10617         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10618           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10619           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10620           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10621           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10622           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10623          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10624
10625       if (!can_replace[xx][yy])
10626         complete_replace = FALSE;
10627     }
10628
10629     if (!change->only_if_complete || complete_replace)
10630     {
10631       boolean something_has_changed = FALSE;
10632
10633       if (change->only_if_complete && change->use_random_replace &&
10634           RND(100) < change->random_percentage)
10635         return FALSE;
10636
10637       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10638       {
10639         int ex = x + xx - 1;
10640         int ey = y + yy - 1;
10641         int content_element;
10642
10643         if (can_replace[xx][yy] && (!change->use_random_replace ||
10644                                     RND(100) < change->random_percentage))
10645         {
10646           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10647             RemoveMovingField(ex, ey);
10648
10649           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10650
10651           content_element = change->target_content.e[xx][yy];
10652           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10653                                               ce_value, ce_score);
10654
10655           CreateElementFromChange(ex, ey, target_element);
10656
10657           something_has_changed = TRUE;
10658
10659           /* for symmetry reasons, freeze newly created border elements */
10660           if (ex != x || ey != y)
10661             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10662         }
10663       }
10664
10665       if (something_has_changed)
10666       {
10667         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10668         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10669       }
10670     }
10671   }
10672   else
10673   {
10674     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10675                                         ce_value, ce_score);
10676
10677     if (element == EL_DIAGONAL_GROWING ||
10678         element == EL_DIAGONAL_SHRINKING)
10679     {
10680       target_element = Store[x][y];
10681
10682       Store[x][y] = EL_EMPTY;
10683     }
10684
10685     CreateElementFromChange(x, y, target_element);
10686
10687     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10688     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10689   }
10690
10691   /* this uses direct change before indirect change */
10692   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10693
10694   return TRUE;
10695 }
10696
10697 #if USE_NEW_DELAYED_ACTION
10698
10699 static void HandleElementChange(int x, int y, int page)
10700 {
10701   int element = MovingOrBlocked2Element(x, y);
10702   struct ElementInfo *ei = &element_info[element];
10703   struct ElementChangeInfo *change = &ei->change_page[page];
10704
10705 #ifdef DEBUG
10706   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10707       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10708   {
10709     printf("\n\n");
10710     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10711            x, y, element, element_info[element].token_name);
10712     printf("HandleElementChange(): This should never happen!\n");
10713     printf("\n\n");
10714   }
10715 #endif
10716
10717   /* this can happen with classic bombs on walkable, changing elements */
10718   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10719   {
10720 #if 0
10721     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10722       ChangeDelay[x][y] = 0;
10723 #endif
10724
10725     return;
10726   }
10727
10728   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10729   {
10730     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10731
10732     if (change->can_change)
10733     {
10734 #if 1
10735       /* !!! not clear why graphic animation should be reset at all here !!! */
10736       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10737 #if USE_GFX_RESET_WHEN_NOT_MOVING
10738       /* when a custom element is about to change (for example by change delay),
10739          do not reset graphic animation when the custom element is moving */
10740       if (!IS_MOVING(x, y))
10741 #endif
10742       {
10743         ResetGfxAnimation(x, y);
10744         ResetRandomAnimationValue(x, y);
10745       }
10746 #endif
10747
10748       if (change->pre_change_function)
10749         change->pre_change_function(x, y);
10750     }
10751   }
10752
10753   ChangeDelay[x][y]--;
10754
10755   if (ChangeDelay[x][y] != 0)           /* continue element change */
10756   {
10757     if (change->can_change)
10758     {
10759       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10760
10761       if (IS_ANIMATED(graphic))
10762         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10763
10764       if (change->change_function)
10765         change->change_function(x, y);
10766     }
10767   }
10768   else                                  /* finish element change */
10769   {
10770     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10771     {
10772       page = ChangePage[x][y];
10773       ChangePage[x][y] = -1;
10774
10775       change = &ei->change_page[page];
10776     }
10777
10778     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10779     {
10780       ChangeDelay[x][y] = 1;            /* try change after next move step */
10781       ChangePage[x][y] = page;          /* remember page to use for change */
10782
10783       return;
10784     }
10785
10786     if (change->can_change)
10787     {
10788       if (ChangeElement(x, y, element, page))
10789       {
10790         if (change->post_change_function)
10791           change->post_change_function(x, y);
10792       }
10793     }
10794
10795     if (change->has_action)
10796       ExecuteCustomElementAction(x, y, element, page);
10797   }
10798 }
10799
10800 #else
10801
10802 static void HandleElementChange(int x, int y, int page)
10803 {
10804   int element = MovingOrBlocked2Element(x, y);
10805   struct ElementInfo *ei = &element_info[element];
10806   struct ElementChangeInfo *change = &ei->change_page[page];
10807
10808 #ifdef DEBUG
10809   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10810   {
10811     printf("\n\n");
10812     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10813            x, y, element, element_info[element].token_name);
10814     printf("HandleElementChange(): This should never happen!\n");
10815     printf("\n\n");
10816   }
10817 #endif
10818
10819   /* this can happen with classic bombs on walkable, changing elements */
10820   if (!CAN_CHANGE(element))
10821   {
10822 #if 0
10823     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10824       ChangeDelay[x][y] = 0;
10825 #endif
10826
10827     return;
10828   }
10829
10830   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10831   {
10832     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10833
10834     ResetGfxAnimation(x, y);
10835     ResetRandomAnimationValue(x, y);
10836
10837     if (change->pre_change_function)
10838       change->pre_change_function(x, y);
10839   }
10840
10841   ChangeDelay[x][y]--;
10842
10843   if (ChangeDelay[x][y] != 0)           /* continue element change */
10844   {
10845     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10846
10847     if (IS_ANIMATED(graphic))
10848       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10849
10850     if (change->change_function)
10851       change->change_function(x, y);
10852   }
10853   else                                  /* finish element change */
10854   {
10855     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10856     {
10857       page = ChangePage[x][y];
10858       ChangePage[x][y] = -1;
10859
10860       change = &ei->change_page[page];
10861     }
10862
10863     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10864     {
10865       ChangeDelay[x][y] = 1;            /* try change after next move step */
10866       ChangePage[x][y] = page;          /* remember page to use for change */
10867
10868       return;
10869     }
10870
10871     if (ChangeElement(x, y, element, page))
10872     {
10873       if (change->post_change_function)
10874         change->post_change_function(x, y);
10875     }
10876   }
10877 }
10878
10879 #endif
10880
10881 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10882                                               int trigger_element,
10883                                               int trigger_event,
10884                                               int trigger_player,
10885                                               int trigger_side,
10886                                               int trigger_page)
10887 {
10888   boolean change_done_any = FALSE;
10889   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10890   int i;
10891
10892   if (!(trigger_events[trigger_element][trigger_event]))
10893     return FALSE;
10894
10895 #if 0
10896   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10897          trigger_event, recursion_loop_depth, recursion_loop_detected,
10898          recursion_loop_element, EL_NAME(recursion_loop_element));
10899 #endif
10900
10901   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10902
10903   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10904   {
10905     int element = EL_CUSTOM_START + i;
10906     boolean change_done = FALSE;
10907     int p;
10908
10909     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10910         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10911       continue;
10912
10913     for (p = 0; p < element_info[element].num_change_pages; p++)
10914     {
10915       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10916
10917       if (change->can_change_or_has_action &&
10918           change->has_event[trigger_event] &&
10919           change->trigger_side & trigger_side &&
10920           change->trigger_player & trigger_player &&
10921           change->trigger_page & trigger_page_bits &&
10922           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10923       {
10924         change->actual_trigger_element = trigger_element;
10925         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10926         change->actual_trigger_player_bits = trigger_player;
10927         change->actual_trigger_side = trigger_side;
10928         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10929         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10930
10931         if ((change->can_change && !change_done) || change->has_action)
10932         {
10933           int x, y;
10934
10935           SCAN_PLAYFIELD(x, y)
10936           {
10937             if (Feld[x][y] == element)
10938             {
10939               if (change->can_change && !change_done)
10940               {
10941                 ChangeDelay[x][y] = 1;
10942                 ChangeEvent[x][y] = trigger_event;
10943
10944                 HandleElementChange(x, y, p);
10945               }
10946 #if USE_NEW_DELAYED_ACTION
10947               else if (change->has_action)
10948               {
10949                 ExecuteCustomElementAction(x, y, element, p);
10950                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10951               }
10952 #else
10953               if (change->has_action)
10954               {
10955                 ExecuteCustomElementAction(x, y, element, p);
10956                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10957               }
10958 #endif
10959             }
10960           }
10961
10962           if (change->can_change)
10963           {
10964             change_done = TRUE;
10965             change_done_any = TRUE;
10966           }
10967         }
10968       }
10969     }
10970   }
10971
10972   RECURSION_LOOP_DETECTION_END();
10973
10974   return change_done_any;
10975 }
10976
10977 static boolean CheckElementChangeExt(int x, int y,
10978                                      int element,
10979                                      int trigger_element,
10980                                      int trigger_event,
10981                                      int trigger_player,
10982                                      int trigger_side)
10983 {
10984   boolean change_done = FALSE;
10985   int p;
10986
10987   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10988       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10989     return FALSE;
10990
10991   if (Feld[x][y] == EL_BLOCKED)
10992   {
10993     Blocked2Moving(x, y, &x, &y);
10994     element = Feld[x][y];
10995   }
10996
10997 #if 0
10998   /* check if element has already changed */
10999   if (Feld[x][y] != element)
11000     return FALSE;
11001 #else
11002   /* check if element has already changed or is about to change after moving */
11003   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11004        Feld[x][y] != element) ||
11005
11006       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11007        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11008         ChangePage[x][y] != -1)))
11009     return FALSE;
11010 #endif
11011
11012 #if 0
11013   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11014          trigger_event, recursion_loop_depth, recursion_loop_detected,
11015          recursion_loop_element, EL_NAME(recursion_loop_element));
11016 #endif
11017
11018   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11019
11020   for (p = 0; p < element_info[element].num_change_pages; p++)
11021   {
11022     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11023
11024     /* check trigger element for all events where the element that is checked
11025        for changing interacts with a directly adjacent element -- this is
11026        different to element changes that affect other elements to change on the
11027        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11028     boolean check_trigger_element =
11029       (trigger_event == CE_TOUCHING_X ||
11030        trigger_event == CE_HITTING_X ||
11031        trigger_event == CE_HIT_BY_X ||
11032 #if 1
11033        /* this one was forgotten until 3.2.3 */
11034        trigger_event == CE_DIGGING_X);
11035 #endif
11036
11037     if (change->can_change_or_has_action &&
11038         change->has_event[trigger_event] &&
11039         change->trigger_side & trigger_side &&
11040         change->trigger_player & trigger_player &&
11041         (!check_trigger_element ||
11042          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11043     {
11044       change->actual_trigger_element = trigger_element;
11045       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11046       change->actual_trigger_player_bits = trigger_player;
11047       change->actual_trigger_side = trigger_side;
11048       change->actual_trigger_ce_value = CustomValue[x][y];
11049       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11050
11051       /* special case: trigger element not at (x,y) position for some events */
11052       if (check_trigger_element)
11053       {
11054         static struct
11055         {
11056           int dx, dy;
11057         } move_xy[] =
11058           {
11059             {  0,  0 },
11060             { -1,  0 },
11061             { +1,  0 },
11062             {  0,  0 },
11063             {  0, -1 },
11064             {  0,  0 }, { 0, 0 }, { 0, 0 },
11065             {  0, +1 }
11066           };
11067
11068         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11069         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11070
11071         change->actual_trigger_ce_value = CustomValue[xx][yy];
11072         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11073       }
11074
11075       if (change->can_change && !change_done)
11076       {
11077         ChangeDelay[x][y] = 1;
11078         ChangeEvent[x][y] = trigger_event;
11079
11080         HandleElementChange(x, y, p);
11081
11082         change_done = TRUE;
11083       }
11084 #if USE_NEW_DELAYED_ACTION
11085       else if (change->has_action)
11086       {
11087         ExecuteCustomElementAction(x, y, element, p);
11088         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11089       }
11090 #else
11091       if (change->has_action)
11092       {
11093         ExecuteCustomElementAction(x, y, element, p);
11094         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11095       }
11096 #endif
11097     }
11098   }
11099
11100   RECURSION_LOOP_DETECTION_END();
11101
11102   return change_done;
11103 }
11104
11105 static void PlayPlayerSound(struct PlayerInfo *player)
11106 {
11107   int jx = player->jx, jy = player->jy;
11108   int sound_element = player->artwork_element;
11109   int last_action = player->last_action_waiting;
11110   int action = player->action_waiting;
11111
11112   if (player->is_waiting)
11113   {
11114     if (action != last_action)
11115       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11116     else
11117       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11118   }
11119   else
11120   {
11121     if (action != last_action)
11122       StopSound(element_info[sound_element].sound[last_action]);
11123
11124     if (last_action == ACTION_SLEEPING)
11125       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11126   }
11127 }
11128
11129 static void PlayAllPlayersSound()
11130 {
11131   int i;
11132
11133   for (i = 0; i < MAX_PLAYERS; i++)
11134     if (stored_player[i].active)
11135       PlayPlayerSound(&stored_player[i]);
11136 }
11137
11138 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11139 {
11140   boolean last_waiting = player->is_waiting;
11141   int move_dir = player->MovDir;
11142
11143   player->dir_waiting = move_dir;
11144   player->last_action_waiting = player->action_waiting;
11145
11146   if (is_waiting)
11147   {
11148     if (!last_waiting)          /* not waiting -> waiting */
11149     {
11150       player->is_waiting = TRUE;
11151
11152       player->frame_counter_bored =
11153         FrameCounter +
11154         game.player_boring_delay_fixed +
11155         GetSimpleRandom(game.player_boring_delay_random);
11156       player->frame_counter_sleeping =
11157         FrameCounter +
11158         game.player_sleeping_delay_fixed +
11159         GetSimpleRandom(game.player_sleeping_delay_random);
11160
11161       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11162     }
11163
11164     if (game.player_sleeping_delay_fixed +
11165         game.player_sleeping_delay_random > 0 &&
11166         player->anim_delay_counter == 0 &&
11167         player->post_delay_counter == 0 &&
11168         FrameCounter >= player->frame_counter_sleeping)
11169       player->is_sleeping = TRUE;
11170     else if (game.player_boring_delay_fixed +
11171              game.player_boring_delay_random > 0 &&
11172              FrameCounter >= player->frame_counter_bored)
11173       player->is_bored = TRUE;
11174
11175     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11176                               player->is_bored ? ACTION_BORING :
11177                               ACTION_WAITING);
11178
11179     if (player->is_sleeping && player->use_murphy)
11180     {
11181       /* special case for sleeping Murphy when leaning against non-free tile */
11182
11183       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11184           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11185            !IS_MOVING(player->jx - 1, player->jy)))
11186         move_dir = MV_LEFT;
11187       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11188                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11189                 !IS_MOVING(player->jx + 1, player->jy)))
11190         move_dir = MV_RIGHT;
11191       else
11192         player->is_sleeping = FALSE;
11193
11194       player->dir_waiting = move_dir;
11195     }
11196
11197     if (player->is_sleeping)
11198     {
11199       if (player->num_special_action_sleeping > 0)
11200       {
11201         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11202         {
11203           int last_special_action = player->special_action_sleeping;
11204           int num_special_action = player->num_special_action_sleeping;
11205           int special_action =
11206             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11207              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11208              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11209              last_special_action + 1 : ACTION_SLEEPING);
11210           int special_graphic =
11211             el_act_dir2img(player->artwork_element, special_action, move_dir);
11212
11213           player->anim_delay_counter =
11214             graphic_info[special_graphic].anim_delay_fixed +
11215             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11216           player->post_delay_counter =
11217             graphic_info[special_graphic].post_delay_fixed +
11218             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11219
11220           player->special_action_sleeping = special_action;
11221         }
11222
11223         if (player->anim_delay_counter > 0)
11224         {
11225           player->action_waiting = player->special_action_sleeping;
11226           player->anim_delay_counter--;
11227         }
11228         else if (player->post_delay_counter > 0)
11229         {
11230           player->post_delay_counter--;
11231         }
11232       }
11233     }
11234     else if (player->is_bored)
11235     {
11236       if (player->num_special_action_bored > 0)
11237       {
11238         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11239         {
11240           int special_action =
11241             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11242           int special_graphic =
11243             el_act_dir2img(player->artwork_element, special_action, move_dir);
11244
11245           player->anim_delay_counter =
11246             graphic_info[special_graphic].anim_delay_fixed +
11247             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11248           player->post_delay_counter =
11249             graphic_info[special_graphic].post_delay_fixed +
11250             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11251
11252           player->special_action_bored = special_action;
11253         }
11254
11255         if (player->anim_delay_counter > 0)
11256         {
11257           player->action_waiting = player->special_action_bored;
11258           player->anim_delay_counter--;
11259         }
11260         else if (player->post_delay_counter > 0)
11261         {
11262           player->post_delay_counter--;
11263         }
11264       }
11265     }
11266   }
11267   else if (last_waiting)        /* waiting -> not waiting */
11268   {
11269     player->is_waiting = FALSE;
11270     player->is_bored = FALSE;
11271     player->is_sleeping = FALSE;
11272
11273     player->frame_counter_bored = -1;
11274     player->frame_counter_sleeping = -1;
11275
11276     player->anim_delay_counter = 0;
11277     player->post_delay_counter = 0;
11278
11279     player->dir_waiting = player->MovDir;
11280     player->action_waiting = ACTION_DEFAULT;
11281
11282     player->special_action_bored = ACTION_DEFAULT;
11283     player->special_action_sleeping = ACTION_DEFAULT;
11284   }
11285 }
11286
11287 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11288 {
11289   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11290   int left      = player_action & JOY_LEFT;
11291   int right     = player_action & JOY_RIGHT;
11292   int up        = player_action & JOY_UP;
11293   int down      = player_action & JOY_DOWN;
11294   int button1   = player_action & JOY_BUTTON_1;
11295   int button2   = player_action & JOY_BUTTON_2;
11296   int dx        = (left ? -1 : right ? 1 : 0);
11297   int dy        = (up   ? -1 : down  ? 1 : 0);
11298
11299   if (!player->active || tape.pausing)
11300     return 0;
11301
11302   if (player_action)
11303   {
11304     if (button1)
11305       snapped = SnapField(player, dx, dy);
11306     else
11307     {
11308       if (button2)
11309         dropped = DropElement(player);
11310
11311       moved = MovePlayer(player, dx, dy);
11312     }
11313
11314     if (tape.single_step && tape.recording && !tape.pausing)
11315     {
11316       if (button1 || (dropped && !moved))
11317       {
11318         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11319         SnapField(player, 0, 0);                /* stop snapping */
11320       }
11321     }
11322
11323     SetPlayerWaiting(player, FALSE);
11324
11325     return player_action;
11326   }
11327   else
11328   {
11329     /* no actions for this player (no input at player's configured device) */
11330
11331     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11332     SnapField(player, 0, 0);
11333     CheckGravityMovementWhenNotMoving(player);
11334
11335     if (player->MovPos == 0)
11336       SetPlayerWaiting(player, TRUE);
11337
11338     if (player->MovPos == 0)    /* needed for tape.playing */
11339       player->is_moving = FALSE;
11340
11341     player->is_dropping = FALSE;
11342     player->is_dropping_pressed = FALSE;
11343     player->drop_pressed_delay = 0;
11344
11345     return 0;
11346   }
11347 }
11348
11349 static void CheckLevelTime()
11350 {
11351   int i;
11352
11353   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11354   {
11355     if (level.native_em_level->lev->home == 0)  /* all players at home */
11356     {
11357       PlayerWins(local_player);
11358
11359       AllPlayersGone = TRUE;
11360
11361       level.native_em_level->lev->home = -1;
11362     }
11363
11364     if (level.native_em_level->ply[0]->alive == 0 &&
11365         level.native_em_level->ply[1]->alive == 0 &&
11366         level.native_em_level->ply[2]->alive == 0 &&
11367         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11368       AllPlayersGone = TRUE;
11369   }
11370
11371   if (TimeFrames >= FRAMES_PER_SECOND)
11372   {
11373     TimeFrames = 0;
11374     TapeTime++;
11375
11376     for (i = 0; i < MAX_PLAYERS; i++)
11377     {
11378       struct PlayerInfo *player = &stored_player[i];
11379
11380       if (SHIELD_ON(player))
11381       {
11382         player->shield_normal_time_left--;
11383
11384         if (player->shield_deadly_time_left > 0)
11385           player->shield_deadly_time_left--;
11386       }
11387     }
11388
11389     if (!local_player->LevelSolved && !level.use_step_counter)
11390     {
11391       TimePlayed++;
11392
11393       if (TimeLeft > 0)
11394       {
11395         TimeLeft--;
11396
11397         if (TimeLeft <= 10 && setup.time_limit)
11398           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11399
11400 #if 1
11401         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11402
11403         DisplayGameControlValues();
11404 #else
11405         DrawGameValue_Time(TimeLeft);
11406 #endif
11407
11408         if (!TimeLeft && setup.time_limit)
11409         {
11410           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11411             level.native_em_level->lev->killed_out_of_time = TRUE;
11412           else
11413             for (i = 0; i < MAX_PLAYERS; i++)
11414               KillPlayer(&stored_player[i]);
11415         }
11416       }
11417 #if 1
11418       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11419       {
11420         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11421
11422         DisplayGameControlValues();
11423       }
11424 #else
11425       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11426         DrawGameValue_Time(TimePlayed);
11427 #endif
11428
11429       level.native_em_level->lev->time =
11430         (level.time == 0 ? TimePlayed : TimeLeft);
11431     }
11432
11433     if (tape.recording || tape.playing)
11434       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11435   }
11436
11437 #if 1
11438   UpdateAndDisplayGameControlValues();
11439 #else
11440   UpdateGameDoorValues();
11441   DrawGameDoorValues();
11442 #endif
11443 }
11444
11445 void AdvanceFrameAndPlayerCounters(int player_nr)
11446 {
11447   int i;
11448
11449   /* advance frame counters (global frame counter and time frame counter) */
11450   FrameCounter++;
11451   TimeFrames++;
11452
11453   /* advance player counters (counters for move delay, move animation etc.) */
11454   for (i = 0; i < MAX_PLAYERS; i++)
11455   {
11456     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11457     int move_delay_value = stored_player[i].move_delay_value;
11458     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11459
11460     if (!advance_player_counters)       /* not all players may be affected */
11461       continue;
11462
11463 #if USE_NEW_PLAYER_ANIM
11464     if (move_frames == 0)       /* less than one move per game frame */
11465     {
11466       int stepsize = TILEX / move_delay_value;
11467       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11468       int count = (stored_player[i].is_moving ?
11469                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11470
11471       if (count % delay == 0)
11472         move_frames = 1;
11473     }
11474 #endif
11475
11476     stored_player[i].Frame += move_frames;
11477
11478     if (stored_player[i].MovPos != 0)
11479       stored_player[i].StepFrame += move_frames;
11480
11481     if (stored_player[i].move_delay > 0)
11482       stored_player[i].move_delay--;
11483
11484     /* due to bugs in previous versions, counter must count up, not down */
11485     if (stored_player[i].push_delay != -1)
11486       stored_player[i].push_delay++;
11487
11488     if (stored_player[i].drop_delay > 0)
11489       stored_player[i].drop_delay--;
11490
11491     if (stored_player[i].is_dropping_pressed)
11492       stored_player[i].drop_pressed_delay++;
11493   }
11494 }
11495
11496 void StartGameActions(boolean init_network_game, boolean record_tape,
11497                       long random_seed)
11498 {
11499   unsigned long new_random_seed = InitRND(random_seed);
11500
11501   if (record_tape)
11502     TapeStartRecording(new_random_seed);
11503
11504 #if defined(NETWORK_AVALIABLE)
11505   if (init_network_game)
11506   {
11507     SendToServer_StartPlaying();
11508
11509     return;
11510   }
11511 #endif
11512
11513   InitGame();
11514 }
11515
11516 void GameActions()
11517 {
11518   static unsigned long game_frame_delay = 0;
11519   unsigned long game_frame_delay_value;
11520   byte *recorded_player_action;
11521   byte summarized_player_action = 0;
11522   byte tape_action[MAX_PLAYERS];
11523   int i;
11524
11525   /* detect endless loops, caused by custom element programming */
11526   if (recursion_loop_detected && recursion_loop_depth == 0)
11527   {
11528     char *message = getStringCat3("Internal Error ! Element ",
11529                                   EL_NAME(recursion_loop_element),
11530                                   " caused endless loop ! Quit the game ?");
11531
11532     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11533           EL_NAME(recursion_loop_element));
11534
11535     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11536
11537     recursion_loop_detected = FALSE;    /* if game should be continued */
11538
11539     free(message);
11540
11541     return;
11542   }
11543
11544   if (game.restart_level)
11545     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11546
11547   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11548   {
11549     if (level.native_em_level->lev->home == 0)  /* all players at home */
11550     {
11551       PlayerWins(local_player);
11552
11553       AllPlayersGone = TRUE;
11554
11555       level.native_em_level->lev->home = -1;
11556     }
11557
11558     if (level.native_em_level->ply[0]->alive == 0 &&
11559         level.native_em_level->ply[1]->alive == 0 &&
11560         level.native_em_level->ply[2]->alive == 0 &&
11561         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11562       AllPlayersGone = TRUE;
11563   }
11564
11565   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11566     GameWon();
11567
11568   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11569     TapeStop();
11570
11571   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11572     return;
11573
11574   game_frame_delay_value =
11575     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11576
11577   if (tape.playing && tape.warp_forward && !tape.pausing)
11578     game_frame_delay_value = 0;
11579
11580   /* ---------- main game synchronization point ---------- */
11581
11582   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11583
11584   if (network_playing && !network_player_action_received)
11585   {
11586     /* try to get network player actions in time */
11587
11588 #if defined(NETWORK_AVALIABLE)
11589     /* last chance to get network player actions without main loop delay */
11590     HandleNetworking();
11591 #endif
11592
11593     /* game was quit by network peer */
11594     if (game_status != GAME_MODE_PLAYING)
11595       return;
11596
11597     if (!network_player_action_received)
11598       return;           /* failed to get network player actions in time */
11599
11600     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11601   }
11602
11603   if (tape.pausing)
11604     return;
11605
11606   /* at this point we know that we really continue executing the game */
11607
11608   network_player_action_received = FALSE;
11609
11610   /* when playing tape, read previously recorded player input from tape data */
11611   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11612
11613 #if 1
11614   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11615   if (tape.pausing)
11616     return;
11617 #endif
11618
11619   if (tape.set_centered_player)
11620   {
11621     game.centered_player_nr_next = tape.centered_player_nr_next;
11622     game.set_centered_player = TRUE;
11623   }
11624
11625   for (i = 0; i < MAX_PLAYERS; i++)
11626   {
11627     summarized_player_action |= stored_player[i].action;
11628
11629     if (!network_playing)
11630       stored_player[i].effective_action = stored_player[i].action;
11631   }
11632
11633 #if defined(NETWORK_AVALIABLE)
11634   if (network_playing)
11635     SendToServer_MovePlayer(summarized_player_action);
11636 #endif
11637
11638   if (!options.network && !setup.team_mode)
11639     local_player->effective_action = summarized_player_action;
11640
11641   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11642   {
11643     for (i = 0; i < MAX_PLAYERS; i++)
11644       stored_player[i].effective_action =
11645         (i == game.centered_player_nr ? summarized_player_action : 0);
11646   }
11647
11648   if (recorded_player_action != NULL)
11649     for (i = 0; i < MAX_PLAYERS; i++)
11650       stored_player[i].effective_action = recorded_player_action[i];
11651
11652   for (i = 0; i < MAX_PLAYERS; i++)
11653   {
11654     tape_action[i] = stored_player[i].effective_action;
11655
11656     /* (this can only happen in the R'n'D game engine) */
11657     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11658       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11659   }
11660
11661   /* only record actions from input devices, but not programmed actions */
11662   if (tape.recording)
11663     TapeRecordAction(tape_action);
11664
11665   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11666   {
11667     GameActions_EM_Main();
11668   }
11669   else
11670   {
11671     GameActions_RND();
11672   }
11673 }
11674
11675 void GameActions_EM_Main()
11676 {
11677   byte effective_action[MAX_PLAYERS];
11678   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11679   int i;
11680
11681   for (i = 0; i < MAX_PLAYERS; i++)
11682     effective_action[i] = stored_player[i].effective_action;
11683
11684   GameActions_EM(effective_action, warp_mode);
11685
11686   CheckLevelTime();
11687
11688   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11689 }
11690
11691 void GameActions_RND()
11692 {
11693   int magic_wall_x = 0, magic_wall_y = 0;
11694   int i, x, y, element, graphic;
11695
11696   InitPlayfieldScanModeVars();
11697
11698 #if USE_ONE_MORE_CHANGE_PER_FRAME
11699   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11700   {
11701     SCAN_PLAYFIELD(x, y)
11702     {
11703       ChangeCount[x][y] = 0;
11704       ChangeEvent[x][y] = -1;
11705     }
11706   }
11707 #endif
11708
11709   if (game.set_centered_player)
11710   {
11711     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11712
11713     /* switching to "all players" only possible if all players fit to screen */
11714     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11715     {
11716       game.centered_player_nr_next = game.centered_player_nr;
11717       game.set_centered_player = FALSE;
11718     }
11719
11720     /* do not switch focus to non-existing (or non-active) player */
11721     if (game.centered_player_nr_next >= 0 &&
11722         !stored_player[game.centered_player_nr_next].active)
11723     {
11724       game.centered_player_nr_next = game.centered_player_nr;
11725       game.set_centered_player = FALSE;
11726     }
11727   }
11728
11729   if (game.set_centered_player &&
11730       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11731   {
11732     int sx, sy;
11733
11734     if (game.centered_player_nr_next == -1)
11735     {
11736       setScreenCenteredToAllPlayers(&sx, &sy);
11737     }
11738     else
11739     {
11740       sx = stored_player[game.centered_player_nr_next].jx;
11741       sy = stored_player[game.centered_player_nr_next].jy;
11742     }
11743
11744     game.centered_player_nr = game.centered_player_nr_next;
11745     game.set_centered_player = FALSE;
11746
11747     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11748     DrawGameDoorValues();
11749   }
11750
11751   for (i = 0; i < MAX_PLAYERS; i++)
11752   {
11753     int actual_player_action = stored_player[i].effective_action;
11754
11755 #if 1
11756     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11757        - rnd_equinox_tetrachloride 048
11758        - rnd_equinox_tetrachloride_ii 096
11759        - rnd_emanuel_schmieg 002
11760        - doctor_sloan_ww 001, 020
11761     */
11762     if (stored_player[i].MovPos == 0)
11763       CheckGravityMovement(&stored_player[i]);
11764 #endif
11765
11766     /* overwrite programmed action with tape action */
11767     if (stored_player[i].programmed_action)
11768       actual_player_action = stored_player[i].programmed_action;
11769
11770     PlayerActions(&stored_player[i], actual_player_action);
11771
11772     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11773   }
11774
11775   ScrollScreen(NULL, SCROLL_GO_ON);
11776
11777   /* for backwards compatibility, the following code emulates a fixed bug that
11778      occured when pushing elements (causing elements that just made their last
11779      pushing step to already (if possible) make their first falling step in the
11780      same game frame, which is bad); this code is also needed to use the famous
11781      "spring push bug" which is used in older levels and might be wanted to be
11782      used also in newer levels, but in this case the buggy pushing code is only
11783      affecting the "spring" element and no other elements */
11784
11785   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11786   {
11787     for (i = 0; i < MAX_PLAYERS; i++)
11788     {
11789       struct PlayerInfo *player = &stored_player[i];
11790       int x = player->jx;
11791       int y = player->jy;
11792
11793       if (player->active && player->is_pushing && player->is_moving &&
11794           IS_MOVING(x, y) &&
11795           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11796            Feld[x][y] == EL_SPRING))
11797       {
11798         ContinueMoving(x, y);
11799
11800         /* continue moving after pushing (this is actually a bug) */
11801         if (!IS_MOVING(x, y))
11802           Stop[x][y] = FALSE;
11803       }
11804     }
11805   }
11806
11807 #if 0
11808   debug_print_timestamp(0, "start main loop profiling");
11809 #endif
11810
11811   SCAN_PLAYFIELD(x, y)
11812   {
11813     ChangeCount[x][y] = 0;
11814     ChangeEvent[x][y] = -1;
11815
11816     /* this must be handled before main playfield loop */
11817     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11818     {
11819       MovDelay[x][y]--;
11820       if (MovDelay[x][y] <= 0)
11821         RemoveField(x, y);
11822     }
11823
11824 #if USE_NEW_SNAP_DELAY
11825     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11826     {
11827       MovDelay[x][y]--;
11828       if (MovDelay[x][y] <= 0)
11829       {
11830         RemoveField(x, y);
11831         DrawLevelField(x, y);
11832
11833         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11834       }
11835     }
11836 #endif
11837
11838 #if DEBUG
11839     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11840     {
11841       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11842       printf("GameActions(): This should never happen!\n");
11843
11844       ChangePage[x][y] = -1;
11845     }
11846 #endif
11847
11848     Stop[x][y] = FALSE;
11849     if (WasJustMoving[x][y] > 0)
11850       WasJustMoving[x][y]--;
11851     if (WasJustFalling[x][y] > 0)
11852       WasJustFalling[x][y]--;
11853     if (CheckCollision[x][y] > 0)
11854       CheckCollision[x][y]--;
11855     if (CheckImpact[x][y] > 0)
11856       CheckImpact[x][y]--;
11857
11858     GfxFrame[x][y]++;
11859
11860     /* reset finished pushing action (not done in ContinueMoving() to allow
11861        continuous pushing animation for elements with zero push delay) */
11862     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11863     {
11864       ResetGfxAnimation(x, y);
11865       DrawLevelField(x, y);
11866     }
11867
11868 #if DEBUG
11869     if (IS_BLOCKED(x, y))
11870     {
11871       int oldx, oldy;
11872
11873       Blocked2Moving(x, y, &oldx, &oldy);
11874       if (!IS_MOVING(oldx, oldy))
11875       {
11876         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11877         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11878         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11879         printf("GameActions(): This should never happen!\n");
11880       }
11881     }
11882 #endif
11883   }
11884
11885 #if 0
11886   debug_print_timestamp(0, "- time for pre-main loop:");
11887 #endif
11888
11889 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11890   SCAN_PLAYFIELD(x, y)
11891   {
11892     element = Feld[x][y];
11893     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11894
11895 #if 1
11896     {
11897 #if 1
11898       int element2 = element;
11899       int graphic2 = graphic;
11900 #else
11901       int element2 = Feld[x][y];
11902       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11903 #endif
11904       int last_gfx_frame = GfxFrame[x][y];
11905
11906       if (graphic_info[graphic2].anim_global_sync)
11907         GfxFrame[x][y] = FrameCounter;
11908       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11909         GfxFrame[x][y] = CustomValue[x][y];
11910       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11911         GfxFrame[x][y] = element_info[element2].collect_score;
11912       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11913         GfxFrame[x][y] = ChangeDelay[x][y];
11914
11915       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11916         DrawLevelGraphicAnimation(x, y, graphic2);
11917     }
11918 #else
11919     ResetGfxFrame(x, y, TRUE);
11920 #endif
11921
11922 #if 1
11923     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11924         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11925       ResetRandomAnimationValue(x, y);
11926 #endif
11927
11928 #if 1
11929     SetRandomAnimationValue(x, y);
11930 #endif
11931
11932 #if 1
11933     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11934 #endif
11935   }
11936 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11937
11938 #if 0
11939   debug_print_timestamp(0, "- time for TEST loop:     -->");
11940 #endif
11941
11942   SCAN_PLAYFIELD(x, y)
11943   {
11944     element = Feld[x][y];
11945     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11946
11947     ResetGfxFrame(x, y, TRUE);
11948
11949     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11950         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11951       ResetRandomAnimationValue(x, y);
11952
11953     SetRandomAnimationValue(x, y);
11954
11955     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11956
11957     if (IS_INACTIVE(element))
11958     {
11959       if (IS_ANIMATED(graphic))
11960         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11961
11962       continue;
11963     }
11964
11965     /* this may take place after moving, so 'element' may have changed */
11966     if (IS_CHANGING(x, y) &&
11967         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11968     {
11969       int page = element_info[element].event_page_nr[CE_DELAY];
11970
11971 #if 1
11972       HandleElementChange(x, y, page);
11973 #else
11974       if (CAN_CHANGE(element))
11975         HandleElementChange(x, y, page);
11976
11977       if (HAS_ACTION(element))
11978         ExecuteCustomElementAction(x, y, element, page);
11979 #endif
11980
11981       element = Feld[x][y];
11982       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11983     }
11984
11985 #if 0   // ---------------------------------------------------------------------
11986
11987     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11988     {
11989       StartMoving(x, y);
11990
11991       element = Feld[x][y];
11992       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11993
11994       if (IS_ANIMATED(graphic) &&
11995           !IS_MOVING(x, y) &&
11996           !Stop[x][y])
11997         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11998
11999       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12000         DrawTwinkleOnField(x, y);
12001     }
12002     else if (IS_MOVING(x, y))
12003       ContinueMoving(x, y);
12004     else
12005     {
12006       switch (element)
12007       {
12008         case EL_ACID:
12009         case EL_EXIT_OPEN:
12010         case EL_EM_EXIT_OPEN:
12011         case EL_SP_EXIT_OPEN:
12012         case EL_STEEL_EXIT_OPEN:
12013         case EL_EM_STEEL_EXIT_OPEN:
12014         case EL_SP_TERMINAL:
12015         case EL_SP_TERMINAL_ACTIVE:
12016         case EL_EXTRA_TIME:
12017         case EL_SHIELD_NORMAL:
12018         case EL_SHIELD_DEADLY:
12019           if (IS_ANIMATED(graphic))
12020             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12021           break;
12022
12023         case EL_DYNAMITE_ACTIVE:
12024         case EL_EM_DYNAMITE_ACTIVE:
12025         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12026         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12027         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12028         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12029         case EL_SP_DISK_RED_ACTIVE:
12030           CheckDynamite(x, y);
12031           break;
12032
12033         case EL_AMOEBA_GROWING:
12034           AmoebeWaechst(x, y);
12035           break;
12036
12037         case EL_AMOEBA_SHRINKING:
12038           AmoebaDisappearing(x, y);
12039           break;
12040
12041 #if !USE_NEW_AMOEBA_CODE
12042         case EL_AMOEBA_WET:
12043         case EL_AMOEBA_DRY:
12044         case EL_AMOEBA_FULL:
12045         case EL_BD_AMOEBA:
12046         case EL_EMC_DRIPPER:
12047           AmoebeAbleger(x, y);
12048           break;
12049 #endif
12050
12051         case EL_GAME_OF_LIFE:
12052         case EL_BIOMAZE:
12053           Life(x, y);
12054           break;
12055
12056         case EL_EXIT_CLOSED:
12057           CheckExit(x, y);
12058           break;
12059
12060         case EL_EM_EXIT_CLOSED:
12061           CheckExitEM(x, y);
12062           break;
12063
12064         case EL_STEEL_EXIT_CLOSED:
12065           CheckExitSteel(x, y);
12066           break;
12067
12068         case EL_EM_STEEL_EXIT_CLOSED:
12069           CheckExitSteelEM(x, y);
12070           break;
12071
12072         case EL_SP_EXIT_CLOSED:
12073           CheckExitSP(x, y);
12074           break;
12075
12076         case EL_EXPANDABLE_WALL_GROWING:
12077         case EL_EXPANDABLE_STEELWALL_GROWING:
12078           MauerWaechst(x, y);
12079           break;
12080
12081         case EL_EXPANDABLE_WALL:
12082         case EL_EXPANDABLE_WALL_HORIZONTAL:
12083         case EL_EXPANDABLE_WALL_VERTICAL:
12084         case EL_EXPANDABLE_WALL_ANY:
12085         case EL_BD_EXPANDABLE_WALL:
12086           MauerAbleger(x, y);
12087           break;
12088
12089         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12090         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12091         case EL_EXPANDABLE_STEELWALL_ANY:
12092           MauerAblegerStahl(x, y);
12093           break;
12094
12095         case EL_FLAMES:
12096           CheckForDragon(x, y);
12097           break;
12098
12099         case EL_EXPLOSION:
12100           break;
12101
12102         case EL_ELEMENT_SNAPPING:
12103         case EL_DIAGONAL_SHRINKING:
12104         case EL_DIAGONAL_GROWING:
12105         {
12106           graphic =
12107             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12108
12109           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12110           break;
12111         }
12112
12113         default:
12114           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12115             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12116           break;
12117       }
12118     }
12119
12120 #else   // ---------------------------------------------------------------------
12121
12122     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12123     {
12124       StartMoving(x, y);
12125
12126       element = Feld[x][y];
12127       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12128
12129       if (IS_ANIMATED(graphic) &&
12130           !IS_MOVING(x, y) &&
12131           !Stop[x][y])
12132         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12133
12134       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12135         DrawTwinkleOnField(x, y);
12136     }
12137     else if ((element == EL_ACID ||
12138               element == EL_EXIT_OPEN ||
12139               element == EL_EM_EXIT_OPEN ||
12140               element == EL_SP_EXIT_OPEN ||
12141               element == EL_STEEL_EXIT_OPEN ||
12142               element == EL_EM_STEEL_EXIT_OPEN ||
12143               element == EL_SP_TERMINAL ||
12144               element == EL_SP_TERMINAL_ACTIVE ||
12145               element == EL_EXTRA_TIME ||
12146               element == EL_SHIELD_NORMAL ||
12147               element == EL_SHIELD_DEADLY) &&
12148              IS_ANIMATED(graphic))
12149       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12150     else if (IS_MOVING(x, y))
12151       ContinueMoving(x, y);
12152     else if (IS_ACTIVE_BOMB(element))
12153       CheckDynamite(x, y);
12154     else if (element == EL_AMOEBA_GROWING)
12155       AmoebeWaechst(x, y);
12156     else if (element == EL_AMOEBA_SHRINKING)
12157       AmoebaDisappearing(x, y);
12158
12159 #if !USE_NEW_AMOEBA_CODE
12160     else if (IS_AMOEBALIVE(element))
12161       AmoebeAbleger(x, y);
12162 #endif
12163
12164     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12165       Life(x, y);
12166     else if (element == EL_EXIT_CLOSED)
12167       CheckExit(x, y);
12168     else if (element == EL_EM_EXIT_CLOSED)
12169       CheckExitEM(x, y);
12170     else if (element == EL_STEEL_EXIT_CLOSED)
12171       CheckExitSteel(x, y);
12172     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12173       CheckExitSteelEM(x, y);
12174     else if (element == EL_SP_EXIT_CLOSED)
12175       CheckExitSP(x, y);
12176     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12177              element == EL_EXPANDABLE_STEELWALL_GROWING)
12178       MauerWaechst(x, y);
12179     else if (element == EL_EXPANDABLE_WALL ||
12180              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12181              element == EL_EXPANDABLE_WALL_VERTICAL ||
12182              element == EL_EXPANDABLE_WALL_ANY ||
12183              element == EL_BD_EXPANDABLE_WALL)
12184       MauerAbleger(x, y);
12185     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12186              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12187              element == EL_EXPANDABLE_STEELWALL_ANY)
12188       MauerAblegerStahl(x, y);
12189     else if (element == EL_FLAMES)
12190       CheckForDragon(x, y);
12191     else if (element == EL_EXPLOSION)
12192       ; /* drawing of correct explosion animation is handled separately */
12193     else if (element == EL_ELEMENT_SNAPPING ||
12194              element == EL_DIAGONAL_SHRINKING ||
12195              element == EL_DIAGONAL_GROWING)
12196     {
12197       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12198
12199       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12200     }
12201     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12202       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12203
12204 #endif  // ---------------------------------------------------------------------
12205
12206     if (IS_BELT_ACTIVE(element))
12207       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12208
12209     if (game.magic_wall_active)
12210     {
12211       int jx = local_player->jx, jy = local_player->jy;
12212
12213       /* play the element sound at the position nearest to the player */
12214       if ((element == EL_MAGIC_WALL_FULL ||
12215            element == EL_MAGIC_WALL_ACTIVE ||
12216            element == EL_MAGIC_WALL_EMPTYING ||
12217            element == EL_BD_MAGIC_WALL_FULL ||
12218            element == EL_BD_MAGIC_WALL_ACTIVE ||
12219            element == EL_BD_MAGIC_WALL_EMPTYING ||
12220            element == EL_DC_MAGIC_WALL_FULL ||
12221            element == EL_DC_MAGIC_WALL_ACTIVE ||
12222            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12223           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12224       {
12225         magic_wall_x = x;
12226         magic_wall_y = y;
12227       }
12228     }
12229   }
12230
12231 #if 0
12232   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12233 #endif
12234
12235 #if USE_NEW_AMOEBA_CODE
12236   /* new experimental amoeba growth stuff */
12237   if (!(FrameCounter % 8))
12238   {
12239     static unsigned long random = 1684108901;
12240
12241     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12242     {
12243       x = RND(lev_fieldx);
12244       y = RND(lev_fieldy);
12245       element = Feld[x][y];
12246
12247       if (!IS_PLAYER(x,y) &&
12248           (element == EL_EMPTY ||
12249            CAN_GROW_INTO(element) ||
12250            element == EL_QUICKSAND_EMPTY ||
12251            element == EL_QUICKSAND_FAST_EMPTY ||
12252            element == EL_ACID_SPLASH_LEFT ||
12253            element == EL_ACID_SPLASH_RIGHT))
12254       {
12255         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12256             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12257             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12258             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12259           Feld[x][y] = EL_AMOEBA_DROP;
12260       }
12261
12262       random = random * 129 + 1;
12263     }
12264   }
12265 #endif
12266
12267 #if 0
12268   if (game.explosions_delayed)
12269 #endif
12270   {
12271     game.explosions_delayed = FALSE;
12272
12273     SCAN_PLAYFIELD(x, y)
12274     {
12275       element = Feld[x][y];
12276
12277       if (ExplodeField[x][y])
12278         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12279       else if (element == EL_EXPLOSION)
12280         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12281
12282       ExplodeField[x][y] = EX_TYPE_NONE;
12283     }
12284
12285     game.explosions_delayed = TRUE;
12286   }
12287
12288   if (game.magic_wall_active)
12289   {
12290     if (!(game.magic_wall_time_left % 4))
12291     {
12292       int element = Feld[magic_wall_x][magic_wall_y];
12293
12294       if (element == EL_BD_MAGIC_WALL_FULL ||
12295           element == EL_BD_MAGIC_WALL_ACTIVE ||
12296           element == EL_BD_MAGIC_WALL_EMPTYING)
12297         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12298       else if (element == EL_DC_MAGIC_WALL_FULL ||
12299                element == EL_DC_MAGIC_WALL_ACTIVE ||
12300                element == EL_DC_MAGIC_WALL_EMPTYING)
12301         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12302       else
12303         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12304     }
12305
12306     if (game.magic_wall_time_left > 0)
12307     {
12308       game.magic_wall_time_left--;
12309
12310       if (!game.magic_wall_time_left)
12311       {
12312         SCAN_PLAYFIELD(x, y)
12313         {
12314           element = Feld[x][y];
12315
12316           if (element == EL_MAGIC_WALL_ACTIVE ||
12317               element == EL_MAGIC_WALL_FULL)
12318           {
12319             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12320             DrawLevelField(x, y);
12321           }
12322           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12323                    element == EL_BD_MAGIC_WALL_FULL)
12324           {
12325             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12326             DrawLevelField(x, y);
12327           }
12328           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12329                    element == EL_DC_MAGIC_WALL_FULL)
12330           {
12331             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12332             DrawLevelField(x, y);
12333           }
12334         }
12335
12336         game.magic_wall_active = FALSE;
12337       }
12338     }
12339   }
12340
12341   if (game.light_time_left > 0)
12342   {
12343     game.light_time_left--;
12344
12345     if (game.light_time_left == 0)
12346       RedrawAllLightSwitchesAndInvisibleElements();
12347   }
12348
12349   if (game.timegate_time_left > 0)
12350   {
12351     game.timegate_time_left--;
12352
12353     if (game.timegate_time_left == 0)
12354       CloseAllOpenTimegates();
12355   }
12356
12357   if (game.lenses_time_left > 0)
12358   {
12359     game.lenses_time_left--;
12360
12361     if (game.lenses_time_left == 0)
12362       RedrawAllInvisibleElementsForLenses();
12363   }
12364
12365   if (game.magnify_time_left > 0)
12366   {
12367     game.magnify_time_left--;
12368
12369     if (game.magnify_time_left == 0)
12370       RedrawAllInvisibleElementsForMagnifier();
12371   }
12372
12373   for (i = 0; i < MAX_PLAYERS; i++)
12374   {
12375     struct PlayerInfo *player = &stored_player[i];
12376
12377     if (SHIELD_ON(player))
12378     {
12379       if (player->shield_deadly_time_left)
12380         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12381       else if (player->shield_normal_time_left)
12382         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12383     }
12384   }
12385
12386   CheckLevelTime();
12387
12388   DrawAllPlayers();
12389   PlayAllPlayersSound();
12390
12391   if (options.debug)                    /* calculate frames per second */
12392   {
12393     static unsigned long fps_counter = 0;
12394     static int fps_frames = 0;
12395     unsigned long fps_delay_ms = Counter() - fps_counter;
12396
12397     fps_frames++;
12398
12399     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12400     {
12401       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12402
12403       fps_frames = 0;
12404       fps_counter = Counter();
12405     }
12406
12407     redraw_mask |= REDRAW_FPS;
12408   }
12409
12410   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12411
12412   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12413   {
12414     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12415
12416     local_player->show_envelope = 0;
12417   }
12418
12419 #if 0
12420   debug_print_timestamp(0, "stop main loop profiling ");
12421   printf("----------------------------------------------------------\n");
12422 #endif
12423
12424   /* use random number generator in every frame to make it less predictable */
12425   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12426     RND(1);
12427 }
12428
12429 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12430 {
12431   int min_x = x, min_y = y, max_x = x, max_y = y;
12432   int i;
12433
12434   for (i = 0; i < MAX_PLAYERS; i++)
12435   {
12436     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12437
12438     if (!stored_player[i].active || &stored_player[i] == player)
12439       continue;
12440
12441     min_x = MIN(min_x, jx);
12442     min_y = MIN(min_y, jy);
12443     max_x = MAX(max_x, jx);
12444     max_y = MAX(max_y, jy);
12445   }
12446
12447   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12448 }
12449
12450 static boolean AllPlayersInVisibleScreen()
12451 {
12452   int i;
12453
12454   for (i = 0; i < MAX_PLAYERS; i++)
12455   {
12456     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12457
12458     if (!stored_player[i].active)
12459       continue;
12460
12461     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12462       return FALSE;
12463   }
12464
12465   return TRUE;
12466 }
12467
12468 void ScrollLevel(int dx, int dy)
12469 {
12470 #if 1
12471   static Bitmap *bitmap_db_field2 = NULL;
12472   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12473   int x, y;
12474 #else
12475   int i, x, y;
12476 #endif
12477
12478 #if 0
12479   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12480   /* only horizontal XOR vertical scroll direction allowed */
12481   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12482     return;
12483 #endif
12484
12485 #if 1
12486   if (bitmap_db_field2 == NULL)
12487     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12488
12489   /* needed when blitting directly to same bitmap -- should not be needed with
12490      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12491   BlitBitmap(drawto_field, bitmap_db_field2,
12492              FX + TILEX * (dx == -1) - softscroll_offset,
12493              FY + TILEY * (dy == -1) - softscroll_offset,
12494              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12495              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12496              FX + TILEX * (dx == 1) - softscroll_offset,
12497              FY + TILEY * (dy == 1) - softscroll_offset);
12498   BlitBitmap(bitmap_db_field2, drawto_field,
12499              FX + TILEX * (dx == 1) - softscroll_offset,
12500              FY + TILEY * (dy == 1) - softscroll_offset,
12501              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12502              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12503              FX + TILEX * (dx == 1) - softscroll_offset,
12504              FY + TILEY * (dy == 1) - softscroll_offset);
12505
12506 #else
12507
12508 #if 0
12509   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12510   int xsize = (BX2 - BX1 + 1);
12511   int ysize = (BY2 - BY1 + 1);
12512   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12513   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12514   int step  = (start < end ? +1 : -1);
12515
12516   for (i = start; i != end; i += step)
12517   {
12518     BlitBitmap(drawto_field, drawto_field,
12519                FX + TILEX * (dx != 0 ? i + step : 0),
12520                FY + TILEY * (dy != 0 ? i + step : 0),
12521                TILEX * (dx != 0 ? 1 : xsize),
12522                TILEY * (dy != 0 ? 1 : ysize),
12523                FX + TILEX * (dx != 0 ? i : 0),
12524                FY + TILEY * (dy != 0 ? i : 0));
12525   }
12526
12527 #else
12528
12529   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12530
12531   BlitBitmap(drawto_field, drawto_field,
12532              FX + TILEX * (dx == -1) - softscroll_offset,
12533              FY + TILEY * (dy == -1) - softscroll_offset,
12534              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12535              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12536              FX + TILEX * (dx == 1) - softscroll_offset,
12537              FY + TILEY * (dy == 1) - softscroll_offset);
12538 #endif
12539 #endif
12540
12541   if (dx != 0)
12542   {
12543     x = (dx == 1 ? BX1 : BX2);
12544     for (y = BY1; y <= BY2; y++)
12545       DrawScreenField(x, y);
12546   }
12547
12548   if (dy != 0)
12549   {
12550     y = (dy == 1 ? BY1 : BY2);
12551     for (x = BX1; x <= BX2; x++)
12552       DrawScreenField(x, y);
12553   }
12554
12555   redraw_mask |= REDRAW_FIELD;
12556 }
12557
12558 static boolean canFallDown(struct PlayerInfo *player)
12559 {
12560   int jx = player->jx, jy = player->jy;
12561
12562   return (IN_LEV_FIELD(jx, jy + 1) &&
12563           (IS_FREE(jx, jy + 1) ||
12564            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12565           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12566           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12567 }
12568
12569 static boolean canPassField(int x, int y, int move_dir)
12570 {
12571   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12572   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12573   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12574   int nextx = x + dx;
12575   int nexty = y + dy;
12576   int element = Feld[x][y];
12577
12578   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12579           !CAN_MOVE(element) &&
12580           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12581           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12582           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12583 }
12584
12585 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12586 {
12587   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12588   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12589   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12590   int newx = x + dx;
12591   int newy = y + dy;
12592
12593   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12594           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12595           (IS_DIGGABLE(Feld[newx][newy]) ||
12596            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12597            canPassField(newx, newy, move_dir)));
12598 }
12599
12600 static void CheckGravityMovement(struct PlayerInfo *player)
12601 {
12602 #if USE_PLAYER_GRAVITY
12603   if (player->gravity && !player->programmed_action)
12604 #else
12605   if (game.gravity && !player->programmed_action)
12606 #endif
12607   {
12608     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12609     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12610     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12611     int jx = player->jx, jy = player->jy;
12612     boolean player_is_moving_to_valid_field =
12613       (!player_is_snapping &&
12614        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12615         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12616     boolean player_can_fall_down = canFallDown(player);
12617
12618     if (player_can_fall_down &&
12619         !player_is_moving_to_valid_field)
12620       player->programmed_action = MV_DOWN;
12621   }
12622 }
12623
12624 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12625 {
12626   return CheckGravityMovement(player);
12627
12628 #if USE_PLAYER_GRAVITY
12629   if (player->gravity && !player->programmed_action)
12630 #else
12631   if (game.gravity && !player->programmed_action)
12632 #endif
12633   {
12634     int jx = player->jx, jy = player->jy;
12635     boolean field_under_player_is_free =
12636       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12637     boolean player_is_standing_on_valid_field =
12638       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12639        (IS_WALKABLE(Feld[jx][jy]) &&
12640         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12641
12642     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12643       player->programmed_action = MV_DOWN;
12644   }
12645 }
12646
12647 /*
12648   MovePlayerOneStep()
12649   -----------------------------------------------------------------------------
12650   dx, dy:               direction (non-diagonal) to try to move the player to
12651   real_dx, real_dy:     direction as read from input device (can be diagonal)
12652 */
12653
12654 boolean MovePlayerOneStep(struct PlayerInfo *player,
12655                           int dx, int dy, int real_dx, int real_dy)
12656 {
12657   int jx = player->jx, jy = player->jy;
12658   int new_jx = jx + dx, new_jy = jy + dy;
12659 #if !USE_FIXED_DONT_RUN_INTO
12660   int element;
12661 #endif
12662   int can_move;
12663   boolean player_can_move = !player->cannot_move;
12664
12665   if (!player->active || (!dx && !dy))
12666     return MP_NO_ACTION;
12667
12668   player->MovDir = (dx < 0 ? MV_LEFT :
12669                     dx > 0 ? MV_RIGHT :
12670                     dy < 0 ? MV_UP :
12671                     dy > 0 ? MV_DOWN :  MV_NONE);
12672
12673   if (!IN_LEV_FIELD(new_jx, new_jy))
12674     return MP_NO_ACTION;
12675
12676   if (!player_can_move)
12677   {
12678     if (player->MovPos == 0)
12679     {
12680       player->is_moving = FALSE;
12681       player->is_digging = FALSE;
12682       player->is_collecting = FALSE;
12683       player->is_snapping = FALSE;
12684       player->is_pushing = FALSE;
12685     }
12686   }
12687
12688 #if 1
12689   if (!options.network && game.centered_player_nr == -1 &&
12690       !AllPlayersInSight(player, new_jx, new_jy))
12691     return MP_NO_ACTION;
12692 #else
12693   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12694     return MP_NO_ACTION;
12695 #endif
12696
12697 #if !USE_FIXED_DONT_RUN_INTO
12698   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12699
12700   /* (moved to DigField()) */
12701   if (player_can_move && DONT_RUN_INTO(element))
12702   {
12703     if (element == EL_ACID && dx == 0 && dy == 1)
12704     {
12705       SplashAcid(new_jx, new_jy);
12706       Feld[jx][jy] = EL_PLAYER_1;
12707       InitMovingField(jx, jy, MV_DOWN);
12708       Store[jx][jy] = EL_ACID;
12709       ContinueMoving(jx, jy);
12710       BuryPlayer(player);
12711     }
12712     else
12713       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12714
12715     return MP_MOVING;
12716   }
12717 #endif
12718
12719   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12720   if (can_move != MP_MOVING)
12721     return can_move;
12722
12723   /* check if DigField() has caused relocation of the player */
12724   if (player->jx != jx || player->jy != jy)
12725     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12726
12727   StorePlayer[jx][jy] = 0;
12728   player->last_jx = jx;
12729   player->last_jy = jy;
12730   player->jx = new_jx;
12731   player->jy = new_jy;
12732   StorePlayer[new_jx][new_jy] = player->element_nr;
12733
12734   if (player->move_delay_value_next != -1)
12735   {
12736     player->move_delay_value = player->move_delay_value_next;
12737     player->move_delay_value_next = -1;
12738   }
12739
12740   player->MovPos =
12741     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12742
12743   player->step_counter++;
12744
12745   PlayerVisit[jx][jy] = FrameCounter;
12746
12747 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12748   player->is_moving = TRUE;
12749 #endif
12750
12751 #if 1
12752   /* should better be called in MovePlayer(), but this breaks some tapes */
12753   ScrollPlayer(player, SCROLL_INIT);
12754 #endif
12755
12756   return MP_MOVING;
12757 }
12758
12759 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12760 {
12761   int jx = player->jx, jy = player->jy;
12762   int old_jx = jx, old_jy = jy;
12763   int moved = MP_NO_ACTION;
12764
12765   if (!player->active)
12766     return FALSE;
12767
12768   if (!dx && !dy)
12769   {
12770     if (player->MovPos == 0)
12771     {
12772       player->is_moving = FALSE;
12773       player->is_digging = FALSE;
12774       player->is_collecting = FALSE;
12775       player->is_snapping = FALSE;
12776       player->is_pushing = FALSE;
12777     }
12778
12779     return FALSE;
12780   }
12781
12782   if (player->move_delay > 0)
12783     return FALSE;
12784
12785   player->move_delay = -1;              /* set to "uninitialized" value */
12786
12787   /* store if player is automatically moved to next field */
12788   player->is_auto_moving = (player->programmed_action != MV_NONE);
12789
12790   /* remove the last programmed player action */
12791   player->programmed_action = 0;
12792
12793   if (player->MovPos)
12794   {
12795     /* should only happen if pre-1.2 tape recordings are played */
12796     /* this is only for backward compatibility */
12797
12798     int original_move_delay_value = player->move_delay_value;
12799
12800 #if DEBUG
12801     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12802            tape.counter);
12803 #endif
12804
12805     /* scroll remaining steps with finest movement resolution */
12806     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12807
12808     while (player->MovPos)
12809     {
12810       ScrollPlayer(player, SCROLL_GO_ON);
12811       ScrollScreen(NULL, SCROLL_GO_ON);
12812
12813       AdvanceFrameAndPlayerCounters(player->index_nr);
12814
12815       DrawAllPlayers();
12816       BackToFront();
12817     }
12818
12819     player->move_delay_value = original_move_delay_value;
12820   }
12821
12822   player->is_active = FALSE;
12823
12824   if (player->last_move_dir & MV_HORIZONTAL)
12825   {
12826     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12827       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12828   }
12829   else
12830   {
12831     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12832       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12833   }
12834
12835 #if USE_FIXED_BORDER_RUNNING_GFX
12836   if (!moved && !player->is_active)
12837   {
12838     player->is_moving = FALSE;
12839     player->is_digging = FALSE;
12840     player->is_collecting = FALSE;
12841     player->is_snapping = FALSE;
12842     player->is_pushing = FALSE;
12843   }
12844 #endif
12845
12846   jx = player->jx;
12847   jy = player->jy;
12848
12849 #if 1
12850   if (moved & MP_MOVING && !ScreenMovPos &&
12851       (player->index_nr == game.centered_player_nr ||
12852        game.centered_player_nr == -1))
12853 #else
12854   if (moved & MP_MOVING && !ScreenMovPos &&
12855       (player == local_player || !options.network))
12856 #endif
12857   {
12858     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12859     int offset = game.scroll_delay_value;
12860
12861     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12862     {
12863       /* actual player has left the screen -- scroll in that direction */
12864       if (jx != old_jx)         /* player has moved horizontally */
12865         scroll_x += (jx - old_jx);
12866       else                      /* player has moved vertically */
12867         scroll_y += (jy - old_jy);
12868     }
12869     else
12870     {
12871       if (jx != old_jx)         /* player has moved horizontally */
12872       {
12873         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12874             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12875           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12876
12877         /* don't scroll over playfield boundaries */
12878         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12879           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12880
12881         /* don't scroll more than one field at a time */
12882         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12883
12884         /* don't scroll against the player's moving direction */
12885         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12886             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12887           scroll_x = old_scroll_x;
12888       }
12889       else                      /* player has moved vertically */
12890       {
12891         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12892             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12893           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12894
12895         /* don't scroll over playfield boundaries */
12896         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12897           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12898
12899         /* don't scroll more than one field at a time */
12900         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12901
12902         /* don't scroll against the player's moving direction */
12903         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12904             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12905           scroll_y = old_scroll_y;
12906       }
12907     }
12908
12909     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12910     {
12911 #if 1
12912       if (!options.network && game.centered_player_nr == -1 &&
12913           !AllPlayersInVisibleScreen())
12914       {
12915         scroll_x = old_scroll_x;
12916         scroll_y = old_scroll_y;
12917       }
12918       else
12919 #else
12920       if (!options.network && !AllPlayersInVisibleScreen())
12921       {
12922         scroll_x = old_scroll_x;
12923         scroll_y = old_scroll_y;
12924       }
12925       else
12926 #endif
12927       {
12928         ScrollScreen(player, SCROLL_INIT);
12929         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12930       }
12931     }
12932   }
12933
12934   player->StepFrame = 0;
12935
12936   if (moved & MP_MOVING)
12937   {
12938     if (old_jx != jx && old_jy == jy)
12939       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12940     else if (old_jx == jx && old_jy != jy)
12941       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12942
12943     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12944
12945     player->last_move_dir = player->MovDir;
12946     player->is_moving = TRUE;
12947     player->is_snapping = FALSE;
12948     player->is_switching = FALSE;
12949     player->is_dropping = FALSE;
12950     player->is_dropping_pressed = FALSE;
12951     player->drop_pressed_delay = 0;
12952
12953 #if 0
12954     /* should better be called here than above, but this breaks some tapes */
12955     ScrollPlayer(player, SCROLL_INIT);
12956 #endif
12957   }
12958   else
12959   {
12960     CheckGravityMovementWhenNotMoving(player);
12961
12962     player->is_moving = FALSE;
12963
12964     /* at this point, the player is allowed to move, but cannot move right now
12965        (e.g. because of something blocking the way) -- ensure that the player
12966        is also allowed to move in the next frame (in old versions before 3.1.1,
12967        the player was forced to wait again for eight frames before next try) */
12968
12969     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12970       player->move_delay = 0;   /* allow direct movement in the next frame */
12971   }
12972
12973   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12974     player->move_delay = player->move_delay_value;
12975
12976   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12977   {
12978     TestIfPlayerTouchesBadThing(jx, jy);
12979     TestIfPlayerTouchesCustomElement(jx, jy);
12980   }
12981
12982   if (!player->active)
12983     RemovePlayer(player);
12984
12985   return moved;
12986 }
12987
12988 void ScrollPlayer(struct PlayerInfo *player, int mode)
12989 {
12990   int jx = player->jx, jy = player->jy;
12991   int last_jx = player->last_jx, last_jy = player->last_jy;
12992   int move_stepsize = TILEX / player->move_delay_value;
12993
12994 #if USE_NEW_PLAYER_SPEED
12995   if (!player->active)
12996     return;
12997
12998   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12999     return;
13000 #else
13001   if (!player->active || player->MovPos == 0)
13002     return;
13003 #endif
13004
13005   if (mode == SCROLL_INIT)
13006   {
13007     player->actual_frame_counter = FrameCounter;
13008     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13009
13010     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13011         Feld[last_jx][last_jy] == EL_EMPTY)
13012     {
13013       int last_field_block_delay = 0;   /* start with no blocking at all */
13014       int block_delay_adjustment = player->block_delay_adjustment;
13015
13016       /* if player blocks last field, add delay for exactly one move */
13017       if (player->block_last_field)
13018       {
13019         last_field_block_delay += player->move_delay_value;
13020
13021         /* when blocking enabled, prevent moving up despite gravity */
13022 #if USE_PLAYER_GRAVITY
13023         if (player->gravity && player->MovDir == MV_UP)
13024           block_delay_adjustment = -1;
13025 #else
13026         if (game.gravity && player->MovDir == MV_UP)
13027           block_delay_adjustment = -1;
13028 #endif
13029       }
13030
13031       /* add block delay adjustment (also possible when not blocking) */
13032       last_field_block_delay += block_delay_adjustment;
13033
13034       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13035       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13036     }
13037
13038 #if USE_NEW_PLAYER_SPEED
13039     if (player->MovPos != 0)    /* player has not yet reached destination */
13040       return;
13041 #else
13042     return;
13043 #endif
13044   }
13045   else if (!FrameReached(&player->actual_frame_counter, 1))
13046     return;
13047
13048 #if USE_NEW_PLAYER_SPEED
13049   if (player->MovPos != 0)
13050   {
13051     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13052     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13053
13054     /* before DrawPlayer() to draw correct player graphic for this case */
13055     if (player->MovPos == 0)
13056       CheckGravityMovement(player);
13057   }
13058 #else
13059   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13060   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13061
13062   /* before DrawPlayer() to draw correct player graphic for this case */
13063   if (player->MovPos == 0)
13064     CheckGravityMovement(player);
13065 #endif
13066
13067   if (player->MovPos == 0)      /* player reached destination field */
13068   {
13069     if (player->move_delay_reset_counter > 0)
13070     {
13071       player->move_delay_reset_counter--;
13072
13073       if (player->move_delay_reset_counter == 0)
13074       {
13075         /* continue with normal speed after quickly moving through gate */
13076         HALVE_PLAYER_SPEED(player);
13077
13078         /* be able to make the next move without delay */
13079         player->move_delay = 0;
13080       }
13081     }
13082
13083     player->last_jx = jx;
13084     player->last_jy = jy;
13085
13086     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13087         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13088         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13089         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13090         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13091         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13092     {
13093       DrawPlayer(player);       /* needed here only to cleanup last field */
13094       RemovePlayer(player);
13095
13096       if (local_player->friends_still_needed == 0 ||
13097           IS_SP_ELEMENT(Feld[jx][jy]))
13098         PlayerWins(player);
13099     }
13100
13101     /* this breaks one level: "machine", level 000 */
13102     {
13103       int move_direction = player->MovDir;
13104       int enter_side = MV_DIR_OPPOSITE(move_direction);
13105       int leave_side = move_direction;
13106       int old_jx = last_jx;
13107       int old_jy = last_jy;
13108       int old_element = Feld[old_jx][old_jy];
13109       int new_element = Feld[jx][jy];
13110
13111       if (IS_CUSTOM_ELEMENT(old_element))
13112         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13113                                    CE_LEFT_BY_PLAYER,
13114                                    player->index_bit, leave_side);
13115
13116       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13117                                           CE_PLAYER_LEAVES_X,
13118                                           player->index_bit, leave_side);
13119
13120       if (IS_CUSTOM_ELEMENT(new_element))
13121         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13122                                    player->index_bit, enter_side);
13123
13124       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13125                                           CE_PLAYER_ENTERS_X,
13126                                           player->index_bit, enter_side);
13127
13128       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13129                                         CE_MOVE_OF_X, move_direction);
13130     }
13131
13132     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13133     {
13134       TestIfPlayerTouchesBadThing(jx, jy);
13135       TestIfPlayerTouchesCustomElement(jx, jy);
13136
13137       /* needed because pushed element has not yet reached its destination,
13138          so it would trigger a change event at its previous field location */
13139       if (!player->is_pushing)
13140         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13141
13142       if (!player->active)
13143         RemovePlayer(player);
13144     }
13145
13146     if (!local_player->LevelSolved && level.use_step_counter)
13147     {
13148       int i;
13149
13150       TimePlayed++;
13151
13152       if (TimeLeft > 0)
13153       {
13154         TimeLeft--;
13155
13156         if (TimeLeft <= 10 && setup.time_limit)
13157           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13158
13159 #if 1
13160         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13161
13162         DisplayGameControlValues();
13163 #else
13164         DrawGameValue_Time(TimeLeft);
13165 #endif
13166
13167         if (!TimeLeft && setup.time_limit)
13168           for (i = 0; i < MAX_PLAYERS; i++)
13169             KillPlayer(&stored_player[i]);
13170       }
13171 #if 1
13172       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13173       {
13174         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13175
13176         DisplayGameControlValues();
13177       }
13178 #else
13179       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13180         DrawGameValue_Time(TimePlayed);
13181 #endif
13182     }
13183
13184     if (tape.single_step && tape.recording && !tape.pausing &&
13185         !player->programmed_action)
13186       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13187   }
13188 }
13189
13190 void ScrollScreen(struct PlayerInfo *player, int mode)
13191 {
13192   static unsigned long screen_frame_counter = 0;
13193
13194   if (mode == SCROLL_INIT)
13195   {
13196     /* set scrolling step size according to actual player's moving speed */
13197     ScrollStepSize = TILEX / player->move_delay_value;
13198
13199     screen_frame_counter = FrameCounter;
13200     ScreenMovDir = player->MovDir;
13201     ScreenMovPos = player->MovPos;
13202     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13203     return;
13204   }
13205   else if (!FrameReached(&screen_frame_counter, 1))
13206     return;
13207
13208   if (ScreenMovPos)
13209   {
13210     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13211     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13212     redraw_mask |= REDRAW_FIELD;
13213   }
13214   else
13215     ScreenMovDir = MV_NONE;
13216 }
13217
13218 void TestIfPlayerTouchesCustomElement(int x, int y)
13219 {
13220   static int xy[4][2] =
13221   {
13222     { 0, -1 },
13223     { -1, 0 },
13224     { +1, 0 },
13225     { 0, +1 }
13226   };
13227   static int trigger_sides[4][2] =
13228   {
13229     /* center side       border side */
13230     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13231     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13232     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13233     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13234   };
13235   static int touch_dir[4] =
13236   {
13237     MV_LEFT | MV_RIGHT,
13238     MV_UP   | MV_DOWN,
13239     MV_UP   | MV_DOWN,
13240     MV_LEFT | MV_RIGHT
13241   };
13242   int center_element = Feld[x][y];      /* should always be non-moving! */
13243   int i;
13244
13245   for (i = 0; i < NUM_DIRECTIONS; i++)
13246   {
13247     int xx = x + xy[i][0];
13248     int yy = y + xy[i][1];
13249     int center_side = trigger_sides[i][0];
13250     int border_side = trigger_sides[i][1];
13251     int border_element;
13252
13253     if (!IN_LEV_FIELD(xx, yy))
13254       continue;
13255
13256     if (IS_PLAYER(x, y))
13257     {
13258       struct PlayerInfo *player = PLAYERINFO(x, y);
13259
13260       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13261         border_element = Feld[xx][yy];          /* may be moving! */
13262       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13263         border_element = Feld[xx][yy];
13264       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13265         border_element = MovingOrBlocked2Element(xx, yy);
13266       else
13267         continue;               /* center and border element do not touch */
13268
13269       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13270                                  player->index_bit, border_side);
13271       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13272                                           CE_PLAYER_TOUCHES_X,
13273                                           player->index_bit, border_side);
13274     }
13275     else if (IS_PLAYER(xx, yy))
13276     {
13277       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13278
13279       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13280       {
13281         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13282           continue;             /* center and border element do not touch */
13283       }
13284
13285       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13286                                  player->index_bit, center_side);
13287       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13288                                           CE_PLAYER_TOUCHES_X,
13289                                           player->index_bit, center_side);
13290       break;
13291     }
13292   }
13293 }
13294
13295 #if USE_ELEMENT_TOUCHING_BUGFIX
13296
13297 void TestIfElementTouchesCustomElement(int x, int y)
13298 {
13299   static int xy[4][2] =
13300   {
13301     { 0, -1 },
13302     { -1, 0 },
13303     { +1, 0 },
13304     { 0, +1 }
13305   };
13306   static int trigger_sides[4][2] =
13307   {
13308     /* center side      border side */
13309     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13310     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13311     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13312     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13313   };
13314   static int touch_dir[4] =
13315   {
13316     MV_LEFT | MV_RIGHT,
13317     MV_UP   | MV_DOWN,
13318     MV_UP   | MV_DOWN,
13319     MV_LEFT | MV_RIGHT
13320   };
13321   boolean change_center_element = FALSE;
13322   int center_element = Feld[x][y];      /* should always be non-moving! */
13323   int border_element_old[NUM_DIRECTIONS];
13324   int i;
13325
13326   for (i = 0; i < NUM_DIRECTIONS; i++)
13327   {
13328     int xx = x + xy[i][0];
13329     int yy = y + xy[i][1];
13330     int border_element;
13331
13332     border_element_old[i] = -1;
13333
13334     if (!IN_LEV_FIELD(xx, yy))
13335       continue;
13336
13337     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13338       border_element = Feld[xx][yy];    /* may be moving! */
13339     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13340       border_element = Feld[xx][yy];
13341     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13342       border_element = MovingOrBlocked2Element(xx, yy);
13343     else
13344       continue;                 /* center and border element do not touch */
13345
13346     border_element_old[i] = border_element;
13347   }
13348
13349   for (i = 0; i < NUM_DIRECTIONS; i++)
13350   {
13351     int xx = x + xy[i][0];
13352     int yy = y + xy[i][1];
13353     int center_side = trigger_sides[i][0];
13354     int border_element = border_element_old[i];
13355
13356     if (border_element == -1)
13357       continue;
13358
13359     /* check for change of border element */
13360     CheckElementChangeBySide(xx, yy, border_element, center_element,
13361                              CE_TOUCHING_X, center_side);
13362   }
13363
13364   for (i = 0; i < NUM_DIRECTIONS; i++)
13365   {
13366     int border_side = trigger_sides[i][1];
13367     int border_element = border_element_old[i];
13368
13369     if (border_element == -1)
13370       continue;
13371
13372     /* check for change of center element (but change it only once) */
13373     if (!change_center_element)
13374       change_center_element =
13375         CheckElementChangeBySide(x, y, center_element, border_element,
13376                                  CE_TOUCHING_X, border_side);
13377   }
13378 }
13379
13380 #else
13381
13382 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13383 {
13384   static int xy[4][2] =
13385   {
13386     { 0, -1 },
13387     { -1, 0 },
13388     { +1, 0 },
13389     { 0, +1 }
13390   };
13391   static int trigger_sides[4][2] =
13392   {
13393     /* center side      border side */
13394     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13395     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13396     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13397     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13398   };
13399   static int touch_dir[4] =
13400   {
13401     MV_LEFT | MV_RIGHT,
13402     MV_UP   | MV_DOWN,
13403     MV_UP   | MV_DOWN,
13404     MV_LEFT | MV_RIGHT
13405   };
13406   boolean change_center_element = FALSE;
13407   int center_element = Feld[x][y];      /* should always be non-moving! */
13408   int i;
13409
13410   for (i = 0; i < NUM_DIRECTIONS; i++)
13411   {
13412     int xx = x + xy[i][0];
13413     int yy = y + xy[i][1];
13414     int center_side = trigger_sides[i][0];
13415     int border_side = trigger_sides[i][1];
13416     int border_element;
13417
13418     if (!IN_LEV_FIELD(xx, yy))
13419       continue;
13420
13421     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13422       border_element = Feld[xx][yy];    /* may be moving! */
13423     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13424       border_element = Feld[xx][yy];
13425     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13426       border_element = MovingOrBlocked2Element(xx, yy);
13427     else
13428       continue;                 /* center and border element do not touch */
13429
13430     /* check for change of center element (but change it only once) */
13431     if (!change_center_element)
13432       change_center_element =
13433         CheckElementChangeBySide(x, y, center_element, border_element,
13434                                  CE_TOUCHING_X, border_side);
13435
13436     /* check for change of border element */
13437     CheckElementChangeBySide(xx, yy, border_element, center_element,
13438                              CE_TOUCHING_X, center_side);
13439   }
13440 }
13441
13442 #endif
13443
13444 void TestIfElementHitsCustomElement(int x, int y, int direction)
13445 {
13446   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13447   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13448   int hitx = x + dx, hity = y + dy;
13449   int hitting_element = Feld[x][y];
13450   int touched_element;
13451
13452   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13453     return;
13454
13455   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13456                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13457
13458   if (IN_LEV_FIELD(hitx, hity))
13459   {
13460     int opposite_direction = MV_DIR_OPPOSITE(direction);
13461     int hitting_side = direction;
13462     int touched_side = opposite_direction;
13463     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13464                           MovDir[hitx][hity] != direction ||
13465                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13466
13467     object_hit = TRUE;
13468
13469     if (object_hit)
13470     {
13471       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13472                                CE_HITTING_X, touched_side);
13473
13474       CheckElementChangeBySide(hitx, hity, touched_element,
13475                                hitting_element, CE_HIT_BY_X, hitting_side);
13476
13477       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13478                                CE_HIT_BY_SOMETHING, opposite_direction);
13479     }
13480   }
13481
13482   /* "hitting something" is also true when hitting the playfield border */
13483   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13484                            CE_HITTING_SOMETHING, direction);
13485 }
13486
13487 #if 0
13488 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13489 {
13490   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13491   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13492   int hitx = x + dx, hity = y + dy;
13493   int hitting_element = Feld[x][y];
13494   int touched_element;
13495 #if 0
13496   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13497                         !IS_FREE(hitx, hity) &&
13498                         (!IS_MOVING(hitx, hity) ||
13499                          MovDir[hitx][hity] != direction ||
13500                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13501 #endif
13502
13503   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13504     return;
13505
13506 #if 0
13507   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13508     return;
13509 #endif
13510
13511   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13512                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13513
13514   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13515                            EP_CAN_SMASH_EVERYTHING, direction);
13516
13517   if (IN_LEV_FIELD(hitx, hity))
13518   {
13519     int opposite_direction = MV_DIR_OPPOSITE(direction);
13520     int hitting_side = direction;
13521     int touched_side = opposite_direction;
13522 #if 0
13523     int touched_element = MovingOrBlocked2Element(hitx, hity);
13524 #endif
13525 #if 1
13526     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13527                           MovDir[hitx][hity] != direction ||
13528                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13529
13530     object_hit = TRUE;
13531 #endif
13532
13533     if (object_hit)
13534     {
13535       int i;
13536
13537       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13538                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13539
13540       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13541                                CE_OTHER_IS_SMASHING, touched_side);
13542
13543       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13544                                CE_OTHER_GETS_SMASHED, hitting_side);
13545     }
13546   }
13547 }
13548 #endif
13549
13550 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13551 {
13552   int i, kill_x = -1, kill_y = -1;
13553
13554   int bad_element = -1;
13555   static int test_xy[4][2] =
13556   {
13557     { 0, -1 },
13558     { -1, 0 },
13559     { +1, 0 },
13560     { 0, +1 }
13561   };
13562   static int test_dir[4] =
13563   {
13564     MV_UP,
13565     MV_LEFT,
13566     MV_RIGHT,
13567     MV_DOWN
13568   };
13569
13570   for (i = 0; i < NUM_DIRECTIONS; i++)
13571   {
13572     int test_x, test_y, test_move_dir, test_element;
13573
13574     test_x = good_x + test_xy[i][0];
13575     test_y = good_y + test_xy[i][1];
13576
13577     if (!IN_LEV_FIELD(test_x, test_y))
13578       continue;
13579
13580     test_move_dir =
13581       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13582
13583     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13584
13585     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13586        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13587     */
13588     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13589         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13590     {
13591       kill_x = test_x;
13592       kill_y = test_y;
13593       bad_element = test_element;
13594
13595       break;
13596     }
13597   }
13598
13599   if (kill_x != -1 || kill_y != -1)
13600   {
13601     if (IS_PLAYER(good_x, good_y))
13602     {
13603       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13604
13605       if (player->shield_deadly_time_left > 0 &&
13606           !IS_INDESTRUCTIBLE(bad_element))
13607         Bang(kill_x, kill_y);
13608       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13609         KillPlayer(player);
13610     }
13611     else
13612       Bang(good_x, good_y);
13613   }
13614 }
13615
13616 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13617 {
13618   int i, kill_x = -1, kill_y = -1;
13619   int bad_element = Feld[bad_x][bad_y];
13620   static int test_xy[4][2] =
13621   {
13622     { 0, -1 },
13623     { -1, 0 },
13624     { +1, 0 },
13625     { 0, +1 }
13626   };
13627   static int touch_dir[4] =
13628   {
13629     MV_LEFT | MV_RIGHT,
13630     MV_UP   | MV_DOWN,
13631     MV_UP   | MV_DOWN,
13632     MV_LEFT | MV_RIGHT
13633   };
13634   static int test_dir[4] =
13635   {
13636     MV_UP,
13637     MV_LEFT,
13638     MV_RIGHT,
13639     MV_DOWN
13640   };
13641
13642   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13643     return;
13644
13645   for (i = 0; i < NUM_DIRECTIONS; i++)
13646   {
13647     int test_x, test_y, test_move_dir, test_element;
13648
13649     test_x = bad_x + test_xy[i][0];
13650     test_y = bad_y + test_xy[i][1];
13651     if (!IN_LEV_FIELD(test_x, test_y))
13652       continue;
13653
13654     test_move_dir =
13655       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13656
13657     test_element = Feld[test_x][test_y];
13658
13659     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13660        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13661     */
13662     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13663         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13664     {
13665       /* good thing is player or penguin that does not move away */
13666       if (IS_PLAYER(test_x, test_y))
13667       {
13668         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13669
13670         if (bad_element == EL_ROBOT && player->is_moving)
13671           continue;     /* robot does not kill player if he is moving */
13672
13673         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13674         {
13675           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13676             continue;           /* center and border element do not touch */
13677         }
13678
13679         kill_x = test_x;
13680         kill_y = test_y;
13681         break;
13682       }
13683       else if (test_element == EL_PENGUIN)
13684       {
13685         kill_x = test_x;
13686         kill_y = test_y;
13687         break;
13688       }
13689     }
13690   }
13691
13692   if (kill_x != -1 || kill_y != -1)
13693   {
13694     if (IS_PLAYER(kill_x, kill_y))
13695     {
13696       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13697
13698       if (player->shield_deadly_time_left > 0 &&
13699           !IS_INDESTRUCTIBLE(bad_element))
13700         Bang(bad_x, bad_y);
13701       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13702         KillPlayer(player);
13703     }
13704     else
13705       Bang(kill_x, kill_y);
13706   }
13707 }
13708
13709 void TestIfPlayerTouchesBadThing(int x, int y)
13710 {
13711   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13712 }
13713
13714 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13715 {
13716   TestIfGoodThingHitsBadThing(x, y, move_dir);
13717 }
13718
13719 void TestIfBadThingTouchesPlayer(int x, int y)
13720 {
13721   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13722 }
13723
13724 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13725 {
13726   TestIfBadThingHitsGoodThing(x, y, move_dir);
13727 }
13728
13729 void TestIfFriendTouchesBadThing(int x, int y)
13730 {
13731   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13732 }
13733
13734 void TestIfBadThingTouchesFriend(int x, int y)
13735 {
13736   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13737 }
13738
13739 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13740 {
13741   int i, kill_x = bad_x, kill_y = bad_y;
13742   static int xy[4][2] =
13743   {
13744     { 0, -1 },
13745     { -1, 0 },
13746     { +1, 0 },
13747     { 0, +1 }
13748   };
13749
13750   for (i = 0; i < NUM_DIRECTIONS; i++)
13751   {
13752     int x, y, element;
13753
13754     x = bad_x + xy[i][0];
13755     y = bad_y + xy[i][1];
13756     if (!IN_LEV_FIELD(x, y))
13757       continue;
13758
13759     element = Feld[x][y];
13760     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13761         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13762     {
13763       kill_x = x;
13764       kill_y = y;
13765       break;
13766     }
13767   }
13768
13769   if (kill_x != bad_x || kill_y != bad_y)
13770     Bang(bad_x, bad_y);
13771 }
13772
13773 void KillPlayer(struct PlayerInfo *player)
13774 {
13775   int jx = player->jx, jy = player->jy;
13776
13777   if (!player->active)
13778     return;
13779
13780   /* the following code was introduced to prevent an infinite loop when calling
13781      -> Bang()
13782      -> CheckTriggeredElementChangeExt()
13783      -> ExecuteCustomElementAction()
13784      -> KillPlayer()
13785      -> (infinitely repeating the above sequence of function calls)
13786      which occurs when killing the player while having a CE with the setting
13787      "kill player X when explosion of <player X>"; the solution using a new
13788      field "player->killed" was chosen for backwards compatibility, although
13789      clever use of the fields "player->active" etc. would probably also work */
13790 #if 1
13791   if (player->killed)
13792     return;
13793 #endif
13794
13795   player->killed = TRUE;
13796
13797   /* remove accessible field at the player's position */
13798   Feld[jx][jy] = EL_EMPTY;
13799
13800   /* deactivate shield (else Bang()/Explode() would not work right) */
13801   player->shield_normal_time_left = 0;
13802   player->shield_deadly_time_left = 0;
13803
13804   Bang(jx, jy);
13805   BuryPlayer(player);
13806 }
13807
13808 static void KillPlayerUnlessEnemyProtected(int x, int y)
13809 {
13810   if (!PLAYER_ENEMY_PROTECTED(x, y))
13811     KillPlayer(PLAYERINFO(x, y));
13812 }
13813
13814 static void KillPlayerUnlessExplosionProtected(int x, int y)
13815 {
13816   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13817     KillPlayer(PLAYERINFO(x, y));
13818 }
13819
13820 void BuryPlayer(struct PlayerInfo *player)
13821 {
13822   int jx = player->jx, jy = player->jy;
13823
13824   if (!player->active)
13825     return;
13826
13827   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13828   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13829
13830   player->GameOver = TRUE;
13831   RemovePlayer(player);
13832 }
13833
13834 void RemovePlayer(struct PlayerInfo *player)
13835 {
13836   int jx = player->jx, jy = player->jy;
13837   int i, found = FALSE;
13838
13839   player->present = FALSE;
13840   player->active = FALSE;
13841
13842   if (!ExplodeField[jx][jy])
13843     StorePlayer[jx][jy] = 0;
13844
13845   if (player->is_moving)
13846     DrawLevelField(player->last_jx, player->last_jy);
13847
13848   for (i = 0; i < MAX_PLAYERS; i++)
13849     if (stored_player[i].active)
13850       found = TRUE;
13851
13852   if (!found)
13853     AllPlayersGone = TRUE;
13854
13855   ExitX = ZX = jx;
13856   ExitY = ZY = jy;
13857 }
13858
13859 #if USE_NEW_SNAP_DELAY
13860 static void setFieldForSnapping(int x, int y, int element, int direction)
13861 {
13862   struct ElementInfo *ei = &element_info[element];
13863   int direction_bit = MV_DIR_TO_BIT(direction);
13864   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13865   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13866                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13867
13868   Feld[x][y] = EL_ELEMENT_SNAPPING;
13869   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13870
13871   ResetGfxAnimation(x, y);
13872
13873   GfxElement[x][y] = element;
13874   GfxAction[x][y] = action;
13875   GfxDir[x][y] = direction;
13876   GfxFrame[x][y] = -1;
13877 }
13878 #endif
13879
13880 /*
13881   =============================================================================
13882   checkDiagonalPushing()
13883   -----------------------------------------------------------------------------
13884   check if diagonal input device direction results in pushing of object
13885   (by checking if the alternative direction is walkable, diggable, ...)
13886   =============================================================================
13887 */
13888
13889 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13890                                     int x, int y, int real_dx, int real_dy)
13891 {
13892   int jx, jy, dx, dy, xx, yy;
13893
13894   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13895     return TRUE;
13896
13897   /* diagonal direction: check alternative direction */
13898   jx = player->jx;
13899   jy = player->jy;
13900   dx = x - jx;
13901   dy = y - jy;
13902   xx = jx + (dx == 0 ? real_dx : 0);
13903   yy = jy + (dy == 0 ? real_dy : 0);
13904
13905   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13906 }
13907
13908 /*
13909   =============================================================================
13910   DigField()
13911   -----------------------------------------------------------------------------
13912   x, y:                 field next to player (non-diagonal) to try to dig to
13913   real_dx, real_dy:     direction as read from input device (can be diagonal)
13914   =============================================================================
13915 */
13916
13917 int DigField(struct PlayerInfo *player,
13918              int oldx, int oldy, int x, int y,
13919              int real_dx, int real_dy, int mode)
13920 {
13921   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13922   boolean player_was_pushing = player->is_pushing;
13923   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13924   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13925   int jx = oldx, jy = oldy;
13926   int dx = x - jx, dy = y - jy;
13927   int nextx = x + dx, nexty = y + dy;
13928   int move_direction = (dx == -1 ? MV_LEFT  :
13929                         dx == +1 ? MV_RIGHT :
13930                         dy == -1 ? MV_UP    :
13931                         dy == +1 ? MV_DOWN  : MV_NONE);
13932   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13933   int dig_side = MV_DIR_OPPOSITE(move_direction);
13934   int old_element = Feld[jx][jy];
13935 #if USE_FIXED_DONT_RUN_INTO
13936   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13937 #else
13938   int element;
13939 #endif
13940   int collect_count;
13941
13942   if (is_player)                /* function can also be called by EL_PENGUIN */
13943   {
13944     if (player->MovPos == 0)
13945     {
13946       player->is_digging = FALSE;
13947       player->is_collecting = FALSE;
13948     }
13949
13950     if (player->MovPos == 0)    /* last pushing move finished */
13951       player->is_pushing = FALSE;
13952
13953     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13954     {
13955       player->is_switching = FALSE;
13956       player->push_delay = -1;
13957
13958       return MP_NO_ACTION;
13959     }
13960   }
13961
13962 #if !USE_FIXED_DONT_RUN_INTO
13963   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13964     return MP_NO_ACTION;
13965 #endif
13966
13967   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13968     old_element = Back[jx][jy];
13969
13970   /* in case of element dropped at player position, check background */
13971   else if (Back[jx][jy] != EL_EMPTY &&
13972            game.engine_version >= VERSION_IDENT(2,2,0,0))
13973     old_element = Back[jx][jy];
13974
13975   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13976     return MP_NO_ACTION;        /* field has no opening in this direction */
13977
13978   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13979     return MP_NO_ACTION;        /* field has no opening in this direction */
13980
13981 #if USE_FIXED_DONT_RUN_INTO
13982   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13983   {
13984     SplashAcid(x, y);
13985
13986     Feld[jx][jy] = player->artwork_element;
13987     InitMovingField(jx, jy, MV_DOWN);
13988     Store[jx][jy] = EL_ACID;
13989     ContinueMoving(jx, jy);
13990     BuryPlayer(player);
13991
13992     return MP_DONT_RUN_INTO;
13993   }
13994 #endif
13995
13996 #if USE_FIXED_DONT_RUN_INTO
13997   if (player_can_move && DONT_RUN_INTO(element))
13998   {
13999     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14000
14001     return MP_DONT_RUN_INTO;
14002   }
14003 #endif
14004
14005 #if USE_FIXED_DONT_RUN_INTO
14006   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14007     return MP_NO_ACTION;
14008 #endif
14009
14010 #if !USE_FIXED_DONT_RUN_INTO
14011   element = Feld[x][y];
14012 #endif
14013
14014   collect_count = element_info[element].collect_count_initial;
14015
14016   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14017     return MP_NO_ACTION;
14018
14019   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14020     player_can_move = player_can_move_or_snap;
14021
14022   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14023       game.engine_version >= VERSION_IDENT(2,2,0,0))
14024   {
14025     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14026                                player->index_bit, dig_side);
14027     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14028                                         player->index_bit, dig_side);
14029
14030     if (element == EL_DC_LANDMINE)
14031       Bang(x, y);
14032
14033     if (Feld[x][y] != element)          /* field changed by snapping */
14034       return MP_ACTION;
14035
14036     return MP_NO_ACTION;
14037   }
14038
14039 #if USE_PLAYER_GRAVITY
14040   if (player->gravity && is_player && !player->is_auto_moving &&
14041       canFallDown(player) && move_direction != MV_DOWN &&
14042       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14043     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14044 #else
14045   if (game.gravity && is_player && !player->is_auto_moving &&
14046       canFallDown(player) && move_direction != MV_DOWN &&
14047       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14048     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14049 #endif
14050
14051   if (player_can_move &&
14052       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14053   {
14054     int sound_element = SND_ELEMENT(element);
14055     int sound_action = ACTION_WALKING;
14056
14057     if (IS_RND_GATE(element))
14058     {
14059       if (!player->key[RND_GATE_NR(element)])
14060         return MP_NO_ACTION;
14061     }
14062     else if (IS_RND_GATE_GRAY(element))
14063     {
14064       if (!player->key[RND_GATE_GRAY_NR(element)])
14065         return MP_NO_ACTION;
14066     }
14067     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14068     {
14069       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14070         return MP_NO_ACTION;
14071     }
14072     else if (element == EL_EXIT_OPEN ||
14073              element == EL_EM_EXIT_OPEN ||
14074              element == EL_STEEL_EXIT_OPEN ||
14075              element == EL_EM_STEEL_EXIT_OPEN ||
14076              element == EL_SP_EXIT_OPEN ||
14077              element == EL_SP_EXIT_OPENING)
14078     {
14079       sound_action = ACTION_PASSING;    /* player is passing exit */
14080     }
14081     else if (element == EL_EMPTY)
14082     {
14083       sound_action = ACTION_MOVING;             /* nothing to walk on */
14084     }
14085
14086     /* play sound from background or player, whatever is available */
14087     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14088       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14089     else
14090       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14091   }
14092   else if (player_can_move &&
14093            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14094   {
14095     if (!ACCESS_FROM(element, opposite_direction))
14096       return MP_NO_ACTION;      /* field not accessible from this direction */
14097
14098     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14099       return MP_NO_ACTION;
14100
14101     if (IS_EM_GATE(element))
14102     {
14103       if (!player->key[EM_GATE_NR(element)])
14104         return MP_NO_ACTION;
14105     }
14106     else if (IS_EM_GATE_GRAY(element))
14107     {
14108       if (!player->key[EM_GATE_GRAY_NR(element)])
14109         return MP_NO_ACTION;
14110     }
14111     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14112     {
14113       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14114         return MP_NO_ACTION;
14115     }
14116     else if (IS_EMC_GATE(element))
14117     {
14118       if (!player->key[EMC_GATE_NR(element)])
14119         return MP_NO_ACTION;
14120     }
14121     else if (IS_EMC_GATE_GRAY(element))
14122     {
14123       if (!player->key[EMC_GATE_GRAY_NR(element)])
14124         return MP_NO_ACTION;
14125     }
14126     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14127     {
14128       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14129         return MP_NO_ACTION;
14130     }
14131     else if (element == EL_DC_GATE_WHITE ||
14132              element == EL_DC_GATE_WHITE_GRAY ||
14133              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14134     {
14135       if (player->num_white_keys == 0)
14136         return MP_NO_ACTION;
14137
14138       player->num_white_keys--;
14139     }
14140     else if (IS_SP_PORT(element))
14141     {
14142       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14143           element == EL_SP_GRAVITY_PORT_RIGHT ||
14144           element == EL_SP_GRAVITY_PORT_UP ||
14145           element == EL_SP_GRAVITY_PORT_DOWN)
14146 #if USE_PLAYER_GRAVITY
14147         player->gravity = !player->gravity;
14148 #else
14149         game.gravity = !game.gravity;
14150 #endif
14151       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14152                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14153                element == EL_SP_GRAVITY_ON_PORT_UP ||
14154                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14155 #if USE_PLAYER_GRAVITY
14156         player->gravity = TRUE;
14157 #else
14158         game.gravity = TRUE;
14159 #endif
14160       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14161                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14162                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14163                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14164 #if USE_PLAYER_GRAVITY
14165         player->gravity = FALSE;
14166 #else
14167         game.gravity = FALSE;
14168 #endif
14169     }
14170
14171     /* automatically move to the next field with double speed */
14172     player->programmed_action = move_direction;
14173
14174     if (player->move_delay_reset_counter == 0)
14175     {
14176       player->move_delay_reset_counter = 2;     /* two double speed steps */
14177
14178       DOUBLE_PLAYER_SPEED(player);
14179     }
14180
14181     PlayLevelSoundAction(x, y, ACTION_PASSING);
14182   }
14183   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14184   {
14185     RemoveField(x, y);
14186
14187     if (mode != DF_SNAP)
14188     {
14189       GfxElement[x][y] = GFX_ELEMENT(element);
14190       player->is_digging = TRUE;
14191     }
14192
14193     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14194
14195     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14196                                         player->index_bit, dig_side);
14197
14198     if (mode == DF_SNAP)
14199     {
14200 #if USE_NEW_SNAP_DELAY
14201       if (level.block_snap_field)
14202         setFieldForSnapping(x, y, element, move_direction);
14203       else
14204         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14205 #else
14206       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14207 #endif
14208
14209       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14210                                           player->index_bit, dig_side);
14211     }
14212   }
14213   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14214   {
14215     RemoveField(x, y);
14216
14217     if (is_player && mode != DF_SNAP)
14218     {
14219       GfxElement[x][y] = element;
14220       player->is_collecting = TRUE;
14221     }
14222
14223     if (element == EL_SPEED_PILL)
14224     {
14225       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14226     }
14227     else if (element == EL_EXTRA_TIME && level.time > 0)
14228     {
14229       TimeLeft += level.extra_time;
14230
14231 #if 1
14232       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14233
14234       DisplayGameControlValues();
14235 #else
14236       DrawGameValue_Time(TimeLeft);
14237 #endif
14238     }
14239     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14240     {
14241       player->shield_normal_time_left += level.shield_normal_time;
14242       if (element == EL_SHIELD_DEADLY)
14243         player->shield_deadly_time_left += level.shield_deadly_time;
14244     }
14245     else if (element == EL_DYNAMITE ||
14246              element == EL_EM_DYNAMITE ||
14247              element == EL_SP_DISK_RED)
14248     {
14249       if (player->inventory_size < MAX_INVENTORY_SIZE)
14250         player->inventory_element[player->inventory_size++] = element;
14251
14252       DrawGameDoorValues();
14253     }
14254     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14255     {
14256       player->dynabomb_count++;
14257       player->dynabombs_left++;
14258     }
14259     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14260     {
14261       player->dynabomb_size++;
14262     }
14263     else if (element == EL_DYNABOMB_INCREASE_POWER)
14264     {
14265       player->dynabomb_xl = TRUE;
14266     }
14267     else if (IS_KEY(element))
14268     {
14269       player->key[KEY_NR(element)] = TRUE;
14270
14271       DrawGameDoorValues();
14272     }
14273     else if (element == EL_DC_KEY_WHITE)
14274     {
14275       player->num_white_keys++;
14276
14277       /* display white keys? */
14278       /* DrawGameDoorValues(); */
14279     }
14280     else if (IS_ENVELOPE(element))
14281     {
14282       player->show_envelope = element;
14283     }
14284     else if (element == EL_EMC_LENSES)
14285     {
14286       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14287
14288       RedrawAllInvisibleElementsForLenses();
14289     }
14290     else if (element == EL_EMC_MAGNIFIER)
14291     {
14292       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14293
14294       RedrawAllInvisibleElementsForMagnifier();
14295     }
14296     else if (IS_DROPPABLE(element) ||
14297              IS_THROWABLE(element))     /* can be collected and dropped */
14298     {
14299       int i;
14300
14301       if (collect_count == 0)
14302         player->inventory_infinite_element = element;
14303       else
14304         for (i = 0; i < collect_count; i++)
14305           if (player->inventory_size < MAX_INVENTORY_SIZE)
14306             player->inventory_element[player->inventory_size++] = element;
14307
14308       DrawGameDoorValues();
14309     }
14310     else if (collect_count > 0)
14311     {
14312       local_player->gems_still_needed -= collect_count;
14313       if (local_player->gems_still_needed < 0)
14314         local_player->gems_still_needed = 0;
14315
14316 #if 1
14317       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14318
14319       DisplayGameControlValues();
14320 #else
14321       DrawGameValue_Emeralds(local_player->gems_still_needed);
14322 #endif
14323     }
14324
14325     RaiseScoreElement(element);
14326     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14327
14328     if (is_player)
14329       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14330                                           player->index_bit, dig_side);
14331
14332     if (mode == DF_SNAP)
14333     {
14334 #if USE_NEW_SNAP_DELAY
14335       if (level.block_snap_field)
14336         setFieldForSnapping(x, y, element, move_direction);
14337       else
14338         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14339 #else
14340       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14341 #endif
14342
14343       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14344                                           player->index_bit, dig_side);
14345     }
14346   }
14347   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14348   {
14349     if (mode == DF_SNAP && element != EL_BD_ROCK)
14350       return MP_NO_ACTION;
14351
14352     if (CAN_FALL(element) && dy)
14353       return MP_NO_ACTION;
14354
14355     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14356         !(element == EL_SPRING && level.use_spring_bug))
14357       return MP_NO_ACTION;
14358
14359     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14360         ((move_direction & MV_VERTICAL &&
14361           ((element_info[element].move_pattern & MV_LEFT &&
14362             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14363            (element_info[element].move_pattern & MV_RIGHT &&
14364             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14365          (move_direction & MV_HORIZONTAL &&
14366           ((element_info[element].move_pattern & MV_UP &&
14367             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14368            (element_info[element].move_pattern & MV_DOWN &&
14369             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14370       return MP_NO_ACTION;
14371
14372     /* do not push elements already moving away faster than player */
14373     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14374         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14375       return MP_NO_ACTION;
14376
14377     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14378     {
14379       if (player->push_delay_value == -1 || !player_was_pushing)
14380         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14381     }
14382     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14383     {
14384       if (player->push_delay_value == -1)
14385         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14386     }
14387     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14388     {
14389       if (!player->is_pushing)
14390         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14391     }
14392
14393     player->is_pushing = TRUE;
14394     player->is_active = TRUE;
14395
14396     if (!(IN_LEV_FIELD(nextx, nexty) &&
14397           (IS_FREE(nextx, nexty) ||
14398            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14399             IS_SB_ELEMENT(element)))))
14400       return MP_NO_ACTION;
14401
14402     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14403       return MP_NO_ACTION;
14404
14405     if (player->push_delay == -1)       /* new pushing; restart delay */
14406       player->push_delay = 0;
14407
14408     if (player->push_delay < player->push_delay_value &&
14409         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14410         element != EL_SPRING && element != EL_BALLOON)
14411     {
14412       /* make sure that there is no move delay before next try to push */
14413       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14414         player->move_delay = 0;
14415
14416       return MP_NO_ACTION;
14417     }
14418
14419     if (IS_SB_ELEMENT(element))
14420     {
14421       if (element == EL_SOKOBAN_FIELD_FULL)
14422       {
14423         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14424         local_player->sokobanfields_still_needed++;
14425       }
14426
14427       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14428       {
14429         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14430         local_player->sokobanfields_still_needed--;
14431       }
14432
14433       Feld[x][y] = EL_SOKOBAN_OBJECT;
14434
14435       if (Back[x][y] == Back[nextx][nexty])
14436         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14437       else if (Back[x][y] != 0)
14438         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14439                                     ACTION_EMPTYING);
14440       else
14441         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14442                                     ACTION_FILLING);
14443
14444       if (local_player->sokobanfields_still_needed == 0 &&
14445           game.emulation == EMU_SOKOBAN)
14446       {
14447         PlayerWins(player);
14448
14449         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14450       }
14451     }
14452     else
14453       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14454
14455     InitMovingField(x, y, move_direction);
14456     GfxAction[x][y] = ACTION_PUSHING;
14457
14458     if (mode == DF_SNAP)
14459       ContinueMoving(x, y);
14460     else
14461       MovPos[x][y] = (dx != 0 ? dx : dy);
14462
14463     Pushed[x][y] = TRUE;
14464     Pushed[nextx][nexty] = TRUE;
14465
14466     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14467       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14468     else
14469       player->push_delay_value = -1;    /* get new value later */
14470
14471     /* check for element change _after_ element has been pushed */
14472     if (game.use_change_when_pushing_bug)
14473     {
14474       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14475                                  player->index_bit, dig_side);
14476       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14477                                           player->index_bit, dig_side);
14478     }
14479   }
14480   else if (IS_SWITCHABLE(element))
14481   {
14482     if (PLAYER_SWITCHING(player, x, y))
14483     {
14484       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14485                                           player->index_bit, dig_side);
14486
14487       return MP_ACTION;
14488     }
14489
14490     player->is_switching = TRUE;
14491     player->switch_x = x;
14492     player->switch_y = y;
14493
14494     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14495
14496     if (element == EL_ROBOT_WHEEL)
14497     {
14498       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14499       ZX = x;
14500       ZY = y;
14501
14502       game.robot_wheel_active = TRUE;
14503
14504       DrawLevelField(x, y);
14505     }
14506     else if (element == EL_SP_TERMINAL)
14507     {
14508       int xx, yy;
14509
14510       SCAN_PLAYFIELD(xx, yy)
14511       {
14512         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14513           Bang(xx, yy);
14514         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14515           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14516       }
14517     }
14518     else if (IS_BELT_SWITCH(element))
14519     {
14520       ToggleBeltSwitch(x, y);
14521     }
14522     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14523              element == EL_SWITCHGATE_SWITCH_DOWN ||
14524              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14525              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14526     {
14527       ToggleSwitchgateSwitch(x, y);
14528     }
14529     else if (element == EL_LIGHT_SWITCH ||
14530              element == EL_LIGHT_SWITCH_ACTIVE)
14531     {
14532       ToggleLightSwitch(x, y);
14533     }
14534     else if (element == EL_TIMEGATE_SWITCH ||
14535              element == EL_DC_TIMEGATE_SWITCH)
14536     {
14537       ActivateTimegateSwitch(x, y);
14538     }
14539     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14540              element == EL_BALLOON_SWITCH_RIGHT ||
14541              element == EL_BALLOON_SWITCH_UP    ||
14542              element == EL_BALLOON_SWITCH_DOWN  ||
14543              element == EL_BALLOON_SWITCH_NONE  ||
14544              element == EL_BALLOON_SWITCH_ANY)
14545     {
14546       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14547                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14548                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14549                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14550                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14551                              move_direction);
14552     }
14553     else if (element == EL_LAMP)
14554     {
14555       Feld[x][y] = EL_LAMP_ACTIVE;
14556       local_player->lights_still_needed--;
14557
14558       ResetGfxAnimation(x, y);
14559       DrawLevelField(x, y);
14560     }
14561     else if (element == EL_TIME_ORB_FULL)
14562     {
14563       Feld[x][y] = EL_TIME_ORB_EMPTY;
14564
14565       if (level.time > 0 || level.use_time_orb_bug)
14566       {
14567         TimeLeft += level.time_orb_time;
14568
14569 #if 1
14570         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14571
14572         DisplayGameControlValues();
14573 #else
14574         DrawGameValue_Time(TimeLeft);
14575 #endif
14576       }
14577
14578       ResetGfxAnimation(x, y);
14579       DrawLevelField(x, y);
14580     }
14581     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14582              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14583     {
14584       int xx, yy;
14585
14586       game.ball_state = !game.ball_state;
14587
14588       SCAN_PLAYFIELD(xx, yy)
14589       {
14590         int e = Feld[xx][yy];
14591
14592         if (game.ball_state)
14593         {
14594           if (e == EL_EMC_MAGIC_BALL)
14595             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14596           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14597             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14598         }
14599         else
14600         {
14601           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14602             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14603           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14604             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14605         }
14606       }
14607     }
14608
14609     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14610                                         player->index_bit, dig_side);
14611
14612     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14613                                         player->index_bit, dig_side);
14614
14615     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14616                                         player->index_bit, dig_side);
14617
14618     return MP_ACTION;
14619   }
14620   else
14621   {
14622     if (!PLAYER_SWITCHING(player, x, y))
14623     {
14624       player->is_switching = TRUE;
14625       player->switch_x = x;
14626       player->switch_y = y;
14627
14628       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14629                                  player->index_bit, dig_side);
14630       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14631                                           player->index_bit, dig_side);
14632
14633       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14634                                  player->index_bit, dig_side);
14635       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14636                                           player->index_bit, dig_side);
14637     }
14638
14639     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14640                                player->index_bit, dig_side);
14641     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14642                                         player->index_bit, dig_side);
14643
14644     return MP_NO_ACTION;
14645   }
14646
14647   player->push_delay = -1;
14648
14649   if (is_player)                /* function can also be called by EL_PENGUIN */
14650   {
14651     if (Feld[x][y] != element)          /* really digged/collected something */
14652     {
14653       player->is_collecting = !player->is_digging;
14654       player->is_active = TRUE;
14655     }
14656   }
14657
14658   return MP_MOVING;
14659 }
14660
14661 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14662 {
14663   int jx = player->jx, jy = player->jy;
14664   int x = jx + dx, y = jy + dy;
14665   int snap_direction = (dx == -1 ? MV_LEFT  :
14666                         dx == +1 ? MV_RIGHT :
14667                         dy == -1 ? MV_UP    :
14668                         dy == +1 ? MV_DOWN  : MV_NONE);
14669   boolean can_continue_snapping = (level.continuous_snapping &&
14670                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14671
14672   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14673     return FALSE;
14674
14675   if (!player->active || !IN_LEV_FIELD(x, y))
14676     return FALSE;
14677
14678   if (dx && dy)
14679     return FALSE;
14680
14681   if (!dx && !dy)
14682   {
14683     if (player->MovPos == 0)
14684       player->is_pushing = FALSE;
14685
14686     player->is_snapping = FALSE;
14687
14688     if (player->MovPos == 0)
14689     {
14690       player->is_moving = FALSE;
14691       player->is_digging = FALSE;
14692       player->is_collecting = FALSE;
14693     }
14694
14695     return FALSE;
14696   }
14697
14698 #if USE_NEW_CONTINUOUS_SNAPPING
14699   /* prevent snapping with already pressed snap key when not allowed */
14700   if (player->is_snapping && !can_continue_snapping)
14701     return FALSE;
14702 #else
14703   if (player->is_snapping)
14704     return FALSE;
14705 #endif
14706
14707   player->MovDir = snap_direction;
14708
14709   if (player->MovPos == 0)
14710   {
14711     player->is_moving = FALSE;
14712     player->is_digging = FALSE;
14713     player->is_collecting = FALSE;
14714   }
14715
14716   player->is_dropping = FALSE;
14717   player->is_dropping_pressed = FALSE;
14718   player->drop_pressed_delay = 0;
14719
14720   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14721     return FALSE;
14722
14723   player->is_snapping = TRUE;
14724   player->is_active = TRUE;
14725
14726   if (player->MovPos == 0)
14727   {
14728     player->is_moving = FALSE;
14729     player->is_digging = FALSE;
14730     player->is_collecting = FALSE;
14731   }
14732
14733   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14734     DrawLevelField(player->last_jx, player->last_jy);
14735
14736   DrawLevelField(x, y);
14737
14738   return TRUE;
14739 }
14740
14741 boolean DropElement(struct PlayerInfo *player)
14742 {
14743   int old_element, new_element;
14744   int dropx = player->jx, dropy = player->jy;
14745   int drop_direction = player->MovDir;
14746   int drop_side = drop_direction;
14747 #if 1
14748   int drop_element = get_next_dropped_element(player);
14749 #else
14750   int drop_element = (player->inventory_size > 0 ?
14751                       player->inventory_element[player->inventory_size - 1] :
14752                       player->inventory_infinite_element != EL_UNDEFINED ?
14753                       player->inventory_infinite_element :
14754                       player->dynabombs_left > 0 ?
14755                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14756                       EL_UNDEFINED);
14757 #endif
14758
14759   player->is_dropping_pressed = TRUE;
14760
14761   /* do not drop an element on top of another element; when holding drop key
14762      pressed without moving, dropped element must move away before the next
14763      element can be dropped (this is especially important if the next element
14764      is dynamite, which can be placed on background for historical reasons) */
14765   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14766     return MP_ACTION;
14767
14768   if (IS_THROWABLE(drop_element))
14769   {
14770     dropx += GET_DX_FROM_DIR(drop_direction);
14771     dropy += GET_DY_FROM_DIR(drop_direction);
14772
14773     if (!IN_LEV_FIELD(dropx, dropy))
14774       return FALSE;
14775   }
14776
14777   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14778   new_element = drop_element;           /* default: no change when dropping */
14779
14780   /* check if player is active, not moving and ready to drop */
14781   if (!player->active || player->MovPos || player->drop_delay > 0)
14782     return FALSE;
14783
14784   /* check if player has anything that can be dropped */
14785   if (new_element == EL_UNDEFINED)
14786     return FALSE;
14787
14788   /* check if drop key was pressed long enough for EM style dynamite */
14789   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14790     return FALSE;
14791
14792   /* check if anything can be dropped at the current position */
14793   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14794     return FALSE;
14795
14796   /* collected custom elements can only be dropped on empty fields */
14797   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14798     return FALSE;
14799
14800   if (old_element != EL_EMPTY)
14801     Back[dropx][dropy] = old_element;   /* store old element on this field */
14802
14803   ResetGfxAnimation(dropx, dropy);
14804   ResetRandomAnimationValue(dropx, dropy);
14805
14806   if (player->inventory_size > 0 ||
14807       player->inventory_infinite_element != EL_UNDEFINED)
14808   {
14809     if (player->inventory_size > 0)
14810     {
14811       player->inventory_size--;
14812
14813       DrawGameDoorValues();
14814
14815       if (new_element == EL_DYNAMITE)
14816         new_element = EL_DYNAMITE_ACTIVE;
14817       else if (new_element == EL_EM_DYNAMITE)
14818         new_element = EL_EM_DYNAMITE_ACTIVE;
14819       else if (new_element == EL_SP_DISK_RED)
14820         new_element = EL_SP_DISK_RED_ACTIVE;
14821     }
14822
14823     Feld[dropx][dropy] = new_element;
14824
14825     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14826       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14827                           el2img(Feld[dropx][dropy]), 0);
14828
14829     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14830
14831     /* needed if previous element just changed to "empty" in the last frame */
14832     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14833
14834     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14835                                player->index_bit, drop_side);
14836     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14837                                         CE_PLAYER_DROPS_X,
14838                                         player->index_bit, drop_side);
14839
14840     TestIfElementTouchesCustomElement(dropx, dropy);
14841   }
14842   else          /* player is dropping a dyna bomb */
14843   {
14844     player->dynabombs_left--;
14845
14846     Feld[dropx][dropy] = new_element;
14847
14848     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14849       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14850                           el2img(Feld[dropx][dropy]), 0);
14851
14852     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14853   }
14854
14855   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14856     InitField_WithBug1(dropx, dropy, FALSE);
14857
14858   new_element = Feld[dropx][dropy];     /* element might have changed */
14859
14860   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14861       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14862   {
14863     int move_direction, nextx, nexty;
14864
14865     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14866       MovDir[dropx][dropy] = drop_direction;
14867
14868     move_direction = MovDir[dropx][dropy];
14869     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14870     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14871
14872     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14873
14874 #if USE_FIX_IMPACT_COLLISION
14875     /* do not cause impact style collision by dropping elements that can fall */
14876     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14877 #else
14878     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14879 #endif
14880   }
14881
14882   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14883   player->is_dropping = TRUE;
14884
14885   player->drop_pressed_delay = 0;
14886   player->is_dropping_pressed = FALSE;
14887
14888   player->drop_x = dropx;
14889   player->drop_y = dropy;
14890
14891   return TRUE;
14892 }
14893
14894 /* ------------------------------------------------------------------------- */
14895 /* game sound playing functions                                              */
14896 /* ------------------------------------------------------------------------- */
14897
14898 static int *loop_sound_frame = NULL;
14899 static int *loop_sound_volume = NULL;
14900
14901 void InitPlayLevelSound()
14902 {
14903   int num_sounds = getSoundListSize();
14904
14905   checked_free(loop_sound_frame);
14906   checked_free(loop_sound_volume);
14907
14908   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14909   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14910 }
14911
14912 static void PlayLevelSound(int x, int y, int nr)
14913 {
14914   int sx = SCREENX(x), sy = SCREENY(y);
14915   int volume, stereo_position;
14916   int max_distance = 8;
14917   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14918
14919   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14920       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14921     return;
14922
14923   if (!IN_LEV_FIELD(x, y) ||
14924       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14925       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14926     return;
14927
14928   volume = SOUND_MAX_VOLUME;
14929
14930   if (!IN_SCR_FIELD(sx, sy))
14931   {
14932     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14933     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14934
14935     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14936   }
14937
14938   stereo_position = (SOUND_MAX_LEFT +
14939                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14940                      (SCR_FIELDX + 2 * max_distance));
14941
14942   if (IS_LOOP_SOUND(nr))
14943   {
14944     /* This assures that quieter loop sounds do not overwrite louder ones,
14945        while restarting sound volume comparison with each new game frame. */
14946
14947     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14948       return;
14949
14950     loop_sound_volume[nr] = volume;
14951     loop_sound_frame[nr] = FrameCounter;
14952   }
14953
14954   PlaySoundExt(nr, volume, stereo_position, type);
14955 }
14956
14957 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14958 {
14959   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14960                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14961                  y < LEVELY(BY1) ? LEVELY(BY1) :
14962                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14963                  sound_action);
14964 }
14965
14966 static void PlayLevelSoundAction(int x, int y, int action)
14967 {
14968   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14969 }
14970
14971 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14972 {
14973   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14974
14975   if (sound_effect != SND_UNDEFINED)
14976     PlayLevelSound(x, y, sound_effect);
14977 }
14978
14979 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14980                                               int action)
14981 {
14982   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14983
14984   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14985     PlayLevelSound(x, y, sound_effect);
14986 }
14987
14988 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14989 {
14990   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14991
14992   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14993     PlayLevelSound(x, y, sound_effect);
14994 }
14995
14996 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14997 {
14998   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14999
15000   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15001     StopSound(sound_effect);
15002 }
15003
15004 static void PlayLevelMusic()
15005 {
15006   if (levelset.music[level_nr] != MUS_UNDEFINED)
15007     PlayMusic(levelset.music[level_nr]);        /* from config file */
15008   else
15009     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15010 }
15011
15012 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15013 {
15014   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15015   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15016   int x = xx - 1 - offset;
15017   int y = yy - 1 - offset;
15018
15019   switch (sample)
15020   {
15021     case SAMPLE_blank:
15022       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15023       break;
15024
15025     case SAMPLE_roll:
15026       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15027       break;
15028
15029     case SAMPLE_stone:
15030       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15031       break;
15032
15033     case SAMPLE_nut:
15034       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15035       break;
15036
15037     case SAMPLE_crack:
15038       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15039       break;
15040
15041     case SAMPLE_bug:
15042       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15043       break;
15044
15045     case SAMPLE_tank:
15046       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15047       break;
15048
15049     case SAMPLE_android_clone:
15050       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15051       break;
15052
15053     case SAMPLE_android_move:
15054       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15055       break;
15056
15057     case SAMPLE_spring:
15058       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15059       break;
15060
15061     case SAMPLE_slurp:
15062       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15063       break;
15064
15065     case SAMPLE_eater:
15066       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15067       break;
15068
15069     case SAMPLE_eater_eat:
15070       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15071       break;
15072
15073     case SAMPLE_alien:
15074       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15075       break;
15076
15077     case SAMPLE_collect:
15078       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15079       break;
15080
15081     case SAMPLE_diamond:
15082       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15083       break;
15084
15085     case SAMPLE_squash:
15086       /* !!! CHECK THIS !!! */
15087 #if 1
15088       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15089 #else
15090       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15091 #endif
15092       break;
15093
15094     case SAMPLE_wonderfall:
15095       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15096       break;
15097
15098     case SAMPLE_drip:
15099       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15100       break;
15101
15102     case SAMPLE_push:
15103       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15104       break;
15105
15106     case SAMPLE_dirt:
15107       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15108       break;
15109
15110     case SAMPLE_acid:
15111       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15112       break;
15113
15114     case SAMPLE_ball:
15115       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15116       break;
15117
15118     case SAMPLE_grow:
15119       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15120       break;
15121
15122     case SAMPLE_wonder:
15123       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15124       break;
15125
15126     case SAMPLE_door:
15127       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15128       break;
15129
15130     case SAMPLE_exit_open:
15131       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15132       break;
15133
15134     case SAMPLE_exit_leave:
15135       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15136       break;
15137
15138     case SAMPLE_dynamite:
15139       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15140       break;
15141
15142     case SAMPLE_tick:
15143       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15144       break;
15145
15146     case SAMPLE_press:
15147       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15148       break;
15149
15150     case SAMPLE_wheel:
15151       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15152       break;
15153
15154     case SAMPLE_boom:
15155       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15156       break;
15157
15158     case SAMPLE_die:
15159       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15160       break;
15161
15162     case SAMPLE_time:
15163       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15164       break;
15165
15166     default:
15167       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15168       break;
15169   }
15170 }
15171
15172 #if 0
15173 void ChangeTime(int value)
15174 {
15175   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15176
15177   *time += value;
15178
15179   /* EMC game engine uses value from time counter of RND game engine */
15180   level.native_em_level->lev->time = *time;
15181
15182   DrawGameValue_Time(*time);
15183 }
15184
15185 void RaiseScore(int value)
15186 {
15187   /* EMC game engine and RND game engine have separate score counters */
15188   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15189                 &level.native_em_level->lev->score : &local_player->score);
15190
15191   *score += value;
15192
15193   DrawGameValue_Score(*score);
15194 }
15195 #endif
15196
15197 void RaiseScore(int value)
15198 {
15199   local_player->score += value;
15200
15201 #if 1
15202   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15203
15204   DisplayGameControlValues();
15205 #else
15206   DrawGameValue_Score(local_player->score);
15207 #endif
15208 }
15209
15210 void RaiseScoreElement(int element)
15211 {
15212   switch (element)
15213   {
15214     case EL_EMERALD:
15215     case EL_BD_DIAMOND:
15216     case EL_EMERALD_YELLOW:
15217     case EL_EMERALD_RED:
15218     case EL_EMERALD_PURPLE:
15219     case EL_SP_INFOTRON:
15220       RaiseScore(level.score[SC_EMERALD]);
15221       break;
15222     case EL_DIAMOND:
15223       RaiseScore(level.score[SC_DIAMOND]);
15224       break;
15225     case EL_CRYSTAL:
15226       RaiseScore(level.score[SC_CRYSTAL]);
15227       break;
15228     case EL_PEARL:
15229       RaiseScore(level.score[SC_PEARL]);
15230       break;
15231     case EL_BUG:
15232     case EL_BD_BUTTERFLY:
15233     case EL_SP_ELECTRON:
15234       RaiseScore(level.score[SC_BUG]);
15235       break;
15236     case EL_SPACESHIP:
15237     case EL_BD_FIREFLY:
15238     case EL_SP_SNIKSNAK:
15239       RaiseScore(level.score[SC_SPACESHIP]);
15240       break;
15241     case EL_YAMYAM:
15242     case EL_DARK_YAMYAM:
15243       RaiseScore(level.score[SC_YAMYAM]);
15244       break;
15245     case EL_ROBOT:
15246       RaiseScore(level.score[SC_ROBOT]);
15247       break;
15248     case EL_PACMAN:
15249       RaiseScore(level.score[SC_PACMAN]);
15250       break;
15251     case EL_NUT:
15252       RaiseScore(level.score[SC_NUT]);
15253       break;
15254     case EL_DYNAMITE:
15255     case EL_EM_DYNAMITE:
15256     case EL_SP_DISK_RED:
15257     case EL_DYNABOMB_INCREASE_NUMBER:
15258     case EL_DYNABOMB_INCREASE_SIZE:
15259     case EL_DYNABOMB_INCREASE_POWER:
15260       RaiseScore(level.score[SC_DYNAMITE]);
15261       break;
15262     case EL_SHIELD_NORMAL:
15263     case EL_SHIELD_DEADLY:
15264       RaiseScore(level.score[SC_SHIELD]);
15265       break;
15266     case EL_EXTRA_TIME:
15267       RaiseScore(level.extra_time_score);
15268       break;
15269     case EL_KEY_1:
15270     case EL_KEY_2:
15271     case EL_KEY_3:
15272     case EL_KEY_4:
15273     case EL_EM_KEY_1:
15274     case EL_EM_KEY_2:
15275     case EL_EM_KEY_3:
15276     case EL_EM_KEY_4:
15277     case EL_EMC_KEY_5:
15278     case EL_EMC_KEY_6:
15279     case EL_EMC_KEY_7:
15280     case EL_EMC_KEY_8:
15281     case EL_DC_KEY_WHITE:
15282       RaiseScore(level.score[SC_KEY]);
15283       break;
15284     default:
15285       RaiseScore(element_info[element].collect_score);
15286       break;
15287   }
15288 }
15289
15290 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15291 {
15292   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15293   {
15294 #if defined(NETWORK_AVALIABLE)
15295     if (options.network)
15296       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15297     else
15298 #endif
15299     {
15300       if (quick_quit)
15301       {
15302 #if 1
15303
15304 #if 1
15305         FadeSkipNextFadeIn();
15306 #else
15307         fading = fading_none;
15308 #endif
15309
15310 #else
15311         OpenDoor(DOOR_CLOSE_1);
15312 #endif
15313
15314         game_status = GAME_MODE_MAIN;
15315
15316 #if 1
15317         DrawAndFadeInMainMenu(REDRAW_FIELD);
15318 #else
15319         DrawMainMenu();
15320 #endif
15321       }
15322       else
15323       {
15324 #if 0
15325         FadeOut(REDRAW_FIELD);
15326 #endif
15327
15328         game_status = GAME_MODE_MAIN;
15329
15330         DrawAndFadeInMainMenu(REDRAW_FIELD);
15331       }
15332     }
15333   }
15334   else          /* continue playing the game */
15335   {
15336     if (tape.playing && tape.deactivate_display)
15337       TapeDeactivateDisplayOff(TRUE);
15338
15339     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15340
15341     if (tape.playing && tape.deactivate_display)
15342       TapeDeactivateDisplayOn();
15343   }
15344 }
15345
15346 void RequestQuitGame(boolean ask_if_really_quit)
15347 {
15348   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15349   boolean skip_request = AllPlayersGone || quick_quit;
15350
15351   RequestQuitGameExt(skip_request, quick_quit,
15352                      "Do you really want to quit the game ?");
15353 }
15354
15355
15356 /* ------------------------------------------------------------------------- */
15357 /* random generator functions                                                */
15358 /* ------------------------------------------------------------------------- */
15359
15360 unsigned int InitEngineRandom_RND(long seed)
15361 {
15362   game.num_random_calls = 0;
15363
15364 #if 0
15365   unsigned int rnd_seed = InitEngineRandom(seed);
15366
15367   printf("::: START RND: %d\n", rnd_seed);
15368
15369   return rnd_seed;
15370 #else
15371
15372   return InitEngineRandom(seed);
15373
15374 #endif
15375
15376 }
15377
15378 unsigned int RND(int max)
15379 {
15380   if (max > 0)
15381   {
15382     game.num_random_calls++;
15383
15384     return GetEngineRandom(max);
15385   }
15386
15387   return 0;
15388 }
15389
15390
15391 /* ------------------------------------------------------------------------- */
15392 /* game engine snapshot handling functions                                   */
15393 /* ------------------------------------------------------------------------- */
15394
15395 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15396
15397 struct EngineSnapshotInfo
15398 {
15399   /* runtime values for custom element collect score */
15400   int collect_score[NUM_CUSTOM_ELEMENTS];
15401
15402   /* runtime values for group element choice position */
15403   int choice_pos[NUM_GROUP_ELEMENTS];
15404
15405   /* runtime values for belt position animations */
15406   int belt_graphic[4 * NUM_BELT_PARTS];
15407   int belt_anim_mode[4 * NUM_BELT_PARTS];
15408 };
15409
15410 struct EngineSnapshotNodeInfo
15411 {
15412   void *buffer_orig;
15413   void *buffer_copy;
15414   int size;
15415 };
15416
15417 static struct EngineSnapshotInfo engine_snapshot_rnd;
15418 static ListNode *engine_snapshot_list = NULL;
15419 static char *snapshot_level_identifier = NULL;
15420 static int snapshot_level_nr = -1;
15421
15422 void FreeEngineSnapshot()
15423 {
15424   while (engine_snapshot_list != NULL)
15425     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15426                        checked_free);
15427
15428   setString(&snapshot_level_identifier, NULL);
15429   snapshot_level_nr = -1;
15430 }
15431
15432 static void SaveEngineSnapshotValues_RND()
15433 {
15434   static int belt_base_active_element[4] =
15435   {
15436     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15437     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15438     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15439     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15440   };
15441   int i, j;
15442
15443   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15444   {
15445     int element = EL_CUSTOM_START + i;
15446
15447     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15448   }
15449
15450   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15451   {
15452     int element = EL_GROUP_START + i;
15453
15454     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15455   }
15456
15457   for (i = 0; i < 4; i++)
15458   {
15459     for (j = 0; j < NUM_BELT_PARTS; j++)
15460     {
15461       int element = belt_base_active_element[i] + j;
15462       int graphic = el2img(element);
15463       int anim_mode = graphic_info[graphic].anim_mode;
15464
15465       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15466       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15467     }
15468   }
15469 }
15470
15471 static void LoadEngineSnapshotValues_RND()
15472 {
15473   unsigned long num_random_calls = game.num_random_calls;
15474   int i, j;
15475
15476   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15477   {
15478     int element = EL_CUSTOM_START + i;
15479
15480     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15481   }
15482
15483   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15484   {
15485     int element = EL_GROUP_START + i;
15486
15487     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15488   }
15489
15490   for (i = 0; i < 4; i++)
15491   {
15492     for (j = 0; j < NUM_BELT_PARTS; j++)
15493     {
15494       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15495       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15496
15497       graphic_info[graphic].anim_mode = anim_mode;
15498     }
15499   }
15500
15501   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15502   {
15503     InitRND(tape.random_seed);
15504     for (i = 0; i < num_random_calls; i++)
15505       RND(1);
15506   }
15507
15508   if (game.num_random_calls != num_random_calls)
15509   {
15510     Error(ERR_INFO, "number of random calls out of sync");
15511     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15512     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15513     Error(ERR_EXIT, "this should not happen -- please debug");
15514   }
15515 }
15516
15517 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15518 {
15519   struct EngineSnapshotNodeInfo *bi =
15520     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15521
15522   bi->buffer_orig = buffer;
15523   bi->buffer_copy = checked_malloc(size);
15524   bi->size = size;
15525
15526   memcpy(bi->buffer_copy, buffer, size);
15527
15528   addNodeToList(&engine_snapshot_list, NULL, bi);
15529 }
15530
15531 void SaveEngineSnapshot()
15532 {
15533   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15534
15535   if (level_editor_test_game)   /* do not save snapshots from editor */
15536     return;
15537
15538   /* copy some special values to a structure better suited for the snapshot */
15539
15540   SaveEngineSnapshotValues_RND();
15541   SaveEngineSnapshotValues_EM();
15542
15543   /* save values stored in special snapshot structure */
15544
15545   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15546   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15547
15548   /* save further RND engine values */
15549
15550   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15551   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15553
15554   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15555   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15556   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15558
15559   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15560   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15561   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15562   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15563   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15564
15565   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15566   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15567   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15568
15569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15570
15571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15572
15573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15575
15576   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15577   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15579   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15588   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15594
15595   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15597
15598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15599   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15600   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15601
15602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15604
15605   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15607   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15608   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15609   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15610
15611   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15612   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15613
15614   /* save level identification information */
15615
15616   setString(&snapshot_level_identifier, leveldir_current->identifier);
15617   snapshot_level_nr = level_nr;
15618
15619 #if 0
15620   ListNode *node = engine_snapshot_list;
15621   int num_bytes = 0;
15622
15623   while (node != NULL)
15624   {
15625     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15626
15627     node = node->next;
15628   }
15629
15630   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15631 #endif
15632 }
15633
15634 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15635 {
15636   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15637 }
15638
15639 void LoadEngineSnapshot()
15640 {
15641   ListNode *node = engine_snapshot_list;
15642
15643   if (engine_snapshot_list == NULL)
15644     return;
15645
15646   while (node != NULL)
15647   {
15648     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15649
15650     node = node->next;
15651   }
15652
15653   /* restore special values from snapshot structure */
15654
15655   LoadEngineSnapshotValues_RND();
15656   LoadEngineSnapshotValues_EM();
15657 }
15658
15659 boolean CheckEngineSnapshot()
15660 {
15661   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15662           snapshot_level_nr == level_nr);
15663 }
15664
15665
15666 /* ---------- new game button stuff ---------------------------------------- */
15667
15668 /* graphic position values for game buttons */
15669 #define GAME_BUTTON_XSIZE       30
15670 #define GAME_BUTTON_YSIZE       30
15671 #define GAME_BUTTON_XPOS        5
15672 #define GAME_BUTTON_YPOS        215
15673 #define SOUND_BUTTON_XPOS       5
15674 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15675
15676 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15677 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15678 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15679 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15680 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15681 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15682
15683 static struct
15684 {
15685   int *x, *y;
15686   int gd_x, gd_y;
15687   int gadget_id;
15688   char *infotext;
15689 } gamebutton_info[NUM_GAME_BUTTONS] =
15690 {
15691 #if 1
15692   {
15693     &game.button.stop.x,        &game.button.stop.y,
15694     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15695     GAME_CTRL_ID_STOP,
15696     "stop game"
15697   },
15698   {
15699     &game.button.pause.x,       &game.button.pause.y,
15700     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15701     GAME_CTRL_ID_PAUSE,
15702     "pause game"
15703   },
15704   {
15705     &game.button.play.x,        &game.button.play.y,
15706     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15707     GAME_CTRL_ID_PLAY,
15708     "play game"
15709   },
15710   {
15711     &game.button.sound_music.x, &game.button.sound_music.y,
15712     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15713     SOUND_CTRL_ID_MUSIC,
15714     "background music on/off"
15715   },
15716   {
15717     &game.button.sound_loops.x, &game.button.sound_loops.y,
15718     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15719     SOUND_CTRL_ID_LOOPS,
15720     "sound loops on/off"
15721   },
15722   {
15723     &game.button.sound_simple.x,&game.button.sound_simple.y,
15724     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15725     SOUND_CTRL_ID_SIMPLE,
15726     "normal sounds on/off"
15727   }
15728 #else
15729   {
15730     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15731     GAME_CTRL_ID_STOP,
15732     "stop game"
15733   },
15734   {
15735     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15736     GAME_CTRL_ID_PAUSE,
15737     "pause game"
15738   },
15739   {
15740     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15741     GAME_CTRL_ID_PLAY,
15742     "play game"
15743   },
15744   {
15745     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15746     SOUND_CTRL_ID_MUSIC,
15747     "background music on/off"
15748   },
15749   {
15750     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15751     SOUND_CTRL_ID_LOOPS,
15752     "sound loops on/off"
15753   },
15754   {
15755     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15756     SOUND_CTRL_ID_SIMPLE,
15757     "normal sounds on/off"
15758   }
15759 #endif
15760 };
15761
15762 void CreateGameButtons()
15763 {
15764   int i;
15765
15766   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15767   {
15768     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15769     struct GadgetInfo *gi;
15770     int button_type;
15771     boolean checked;
15772     unsigned long event_mask;
15773     int x, y;
15774     int gd_xoffset, gd_yoffset;
15775     int gd_x1, gd_x2, gd_y1, gd_y2;
15776     int id = i;
15777
15778     x = DX + *gamebutton_info[i].x;
15779     y = DY + *gamebutton_info[i].y;
15780     gd_xoffset = gamebutton_info[i].gd_x;
15781     gd_yoffset = gamebutton_info[i].gd_y;
15782     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15783     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15784
15785     if (id == GAME_CTRL_ID_STOP ||
15786         id == GAME_CTRL_ID_PAUSE ||
15787         id == GAME_CTRL_ID_PLAY)
15788     {
15789       button_type = GD_TYPE_NORMAL_BUTTON;
15790       checked = FALSE;
15791       event_mask = GD_EVENT_RELEASED;
15792       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15793       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15794     }
15795     else
15796     {
15797       button_type = GD_TYPE_CHECK_BUTTON;
15798       checked =
15799         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15800          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15801          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15802       event_mask = GD_EVENT_PRESSED;
15803       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15804       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15805     }
15806
15807     gi = CreateGadget(GDI_CUSTOM_ID, id,
15808                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15809 #if 1
15810                       GDI_X, x,
15811                       GDI_Y, y,
15812 #else
15813                       GDI_X, DX + gd_xoffset,
15814                       GDI_Y, DY + gd_yoffset,
15815 #endif
15816                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15817                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15818                       GDI_TYPE, button_type,
15819                       GDI_STATE, GD_BUTTON_UNPRESSED,
15820                       GDI_CHECKED, checked,
15821                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15822                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15823                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15824                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15825                       GDI_DIRECT_DRAW, FALSE,
15826                       GDI_EVENT_MASK, event_mask,
15827                       GDI_CALLBACK_ACTION, HandleGameButtons,
15828                       GDI_END);
15829
15830     if (gi == NULL)
15831       Error(ERR_EXIT, "cannot create gadget");
15832
15833     game_gadget[id] = gi;
15834   }
15835 }
15836
15837 void FreeGameButtons()
15838 {
15839   int i;
15840
15841   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15842     FreeGadget(game_gadget[i]);
15843 }
15844
15845 static void MapGameButtons()
15846 {
15847   int i;
15848
15849   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15850     MapGadget(game_gadget[i]);
15851 }
15852
15853 void UnmapGameButtons()
15854 {
15855   int i;
15856
15857   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15858     UnmapGadget(game_gadget[i]);
15859 }
15860
15861 void RedrawGameButtons()
15862 {
15863   int i;
15864
15865   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15866     RedrawGadget(game_gadget[i]);
15867 }
15868
15869 static void HandleGameButtons(struct GadgetInfo *gi)
15870 {
15871   int id = gi->custom_id;
15872
15873   if (game_status != GAME_MODE_PLAYING)
15874     return;
15875
15876   switch (id)
15877   {
15878     case GAME_CTRL_ID_STOP:
15879       if (tape.playing)
15880         TapeStop();
15881       else
15882         RequestQuitGame(TRUE);
15883       break;
15884
15885     case GAME_CTRL_ID_PAUSE:
15886       if (options.network)
15887       {
15888 #if defined(NETWORK_AVALIABLE)
15889         if (tape.pausing)
15890           SendToServer_ContinuePlaying();
15891         else
15892           SendToServer_PausePlaying();
15893 #endif
15894       }
15895       else
15896         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15897       break;
15898
15899     case GAME_CTRL_ID_PLAY:
15900       if (tape.pausing)
15901       {
15902 #if defined(NETWORK_AVALIABLE)
15903         if (options.network)
15904           SendToServer_ContinuePlaying();
15905         else
15906 #endif
15907         {
15908           tape.pausing = FALSE;
15909           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15910         }
15911       }
15912       break;
15913
15914     case SOUND_CTRL_ID_MUSIC:
15915       if (setup.sound_music)
15916       { 
15917         setup.sound_music = FALSE;
15918         FadeMusic();
15919       }
15920       else if (audio.music_available)
15921       { 
15922         setup.sound = setup.sound_music = TRUE;
15923
15924         SetAudioMode(setup.sound);
15925
15926         PlayLevelMusic();
15927       }
15928       break;
15929
15930     case SOUND_CTRL_ID_LOOPS:
15931       if (setup.sound_loops)
15932         setup.sound_loops = FALSE;
15933       else if (audio.loops_available)
15934       {
15935         setup.sound = setup.sound_loops = TRUE;
15936         SetAudioMode(setup.sound);
15937       }
15938       break;
15939
15940     case SOUND_CTRL_ID_SIMPLE:
15941       if (setup.sound_simple)
15942         setup.sound_simple = FALSE;
15943       else if (audio.sound_available)
15944       {
15945         setup.sound = setup.sound_simple = TRUE;
15946         SetAudioMode(setup.sound);
15947       }
15948       break;
15949
15950     default:
15951       break;
15952   }
15953 }